aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md5
-rw-r--r--.github/ISSUE_TEMPLATE/critical_nightly_regression.md17
-rw-r--r--.github/workflows/ci.yaml2
-rw-r--r--Cargo.lock74
-rw-r--r--PRIVACY.md2
-rw-r--r--README.md4
-rw-r--r--crates/base_db/src/fixture.rs68
-rw-r--r--crates/base_db/src/input.rs1
-rw-r--r--crates/flycheck/Cargo.toml1
-rw-r--r--crates/flycheck/src/lib.rs68
-rw-r--r--crates/hir/Cargo.toml2
-rw-r--r--crates/hir/src/display.rs4
-rw-r--r--crates/hir/src/lib.rs121
-rw-r--r--crates/hir/src/semantics.rs11
-rw-r--r--crates/hir/src/source_analyzer.rs44
-rw-r--r--crates/hir_def/src/attr.rs196
-rw-r--r--crates/hir_def/src/body.rs26
-rw-r--r--crates/hir_def/src/body/lower.rs105
-rw-r--r--crates/hir_def/src/body/tests.rs12
-rw-r--r--crates/hir_def/src/body/tests/block.rs24
-rw-r--r--crates/hir_def/src/child_by_source.rs4
-rw-r--r--crates/hir_def/src/db.rs18
-rw-r--r--crates/hir_def/src/diagnostics.rs5
-rw-r--r--crates/hir_def/src/expr.rs19
-rw-r--r--crates/hir_def/src/find_path.rs667
-rw-r--r--crates/hir_def/src/generics.rs62
-rw-r--r--crates/hir_def/src/intern.rs27
-rw-r--r--crates/hir_def/src/item_scope.rs13
-rw-r--r--crates/hir_def/src/item_tree.rs78
-rw-r--r--crates/hir_def/src/item_tree/lower.rs11
-rw-r--r--crates/hir_def/src/lib.rs76
-rw-r--r--crates/hir_def/src/nameres.rs55
-rw-r--r--crates/hir_def/src/nameres/collector.rs80
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs59
-rw-r--r--crates/hir_def/src/nameres/tests/diagnostics.rs6
-rw-r--r--crates/hir_def/src/path.rs15
-rw-r--r--crates/hir_def/src/path/lower.rs19
-rw-r--r--crates/hir_def/src/resolver.rs3
-rw-r--r--crates/hir_def/src/test_db.rs51
-rw-r--r--crates/hir_def/src/type_ref.rs18
-rw-r--r--crates/hir_def/src/visibility.rs16
-rw-r--r--crates/hir_expand/src/builtin_derive.rs10
-rw-r--r--crates/hir_expand/src/builtin_macro.rs19
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_expand/src/eager.rs24
-rw-r--r--crates/hir_expand/src/lib.rs21
-rw-r--r--crates/hir_expand/src/name.rs1
-rw-r--r--crates/hir_ty/Cargo.toml8
-rw-r--r--crates/hir_ty/src/autoderef.rs68
-rw-r--r--crates/hir_ty/src/builder.rs30
-rw-r--r--crates/hir_ty/src/chalk_cast.rs73
-rw-r--r--crates/hir_ty/src/chalk_db.rs (renamed from crates/hir_ty/src/traits/chalk.rs)262
-rw-r--r--crates/hir_ty/src/chalk_ext.rs294
-rw-r--r--crates/hir_ty/src/db.rs87
-rw-r--r--crates/hir_ty/src/diagnostics.rs2
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs169
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs14
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs2
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs4
-rw-r--r--crates/hir_ty/src/display.rs275
-rw-r--r--crates/hir_ty/src/infer.rs165
-rw-r--r--crates/hir_ty/src/infer/coerce.rs29
-rw-r--r--crates/hir_ty/src/infer/expr.rs91
-rw-r--r--crates/hir_ty/src/infer/pat.rs59
-rw-r--r--crates/hir_ty/src/infer/path.rs18
-rw-r--r--crates/hir_ty/src/infer/unify.rs218
-rw-r--r--crates/hir_ty/src/interner.rs (renamed from crates/hir_ty/src/traits/chalk/interner.rs)170
-rw-r--r--crates/hir_ty/src/lib.rs1290
-rw-r--r--crates/hir_ty/src/lower.rs300
-rw-r--r--crates/hir_ty/src/mapping.rs154
-rw-r--r--crates/hir_ty/src/method_resolution.rs329
-rw-r--r--crates/hir_ty/src/op.rs66
-rw-r--r--crates/hir_ty/src/primitive.rs5
-rw-r--r--crates/hir_ty/src/tests/macros.rs201
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs57
-rw-r--r--crates/hir_ty/src/tests/patterns.rs28
-rw-r--r--crates/hir_ty/src/tests/regression.rs38
-rw-r--r--crates/hir_ty/src/tests/simple.rs51
-rw-r--r--crates/hir_ty/src/tests/traits.rs1467
-rw-r--r--crates/hir_ty/src/tls.rs (renamed from crates/hir_ty/src/traits/chalk/tls.rs)14
-rw-r--r--crates/hir_ty/src/traits.rs140
-rw-r--r--crates/hir_ty/src/traits/chalk/mapping.rs575
-rw-r--r--crates/hir_ty/src/utils.rs53
-rw-r--r--crates/hir_ty/src/walk.rs150
-rw-r--r--crates/ide/src/diagnostics.rs157
-rw-r--r--crates/ide/src/diagnostics/field_shorthand.rs8
-rw-r--r--crates/ide/src/diagnostics/fixes.rs60
-rw-r--r--crates/ide/src/diagnostics/unlinked_file.rs14
-rw-r--r--crates/ide/src/doc_links.rs190
-rw-r--r--crates/ide/src/expand_macro.rs67
-rw-r--r--crates/ide/src/folding_ranges.rs17
-rw-r--r--crates/ide/src/goto_definition.rs82
-rw-r--r--crates/ide/src/hover.rs42
-rw-r--r--crates/ide/src/lib.rs42
-rw-r--r--crates/ide/src/move_item.rs100
-rw-r--r--crates/ide/src/prime_caches.rs3
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs73
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs104
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs24
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html12
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html2
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs46
-rw-r--r--crates/ide/src/typing.rs368
-rw-r--r--crates/ide/src/typing/on_enter.rs230
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs33
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs276
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs73
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs370
-rw-r--r--crates/ide_assists/src/handlers/flip_comma.rs18
-rw-r--r--crates/ide_assists/src/handlers/generate_deref.rs227
-rw-r--r--crates/ide_assists/src/handlers/inline_local_variable.rs223
-rw-r--r--crates/ide_assists/src/handlers/introduce_named_lifetime.rs112
-rw-r--r--crates/ide_assists/src/handlers/remove_dbg.rs105
-rw-r--r--crates/ide_assists/src/handlers/reorder_fields.rs89
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs108
-rw-r--r--crates/ide_assists/src/lib.rs4
-rw-r--r--crates/ide_assists/src/tests.rs7
-rw-r--r--crates/ide_assists/src/tests/generated.rs27
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs116
-rw-r--r--crates/ide_completion/src/item.rs2
-rw-r--r--crates/ide_completion/src/lib.rs28
-rw-r--r--crates/ide_db/src/apply_change.rs6
-rw-r--r--crates/ide_db/src/call_info.rs10
-rw-r--r--crates/ide_db/src/call_info/tests.rs27
-rw-r--r--crates/ide_db/src/helpers.rs5
-rw-r--r--crates/ide_db/src/helpers/famous_defs_fixture.rs8
-rw-r--r--crates/ide_db/src/helpers/import_assets.rs22
-rw-r--r--crates/ide_db/src/helpers/rust_doc.rs34
-rw-r--r--crates/mbe/src/syntax_bridge.rs2
-rw-r--r--crates/mbe/src/tests/expand.rs42
-rw-r--r--crates/parser/src/grammar.rs37
-rw-r--r--crates/parser/src/grammar/attributes.rs28
-rw-r--r--crates/parser/src/grammar/patterns.rs6
-rw-r--r--crates/parser/src/grammar/types.rs10
-rw-r--r--crates/paths/src/lib.rs7
-rw-r--r--crates/proc_macro_api/Cargo.toml7
-rw-r--r--crates/proc_macro_api/src/lib.rs1
-rw-r--r--crates/project_model/src/build_data.rs294
-rw-r--r--crates/project_model/src/workspace.rs10
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/build.rs3
-rw-r--r--crates/rust-analyzer/src/benchmarks.rs7
-rw-r--r--crates/rust-analyzer/src/bin/flags.rs3
-rw-r--r--crates/rust-analyzer/src/bin/main.rs16
-rw-r--r--crates/rust-analyzer/src/bin/rustc_wrapper.rs46
-rw-r--r--crates/rust-analyzer/src/caps.rs2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs63
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs6
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs10
-rw-r--r--crates/rust-analyzer/src/cli/ssr.rs9
-rw-r--r--crates/rust-analyzer/src/config.rs43
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs1
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt1
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt1
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt1
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt1
-rw-r--r--crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt1
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs57
-rw-r--r--crates/rust-analyzer/src/from_proto.rs30
-rw-r--r--crates/rust-analyzer/src/global_state.rs63
-rw-r--r--crates/rust-analyzer/src/handlers.rs130
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs38
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs24
-rw-r--r--crates/rust-analyzer/src/main_loop.rs110
-rw-r--r--crates/rust-analyzer/src/markdown.rs22
-rw-r--r--crates/rust-analyzer/src/op_queue.rs38
-rw-r--r--crates/rust-analyzer/src/reload.rs215
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs5
-rw-r--r--crates/rust-analyzer/src/to_proto.rs185
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/main.rs2
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs32
-rw-r--r--crates/stdx/Cargo.toml5
-rw-r--r--crates/stdx/src/lib.rs31
-rw-r--r--crates/stdx/src/process.rs238
-rw-r--r--crates/syntax/Cargo.toml6
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs153
-rw-r--r--crates/syntax/src/ast/make.rs26
-rw-r--r--crates/syntax/src/ast/node_ext.rs12
-rw-r--r--crates/syntax/src/ted.rs7
-rw-r--r--crates/syntax/src/validation/block.rs2
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast48
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast58
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs1
-rw-r--r--crates/syntax/test_data/parser/ok/0045_block_attrs.rast218
-rw-r--r--crates/syntax/test_data/parser/ok/0045_block_attrs.rs (renamed from crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rs)6
-rw-r--r--crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rast178
-rw-r--r--crates/test_utils/src/assert_linear.rs112
-rw-r--r--crates/test_utils/src/bench_fixture.rs3
-rw-r--r--crates/test_utils/src/fixture.rs62
-rw-r--r--crates/test_utils/src/lib.rs3
-rw-r--r--crates/vfs/Cargo.toml1
-rw-r--r--crates/vfs/src/path_interner.rs29
-rw-r--r--docs/dev/README.md37
-rw-r--r--docs/dev/architecture.md13
-rw-r--r--docs/dev/lsp-extensions.md57
-rw-r--r--docs/dev/style.md52
-rw-r--r--docs/dev/syntax.md8
-rw-r--r--docs/user/generated_config.adoc12
-rw-r--r--docs/user/manual.adoc76
-rw-r--r--editors/code/package.json12
-rw-r--r--editors/code/src/client.ts2
-rw-r--r--editors/code/src/commands.ts26
-rw-r--r--editors/code/src/ctx.ts47
-rw-r--r--editors/code/src/lsp_ext.ts11
-rw-r--r--lib/arena/src/lib.rs2
-rw-r--r--xtask/src/release.rs39
-rw-r--r--xtask/src/release/changelog.rs159
-rw-r--r--xtask/src/tidy.rs2
208 files changed, 9902 insertions, 6156 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index b5160eaa3..060a05d41 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -14,8 +14,9 @@ Forum for questions: https://users.rust-lang.org/c/ide/14
14Before submitting, please make sure that you're not running into one of these known issues: 14Before submitting, please make sure that you're not running into one of these known issues:
15 15
16 1. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file) 16 1. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file)
17 2. some settings are required for procedural macro and build script support (`rust-analyzer.cargo.loadOutDirsFromCheck`, `rust-analyzer.procMacro.enable`): #6448 17 2. some platform-specific imports are not resolved: #6038
18 3. some platform-specific imports are not resolved: #6038 18 3. attribute proc macros are not supported: #6029
19 4. the version string is misleading (includes the previous week): #8571
19 20
20Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3. 21Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
21--> 22-->
diff --git a/.github/ISSUE_TEMPLATE/critical_nightly_regression.md b/.github/ISSUE_TEMPLATE/critical_nightly_regression.md
new file mode 100644
index 000000000..a0b1627d7
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/critical_nightly_regression.md
@@ -0,0 +1,17 @@
1---
2name: Critical Nightly Regression
3about: You are using nightly rust-analyzer and the latest version is unusable.
4title: ''
5labels: ''
6assignees: 'matklad'
7
8---
9
10<!--
11Troubleshooting guide: https://rust-analyzer.github.io/manual.html#troubleshooting
12
13Please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
14-->
15
16This is a serious regression in nightly and it's important to fix it before the next release.
17@matklad, please take a look.
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 1850068a3..0f68b234c 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -76,7 +76,7 @@ jobs:
76 run: cargo test --no-run --locked 76 run: cargo test --no-run --locked
77 77
78 - name: Test 78 - name: Test
79 run: cargo test 79 run: cargo test -- --nocapture
80 80
81 - name: Prepare cache 81 - name: Prepare cache
82 run: cargo xtask pre-cache 82 run: cargo xtask pre-cache
diff --git a/Cargo.lock b/Cargo.lock
index 05383d8b7..907973412 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -49,9 +49,9 @@ checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344"
49 49
50[[package]] 50[[package]]
51name = "arrayvec" 51name = "arrayvec"
52version = "0.6.1" 52version = "0.7.0"
53source = "registry+https://github.com/rust-lang/crates.io-index" 53source = "registry+https://github.com/rust-lang/crates.io-index"
54checksum = "269d0f5e68353a7cab87f81e7c736adc008d279a36ebc6a05dfe01193a89f0c9" 54checksum = "5a2f58b0bb10c380af2b26e57212856b8c9a59e0925b4c20f4a174a49734eaf7"
55 55
56[[package]] 56[[package]]
57name = "atty" 57name = "atty"
@@ -72,9 +72,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
72 72
73[[package]] 73[[package]]
74name = "backtrace" 74name = "backtrace"
75version = "0.3.56" 75version = "0.3.57"
76source = "registry+https://github.com/rust-lang/crates.io-index" 76source = "registry+https://github.com/rust-lang/crates.io-index"
77checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" 77checksum = "78ed203b9ba68b242c62b3fb7480f589dd49829be1edb3fe8fc8b4ffda2dcb8d"
78dependencies = [ 78dependencies = [
79 "addr2line", 79 "addr2line",
80 "cfg-if", 80 "cfg-if",
@@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
168 168
169[[package]] 169[[package]]
170name = "chalk-derive" 170name = "chalk-derive"
171version = "0.60.0" 171version = "0.64.0"
172source = "registry+https://github.com/rust-lang/crates.io-index" 172source = "registry+https://github.com/rust-lang/crates.io-index"
173checksum = "ab0f74445d4fbeaf0217bc1d23978cc73b95b28e8a738b81894580dd646822d2" 173checksum = "d9acf2a9eab79ae7d44cd77ad86a8b1569d7a5e6d9a7db4a0a57a7344dd82c24"
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.60.0" 183version = "0.64.0"
184source = "registry+https://github.com/rust-lang/crates.io-index" 184source = "registry+https://github.com/rust-lang/crates.io-index"
185checksum = "294b1fc6210a5b3bd06c1d01dda48a581e2cafec80b8d659139ce45456644be2" 185checksum = "877661627f54ba3666a72943c43b326cb170d60899e50a8426111e7a657ff032"
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.60.0" 194version = "0.64.0"
195source = "registry+https://github.com/rust-lang/crates.io-index" 195source = "registry+https://github.com/rust-lang/crates.io-index"
196checksum = "1b9386936070be4545bfa22b094b7065af79aa2aeaccc945438f1c5ffe74c30a" 196checksum = "072ffcf17243c2aa3e4b9ea6de3d29e7ef64cfdb0ceccaa431965070a1dc1475"
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.60.0" 207version = "0.64.0"
208source = "registry+https://github.com/rust-lang/crates.io-index" 208source = "registry+https://github.com/rust-lang/crates.io-index"
209checksum = "7c12a1ec7e850b50a049f27ef9cf5df3056bbd1acbb3eeb44d024e501a641f3a" 209checksum = "97d4920c9ef2b26dd0b98ffdf070e27fa31e0b6f637463132083cee597e3d326"
210dependencies = [ 210dependencies = [
211 "chalk-derive", 211 "chalk-derive",
212 "chalk-ir", 212 "chalk-ir",
@@ -259,9 +259,9 @@ dependencies = [
259 259
260[[package]] 260[[package]]
261name = "crossbeam-channel" 261name = "crossbeam-channel"
262version = "0.5.0" 262version = "0.5.1"
263source = "registry+https://github.com/rust-lang/crates.io-index" 263source = "registry+https://github.com/rust-lang/crates.io-index"
264checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" 264checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
265dependencies = [ 265dependencies = [
266 "cfg-if", 266 "cfg-if",
267 "crossbeam-utils", 267 "crossbeam-utils",
@@ -396,6 +396,7 @@ dependencies = [
396 "crossbeam-channel", 396 "crossbeam-channel",
397 "jod-thread", 397 "jod-thread",
398 "log", 398 "log",
399 "serde",
399 "serde_json", 400 "serde_json",
400 "stdx", 401 "stdx",
401 "toolchain", 402 "toolchain",
@@ -684,9 +685,9 @@ dependencies = [
684 685
685[[package]] 686[[package]]
686name = "idna" 687name = "idna"
687version = "0.2.2" 688version = "0.2.3"
688source = "registry+https://github.com/rust-lang/crates.io-index" 689source = "registry+https://github.com/rust-lang/crates.io-index"
689checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21" 690checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
690dependencies = [ 691dependencies = [
691 "matches", 692 "matches",
692 "unicode-bidi", 693 "unicode-bidi",
@@ -797,9 +798,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
797 798
798[[package]] 799[[package]]
799name = "libc" 800name = "libc"
800version = "0.2.92" 801version = "0.2.93"
801source = "registry+https://github.com/rust-lang/crates.io-index" 802source = "registry+https://github.com/rust-lang/crates.io-index"
802checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714" 803checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
803 804
804[[package]] 805[[package]]
805name = "libloading" 806name = "libloading"
@@ -852,9 +853,9 @@ dependencies = [
852 853
853[[package]] 854[[package]]
854name = "lsp-types" 855name = "lsp-types"
855version = "0.88.0" 856version = "0.89.0"
856source = "registry+https://github.com/rust-lang/crates.io-index" 857source = "registry+https://github.com/rust-lang/crates.io-index"
857checksum = "d8e8e042772e4e10b3785822f63c82399d0dd233825de44d2596f7fa86e023e0" 858checksum = "07731ecd4ee0654728359a5b95e2a254c857876c04b85225496a35d60345daa7"
858dependencies = [ 859dependencies = [
859 "bitflags", 860 "bitflags",
860 "serde", 861 "serde",
@@ -912,9 +913,9 @@ dependencies = [
912 913
913[[package]] 914[[package]]
914name = "memmap2" 915name = "memmap2"
915version = "0.2.1" 916version = "0.2.2"
916source = "registry+https://github.com/rust-lang/crates.io-index" 917source = "registry+https://github.com/rust-lang/crates.io-index"
917checksum = "04e3e85b970d650e2ae6d70592474087051c11c54da7f7b4949725c5735fbcc6" 918checksum = "397d1a6d6d0563c0f5462bbdae662cf6c784edf5e828e40c7257f85d82bf56dd"
918dependencies = [ 919dependencies = [
919 "libc", 920 "libc",
920] 921]
@@ -971,9 +972,9 @@ dependencies = [
971 972
972[[package]] 973[[package]]
973name = "notify" 974name = "notify"
974version = "5.0.0-pre.6" 975version = "5.0.0-pre.7"
975source = "registry+https://github.com/rust-lang/crates.io-index" 976source = "registry+https://github.com/rust-lang/crates.io-index"
976checksum = "e5fd82b93434edb9c00ae65ee741e0e081cdc8c63346ab9f687935a629aaf4c3" 977checksum = "1ebe7699a0f8c5759450716ee03d231685c22b4fe8f406c42c22e0ad94d40ce7"
977dependencies = [ 978dependencies = [
978 "anymap", 979 "anymap",
979 "bitflags", 980 "bitflags",
@@ -1174,6 +1175,7 @@ dependencies = [
1174 "log", 1175 "log",
1175 "memmap", 1176 "memmap",
1176 "object", 1177 "object",
1178 "profile",
1177 "serde", 1179 "serde",
1178 "serde_json", 1180 "serde_json",
1179 "snap", 1181 "snap",
@@ -1292,9 +1294,9 @@ dependencies = [
1292 1294
1293[[package]] 1295[[package]]
1294name = "redox_syscall" 1296name = "redox_syscall"
1295version = "0.2.5" 1297version = "0.2.6"
1296source = "registry+https://github.com/rust-lang/crates.io-index" 1298source = "registry+https://github.com/rust-lang/crates.io-index"
1297checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" 1299checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
1298dependencies = [ 1300dependencies = [
1299 "bitflags", 1301 "bitflags",
1300] 1302]
@@ -1391,9 +1393,9 @@ dependencies = [
1391 1393
1392[[package]] 1394[[package]]
1393name = "rustc-ap-rustc_lexer" 1395name = "rustc-ap-rustc_lexer"
1394version = "710.0.0" 1396version = "716.0.0"
1395source = "registry+https://github.com/rust-lang/crates.io-index" 1397source = "registry+https://github.com/rust-lang/crates.io-index"
1396checksum = "b0bba1ca6787b6d4af505b7a940eae9ecb084dd03e07f03bf3ddbf78e738b617" 1398checksum = "12eac7554c1d3f49f105f14d53c0f3402220e875983113562701d8e597a0995c"
1397dependencies = [ 1399dependencies = [
1398 "unicode-xid", 1400 "unicode-xid",
1399] 1401]
@@ -1573,13 +1575,16 @@ version = "0.0.0"
1573dependencies = [ 1575dependencies = [
1574 "always-assert", 1576 "always-assert",
1575 "backtrace", 1577 "backtrace",
1578 "libc",
1579 "miow",
1580 "winapi",
1576] 1581]
1577 1582
1578[[package]] 1583[[package]]
1579name = "syn" 1584name = "syn"
1580version = "1.0.68" 1585version = "1.0.69"
1581source = "registry+https://github.com/rust-lang/crates.io-index" 1586source = "registry+https://github.com/rust-lang/crates.io-index"
1582checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87" 1587checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
1583dependencies = [ 1588dependencies = [
1584 "proc-macro2", 1589 "proc-macro2",
1585 "quote", 1590 "quote",
@@ -1675,9 +1680,9 @@ dependencies = [
1675 1680
1676[[package]] 1681[[package]]
1677name = "tinyvec" 1682name = "tinyvec"
1678version = "1.1.1" 1683version = "1.2.0"
1679source = "registry+https://github.com/rust-lang/crates.io-index" 1684source = "registry+https://github.com/rust-lang/crates.io-index"
1680checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" 1685checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
1681dependencies = [ 1686dependencies = [
1682 "tinyvec_macros", 1687 "tinyvec_macros",
1683] 1688]
@@ -1815,9 +1820,9 @@ dependencies = [
1815 1820
1816[[package]] 1821[[package]]
1817name = "unicode-bidi" 1822name = "unicode-bidi"
1818version = "0.3.4" 1823version = "0.3.5"
1819source = "registry+https://github.com/rust-lang/crates.io-index" 1824source = "registry+https://github.com/rust-lang/crates.io-index"
1820checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1825checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0"
1821dependencies = [ 1826dependencies = [
1822 "matches", 1827 "matches",
1823] 1828]
@@ -1867,6 +1872,7 @@ name = "vfs"
1867version = "0.0.0" 1872version = "0.0.0"
1868dependencies = [ 1873dependencies = [
1869 "fst", 1874 "fst",
1875 "indexmap",
1870 "paths", 1876 "paths",
1871 "rustc-hash", 1877 "rustc-hash",
1872] 1878]
diff --git a/PRIVACY.md b/PRIVACY.md
index dd165c0e2..27e39ca60 100644
--- a/PRIVACY.md
+++ b/PRIVACY.md
@@ -14,4 +14,4 @@ Any other editor plugins that integrate with `rust-analyzer` are not under the c
14 14
15## Others 15## Others
16 16
17If `cargo check` is enabled (the default), any build scripts or procedural macros used by the project or its dependencies will be executed. This is also the case when `cargo check` is disabled, but build script or procedural macro support is enabled in `rust-analyzer` (off by default). 17If `cargo check` is enabled (the default), any build scripts or procedural macros used by the project or its dependencies will be executed. This is also the case when `cargo check` is disabled, but build script or procedural macro support is enabled in `rust-analyzer` (on by default).
diff --git a/README.md b/README.md
index 085c8c0f6..5fbc03964 100644
--- a/README.md
+++ b/README.md
@@ -33,9 +33,9 @@ For usage and troubleshooting requests, please use "IDEs and Editors" category o
33 33
34https://users.rust-lang.org/c/ide/14 34https://users.rust-lang.org/c/ide/14
35 35
36For questions about development and implementation, join rls-2.0 working group on Zulip: 36For questions about development and implementation, join rust-analyzer working group on Zulip:
37 37
38https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frls-2.2E0 38https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
39 39
40## Quick Links 40## Quick Links
41 41
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 8d4641355..0132565e4 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -1,62 +1,4 @@
1//! Fixtures are strings containing rust source code with optional metadata. 1//! A set of high-level utility fixture methods to use in tests.
2//! A fixture without metadata is parsed into a single source file.
3//! Use this to test functionality local to one file.
4//!
5//! Simple Example:
6//! ```
7//! r#"
8//! fn main() {
9//! println!("Hello World")
10//! }
11//! "#
12//! ```
13//!
14//! Metadata can be added to a fixture after a `//-` comment.
15//! The basic form is specifying filenames,
16//! which is also how to define multiple files in a single test fixture
17//!
18//! Example using two files in the same crate:
19//! ```
20//! "
21//! //- /main.rs
22//! mod foo;
23//! fn main() {
24//! foo::bar();
25//! }
26//!
27//! //- /foo.rs
28//! pub fn bar() {}
29//! "
30//! ```
31//!
32//! Example using two crates with one file each, with one crate depending on the other:
33//! ```
34//! r#"
35//! //- /main.rs crate:a deps:b
36//! fn main() {
37//! b::foo();
38//! }
39//! //- /lib.rs crate:b
40//! pub fn b() {
41//! println!("Hello World")
42//! }
43//! "#
44//! ```
45//!
46//! Metadata allows specifying all settings and variables
47//! that are available in a real rust project:
48//! - crate names via `crate:cratename`
49//! - dependencies via `deps:dep1,dep2`
50//! - configuration settings via `cfg:dbg=false,opt_level=2`
51//! - environment variables via `env:PATH=/bin,RUST_LOG=debug`
52//!
53//! Example using all available metadata:
54//! ```
55//! "
56//! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
57//! fn insert_source_code_here() {}
58//! "
59//! ```
60use std::{mem, str::FromStr, sync::Arc}; 2use std::{mem, str::FromStr, sync::Arc};
61 3
62use cfg::CfgOptions; 4use cfg::CfgOptions;
@@ -93,7 +35,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
93 fn with_position(ra_fixture: &str) -> (Self, FilePosition) { 35 fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
94 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); 36 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
95 let offset = match range_or_offset { 37 let offset = match range_or_offset {
96 RangeOrOffset::Range(_) => panic!(), 38 RangeOrOffset::Range(_) => panic!("Expected a cursor position, got a range instead"),
97 RangeOrOffset::Offset(it) => it, 39 RangeOrOffset::Offset(it) => it,
98 }; 40 };
99 (db, FilePosition { file_id, offset }) 41 (db, FilePosition { file_id, offset })
@@ -103,7 +45,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
103 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); 45 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
104 let range = match range_or_offset { 46 let range = match range_or_offset {
105 RangeOrOffset::Range(it) => it, 47 RangeOrOffset::Range(it) => it,
106 RangeOrOffset::Offset(_) => panic!(), 48 RangeOrOffset::Offset(_) => panic!("Expected a cursor range, got a position instead"),
107 }; 49 };
108 (db, FileRange { file_id, range }) 50 (db, FileRange { file_id, range })
109 } 51 }
@@ -112,7 +54,9 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
112 let fixture = ChangeFixture::parse(ra_fixture); 54 let fixture = ChangeFixture::parse(ra_fixture);
113 let mut db = Self::default(); 55 let mut db = Self::default();
114 fixture.change.apply(&mut db); 56 fixture.change.apply(&mut db);
115 let (file_id, range_or_offset) = fixture.file_position.unwrap(); 57 let (file_id, range_or_offset) = fixture
58 .file_position
59 .expect("Could not find file position in fixture. Did you forget to add an `$0`?");
116 (db, file_id, range_or_offset) 60 (db, file_id, range_or_offset)
117 } 61 }
118 62
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 07628935f..0ef77ef5d 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -239,6 +239,7 @@ impl CrateGraph {
239 name: CrateName, 239 name: CrateName,
240 to: CrateId, 240 to: CrateId,
241 ) -> Result<(), CyclicDependenciesError> { 241 ) -> Result<(), CyclicDependenciesError> {
242 let _p = profile::span("add_dep");
242 if self.dfs_find(from, to, &mut FxHashSet::default()) { 243 if self.dfs_find(from, to, &mut FxHashSet::default()) {
243 return Err(CyclicDependenciesError { 244 return Err(CyclicDependenciesError {
244 from: (from, self[from].display_name.clone()), 245 from: (from, self[from].display_name.clone()),
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml
index 2a1a21b28..18b9ce7df 100644
--- a/crates/flycheck/Cargo.toml
+++ b/crates/flycheck/Cargo.toml
@@ -13,6 +13,7 @@ doctest = false
13crossbeam-channel = "0.5.0" 13crossbeam-channel = "0.5.0"
14log = "0.4.8" 14log = "0.4.8"
15cargo_metadata = "0.13" 15cargo_metadata = "0.13"
16serde = { version = "1.0.106", features = ["derive"] }
16serde_json = "1.0.48" 17serde_json = "1.0.48"
17jod-thread = "0.1.1" 18jod-thread = "0.1.1"
18 19
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index e2a59497a..1682d8bde 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -4,13 +4,14 @@
4 4
5use std::{ 5use std::{
6 fmt, 6 fmt,
7 io::{self, BufReader}, 7 io::{self, BufRead, BufReader},
8 path::PathBuf, 8 path::PathBuf,
9 process::{self, Command, Stdio}, 9 process::{self, Command, Stdio},
10 time::Duration, 10 time::Duration,
11}; 11};
12 12
13use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; 13use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
14use serde::Deserialize;
14use stdx::JodChild; 15use stdx::JodChild;
15 16
16pub use cargo_metadata::diagnostic::{ 17pub use cargo_metadata::diagnostic::{
@@ -128,7 +129,7 @@ struct FlycheckActor {
128 129
129enum Event { 130enum Event {
130 Restart(Restart), 131 Restart(Restart),
131 CheckEvent(Option<cargo_metadata::Message>), 132 CheckEvent(Option<CargoMessage>),
132} 133}
133 134
134impl FlycheckActor { 135impl FlycheckActor {
@@ -180,21 +181,16 @@ impl FlycheckActor {
180 self.progress(Progress::DidFinish(res)); 181 self.progress(Progress::DidFinish(res));
181 } 182 }
182 Event::CheckEvent(Some(message)) => match message { 183 Event::CheckEvent(Some(message)) => match message {
183 cargo_metadata::Message::CompilerArtifact(msg) => { 184 CargoMessage::CompilerArtifact(msg) => {
184 self.progress(Progress::DidCheckCrate(msg.target.name)); 185 self.progress(Progress::DidCheckCrate(msg.target.name));
185 } 186 }
186 187
187 cargo_metadata::Message::CompilerMessage(msg) => { 188 CargoMessage::Diagnostic(msg) => {
188 self.send(Message::AddDiagnostic { 189 self.send(Message::AddDiagnostic {
189 workspace_root: self.workspace_root.clone(), 190 workspace_root: self.workspace_root.clone(),
190 diagnostic: msg.message, 191 diagnostic: msg,
191 }); 192 });
192 } 193 }
193
194 cargo_metadata::Message::BuildScriptExecuted(_)
195 | cargo_metadata::Message::BuildFinished(_)
196 | cargo_metadata::Message::TextLine(_)
197 | _ => {}
198 }, 194 },
199 } 195 }
200 } 196 }
@@ -261,7 +257,7 @@ struct CargoHandle {
261 child: JodChild, 257 child: JodChild,
262 #[allow(unused)] 258 #[allow(unused)]
263 thread: jod_thread::JoinHandle<io::Result<bool>>, 259 thread: jod_thread::JoinHandle<io::Result<bool>>,
264 receiver: Receiver<cargo_metadata::Message>, 260 receiver: Receiver<CargoMessage>,
265} 261}
266 262
267impl CargoHandle { 263impl CargoHandle {
@@ -294,14 +290,11 @@ impl CargoHandle {
294 290
295struct CargoActor { 291struct CargoActor {
296 child_stdout: process::ChildStdout, 292 child_stdout: process::ChildStdout,
297 sender: Sender<cargo_metadata::Message>, 293 sender: Sender<CargoMessage>,
298} 294}
299 295
300impl CargoActor { 296impl CargoActor {
301 fn new( 297 fn new(child_stdout: process::ChildStdout, sender: Sender<CargoMessage>) -> CargoActor {
302 child_stdout: process::ChildStdout,
303 sender: Sender<cargo_metadata::Message>,
304 ) -> CargoActor {
305 CargoActor { child_stdout, sender } 298 CargoActor { child_stdout, sender }
306 } 299 }
307 fn run(self) -> io::Result<bool> { 300 fn run(self) -> io::Result<bool> {
@@ -315,7 +308,7 @@ impl CargoActor {
315 // erroneus output. 308 // erroneus output.
316 let stdout = BufReader::new(self.child_stdout); 309 let stdout = BufReader::new(self.child_stdout);
317 let mut read_at_least_one_message = false; 310 let mut read_at_least_one_message = false;
318 for message in cargo_metadata::Message::parse_stream(stdout) { 311 for message in stdout.lines() {
319 let message = match message { 312 let message = match message {
320 Ok(message) => message, 313 Ok(message) => message,
321 Err(err) => { 314 Err(err) => {
@@ -326,13 +319,44 @@ impl CargoActor {
326 319
327 read_at_least_one_message = true; 320 read_at_least_one_message = true;
328 321
329 // Skip certain kinds of messages to only spend time on what's useful 322 // Try to deserialize a message from Cargo or Rustc.
330 match &message { 323 let mut deserializer = serde_json::Deserializer::from_str(&message);
331 cargo_metadata::Message::CompilerArtifact(artifact) if artifact.fresh => (), 324 deserializer.disable_recursion_limit();
332 cargo_metadata::Message::BuildScriptExecuted(_) => (), 325 if let Ok(message) = JsonMessage::deserialize(&mut deserializer) {
333 _ => self.sender.send(message).unwrap(), 326 match message {
327 // Skip certain kinds of messages to only spend time on what's useful
328 JsonMessage::Cargo(message) => match message {
329 cargo_metadata::Message::CompilerArtifact(artifact) if !artifact.fresh => {
330 self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap()
331 }
332 cargo_metadata::Message::CompilerMessage(msg) => {
333 self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap()
334 }
335
336 cargo_metadata::Message::CompilerArtifact(_)
337 | cargo_metadata::Message::BuildScriptExecuted(_)
338 | cargo_metadata::Message::BuildFinished(_)
339 | cargo_metadata::Message::TextLine(_)
340 | _ => (),
341 },
342 JsonMessage::Rustc(message) => {
343 self.sender.send(CargoMessage::Diagnostic(message)).unwrap()
344 }
345 }
334 } 346 }
335 } 347 }
336 Ok(read_at_least_one_message) 348 Ok(read_at_least_one_message)
337 } 349 }
338} 350}
351
352enum CargoMessage {
353 CompilerArtifact(cargo_metadata::Artifact),
354 Diagnostic(Diagnostic),
355}
356
357#[derive(Deserialize)]
358#[serde(untagged)]
359enum JsonMessage {
360 Cargo(cargo_metadata::Message),
361 Rustc(Diagnostic),
362}
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index 2ef5bcbc9..9e329656f 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13log = "0.4.8" 13log = "0.4.8"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15either = "1.5.3" 15either = "1.5.3"
16arrayvec = "0.6" 16arrayvec = "0.7"
17itertools = "0.10.0" 17itertools = "0.10.0"
18smallvec = "1.4.0" 18smallvec = "1.4.0"
19 19
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 993772aac..01a4d205f 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -9,6 +9,7 @@ use hir_ty::display::{
9 write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, 9 write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
10 HirFormatter, 10 HirFormatter,
11}; 11};
12use hir_ty::Interner;
12use syntax::ast::{self, NameOwner}; 13use syntax::ast::{self, NameOwner};
13 14
14use crate::{ 15use crate::{
@@ -235,7 +236,8 @@ impl HirDisplay for TypeParam {
235 write!(f, "{}", self.name(f.db))?; 236 write!(f, "{}", self.name(f.db))?;
236 let bounds = f.db.generic_predicates_for_param(self.id); 237 let bounds = f.db.generic_predicates_for_param(self.id);
237 let substs = TyBuilder::type_params_subst(f.db, self.id.parent); 238 let substs = TyBuilder::type_params_subst(f.db, self.id.parent);
238 let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>(); 239 let predicates =
240 bounds.iter().cloned().map(|b| b.substitute(&Interner, &substs)).collect::<Vec<_>>();
239 if !(predicates.is_empty() || f.omit_verbose_types()) { 241 if !(predicates.is_empty() || f.omit_verbose_types()) {
240 write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?; 242 write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?;
241 } 243 }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 19901ed33..0acfa582a 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -53,12 +53,14 @@ use hir_def::{
53use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; 53use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind};
54use hir_ty::{ 54use hir_ty::{
55 autoderef, could_unify, 55 autoderef, could_unify,
56 method_resolution::{self, TyFingerprint}, 56 method_resolution::{self, def_crates, TyFingerprint},
57 primitive::UintTy, 57 primitive::UintTy,
58 traits::{FnTrait, Solution, SolutionVariables}, 58 subst_prefix,
59 traits::FnTrait,
59 AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, 60 AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast,
60 DebruijnIndex, InEnvironment, Interner, QuantifiedWhereClause, Scalar, Substitution, 61 DebruijnIndex, InEnvironment, Interner, QuantifiedWhereClause, Scalar, Solution, Substitution,
61 TraitEnvironment, Ty, TyBuilder, TyDefId, TyKind, TyVariableKind, WhereClause, 62 TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, TyVariableKind,
63 WhereClause,
62}; 64};
63use itertools::Itertools; 65use itertools::Itertools;
64use rustc_hash::FxHashSet; 66use rustc_hash::FxHashSet;
@@ -515,7 +517,7 @@ impl Field {
515 VariantDef::Variant(it) => it.parent.id.into(), 517 VariantDef::Variant(it) => it.parent.id.into(),
516 }; 518 };
517 let substs = TyBuilder::type_params_subst(db, generic_def_id); 519 let substs = TyBuilder::type_params_subst(db, generic_def_id);
518 let ty = db.field_types(var_id)[self.id].clone().subst(&substs); 520 let ty = db.field_types(var_id)[self.id].clone().substitute(&Interner, &substs);
519 Type::new(db, self.parent.module(db).id.krate(), var_id, ty) 521 Type::new(db, self.parent.module(db).id.krate(), var_id, ty)
520 } 522 }
521 523
@@ -701,7 +703,7 @@ impl_from!(Struct, Union, Enum for Adt);
701impl Adt { 703impl Adt {
702 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { 704 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
703 let subst = db.generic_defaults(self.into()); 705 let subst = db.generic_defaults(self.into());
704 subst.iter().any(|ty| ty.value.is_unknown()) 706 subst.iter().any(|ty| ty.skip_binders().is_unknown())
705 } 707 }
706 708
707 /// Turns this ADT into a type. Any type parameters of the ADT will be 709 /// Turns this ADT into a type. Any type parameters of the ADT will be
@@ -1088,7 +1090,7 @@ pub struct TypeAlias {
1088impl TypeAlias { 1090impl TypeAlias {
1089 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { 1091 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
1090 let subst = db.generic_defaults(self.id.into()); 1092 let subst = db.generic_defaults(self.id.into());
1091 subst.iter().any(|ty| ty.value.is_unknown()) 1093 subst.iter().any(|ty| ty.skip_binders().is_unknown())
1092 } 1094 }
1093 1095
1094 pub fn module(self, db: &dyn HirDatabase) -> Module { 1096 pub fn module(self, db: &dyn HirDatabase) -> Module {
@@ -1502,7 +1504,7 @@ impl TypeParam {
1502 let krate = self.id.parent.module(db.upcast()).krate(); 1504 let krate = self.id.parent.module(db.upcast()).krate();
1503 let ty = params.get(local_idx)?.clone(); 1505 let ty = params.get(local_idx)?.clone();
1504 let subst = TyBuilder::type_params_subst(db, self.id.parent); 1506 let subst = TyBuilder::type_params_subst(db, self.id.parent);
1505 let ty = ty.subst(&subst.prefix(local_idx)); 1507 let ty = ty.substitute(&Interner, &subst_prefix(&subst, local_idx));
1506 Some(Type::new_with_resolver_inner(db, krate, &resolver, ty)) 1508 Some(Type::new_with_resolver_inner(db, krate, &resolver, ty))
1507 } 1509 }
1508} 1510}
@@ -1567,7 +1569,7 @@ impl Impl {
1567 } 1569 }
1568 1570
1569 pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> { 1571 pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty, .. }: Type) -> Vec<Impl> {
1570 let def_crates = match ty.def_crates(db, krate) { 1572 let def_crates = match def_crates(db, &ty, krate) {
1571 Some(def_crates) => def_crates, 1573 Some(def_crates) => def_crates,
1572 None => return Vec::new(), 1574 None => return Vec::new(),
1573 }; 1575 };
@@ -1578,11 +1580,24 @@ impl Impl {
1578 ty.equals_ctor(rref.as_ref().map_or(&self_ty.ty, |it| &it.ty)) 1580 ty.equals_ctor(rref.as_ref().map_or(&self_ty.ty, |it| &it.ty))
1579 }; 1581 };
1580 1582
1583 let fp = TyFingerprint::for_inherent_impl(&ty);
1584 let fp = if let Some(fp) = fp {
1585 fp
1586 } else {
1587 return Vec::new();
1588 };
1589
1581 let mut all = Vec::new(); 1590 let mut all = Vec::new();
1582 def_crates.iter().for_each(|&id| { 1591 def_crates.iter().for_each(|&id| {
1583 all.extend(db.inherent_impls_in_crate(id).all_impls().map(Self::from).filter(filter)) 1592 all.extend(
1593 db.inherent_impls_in_crate(id)
1594 .for_self_ty(&ty)
1595 .into_iter()
1596 .cloned()
1597 .map(Self::from)
1598 .filter(filter),
1599 )
1584 }); 1600 });
1585 let fp = TyFingerprint::for_impl(&ty);
1586 for id in def_crates 1601 for id in def_crates
1587 .iter() 1602 .iter()
1588 .flat_map(|&id| Crate { id }.transitive_reverse_dependencies(db)) 1603 .flat_map(|&id| Crate { id }.transitive_reverse_dependencies(db))
@@ -1590,13 +1605,12 @@ impl Impl {
1590 .chain(def_crates.iter().copied()) 1605 .chain(def_crates.iter().copied())
1591 .unique() 1606 .unique()
1592 { 1607 {
1593 match fp { 1608 all.extend(
1594 Some(fp) => all.extend( 1609 db.trait_impls_in_crate(id)
1595 db.trait_impls_in_crate(id).for_self_ty(fp).map(Self::from).filter(filter), 1610 .for_self_ty_without_blanket_impls(fp)
1596 ), 1611 .map(Self::from)
1597 None => all 1612 .filter(filter),
1598 .extend(db.trait_impls_in_crate(id).all_impls().map(Self::from).filter(filter)), 1613 );
1599 }
1600 } 1614 }
1601 all 1615 all
1602 } 1616 }
@@ -1789,7 +1803,7 @@ impl Type {
1789 .build(); 1803 .build();
1790 1804
1791 let goal = Canonical { 1805 let goal = Canonical {
1792 value: hir_ty::InEnvironment::new(self.env.env.clone(), trait_ref.cast(&Interner)), 1806 value: hir_ty::InEnvironment::new(&self.env.env, trait_ref.cast(&Interner)),
1793 binders: CanonicalVarKinds::empty(&Interner), 1807 binders: CanonicalVarKinds::empty(&Interner),
1794 }; 1808 };
1795 1809
@@ -1806,9 +1820,9 @@ impl Type {
1806 .push(self.ty.clone()) 1820 .push(self.ty.clone())
1807 .fill(args.iter().map(|t| t.ty.clone())) 1821 .fill(args.iter().map(|t| t.ty.clone()))
1808 .build(); 1822 .build();
1809 let goal = Canonical::new( 1823 let goal = hir_ty::make_canonical(
1810 InEnvironment::new( 1824 InEnvironment::new(
1811 self.env.env.clone(), 1825 &self.env.env,
1812 AliasEq { 1826 AliasEq {
1813 alias: AliasTy::Projection(projection), 1827 alias: AliasTy::Projection(projection),
1814 ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) 1828 ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
@@ -1820,9 +1834,10 @@ impl Type {
1820 ); 1834 );
1821 1835
1822 match db.trait_solve(self.krate, goal)? { 1836 match db.trait_solve(self.krate, goal)? {
1823 Solution::Unique(SolutionVariables(subst)) => subst 1837 Solution::Unique(s) => s
1824 .value 1838 .value
1825 .interned(&Interner) 1839 .subst
1840 .as_slice(&Interner)
1826 .first() 1841 .first()
1827 .map(|ty| self.derived(ty.assert_ty_ref(&Interner).clone())), 1842 .map(|ty| self.derived(ty.assert_ty_ref(&Interner).clone())),
1828 Solution::Ambig(_) => None, 1843 Solution::Ambig(_) => None,
@@ -1875,7 +1890,7 @@ impl Type {
1875 1890
1876 fn go(ty: &Ty) -> bool { 1891 fn go(ty: &Ty) -> bool {
1877 match ty.kind(&Interner) { 1892 match ty.kind(&Interner) {
1878 TyKind::Unknown => true, 1893 TyKind::Error => true,
1879 1894
1880 TyKind::Adt(_, substs) 1895 TyKind::Adt(_, substs)
1881 | TyKind::AssociatedType(_, substs) 1896 | TyKind::AssociatedType(_, substs)
@@ -1886,9 +1901,10 @@ impl Type {
1886 substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go) 1901 substs.iter(&Interner).filter_map(|a| a.ty(&Interner)).any(go)
1887 } 1902 }
1888 1903
1889 TyKind::Array(ty) | TyKind::Slice(ty) | TyKind::Raw(_, ty) | TyKind::Ref(_, ty) => { 1904 TyKind::Array(ty, _)
1890 go(ty) 1905 | TyKind::Slice(ty)
1891 } 1906 | TyKind::Raw(_, ty)
1907 | TyKind::Ref(_, _, ty) => go(ty),
1892 1908
1893 TyKind::Scalar(_) 1909 TyKind::Scalar(_)
1894 | TyKind::Str 1910 | TyKind::Str
@@ -1899,7 +1915,9 @@ impl Type {
1899 | TyKind::Dyn(_) 1915 | TyKind::Dyn(_)
1900 | TyKind::Function(_) 1916 | TyKind::Function(_)
1901 | TyKind::Alias(_) 1917 | TyKind::Alias(_)
1902 | TyKind::ForeignType(_) => false, 1918 | TyKind::Foreign(_)
1919 | TyKind::Generator(..)
1920 | TyKind::GeneratorWitness(..) => false,
1903 } 1921 }
1904 } 1922 }
1905 } 1923 }
@@ -1915,7 +1933,7 @@ impl Type {
1915 .iter() 1933 .iter()
1916 .map(|(local_id, ty)| { 1934 .map(|(local_id, ty)| {
1917 let def = Field { parent: variant_id.into(), id: local_id }; 1935 let def = Field { parent: variant_id.into(), id: local_id };
1918 let ty = ty.clone().subst(substs); 1936 let ty = ty.clone().substitute(&Interner, substs);
1919 (def, self.derived(ty)) 1937 (def, self.derived(ty))
1920 }) 1938 })
1921 .collect() 1939 .collect()
@@ -1952,7 +1970,7 @@ impl Type {
1952 krate: Crate, 1970 krate: Crate,
1953 mut callback: impl FnMut(AssocItem) -> Option<T>, 1971 mut callback: impl FnMut(AssocItem) -> Option<T>,
1954 ) -> Option<T> { 1972 ) -> Option<T> {
1955 for krate in self.ty.def_crates(db, krate.id)? { 1973 for krate in def_crates(db, &self.ty, krate.id)? {
1956 let impls = db.inherent_impls_in_crate(krate); 1974 let impls = db.inherent_impls_in_crate(krate);
1957 1975
1958 for impl_def in impls.for_self_ty(&self.ty) { 1976 for impl_def in impls.for_self_ty(&self.ty) {
@@ -1969,9 +1987,9 @@ impl Type {
1969 pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ { 1987 pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ {
1970 self.ty 1988 self.ty
1971 .strip_references() 1989 .strip_references()
1972 .substs() 1990 .as_adt()
1973 .into_iter() 1991 .into_iter()
1974 .flat_map(|substs| substs.iter(&Interner)) 1992 .flat_map(|(_, substs)| substs.iter(&Interner))
1975 .filter_map(|arg| arg.ty(&Interner).cloned()) 1993 .filter_map(|arg| arg.ty(&Interner).cloned())
1976 .map(move |ty| self.derived(ty)) 1994 .map(move |ty| self.derived(ty))
1977 } 1995 }
@@ -2048,6 +2066,18 @@ impl Type {
2048 self.ty.dyn_trait().map(Into::into) 2066 self.ty.dyn_trait().map(Into::into)
2049 } 2067 }
2050 2068
2069 /// If a type can be represented as `dyn Trait`, returns all traits accessible via this type,
2070 /// or an empty iterator otherwise.
2071 pub fn applicable_inherent_traits<'a>(
2072 &'a self,
2073 db: &'a dyn HirDatabase,
2074 ) -> impl Iterator<Item = Trait> + 'a {
2075 self.autoderef(db)
2076 .filter_map(|derefed_type| derefed_type.ty.dyn_trait())
2077 .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id))
2078 .map(Trait::from)
2079 }
2080
2051 pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> { 2081 pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> {
2052 self.ty.impl_trait_bounds(db).map(|it| { 2082 self.ty.impl_trait_bounds(db).map(|it| {
2053 it.into_iter() 2083 it.into_iter()
@@ -2112,18 +2142,22 @@ impl Type {
2112 fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) { 2142 fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
2113 let ty = type_.ty.strip_references(); 2143 let ty = type_.ty.strip_references();
2114 match ty.kind(&Interner) { 2144 match ty.kind(&Interner) {
2115 TyKind::Adt(..) => { 2145 TyKind::Adt(_, substs) => {
2116 cb(type_.derived(ty.clone())); 2146 cb(type_.derived(ty.clone()));
2147 walk_substs(db, type_, &substs, cb);
2117 } 2148 }
2118 TyKind::AssociatedType(..) => { 2149 TyKind::AssociatedType(_, substs) => {
2119 if let Some(_) = ty.associated_type_parent_trait(db) { 2150 if let Some(_) = ty.associated_type_parent_trait(db) {
2120 cb(type_.derived(ty.clone())); 2151 cb(type_.derived(ty.clone()));
2121 } 2152 }
2153 walk_substs(db, type_, &substs, cb);
2122 } 2154 }
2123 TyKind::OpaqueType(..) => { 2155 TyKind::OpaqueType(_, subst) => {
2124 if let Some(bounds) = ty.impl_trait_bounds(db) { 2156 if let Some(bounds) = ty.impl_trait_bounds(db) {
2125 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); 2157 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
2126 } 2158 }
2159
2160 walk_substs(db, type_, subst, cb);
2127 } 2161 }
2128 TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { 2162 TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
2129 if let Some(bounds) = ty.impl_trait_bounds(db) { 2163 if let Some(bounds) = ty.impl_trait_bounds(db) {
@@ -2146,15 +2180,24 @@ impl Type {
2146 ); 2180 );
2147 } 2181 }
2148 2182
2149 TyKind::Ref(_, ty) | TyKind::Raw(_, ty) | TyKind::Array(ty) | TyKind::Slice(ty) => { 2183 TyKind::Ref(_, _, ty)
2184 | TyKind::Raw(_, ty)
2185 | TyKind::Array(ty, _)
2186 | TyKind::Slice(ty) => {
2150 walk_type(db, &type_.derived(ty.clone()), cb); 2187 walk_type(db, &type_.derived(ty.clone()), cb);
2151 } 2188 }
2152 2189
2190 TyKind::FnDef(_, substs)
2191 | TyKind::Tuple(_, substs)
2192 | TyKind::Closure(.., substs) => {
2193 walk_substs(db, type_, &substs, cb);
2194 }
2195 TyKind::Function(hir_ty::FnPointer { substitution, .. }) => {
2196 walk_substs(db, type_, &substitution.0, cb);
2197 }
2198
2153 _ => {} 2199 _ => {}
2154 } 2200 }
2155 if let Some(substs) = ty.substs() {
2156 walk_substs(db, type_, &substs, cb);
2157 }
2158 } 2201 }
2159 2202
2160 walk_type(db, self, &mut cb); 2203 walk_type(db, self, &mut cb);
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 3bf722d2a..62500602a 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -6,10 +6,11 @@ use std::{cell::RefCell, fmt, iter::successors};
6 6
7use base_db::{FileId, FileRange}; 7use base_db::{FileId, FileRange};
8use hir_def::{ 8use hir_def::{
9 body,
9 resolver::{self, HasResolver, Resolver, TypeNs}, 10 resolver::{self, HasResolver, Resolver, TypeNs},
10 AsMacroCall, FunctionId, TraitId, VariantId, 11 AsMacroCall, FunctionId, TraitId, VariantId,
11}; 12};
12use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo}; 13use hir_expand::{name::AsName, ExpansionInfo};
13use hir_ty::associated_type_shorthand_candidates; 14use hir_ty::associated_type_shorthand_candidates;
14use itertools::Itertools; 15use itertools::Itertools;
15use rustc_hash::{FxHashMap, FxHashSet}; 16use rustc_hash::{FxHashMap, FxHashSet};
@@ -494,9 +495,9 @@ impl<'db> SemanticsImpl<'db> {
494 fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> { 495 fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
495 // FIXME: this erases Substs 496 // FIXME: this erases Substs
496 let func = self.resolve_method_call(call)?; 497 let func = self.resolve_method_call(call)?;
497 let ty = self.db.value_ty(func.into()); 498 let (ty, _) = self.db.value_ty(func.into()).into_value_and_skipped_binders();
498 let resolver = self.analyze(call.syntax()).resolver; 499 let resolver = self.analyze(call.syntax()).resolver;
499 let ty = Type::new_with_resolver(self.db, &resolver, ty.value)?; 500 let ty = Type::new_with_resolver(self.db, &resolver, ty)?;
500 let mut res = ty.as_callable(self.db)?; 501 let mut res = ty.as_callable(self.db)?;
501 res.is_bound_method = true; 502 res.is_bound_method = true;
502 Some(res) 503 Some(res)
@@ -853,8 +854,8 @@ impl<'a> SemanticsScope<'a> {
853 /// Resolve a path as-if it was written at the given scope. This is 854 /// Resolve a path as-if it was written at the given scope. This is
854 /// necessary a heuristic, as it doesn't take hygiene into account. 855 /// necessary a heuristic, as it doesn't take hygiene into account.
855 pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> { 856 pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
856 let hygiene = Hygiene::new(self.db.upcast(), self.file_id); 857 let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id);
857 let path = Path::from_src(path.clone(), &hygiene)?; 858 let path = Path::from_src(path.clone(), &ctx)?;
858 resolve_hir_path(self.db, &self.resolver, &path) 859 resolve_hir_path(self.db, &self.resolver, &path)
859 } 860 }
860} 861}
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 8e9ea0a03..0895bd6f1 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -9,6 +9,7 @@ use std::{iter::once, sync::Arc};
9 9
10use hir_def::{ 10use hir_def::{
11 body::{ 11 body::{
12 self,
12 scope::{ExprScopes, ScopeId}, 13 scope::{ExprScopes, ScopeId},
13 Body, BodySourceMap, 14 Body, BodySourceMap,
14 }, 15 },
@@ -20,7 +21,7 @@ use hir_def::{
20use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; 21use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
21use hir_ty::{ 22use hir_ty::{
22 diagnostics::{record_literal_missing_fields, record_pattern_missing_fields}, 23 diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
23 InferenceResult, Substitution, TyLoweringContext, 24 InferenceResult, Interner, Substitution, TyExt, TyLoweringContext,
24}; 25};
25use syntax::{ 26use syntax::{
26 ast::{self, AstNode}, 27 ast::{self, AstNode},
@@ -161,14 +162,15 @@ impl SourceAnalyzer {
161 db: &dyn HirDatabase, 162 db: &dyn HirDatabase,
162 field: &ast::RecordExprField, 163 field: &ast::RecordExprField,
163 ) -> Option<(Field, Option<Local>)> { 164 ) -> Option<(Field, Option<Local>)> {
164 let expr_id = 165 let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
165 self.body_source_map.as_ref()?.node_field(InFile::new(self.file_id, field))?; 166 let expr = ast::Expr::from(record_expr);
167 let expr_id = self.body_source_map.as_ref()?.node_expr(InFile::new(self.file_id, &expr))?;
166 168
169 let local_name = field.field_name()?.as_name();
167 let local = if field.name_ref().is_some() { 170 let local = if field.name_ref().is_some() {
168 None 171 None
169 } else { 172 } else {
170 let local_name = field.field_name()?.as_name(); 173 let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone()));
171 let path = ModPath::from_segments(PathKind::Plain, once(local_name));
172 match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { 174 match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
173 Some(ValueNs::LocalBinding(pat_id)) => { 175 Some(ValueNs::LocalBinding(pat_id)) => {
174 Some(Local { pat_id, parent: self.resolver.body_owner()? }) 176 Some(Local { pat_id, parent: self.resolver.body_owner()? })
@@ -176,18 +178,24 @@ impl SourceAnalyzer {
176 _ => None, 178 _ => None,
177 } 179 }
178 }; 180 };
179 let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?; 181 let variant = self.infer.as_ref()?.variant_resolution_for_expr(expr_id)?;
180 Some((struct_field.into(), local)) 182 let variant_data = variant.variant_data(db.upcast());
183 let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? };
184 Some((field.into(), local))
181 } 185 }
182 186
183 pub(crate) fn resolve_record_pat_field( 187 pub(crate) fn resolve_record_pat_field(
184 &self, 188 &self,
185 _db: &dyn HirDatabase, 189 db: &dyn HirDatabase,
186 field: &ast::RecordPatField, 190 field: &ast::RecordPatField,
187 ) -> Option<Field> { 191 ) -> Option<Field> {
188 let pat_id = self.pat_id(&field.pat()?)?; 192 let field_name = field.field_name()?.as_name();
189 let struct_field = self.infer.as_ref()?.record_pat_field_resolution(pat_id)?; 193 let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
190 Some(struct_field.into()) 194 let pat_id = self.pat_id(&record_pat.into())?;
195 let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?;
196 let variant_data = variant.variant_data(db.upcast());
197 let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? };
198 Some(field.into())
191 } 199 }
192 200
193 pub(crate) fn resolve_macro_call( 201 pub(crate) fn resolve_macro_call(
@@ -195,8 +203,8 @@ impl SourceAnalyzer {
195 db: &dyn HirDatabase, 203 db: &dyn HirDatabase,
196 macro_call: InFile<&ast::MacroCall>, 204 macro_call: InFile<&ast::MacroCall>,
197 ) -> Option<MacroDef> { 205 ) -> Option<MacroDef> {
198 let hygiene = Hygiene::new(db.upcast(), macro_call.file_id); 206 let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id);
199 let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?; 207 let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
200 self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into()) 208 self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into())
201 } 209 }
202 210
@@ -274,7 +282,9 @@ impl SourceAnalyzer {
274 } 282 }
275 283
276 // This must be a normal source file rather than macro file. 284 // This must be a normal source file rather than macro file.
277 let hir_path = Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?; 285 let hygiene = Hygiene::new(db.upcast(), self.file_id);
286 let ctx = body::LowerCtx::with_hygiene(&hygiene);
287 let hir_path = Path::from_src(path.clone(), &ctx)?;
278 288
279 // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we 289 // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we
280 // trying to resolve foo::bar. 290 // trying to resolve foo::bar.
@@ -299,7 +309,7 @@ impl SourceAnalyzer {
299 let infer = self.infer.as_ref()?; 309 let infer = self.infer.as_ref()?;
300 310
301 let expr_id = self.expr_id(db, &literal.clone().into())?; 311 let expr_id = self.expr_id(db, &literal.clone().into())?;
302 let substs = infer.type_of_expr[expr_id].substs()?; 312 let substs = infer.type_of_expr[expr_id].as_adt()?.1;
303 313
304 let (variant, missing_fields, _exhaustive) = 314 let (variant, missing_fields, _exhaustive) =
305 record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?; 315 record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?;
@@ -317,7 +327,7 @@ impl SourceAnalyzer {
317 let infer = self.infer.as_ref()?; 327 let infer = self.infer.as_ref()?;
318 328
319 let pat_id = self.pat_id(&pattern.clone().into())?; 329 let pat_id = self.pat_id(&pattern.clone().into())?;
320 let substs = infer.type_of_pat[pat_id].substs()?; 330 let substs = infer.type_of_pat[pat_id].as_adt()?.1;
321 331
322 let (variant, missing_fields, _exhaustive) = 332 let (variant, missing_fields, _exhaustive) =
323 record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?; 333 record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?;
@@ -339,7 +349,7 @@ impl SourceAnalyzer {
339 .into_iter() 349 .into_iter()
340 .map(|local_id| { 350 .map(|local_id| {
341 let field = FieldId { parent: variant, local_id }; 351 let field = FieldId { parent: variant, local_id };
342 let ty = field_types[local_id].clone().subst(substs); 352 let ty = field_types[local_id].clone().substitute(&Interner, substs);
343 (field.into(), Type::new_with_resolver_inner(db, krate, &self.resolver, ty)) 353 (field.into(), Type::new_with_resolver_inner(db, krate, &self.resolver, ty))
344 }) 354 })
345 .collect() 355 .collect()
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 442c5fb5b..d9294d93a 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -1,18 +1,22 @@
1//! A higher level attributes based on TokenTree, with also some shortcuts. 1//! A higher level attributes based on TokenTree, with also some shortcuts.
2 2
3use std::{ops, sync::Arc}; 3use std::{
4 convert::{TryFrom, TryInto},
5 ops,
6 sync::Arc,
7};
4 8
5use base_db::CrateId; 9use base_db::CrateId;
6use cfg::{CfgExpr, CfgOptions}; 10use cfg::{CfgExpr, CfgOptions};
7use either::Either; 11use either::Either;
8use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile}; 12use hir_expand::{hygiene::Hygiene, name::AsName, AstId, AttrId, InFile};
9use itertools::Itertools; 13use itertools::Itertools;
10use la_arena::ArenaMap; 14use la_arena::ArenaMap;
11use mbe::ast_to_token_tree; 15use mbe::ast_to_token_tree;
12use smallvec::{smallvec, SmallVec}; 16use smallvec::{smallvec, SmallVec};
13use syntax::{ 17use syntax::{
14 ast::{self, AstNode, AttrsOwner}, 18 ast::{self, AstNode, AttrsOwner},
15 match_ast, AstToken, SmolStr, SyntaxNode, 19 match_ast, AstPtr, AstToken, SmolStr, SyntaxNode, TextRange, TextSize,
16}; 20};
17use tt::Subtree; 21use tt::Subtree;
18 22
@@ -94,13 +98,16 @@ impl RawAttrs {
94 pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self { 98 pub(crate) fn new(owner: &dyn ast::AttrsOwner, hygiene: &Hygiene) -> Self {
95 let entries = collect_attrs(owner) 99 let entries = collect_attrs(owner)
96 .enumerate() 100 .enumerate()
97 .flat_map(|(i, attr)| match attr { 101 .flat_map(|(i, attr)| {
98 Either::Left(attr) => Attr::from_src(attr, hygiene, i as u32), 102 let index = AttrId(i as u32);
99 Either::Right(comment) => comment.doc_comment().map(|doc| Attr { 103 match attr {
100 index: i as u32, 104 Either::Left(attr) => Attr::from_src(attr, hygiene, index),
101 input: Some(AttrInput::Literal(SmolStr::new(doc))), 105 Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
102 path: Interned::new(ModPath::from(hir_expand::name!(doc))), 106 id: index,
103 }), 107 input: Some(AttrInput::Literal(SmolStr::new(doc))),
108 path: Interned::new(ModPath::from(hir_expand::name!(doc))),
109 }),
110 }
104 }) 111 })
105 .collect::<Arc<_>>(); 112 .collect::<Arc<_>>();
106 113
@@ -157,7 +164,7 @@ impl RawAttrs {
157 let cfg = parts.next().unwrap(); 164 let cfg = parts.next().unwrap();
158 let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() }; 165 let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
159 let cfg = CfgExpr::parse(&cfg); 166 let cfg = CfgExpr::parse(&cfg);
160 let index = attr.index; 167 let index = attr.id;
161 let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| { 168 let attrs = parts.filter(|a| !a.is_empty()).filter_map(|attr| {
162 let tree = Subtree { delimiter: None, token_trees: attr.to_vec() }; 169 let tree = Subtree { delimiter: None, token_trees: attr.to_vec() };
163 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; 170 let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?;
@@ -211,12 +218,11 @@ impl Attrs {
211 let mut res = ArenaMap::default(); 218 let mut res = ArenaMap::default();
212 219
213 for (id, fld) in src.value.iter() { 220 for (id, fld) in src.value.iter() {
214 let attrs = match fld { 221 let owner: &dyn AttrsOwner = match fld {
215 Either::Left(_tuple) => Attrs::default(), 222 Either::Left(tuple) => tuple,
216 Either::Right(record) => { 223 Either::Right(record) => record,
217 RawAttrs::from_attrs_owner(db, src.with_value(record)).filter(db, krate)
218 }
219 }; 224 };
225 let attrs = RawAttrs::from_attrs_owner(db, src.with_value(owner)).filter(db, krate);
220 226
221 res.insert(id, attrs); 227 res.insert(id, attrs);
222 } 228 }
@@ -400,10 +406,14 @@ impl AttrsWithOwner {
400 return AttrSourceMap { attrs }; 406 return AttrSourceMap { attrs };
401 } 407 }
402 AttrDefId::FieldId(id) => { 408 AttrDefId::FieldId(id) => {
403 id.parent.child_source(db).map(|source| match &source[id.local_id] { 409 let map = db.fields_attrs_source_map(id.parent);
404 Either::Left(field) => ast::AttrsOwnerNode::new(field.clone()), 410 let file_id = id.parent.file_id(db);
405 Either::Right(field) => ast::AttrsOwnerNode::new(field.clone()), 411 let root = db.parse_or_expand(file_id).unwrap();
406 }) 412 let owner = match &map[id.local_id] {
413 Either::Left(it) => ast::AttrsOwnerNode::new(it.to_node(&root)),
414 Either::Right(it) => ast::AttrsOwnerNode::new(it.to_node(&root)),
415 };
416 InFile::new(file_id, owner)
407 } 417 }
408 AttrDefId::AdtId(adt) => match adt { 418 AttrDefId::AdtId(adt) => match adt {
409 AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), 419 AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
@@ -411,10 +421,12 @@ impl AttrsWithOwner {
411 AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), 421 AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
412 }, 422 },
413 AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), 423 AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
414 AttrDefId::EnumVariantId(id) => id 424 AttrDefId::EnumVariantId(id) => {
415 .parent 425 let map = db.variants_attrs_source_map(id.parent);
416 .child_source(db) 426 let file_id = id.parent.lookup(db).id.file_id();
417 .map(|source| ast::AttrsOwnerNode::new(source[id.local_id].clone())), 427 let root = db.parse_or_expand(file_id).unwrap();
428 InFile::new(file_id, ast::AttrsOwnerNode::new(map[id.local_id].to_node(&root)))
429 }
418 AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), 430 AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
419 AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), 431 AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
420 AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new), 432 AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AttrsOwnerNode::new),
@@ -452,6 +464,55 @@ impl AttrsWithOwner {
452 .collect(), 464 .collect(),
453 } 465 }
454 } 466 }
467
468 pub fn docs_with_rangemap(
469 &self,
470 db: &dyn DefDatabase,
471 ) -> Option<(Documentation, DocsRangeMap)> {
472 // FIXME: code duplication in `docs` above
473 let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_ref()? {
474 AttrInput::Literal(s) => Some((s, attr.id)),
475 AttrInput::TokenTree(_) => None,
476 });
477 let indent = docs
478 .clone()
479 .flat_map(|(s, _)| s.lines())
480 .filter(|line| !line.chars().all(|c| c.is_whitespace()))
481 .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
482 .min()
483 .unwrap_or(0);
484 let mut buf = String::new();
485 let mut mapping = Vec::new();
486 for (doc, idx) in docs {
487 // str::lines doesn't yield anything for the empty string
488 if !doc.is_empty() {
489 for line in doc.split('\n') {
490 let line = line.trim_end();
491 let line_len = line.len();
492 let (offset, line) = match line.char_indices().nth(indent) {
493 Some((offset, _)) => (offset, &line[offset..]),
494 None => (0, line),
495 };
496 let buf_offset = buf.len();
497 buf.push_str(line);
498 mapping.push((
499 TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?),
500 idx,
501 TextRange::new(offset.try_into().ok()?, line_len.try_into().ok()?),
502 ));
503 buf.push('\n');
504 }
505 } else {
506 buf.push('\n');
507 }
508 }
509 buf.pop();
510 if buf.is_empty() {
511 None
512 } else {
513 Some((Documentation(buf), DocsRangeMap { mapping, source: self.source_map(db).attrs }))
514 }
515 }
455} 516}
456 517
457fn inner_attributes( 518fn inner_attributes(
@@ -484,7 +545,7 @@ fn inner_attributes(
484 _ => return None, 545 _ => return None,
485 } 546 }
486 }; 547 };
487 let attrs = attrs.filter(|attr| attr.excl_token().is_some()); 548 let attrs = attrs.filter(|attr| attr.kind().is_inner());
488 let docs = docs.filter(|doc| doc.is_inner()); 549 let docs = docs.filter(|doc| doc.is_inner());
489 Some((attrs, docs)) 550 Some((attrs, docs))
490} 551}
@@ -502,15 +563,53 @@ impl AttrSourceMap {
502 /// the attribute represented by `Attr`. 563 /// the attribute represented by `Attr`.
503 pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> { 564 pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> {
504 self.attrs 565 self.attrs
505 .get(attr.index as usize) 566 .get(attr.id.0 as usize)
506 .unwrap_or_else(|| panic!("cannot find `Attr` at index {}", attr.index)) 567 .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", attr.id))
507 .as_ref() 568 .as_ref()
508 } 569 }
509} 570}
510 571
572/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
573pub struct DocsRangeMap {
574 source: Vec<InFile<Either<ast::Attr, ast::Comment>>>,
575 // (docstring-line-range, attr_index, attr-string-range)
576 // a mapping from the text range of a line of the [`Documentation`] to the attribute index and
577 // the original (untrimmed) syntax doc line
578 mapping: Vec<(TextRange, AttrId, TextRange)>,
579}
580
581impl DocsRangeMap {
582 pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> {
583 let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?;
584 let (line_docs_range, idx, original_line_src_range) = self.mapping[found].clone();
585 if !line_docs_range.contains_range(range) {
586 return None;
587 }
588
589 let relative_range = range - line_docs_range.start();
590
591 let &InFile { file_id, value: ref source } = &self.source[idx.0 as usize];
592 match source {
593 Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here
594 // as well as for whats done in syntax highlight doc injection
595 Either::Right(comment) => {
596 let text_range = comment.syntax().text_range();
597 let range = TextRange::at(
598 text_range.start()
599 + TextSize::try_from(comment.prefix().len()).ok()?
600 + original_line_src_range.start()
601 + relative_range.start(),
602 text_range.len().min(range.len()),
603 );
604 Some(InFile { file_id, value: range })
605 }
606 }
607 }
608}
609
511#[derive(Debug, Clone, PartialEq, Eq)] 610#[derive(Debug, Clone, PartialEq, Eq)]
512pub struct Attr { 611pub struct Attr {
513 index: u32, 612 pub(crate) id: AttrId,
514 pub(crate) path: Interned<ModPath>, 613 pub(crate) path: Interned<ModPath>,
515 pub(crate) input: Option<AttrInput>, 614 pub(crate) input: Option<AttrInput>,
516} 615}
@@ -524,7 +623,7 @@ pub enum AttrInput {
524} 623}
525 624
526impl Attr { 625impl Attr {
527 fn from_src(ast: ast::Attr, hygiene: &Hygiene, index: u32) -> Option<Attr> { 626 fn from_src(ast: ast::Attr, hygiene: &Hygiene, id: AttrId) -> Option<Attr> {
528 let path = Interned::new(ModPath::from_src(ast.path()?, hygiene)?); 627 let path = Interned::new(ModPath::from_src(ast.path()?, hygiene)?);
529 let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() { 628 let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
530 let value = match lit.kind() { 629 let value = match lit.kind() {
@@ -537,7 +636,7 @@ impl Attr {
537 } else { 636 } else {
538 None 637 None
539 }; 638 };
540 Some(Attr { index, path, input }) 639 Some(Attr { id, path, input })
541 } 640 }
542 641
543 /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths 642 /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths
@@ -641,7 +740,7 @@ fn collect_attrs(
641 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax()) 740 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
642 .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs))); 741 .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs)));
643 742
644 let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none()); 743 let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer());
645 let attrs = outer_attrs 744 let attrs = outer_attrs
646 .chain(inner_attrs.into_iter().flatten()) 745 .chain(inner_attrs.into_iter().flatten())
647 .map(|attr| (attr.syntax().text_range().start(), Either::Left(attr))); 746 .map(|attr| (attr.syntax().text_range().start(), Either::Left(attr)));
@@ -652,7 +751,38 @@ fn collect_attrs(
652 .chain(inner_docs.into_iter().flatten()) 751 .chain(inner_docs.into_iter().flatten())
653 .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text))); 752 .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text)));
654 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved 753 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
655 let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); 754 docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).map(|(_, attr)| attr)
755}
756
757pub(crate) fn variants_attrs_source_map(
758 db: &dyn DefDatabase,
759 def: EnumId,
760) -> Arc<ArenaMap<LocalEnumVariantId, AstPtr<ast::Variant>>> {
761 let mut res = ArenaMap::default();
762 let child_source = def.child_source(db);
763
764 for (idx, variant) in child_source.value.iter() {
765 res.insert(idx, AstPtr::new(variant));
766 }
767
768 Arc::new(res)
769}
770
771pub(crate) fn fields_attrs_source_map(
772 db: &dyn DefDatabase,
773 def: VariantId,
774) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>> {
775 let mut res = ArenaMap::default();
776 let child_source = def.child_source(db);
777
778 for (idx, variant) in child_source.value.iter() {
779 res.insert(
780 idx,
781 variant
782 .as_ref()
783 .either(|l| Either::Left(AstPtr::new(l)), |r| Either::Right(AstPtr::new(r))),
784 );
785 }
656 786
657 attrs.into_iter().map(|(_, attr)| attr) 787 Arc::new(res)
658} 788}
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index 96b959967..131f424cc 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -21,7 +21,7 @@ use profile::Count;
21use rustc_hash::FxHashMap; 21use rustc_hash::FxHashMap;
22use syntax::{ast, AstNode, AstPtr}; 22use syntax::{ast, AstNode, AstPtr};
23 23
24pub(crate) use lower::LowerCtx; 24pub use lower::LowerCtx;
25 25
26use crate::{ 26use crate::{
27 attr::{Attrs, RawAttrs}, 27 attr::{Attrs, RawAttrs},
@@ -37,13 +37,15 @@ use crate::{
37 37
38/// A subset of Expander that only deals with cfg attributes. We only need it to 38/// A subset of Expander that only deals with cfg attributes. We only need it to
39/// avoid cyclic queries in crate def map during enum processing. 39/// avoid cyclic queries in crate def map during enum processing.
40#[derive(Debug)]
40pub(crate) struct CfgExpander { 41pub(crate) struct CfgExpander {
41 cfg_options: CfgOptions, 42 cfg_options: CfgOptions,
42 hygiene: Hygiene, 43 hygiene: Hygiene,
43 krate: CrateId, 44 krate: CrateId,
44} 45}
45 46
46pub(crate) struct Expander { 47#[derive(Debug)]
48pub struct Expander {
47 cfg_expander: CfgExpander, 49 cfg_expander: CfgExpander,
48 def_map: Arc<DefMap>, 50 def_map: Arc<DefMap>,
49 current_file_id: HirFileId, 51 current_file_id: HirFileId,
@@ -80,11 +82,7 @@ impl CfgExpander {
80} 82}
81 83
82impl Expander { 84impl Expander {
83 pub(crate) fn new( 85 pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
84 db: &dyn DefDatabase,
85 current_file_id: HirFileId,
86 module: ModuleId,
87 ) -> Expander {
88 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); 86 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
89 let def_map = module.def_map(db); 87 let def_map = module.def_map(db);
90 let ast_id_map = db.ast_id_map(current_file_id); 88 let ast_id_map = db.ast_id_map(current_file_id);
@@ -98,7 +96,7 @@ impl Expander {
98 } 96 }
99 } 97 }
100 98
101 pub(crate) fn enter_expand<T: ast::AstNode>( 99 pub fn enter_expand<T: ast::AstNode>(
102 &mut self, 100 &mut self,
103 db: &dyn DefDatabase, 101 db: &dyn DefDatabase,
104 macro_call: ast::MacroCall, 102 macro_call: ast::MacroCall,
@@ -170,7 +168,7 @@ impl Expander {
170 Ok(ExpandResult { value: Some((mark, node)), err }) 168 Ok(ExpandResult { value: Some((mark, node)), err })
171 } 169 }
172 170
173 pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { 171 pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
174 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); 172 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
175 self.current_file_id = mark.file_id; 173 self.current_file_id = mark.file_id;
176 self.ast_id_map = mem::take(&mut mark.ast_id_map); 174 self.ast_id_map = mem::take(&mut mark.ast_id_map);
@@ -190,8 +188,13 @@ impl Expander {
190 &self.cfg_expander.cfg_options 188 &self.cfg_expander.cfg_options
191 } 189 }
192 190
191 pub fn current_file_id(&self) -> HirFileId {
192 self.current_file_id
193 }
194
193 fn parse_path(&mut self, path: ast::Path) -> Option<Path> { 195 fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
194 Path::from_src(path, &self.cfg_expander.hygiene) 196 let ctx = LowerCtx::with_hygiene(&self.cfg_expander.hygiene);
197 Path::from_src(path, &ctx)
195 } 198 }
196 199
197 fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> { 200 fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> {
@@ -204,7 +207,8 @@ impl Expander {
204 } 207 }
205} 208}
206 209
207pub(crate) struct Mark { 210#[derive(Debug)]
211pub struct Mark {
208 file_id: HirFileId, 212 file_id: HirFileId,
209 ast_id_map: Arc<AstIdMap>, 213 ast_id_map: Arc<AstIdMap>,
210 bomb: DropBomb, 214 bomb: DropBomb,
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 63e89a1f4..c11da30d2 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -1,10 +1,11 @@
1//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` 1//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
2//! representation. 2//! representation.
3 3
4use std::mem; 4use std::{mem, sync::Arc};
5 5
6use either::Either; 6use either::Either;
7use hir_expand::{ 7use hir_expand::{
8 ast_id_map::{AstIdMap, FileAstId},
8 hygiene::Hygiene, 9 hygiene::Hygiene,
9 name::{name, AsName, Name}, 10 name::{name, AsName, Name},
10 ExpandError, HirFileId, 11 ExpandError, HirFileId,
@@ -30,6 +31,7 @@ use crate::{
30 LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, 31 LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField,
31 Statement, 32 Statement,
32 }, 33 },
34 intern::Interned,
33 item_scope::BuiltinShadowMode, 35 item_scope::BuiltinShadowMode,
34 path::{GenericArgs, Path}, 36 path::{GenericArgs, Path},
35 type_ref::{Mutability, Rawness, TypeRef}, 37 type_ref::{Mutability, Rawness, TypeRef},
@@ -38,20 +40,39 @@ use crate::{
38 40
39use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; 41use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
40 42
41pub(crate) struct LowerCtx { 43pub struct LowerCtx {
42 hygiene: Hygiene, 44 hygiene: Hygiene,
45 file_id: Option<HirFileId>,
46 source_ast_id_map: Option<Arc<AstIdMap>>,
43} 47}
44 48
45impl LowerCtx { 49impl LowerCtx {
46 pub(crate) fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self { 50 pub fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self {
47 LowerCtx { hygiene: Hygiene::new(db.upcast(), file_id) } 51 LowerCtx {
52 hygiene: Hygiene::new(db.upcast(), file_id),
53 file_id: Some(file_id),
54 source_ast_id_map: Some(db.ast_id_map(file_id)),
55 }
48 } 56 }
49 pub(crate) fn with_hygiene(hygiene: &Hygiene) -> Self { 57
50 LowerCtx { hygiene: hygiene.clone() } 58 pub fn with_hygiene(hygiene: &Hygiene) -> Self {
59 LowerCtx { hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None }
60 }
61
62 pub(crate) fn hygiene(&self) -> &Hygiene {
63 &self.hygiene
64 }
65
66 pub(crate) fn file_id(&self) -> HirFileId {
67 self.file_id.unwrap()
51 } 68 }
52 69
53 pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> { 70 pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
54 Path::from_src(ast, &self.hygiene) 71 Path::from_src(ast, self)
72 }
73
74 pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<FileAstId<N>> {
75 self.source_ast_id_map.as_ref().map(|ast_id_map| ast_id_map.ast_id(item))
55 } 76 }
56} 77}
57 78
@@ -322,8 +343,10 @@ impl ExprCollector<'_> {
322 Vec::new() 343 Vec::new()
323 }; 344 };
324 let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); 345 let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
325 let generic_args = 346 let generic_args = e
326 e.generic_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx(), it)); 347 .generic_arg_list()
348 .and_then(|it| GenericArgs::from_ast(&self.ctx(), it))
349 .map(Box::new);
327 self.alloc_expr( 350 self.alloc_expr(
328 Expr::MethodCall { receiver, method_name, args, generic_args }, 351 Expr::MethodCall { receiver, method_name, args, generic_args },
329 syntax_ptr, 352 syntax_ptr,
@@ -385,7 +408,7 @@ impl ExprCollector<'_> {
385 self.alloc_expr(Expr::Yield { expr }, syntax_ptr) 408 self.alloc_expr(Expr::Yield { expr }, syntax_ptr)
386 } 409 }
387 ast::Expr::RecordExpr(e) => { 410 ast::Expr::RecordExpr(e) => {
388 let path = e.path().and_then(|path| self.expander.parse_path(path)); 411 let path = e.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
389 let record_lit = if let Some(nfl) = e.record_expr_field_list() { 412 let record_lit = if let Some(nfl) = e.record_expr_field_list() {
390 let fields = nfl 413 let fields = nfl
391 .fields() 414 .fields()
@@ -430,7 +453,7 @@ impl ExprCollector<'_> {
430 } 453 }
431 ast::Expr::CastExpr(e) => { 454 ast::Expr::CastExpr(e) => {
432 let expr = self.collect_expr_opt(e.expr()); 455 let expr = self.collect_expr_opt(e.expr());
433 let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.ty()); 456 let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
434 self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) 457 self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
435 } 458 }
436 ast::Expr::RefExpr(e) => { 459 ast::Expr::RefExpr(e) => {
@@ -464,13 +487,16 @@ impl ExprCollector<'_> {
464 if let Some(pl) = e.param_list() { 487 if let Some(pl) = e.param_list() {
465 for param in pl.params() { 488 for param in pl.params() {
466 let pat = self.collect_pat_opt(param.pat()); 489 let pat = self.collect_pat_opt(param.pat());
467 let type_ref = param.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); 490 let type_ref =
491 param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
468 args.push(pat); 492 args.push(pat);
469 arg_types.push(type_ref); 493 arg_types.push(type_ref);
470 } 494 }
471 } 495 }
472 let ret_type = 496 let ret_type = e
473 e.ret_type().and_then(|r| r.ty()).map(|it| TypeRef::from_ast(&self.ctx(), it)); 497 .ret_type()
498 .and_then(|r| r.ty())
499 .map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
474 let body = self.collect_expr_opt(e.body()); 500 let body = self.collect_expr_opt(e.body());
475 self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr) 501 self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr)
476 } 502 }
@@ -525,8 +551,9 @@ impl ExprCollector<'_> {
525 } 551 }
526 } 552 }
527 ast::Expr::MacroCall(e) => { 553 ast::Expr::MacroCall(e) => {
554 let macro_ptr = AstPtr::new(&e);
528 let mut ids = vec![]; 555 let mut ids = vec![];
529 self.collect_macro_call(e, syntax_ptr.clone(), true, |this, expansion| { 556 self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
530 ids.push(match expansion { 557 ids.push(match expansion {
531 Some(it) => this.collect_expr(it), 558 Some(it) => this.collect_expr(it),
532 None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()), 559 None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()),
@@ -549,7 +576,7 @@ impl ExprCollector<'_> {
549 fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>( 576 fn collect_macro_call<F: FnMut(&mut Self, Option<T>), T: ast::AstNode>(
550 &mut self, 577 &mut self,
551 e: ast::MacroCall, 578 e: ast::MacroCall,
552 syntax_ptr: AstPtr<ast::Expr>, 579 syntax_ptr: AstPtr<ast::MacroCall>,
553 is_error_recoverable: bool, 580 is_error_recoverable: bool,
554 mut collector: F, 581 mut collector: F,
555 ) { 582 ) {
@@ -561,9 +588,13 @@ impl ExprCollector<'_> {
561 588
562 let res = match res { 589 let res = match res {
563 Ok(res) => res, 590 Ok(res) => res,
564 Err(UnresolvedMacro) => { 591 Err(UnresolvedMacro { path }) => {
565 self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall( 592 self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall(
566 UnresolvedMacroCall { file: outer_file, node: syntax_ptr.cast().unwrap() }, 593 UnresolvedMacroCall {
594 file: outer_file,
595 node: syntax_ptr.cast().unwrap(),
596 path,
597 },
567 )); 598 ));
568 collector(self, None); 599 collector(self, None);
569 return; 600 return;
@@ -625,7 +656,8 @@ impl ExprCollector<'_> {
625 return; 656 return;
626 } 657 }
627 let pat = self.collect_pat_opt(stmt.pat()); 658 let pat = self.collect_pat_opt(stmt.pat());
628 let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); 659 let type_ref =
660 stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
629 let initializer = stmt.initializer().map(|e| self.collect_expr(e)); 661 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
630 self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer }); 662 self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer });
631 } 663 }
@@ -636,10 +668,14 @@ impl ExprCollector<'_> {
636 668
637 // Note that macro could be expended to multiple statements 669 // Note that macro could be expended to multiple statements
638 if let Some(ast::Expr::MacroCall(m)) = stmt.expr() { 670 if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {
671 let macro_ptr = AstPtr::new(&m);
639 let syntax_ptr = AstPtr::new(&stmt.expr().unwrap()); 672 let syntax_ptr = AstPtr::new(&stmt.expr().unwrap());
640 673
641 self.collect_macro_call(m, syntax_ptr.clone(), false, |this, expansion| { 674 self.collect_macro_call(
642 match expansion { 675 m,
676 macro_ptr,
677 false,
678 |this, expansion| match expansion {
643 Some(expansion) => { 679 Some(expansion) => {
644 let statements: ast::MacroStmts = expansion; 680 let statements: ast::MacroStmts = expansion;
645 681
@@ -653,8 +689,8 @@ impl ExprCollector<'_> {
653 let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone()); 689 let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone());
654 this.statements_in_scope.push(Statement::Expr(expr)); 690 this.statements_in_scope.push(Statement::Expr(expr));
655 } 691 }
656 } 692 },
657 }); 693 );
658 } else { 694 } else {
659 let expr = self.collect_expr_opt(stmt.expr()); 695 let expr = self.collect_expr_opt(stmt.expr());
660 self.statements_in_scope.push(Statement::Expr(expr)); 696 self.statements_in_scope.push(Statement::Expr(expr));
@@ -755,7 +791,7 @@ impl ExprCollector<'_> {
755 } 791 }
756 } 792 }
757 ast::Pat::TupleStructPat(p) => { 793 ast::Pat::TupleStructPat(p) => {
758 let path = p.path().and_then(|path| self.expander.parse_path(path)); 794 let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
759 let (args, ellipsis) = self.collect_tuple_pat(p.fields()); 795 let (args, ellipsis) = self.collect_tuple_pat(p.fields());
760 Pat::TupleStruct { path, args, ellipsis } 796 Pat::TupleStruct { path, args, ellipsis }
761 } 797 }
@@ -765,7 +801,7 @@ impl ExprCollector<'_> {
765 Pat::Ref { pat, mutability } 801 Pat::Ref { pat, mutability }
766 } 802 }
767 ast::Pat::PathPat(p) => { 803 ast::Pat::PathPat(p) => {
768 let path = p.path().and_then(|path| self.expander.parse_path(path)); 804 let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
769 path.map(Pat::Path).unwrap_or(Pat::Missing) 805 path.map(Pat::Path).unwrap_or(Pat::Missing)
770 } 806 }
771 ast::Pat::OrPat(p) => { 807 ast::Pat::OrPat(p) => {
@@ -779,7 +815,7 @@ impl ExprCollector<'_> {
779 } 815 }
780 ast::Pat::WildcardPat(_) => Pat::Wild, 816 ast::Pat::WildcardPat(_) => Pat::Wild,
781 ast::Pat::RecordPat(p) => { 817 ast::Pat::RecordPat(p) => {
782 let path = p.path().and_then(|path| self.expander.parse_path(path)); 818 let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
783 let args: Vec<_> = p 819 let args: Vec<_> = p
784 .record_pat_field_list() 820 .record_pat_field_list()
785 .expect("every struct should have a field list") 821 .expect("every struct should have a field list")
@@ -841,8 +877,23 @@ impl ExprCollector<'_> {
841 Pat::Missing 877 Pat::Missing
842 } 878 }
843 } 879 }
880 ast::Pat::MacroPat(mac) => match mac.macro_call() {
881 Some(call) => {
882 let macro_ptr = AstPtr::new(&call);
883 let mut pat = None;
884 self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
885 pat = Some(this.collect_pat_opt(expanded_pat));
886 });
887
888 match pat {
889 Some(pat) => return pat,
890 None => Pat::Missing,
891 }
892 }
893 None => Pat::Missing,
894 },
844 // FIXME: implement 895 // FIXME: implement
845 ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, 896 ast::Pat::RangePat(_) => Pat::Missing,
846 }; 897 };
847 let ptr = AstPtr::new(&pat); 898 let ptr = AstPtr::new(&pat);
848 self.alloc_pat(pattern, Either::Left(ptr)) 899 self.alloc_pat(pattern, Either::Left(ptr))
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs
index faa133297..3e8f16306 100644
--- a/crates/hir_def/src/body/tests.rs
+++ b/crates/hir_def/src/body/tests.rs
@@ -40,6 +40,14 @@ fn block_def_map_at(ra_fixture: &str) -> String {
40 module.def_map(&db).dump(&db) 40 module.def_map(&db).dump(&db)
41} 41}
42 42
43fn check_block_scopes_at(ra_fixture: &str, expect: Expect) {
44 let (db, position) = crate::test_db::TestDB::with_position(ra_fixture);
45
46 let module = db.module_at_position(position);
47 let actual = module.def_map(&db).dump_block_scopes(&db);
48 expect.assert_eq(&actual);
49}
50
43fn check_at(ra_fixture: &str, expect: Expect) { 51fn check_at(ra_fixture: &str, expect: Expect) {
44 let actual = block_def_map_at(ra_fixture); 52 let actual = block_def_map_at(ra_fixture);
45 expect.assert_eq(&actual); 53 expect.assert_eq(&actual);
@@ -143,7 +151,7 @@ fn f() {
143 //^^^^^^^^^^^^^ could not convert tokens 151 //^^^^^^^^^^^^^ could not convert tokens
144 152
145 env!("OUT_DIR"); 153 env!("OUT_DIR");
146 //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix 154 //^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
147 155
148 compile_error!("compile_error works"); 156 compile_error!("compile_error works");
149 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works 157 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ compile_error works
@@ -180,7 +188,7 @@ fn unresolved_macro_diag() {
180 r#" 188 r#"
181fn f() { 189fn f() {
182 m!(); 190 m!();
183 //^^^^ unresolved macro call 191 //^^^^ unresolved macro `m!`
184} 192}
185 "#, 193 "#,
186 ); 194 );
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs
index 3b6ba4cde..bc3d0f138 100644
--- a/crates/hir_def/src/body/tests/block.rs
+++ b/crates/hir_def/src/body/tests/block.rs
@@ -134,6 +134,30 @@ struct Struct {}
134} 134}
135 135
136#[test] 136#[test]
137fn nested_module_scoping() {
138 check_block_scopes_at(
139 r#"
140fn f() {
141 mod module {
142 struct Struct {}
143 fn f() {
144 use self::Struct;
145 $0
146 }
147 }
148}
149 "#,
150 expect![[r#"
151 BlockId(1) in ModuleId { krate: CrateId(0), block: Some(BlockId(0)), local_id: Idx::<ModuleData>(0) }
152 BlockId(0) in ModuleId { krate: CrateId(0), block: None, local_id: Idx::<ModuleData>(0) }
153 crate scope
154 "#]],
155 );
156 // FIXME: The module nesting here is wrong!
157 // The first block map should be located in module #1 (`mod module`), not #0 (BlockId(0) root module)
158}
159
160#[test]
137fn legacy_macro_items() { 161fn legacy_macro_items() {
138 // Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded 162 // Checks that legacy-scoped `macro_rules!` from parent namespaces are resolved and expanded
139 // correctly. 163 // correctly.
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs
index f40a7f80d..f2e809ca9 100644
--- a/crates/hir_def/src/child_by_source.rs
+++ b/crates/hir_def/src/child_by_source.rs
@@ -80,6 +80,10 @@ impl ChildBySource for ModuleId {
80impl ChildBySource for ItemScope { 80impl ChildBySource for ItemScope {
81 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { 81 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
82 self.declarations().for_each(|item| add_module_def(db, res, item)); 82 self.declarations().for_each(|item| add_module_def(db, res, item));
83 self.unnamed_consts().for_each(|konst| {
84 let src = konst.lookup(db).source(db);
85 res[keys::CONST].insert(src, konst);
86 });
83 self.impls().for_each(|imp| add_impl(db, res, imp)); 87 self.impls().for_each(|imp| add_impl(db, res, imp));
84 88
85 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { 89 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) {
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index 068b2ee38..7eadc8e0d 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -2,9 +2,10 @@
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use base_db::{salsa, CrateId, SourceDatabase, Upcast}; 4use base_db::{salsa, CrateId, SourceDatabase, Upcast};
5use either::Either;
5use hir_expand::{db::AstDatabase, HirFileId}; 6use hir_expand::{db::AstDatabase, HirFileId};
6use la_arena::ArenaMap; 7use la_arena::ArenaMap;
7use syntax::SmolStr; 8use syntax::{ast, AstPtr, SmolStr};
8 9
9use crate::{ 10use crate::{
10 adt::{EnumData, StructData}, 11 adt::{EnumData, StructData},
@@ -13,6 +14,7 @@ use crate::{
13 data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, 14 data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData},
14 generics::GenericParams, 15 generics::GenericParams,
15 import_map::ImportMap, 16 import_map::ImportMap,
17 intern::Interned,
16 item_tree::ItemTree, 18 item_tree::ItemTree,
17 lang_item::{LangItemTarget, LangItems}, 19 lang_item::{LangItemTarget, LangItems},
18 nameres::DefMap, 20 nameres::DefMap,
@@ -113,7 +115,7 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
113 fn expr_scopes(&self, def: DefWithBodyId) -> Arc<ExprScopes>; 115 fn expr_scopes(&self, def: DefWithBodyId) -> Arc<ExprScopes>;
114 116
115 #[salsa::invoke(GenericParams::generic_params_query)] 117 #[salsa::invoke(GenericParams::generic_params_query)]
116 fn generic_params(&self, def: GenericDefId) -> Arc<GenericParams>; 118 fn generic_params(&self, def: GenericDefId) -> Interned<GenericParams>;
117 119
118 #[salsa::invoke(Attrs::variants_attrs_query)] 120 #[salsa::invoke(Attrs::variants_attrs_query)]
119 fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>; 121 fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>;
@@ -121,6 +123,18 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
121 #[salsa::invoke(Attrs::fields_attrs_query)] 123 #[salsa::invoke(Attrs::fields_attrs_query)]
122 fn fields_attrs(&self, def: VariantId) -> Arc<ArenaMap<LocalFieldId, Attrs>>; 124 fn fields_attrs(&self, def: VariantId) -> Arc<ArenaMap<LocalFieldId, Attrs>>;
123 125
126 #[salsa::invoke(crate::attr::variants_attrs_source_map)]
127 fn variants_attrs_source_map(
128 &self,
129 def: EnumId,
130 ) -> Arc<ArenaMap<LocalEnumVariantId, AstPtr<ast::Variant>>>;
131
132 #[salsa::invoke(crate::attr::fields_attrs_source_map)]
133 fn fields_attrs_source_map(
134 &self,
135 def: VariantId,
136 ) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>>;
137
124 #[salsa::invoke(AttrsWithOwner::attrs_query)] 138 #[salsa::invoke(AttrsWithOwner::attrs_query)]
125 fn attrs(&self, def: AttrDefId) -> AttrsWithOwner; 139 fn attrs(&self, def: AttrDefId) -> AttrsWithOwner;
126 140
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
index 97abf8653..a71ae2668 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -8,7 +8,7 @@ use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink};
8use hir_expand::{HirFileId, InFile}; 8use hir_expand::{HirFileId, InFile};
9use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; 9use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
10 10
11use crate::{db::DefDatabase, DefWithBodyId}; 11use crate::{db::DefDatabase, path::ModPath, DefWithBodyId};
12 12
13pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { 13pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
14 let source_map = db.body_with_source_map(owner).1; 14 let source_map = db.body_with_source_map(owner).1;
@@ -103,6 +103,7 @@ impl Diagnostic for UnresolvedImport {
103pub struct UnresolvedMacroCall { 103pub struct UnresolvedMacroCall {
104 pub file: HirFileId, 104 pub file: HirFileId,
105 pub node: AstPtr<ast::MacroCall>, 105 pub node: AstPtr<ast::MacroCall>,
106 pub path: ModPath,
106} 107}
107 108
108impl Diagnostic for UnresolvedMacroCall { 109impl Diagnostic for UnresolvedMacroCall {
@@ -110,7 +111,7 @@ impl Diagnostic for UnresolvedMacroCall {
110 DiagnosticCode("unresolved-macro-call") 111 DiagnosticCode("unresolved-macro-call")
111 } 112 }
112 fn message(&self) -> String { 113 fn message(&self) -> String {
113 "unresolved macro call".to_string() 114 format!("unresolved macro `{}!`", self.path)
114 } 115 }
115 fn display_source(&self) -> InFile<SyntaxNodePtr> { 116 fn display_source(&self) -> InFile<SyntaxNodePtr> {
116 InFile::new(self.file, self.node.clone().into()) 117 InFile::new(self.file, self.node.clone().into())
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index 6c7376fad..b4ad984bd 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -18,6 +18,7 @@ use syntax::ast::RangeOp;
18 18
19use crate::{ 19use crate::{
20 builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, 20 builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
21 intern::Interned,
21 path::{GenericArgs, Path}, 22 path::{GenericArgs, Path},
22 type_ref::{Mutability, Rawness, TypeRef}, 23 type_ref::{Mutability, Rawness, TypeRef},
23 BlockId, 24 BlockId,
@@ -86,7 +87,7 @@ pub enum Expr {
86 receiver: ExprId, 87 receiver: ExprId,
87 method_name: Name, 88 method_name: Name,
88 args: Vec<ExprId>, 89 args: Vec<ExprId>,
89 generic_args: Option<GenericArgs>, 90 generic_args: Option<Box<GenericArgs>>,
90 }, 91 },
91 Match { 92 Match {
92 expr: ExprId, 93 expr: ExprId,
@@ -106,7 +107,7 @@ pub enum Expr {
106 expr: Option<ExprId>, 107 expr: Option<ExprId>,
107 }, 108 },
108 RecordLit { 109 RecordLit {
109 path: Option<Path>, 110 path: Option<Box<Path>>,
110 fields: Vec<RecordLitField>, 111 fields: Vec<RecordLitField>,
111 spread: Option<ExprId>, 112 spread: Option<ExprId>,
112 }, 113 },
@@ -131,7 +132,7 @@ pub enum Expr {
131 }, 132 },
132 Cast { 133 Cast {
133 expr: ExprId, 134 expr: ExprId,
134 type_ref: TypeRef, 135 type_ref: Interned<TypeRef>,
135 }, 136 },
136 Ref { 137 Ref {
137 expr: ExprId, 138 expr: ExprId,
@@ -161,8 +162,8 @@ pub enum Expr {
161 }, 162 },
162 Lambda { 163 Lambda {
163 args: Vec<PatId>, 164 args: Vec<PatId>,
164 arg_types: Vec<Option<TypeRef>>, 165 arg_types: Vec<Option<Interned<TypeRef>>>,
165 ret_type: Option<TypeRef>, 166 ret_type: Option<Interned<TypeRef>>,
166 body: ExprId, 167 body: ExprId,
167 }, 168 },
168 Tuple { 169 Tuple {
@@ -240,7 +241,7 @@ pub struct RecordLitField {
240 241
241#[derive(Debug, Clone, Eq, PartialEq)] 242#[derive(Debug, Clone, Eq, PartialEq)]
242pub enum Statement { 243pub enum Statement {
243 Let { pat: PatId, type_ref: Option<TypeRef>, initializer: Option<ExprId> }, 244 Let { pat: PatId, type_ref: Option<Interned<TypeRef>>, initializer: Option<ExprId> },
244 Expr(ExprId), 245 Expr(ExprId),
245} 246}
246 247
@@ -412,13 +413,13 @@ pub enum Pat {
412 Wild, 413 Wild,
413 Tuple { args: Vec<PatId>, ellipsis: Option<usize> }, 414 Tuple { args: Vec<PatId>, ellipsis: Option<usize> },
414 Or(Vec<PatId>), 415 Or(Vec<PatId>),
415 Record { path: Option<Path>, args: Vec<RecordFieldPat>, ellipsis: bool }, 416 Record { path: Option<Box<Path>>, args: Vec<RecordFieldPat>, ellipsis: bool },
416 Range { start: ExprId, end: ExprId }, 417 Range { start: ExprId, end: ExprId },
417 Slice { prefix: Vec<PatId>, slice: Option<PatId>, suffix: Vec<PatId> }, 418 Slice { prefix: Vec<PatId>, slice: Option<PatId>, suffix: Vec<PatId> },
418 Path(Path), 419 Path(Box<Path>),
419 Lit(ExprId), 420 Lit(ExprId),
420 Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> }, 421 Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> },
421 TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> }, 422 TupleStruct { path: Option<Box<Path>>, args: Vec<PatId>, ellipsis: Option<usize> },
422 Ref { pat: PatId, mutability: Mutability }, 423 Ref { pat: PatId, mutability: Mutability },
423 Box { inner: PatId }, 424 Box { inner: PatId },
424 ConstBlock(ExprId), 425 ConstBlock(ExprId),
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 109d3552f..dc3f2908f 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -119,8 +119,7 @@ fn find_path_inner(
119 119
120 // - if the item is the crate root, return `crate` 120 // - if the item is the crate root, return `crate`
121 let root = def_map.crate_root(db); 121 let root = def_map.crate_root(db);
122 if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) && def_map.block_id().is_none() { 122 if item == ItemInNs::Types(ModuleDefId::ModuleId(root)) {
123 // FIXME: the `block_id()` check should be unnecessary, but affects the result
124 return Some(ModPath::from_segments(PathKind::Crate, Vec::new())); 123 return Some(ModPath::from_segments(PathKind::Crate, Vec::new()));
125 } 124 }
126 125
@@ -131,7 +130,7 @@ fn find_path_inner(
131 } 130 }
132 131
133 // - if the item is the crate root of a dependency crate, return the name from the extern prelude 132 // - if the item is the crate root of a dependency crate, return the name from the extern prelude
134 for (name, def_id) in def_map.extern_prelude() { 133 for (name, def_id) in root.def_map(db).extern_prelude() {
135 if item == ItemInNs::Types(*def_id) { 134 if item == ItemInNs::Types(*def_id) {
136 let name = scope_name.unwrap_or_else(|| name.clone()); 135 let name = scope_name.unwrap_or_else(|| name.clone());
137 return Some(ModPath::from_segments(PathKind::Plain, vec![name])); 136 return Some(ModPath::from_segments(PathKind::Plain, vec![name]));
@@ -298,6 +297,7 @@ fn find_local_import_locations(
298 let data = &def_map[from.local_id]; 297 let data = &def_map[from.local_id];
299 let mut worklist = 298 let mut worklist =
300 data.children.values().map(|child| def_map.module_id(*child)).collect::<Vec<_>>(); 299 data.children.values().map(|child| def_map.module_id(*child)).collect::<Vec<_>>();
300 // FIXME: do we need to traverse out of block expressions here?
301 for ancestor in iter::successors(from.containing_module(db), |m| m.containing_module(db)) { 301 for ancestor in iter::successors(from.containing_module(db), |m| m.containing_module(db)) {
302 worklist.push(ancestor); 302 worklist.push(ancestor);
303 } 303 }
@@ -425,106 +425,142 @@ mod tests {
425 425
426 #[test] 426 #[test]
427 fn same_module() { 427 fn same_module() {
428 let code = r#" 428 check_found_path(
429 //- /main.rs 429 r#"
430 struct S; 430struct S;
431 $0 431$0
432 "#; 432 "#,
433 check_found_path(code, "S", "S", "crate::S", "self::S"); 433 "S",
434 "S",
435 "crate::S",
436 "self::S",
437 );
434 } 438 }
435 439
436 #[test] 440 #[test]
437 fn enum_variant() { 441 fn enum_variant() {
438 let code = r#" 442 check_found_path(
439 //- /main.rs 443 r#"
440 enum E { A } 444enum E { A }
441 $0 445$0
442 "#; 446 "#,
443 check_found_path(code, "E::A", "E::A", "E::A", "E::A"); 447 "E::A",
448 "E::A",
449 "E::A",
450 "E::A",
451 );
444 } 452 }
445 453
446 #[test] 454 #[test]
447 fn sub_module() { 455 fn sub_module() {
448 let code = r#" 456 check_found_path(
449 //- /main.rs 457 r#"
450 mod foo { 458mod foo {
451 pub struct S; 459 pub struct S;
452 } 460}
453 $0 461$0
454 "#; 462 "#,
455 check_found_path(code, "foo::S", "foo::S", "crate::foo::S", "self::foo::S"); 463 "foo::S",
464 "foo::S",
465 "crate::foo::S",
466 "self::foo::S",
467 );
456 } 468 }
457 469
458 #[test] 470 #[test]
459 fn super_module() { 471 fn super_module() {
460 let code = r#" 472 check_found_path(
461 //- /main.rs 473 r#"
462 mod foo; 474//- /main.rs
463 //- /foo.rs 475mod foo;
464 mod bar; 476//- /foo.rs
465 struct S; 477mod bar;
466 //- /foo/bar.rs 478struct S;
467 $0 479//- /foo/bar.rs
468 "#; 480$0
469 check_found_path(code, "super::S", "super::S", "crate::foo::S", "super::S"); 481 "#,
482 "super::S",
483 "super::S",
484 "crate::foo::S",
485 "super::S",
486 );
470 } 487 }
471 488
472 #[test] 489 #[test]
473 fn self_module() { 490 fn self_module() {
474 let code = r#" 491 check_found_path(
475 //- /main.rs 492 r#"
476 mod foo; 493//- /main.rs
477 //- /foo.rs 494mod foo;
478 $0 495//- /foo.rs
479 "#; 496$0
480 check_found_path(code, "self", "self", "crate::foo", "self"); 497 "#,
498 "self",
499 "self",
500 "crate::foo",
501 "self",
502 );
481 } 503 }
482 504
483 #[test] 505 #[test]
484 fn crate_root() { 506 fn crate_root() {
485 let code = r#" 507 check_found_path(
486 //- /main.rs 508 r#"
487 mod foo; 509//- /main.rs
488 //- /foo.rs 510mod foo;
489 $0 511//- /foo.rs
490 "#; 512$0
491 check_found_path(code, "crate", "crate", "crate", "crate"); 513 "#,
514 "crate",
515 "crate",
516 "crate",
517 "crate",
518 );
492 } 519 }
493 520
494 #[test] 521 #[test]
495 fn same_crate() { 522 fn same_crate() {
496 let code = r#" 523 check_found_path(
497 //- /main.rs 524 r#"
498 mod foo; 525//- /main.rs
499 struct S; 526mod foo;
500 //- /foo.rs 527struct S;
501 $0 528//- /foo.rs
502 "#; 529$0
503 check_found_path(code, "crate::S", "crate::S", "crate::S", "crate::S"); 530 "#,
531 "crate::S",
532 "crate::S",
533 "crate::S",
534 "crate::S",
535 );
504 } 536 }
505 537
506 #[test] 538 #[test]
507 fn different_crate() { 539 fn different_crate() {
508 let code = r#" 540 check_found_path(
509 //- /main.rs crate:main deps:std 541 r#"
510 $0 542//- /main.rs crate:main deps:std
511 //- /std.rs crate:std 543$0
512 pub struct S; 544//- /std.rs crate:std
513 "#; 545pub struct S;
514 check_found_path(code, "std::S", "std::S", "std::S", "std::S"); 546 "#,
547 "std::S",
548 "std::S",
549 "std::S",
550 "std::S",
551 );
515 } 552 }
516 553
517 #[test] 554 #[test]
518 fn different_crate_renamed() { 555 fn different_crate_renamed() {
519 let code = r#"
520 //- /main.rs crate:main deps:std
521 extern crate std as std_renamed;
522 $0
523 //- /std.rs crate:std
524 pub struct S;
525 "#;
526 check_found_path( 556 check_found_path(
527 code, 557 r#"
558//- /main.rs crate:main deps:std
559extern crate std as std_renamed;
560$0
561//- /std.rs crate:std
562pub struct S;
563 "#,
528 "std_renamed::S", 564 "std_renamed::S",
529 "std_renamed::S", 565 "std_renamed::S",
530 "std_renamed::S", 566 "std_renamed::S",
@@ -537,41 +573,38 @@ mod tests {
537 cov_mark::check!(partially_imported); 573 cov_mark::check!(partially_imported);
538 // Tests that short paths are used even for external items, when parts of the path are 574 // Tests that short paths are used even for external items, when parts of the path are
539 // already in scope. 575 // already in scope.
540 let code = r#" 576 check_found_path(
541 //- /main.rs crate:main deps:syntax 577 r#"
578//- /main.rs crate:main deps:syntax
542 579
543 use syntax::ast; 580use syntax::ast;
544 $0 581$0
545 582
546 //- /lib.rs crate:syntax 583//- /lib.rs crate:syntax
547 pub mod ast { 584pub mod ast {
548 pub enum ModuleItem { 585 pub enum ModuleItem {
549 A, B, C, 586 A, B, C,
550 } 587 }
551 } 588}
552 "#; 589 "#,
553 check_found_path(
554 code,
555 "ast::ModuleItem", 590 "ast::ModuleItem",
556 "syntax::ast::ModuleItem", 591 "syntax::ast::ModuleItem",
557 "syntax::ast::ModuleItem", 592 "syntax::ast::ModuleItem",
558 "syntax::ast::ModuleItem", 593 "syntax::ast::ModuleItem",
559 ); 594 );
560 595
561 let code = r#"
562 //- /main.rs crate:main deps:syntax
563
564 $0
565
566 //- /lib.rs crate:syntax
567 pub mod ast {
568 pub enum ModuleItem {
569 A, B, C,
570 }
571 }
572 "#;
573 check_found_path( 596 check_found_path(
574 code, 597 r#"
598//- /main.rs crate:main deps:syntax
599$0
600
601//- /lib.rs crate:syntax
602pub mod ast {
603 pub enum ModuleItem {
604 A, B, C,
605 }
606}
607 "#,
575 "syntax::ast::ModuleItem", 608 "syntax::ast::ModuleItem",
576 "syntax::ast::ModuleItem", 609 "syntax::ast::ModuleItem",
577 "syntax::ast::ModuleItem", 610 "syntax::ast::ModuleItem",
@@ -581,68 +614,86 @@ mod tests {
581 614
582 #[test] 615 #[test]
583 fn same_crate_reexport() { 616 fn same_crate_reexport() {
584 let code = r#" 617 check_found_path(
585 //- /main.rs 618 r#"
586 mod bar { 619mod bar {
587 mod foo { pub(super) struct S; } 620 mod foo { pub(super) struct S; }
588 pub(crate) use foo::*; 621 pub(crate) use foo::*;
589 } 622}
590 $0 623$0
591 "#; 624 "#,
592 check_found_path(code, "bar::S", "bar::S", "crate::bar::S", "self::bar::S"); 625 "bar::S",
626 "bar::S",
627 "crate::bar::S",
628 "self::bar::S",
629 );
593 } 630 }
594 631
595 #[test] 632 #[test]
596 fn same_crate_reexport_rename() { 633 fn same_crate_reexport_rename() {
597 let code = r#" 634 check_found_path(
598 //- /main.rs 635 r#"
599 mod bar { 636mod bar {
600 mod foo { pub(super) struct S; } 637 mod foo { pub(super) struct S; }
601 pub(crate) use foo::S as U; 638 pub(crate) use foo::S as U;
602 } 639}
603 $0 640$0
604 "#; 641 "#,
605 check_found_path(code, "bar::U", "bar::U", "crate::bar::U", "self::bar::U"); 642 "bar::U",
643 "bar::U",
644 "crate::bar::U",
645 "self::bar::U",
646 );
606 } 647 }
607 648
608 #[test] 649 #[test]
609 fn different_crate_reexport() { 650 fn different_crate_reexport() {
610 let code = r#" 651 check_found_path(
611 //- /main.rs crate:main deps:std 652 r#"
612 $0 653//- /main.rs crate:main deps:std
613 //- /std.rs crate:std deps:core 654$0
614 pub use core::S; 655//- /std.rs crate:std deps:core
615 //- /core.rs crate:core 656pub use core::S;
616 pub struct S; 657//- /core.rs crate:core
617 "#; 658pub struct S;
618 check_found_path(code, "std::S", "std::S", "std::S", "std::S"); 659 "#,
660 "std::S",
661 "std::S",
662 "std::S",
663 "std::S",
664 );
619 } 665 }
620 666
621 #[test] 667 #[test]
622 fn prelude() { 668 fn prelude() {
623 let code = r#" 669 check_found_path(
624 //- /main.rs crate:main deps:std 670 r#"
625 $0 671//- /main.rs crate:main deps:std
626 //- /std.rs crate:std 672$0
627 pub mod prelude { pub struct S; } 673//- /std.rs crate:std
628 #[prelude_import] 674pub mod prelude { pub struct S; }
629 pub use prelude::*; 675#[prelude_import]
630 "#; 676pub use prelude::*;
631 check_found_path(code, "S", "S", "S", "S"); 677 "#,
678 "S",
679 "S",
680 "S",
681 "S",
682 );
632 } 683 }
633 684
634 #[test] 685 #[test]
635 fn enum_variant_from_prelude() { 686 fn enum_variant_from_prelude() {
636 let code = r#" 687 let code = r#"
637 //- /main.rs crate:main deps:std 688//- /main.rs crate:main deps:std
638 $0 689$0
639 //- /std.rs crate:std 690//- /std.rs crate:std
640 pub mod prelude { 691pub mod prelude {
641 pub enum Option<T> { Some(T), None } 692 pub enum Option<T> { Some(T), None }
642 pub use Option::*; 693 pub use Option::*;
643 } 694}
644 #[prelude_import] 695#[prelude_import]
645 pub use prelude::*; 696pub use prelude::*;
646 "#; 697 "#;
647 check_found_path(code, "None", "None", "None", "None"); 698 check_found_path(code, "None", "None", "None", "None");
648 check_found_path(code, "Some", "Some", "Some", "Some"); 699 check_found_path(code, "Some", "Some", "Some", "Some");
@@ -650,71 +701,85 @@ mod tests {
650 701
651 #[test] 702 #[test]
652 fn shortest_path() { 703 fn shortest_path() {
653 let code = r#" 704 check_found_path(
654 //- /main.rs 705 r#"
655 pub mod foo; 706//- /main.rs
656 pub mod baz; 707pub mod foo;
657 struct S; 708pub mod baz;
658 $0 709struct S;
659 //- /foo.rs 710$0
660 pub mod bar { pub struct S; } 711//- /foo.rs
661 //- /baz.rs 712pub mod bar { pub struct S; }
662 pub use crate::foo::bar::S; 713//- /baz.rs
663 "#; 714pub use crate::foo::bar::S;
664 check_found_path(code, "baz::S", "baz::S", "crate::baz::S", "self::baz::S"); 715 "#,
716 "baz::S",
717 "baz::S",
718 "crate::baz::S",
719 "self::baz::S",
720 );
665 } 721 }
666 722
667 #[test] 723 #[test]
668 fn discount_private_imports() { 724 fn discount_private_imports() {
669 let code = r#" 725 check_found_path(
670 //- /main.rs 726 r#"
671 mod foo; 727//- /main.rs
672 pub mod bar { pub struct S; } 728mod foo;
673 use bar::S; 729pub mod bar { pub struct S; }
674 //- /foo.rs 730use bar::S;
675 $0 731//- /foo.rs
676 "#; 732$0
677 // crate::S would be shorter, but using private imports seems wrong 733 "#,
678 check_found_path(code, "crate::bar::S", "crate::bar::S", "crate::bar::S", "crate::bar::S"); 734 // crate::S would be shorter, but using private imports seems wrong
735 "crate::bar::S",
736 "crate::bar::S",
737 "crate::bar::S",
738 "crate::bar::S",
739 );
679 } 740 }
680 741
681 #[test] 742 #[test]
682 fn import_cycle() { 743 fn import_cycle() {
683 let code = r#" 744 check_found_path(
684 //- /main.rs 745 r#"
685 pub mod foo; 746//- /main.rs
686 pub mod bar; 747pub mod foo;
687 pub mod baz; 748pub mod bar;
688 //- /bar.rs 749pub mod baz;
689 $0 750//- /bar.rs
690 //- /foo.rs 751$0
691 pub use super::baz; 752//- /foo.rs
692 pub struct S; 753pub use super::baz;
693 //- /baz.rs 754pub struct S;
694 pub use super::foo; 755//- /baz.rs
695 "#; 756pub use super::foo;
696 check_found_path(code, "crate::foo::S", "crate::foo::S", "crate::foo::S", "crate::foo::S"); 757 "#,
758 "crate::foo::S",
759 "crate::foo::S",
760 "crate::foo::S",
761 "crate::foo::S",
762 );
697 } 763 }
698 764
699 #[test] 765 #[test]
700 fn prefer_std_paths_over_alloc() { 766 fn prefer_std_paths_over_alloc() {
701 cov_mark::check!(prefer_std_paths); 767 cov_mark::check!(prefer_std_paths);
702 let code = r#" 768 check_found_path(
703 //- /main.rs crate:main deps:alloc,std 769 r#"
704 $0 770//- /main.rs crate:main deps:alloc,std
771$0
705 772
706 //- /std.rs crate:std deps:alloc 773//- /std.rs crate:std deps:alloc
707 pub mod sync { 774pub mod sync {
708 pub use alloc::sync::Arc; 775 pub use alloc::sync::Arc;
709 } 776}
710 777
711 //- /zzz.rs crate:alloc 778//- /zzz.rs crate:alloc
712 pub mod sync { 779pub mod sync {
713 pub struct Arc; 780 pub struct Arc;
714 } 781}
715 "#; 782 "#,
716 check_found_path(
717 code,
718 "std::sync::Arc", 783 "std::sync::Arc",
719 "std::sync::Arc", 784 "std::sync::Arc",
720 "std::sync::Arc", 785 "std::sync::Arc",
@@ -725,26 +790,25 @@ mod tests {
725 #[test] 790 #[test]
726 fn prefer_core_paths_over_std() { 791 fn prefer_core_paths_over_std() {
727 cov_mark::check!(prefer_no_std_paths); 792 cov_mark::check!(prefer_no_std_paths);
728 let code = r#" 793 check_found_path(
729 //- /main.rs crate:main deps:core,std 794 r#"
730 #![no_std] 795//- /main.rs crate:main deps:core,std
796#![no_std]
731 797
732 $0 798$0
733 799
734 //- /std.rs crate:std deps:core 800//- /std.rs crate:std deps:core
735 801
736 pub mod fmt { 802pub mod fmt {
737 pub use core::fmt::Error; 803 pub use core::fmt::Error;
738 } 804}
739 805
740 //- /zzz.rs crate:core 806//- /zzz.rs crate:core
741 807
742 pub mod fmt { 808pub mod fmt {
743 pub struct Error; 809 pub struct Error;
744 } 810}
745 "#; 811 "#,
746 check_found_path(
747 code,
748 "core::fmt::Error", 812 "core::fmt::Error",
749 "core::fmt::Error", 813 "core::fmt::Error",
750 "core::fmt::Error", 814 "core::fmt::Error",
@@ -754,26 +818,25 @@ mod tests {
754 818
755 #[test] 819 #[test]
756 fn prefer_alloc_paths_over_std() { 820 fn prefer_alloc_paths_over_std() {
757 let code = r#" 821 check_found_path(
758 //- /main.rs crate:main deps:alloc,std 822 r#"
759 #![no_std] 823//- /main.rs crate:main deps:alloc,std
824#![no_std]
760 825
761 $0 826$0
762 827
763 //- /std.rs crate:std deps:alloc 828//- /std.rs crate:std deps:alloc
764 829
765 pub mod sync { 830pub mod sync {
766 pub use alloc::sync::Arc; 831 pub use alloc::sync::Arc;
767 } 832}
768 833
769 //- /zzz.rs crate:alloc 834//- /zzz.rs crate:alloc
770 835
771 pub mod sync { 836pub mod sync {
772 pub struct Arc; 837 pub struct Arc;
773 } 838}
774 "#; 839 "#,
775 check_found_path(
776 code,
777 "alloc::sync::Arc", 840 "alloc::sync::Arc",
778 "alloc::sync::Arc", 841 "alloc::sync::Arc",
779 "alloc::sync::Arc", 842 "alloc::sync::Arc",
@@ -783,20 +846,19 @@ mod tests {
783 846
784 #[test] 847 #[test]
785 fn prefer_shorter_paths_if_not_alloc() { 848 fn prefer_shorter_paths_if_not_alloc() {
786 let code = r#" 849 check_found_path(
787 //- /main.rs crate:main deps:megaalloc,std 850 r#"
788 $0 851//- /main.rs crate:main deps:megaalloc,std
852$0
789 853
790 //- /std.rs crate:std deps:megaalloc 854//- /std.rs crate:std deps:megaalloc
791 pub mod sync { 855pub mod sync {
792 pub use megaalloc::sync::Arc; 856 pub use megaalloc::sync::Arc;
793 } 857}
794 858
795 //- /zzz.rs crate:megaalloc 859//- /zzz.rs crate:megaalloc
796 pub struct Arc; 860pub struct Arc;
797 "#; 861 "#,
798 check_found_path(
799 code,
800 "megaalloc::Arc", 862 "megaalloc::Arc",
801 "megaalloc::Arc", 863 "megaalloc::Arc",
802 "megaalloc::Arc", 864 "megaalloc::Arc",
@@ -807,12 +869,11 @@ mod tests {
807 #[test] 869 #[test]
808 fn builtins_are_in_scope() { 870 fn builtins_are_in_scope() {
809 let code = r#" 871 let code = r#"
810 //- /main.rs 872$0
811 $0
812 873
813 pub mod primitive { 874pub mod primitive {
814 pub use u8; 875 pub use u8;
815 } 876}
816 "#; 877 "#;
817 check_found_path(code, "u8", "u8", "u8", "u8"); 878 check_found_path(code, "u8", "u8", "u8", "u8");
818 check_found_path(code, "u16", "u16", "u16", "u16"); 879 check_found_path(code, "u16", "u16", "u16", "u16");
@@ -822,10 +883,10 @@ mod tests {
822 fn inner_items() { 883 fn inner_items() {
823 check_found_path( 884 check_found_path(
824 r#" 885 r#"
825 fn main() { 886fn main() {
826 struct Inner {} 887 struct Inner {}
827 $0 888 $0
828 } 889}
829 "#, 890 "#,
830 "Inner", 891 "Inner",
831 "Inner", 892 "Inner",
@@ -838,12 +899,12 @@ mod tests {
838 fn inner_items_from_outer_scope() { 899 fn inner_items_from_outer_scope() {
839 check_found_path( 900 check_found_path(
840 r#" 901 r#"
841 fn main() { 902fn main() {
842 struct Struct {} 903 struct Struct {}
843 { 904 {
844 $0 905 $0
845 } 906 }
846 } 907}
847 "#, 908 "#,
848 "Struct", 909 "Struct",
849 "Struct", 910 "Struct",
@@ -857,14 +918,14 @@ mod tests {
857 cov_mark::check!(prefixed_in_block_expression); 918 cov_mark::check!(prefixed_in_block_expression);
858 check_found_path( 919 check_found_path(
859 r#" 920 r#"
860 fn main() { 921fn main() {
861 mod module { 922 mod module {
862 struct Struct {} 923 struct Struct {}
863 } 924 }
864 { 925 {
865 $0 926 $0
866 } 927 }
867 } 928}
868 "#, 929 "#,
869 "module::Struct", 930 "module::Struct",
870 "module::Struct", 931 "module::Struct",
@@ -877,19 +938,65 @@ mod tests {
877 fn outer_items_with_inner_items_present() { 938 fn outer_items_with_inner_items_present() {
878 check_found_path( 939 check_found_path(
879 r#" 940 r#"
880 mod module { 941mod module {
881 pub struct CompleteMe; 942 pub struct CompleteMe;
882 } 943}
883 944
884 fn main() { 945fn main() {
885 fn inner() {} 946 fn inner() {}
886 $0 947 $0
887 } 948}
888 "#, 949 "#,
950 // FIXME: these could use fewer/better prefixes
889 "module::CompleteMe", 951 "module::CompleteMe",
890 "module::CompleteMe",
891 "crate::module::CompleteMe", 952 "crate::module::CompleteMe",
892 "self::module::CompleteMe", 953 "crate::module::CompleteMe",
954 "crate::module::CompleteMe",
955 )
956 }
957
958 #[test]
959 fn from_inside_module() {
960 // This worked correctly, but the test suite logic was broken.
961 cov_mark::check!(submodule_in_testdb);
962 check_found_path(
963 r#"
964mod baz {
965 pub struct Foo {}
966}
967
968mod bar {
969 fn bar() {
970 $0
971 }
972}
973 "#,
974 "crate::baz::Foo",
975 "crate::baz::Foo",
976 "crate::baz::Foo",
977 "crate::baz::Foo",
978 )
979 }
980
981 #[test]
982 fn from_inside_module_with_inner_items() {
983 check_found_path(
984 r#"
985mod baz {
986 pub struct Foo {}
987}
988
989mod bar {
990 fn bar() {
991 fn inner() {}
992 $0
993 }
994}
995 "#,
996 "crate::baz::Foo",
997 "crate::baz::Foo",
998 "crate::baz::Foo",
999 "crate::baz::Foo",
893 ) 1000 )
894 } 1001 }
895 1002
@@ -920,4 +1027,34 @@ pub mod name {
920 "self::name::AsName", 1027 "self::name::AsName",
921 ); 1028 );
922 } 1029 }
1030
1031 #[test]
1032 fn extern_crate() {
1033 check_found_path(
1034 r#"
1035//- /main.rs crate:main deps:dep
1036$0
1037//- /dep.rs crate:dep
1038"#,
1039 "dep",
1040 "dep",
1041 "dep",
1042 "dep",
1043 );
1044
1045 check_found_path(
1046 r#"
1047//- /main.rs crate:main deps:dep
1048fn f() {
1049 fn inner() {}
1050 $0
1051}
1052//- /dep.rs crate:dep
1053"#,
1054 "dep",
1055 "dep",
1056 "dep",
1057 "dep",
1058 );
1059 }
923} 1060}
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 7c6cbff11..de5acced8 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -2,7 +2,6 @@
2//! structs, impls, traits, etc. This module provides a common HIR for these 2//! structs, impls, traits, etc. This module provides a common HIR for these
3//! generic parameters. See also the `Generics` type and the `generics_of` query 3//! generic parameters. See also the `Generics` type and the `generics_of` query
4//! in rustc. 4//! in rustc.
5use std::sync::Arc;
6 5
7use base_db::FileId; 6use base_db::FileId;
8use either::Either; 7use either::Either;
@@ -18,6 +17,7 @@ use crate::{
18 child_by_source::ChildBySource, 17 child_by_source::ChildBySource,
19 db::DefDatabase, 18 db::DefDatabase,
20 dyn_map::DynMap, 19 dyn_map::DynMap,
20 intern::Interned,
21 keys, 21 keys,
22 src::{HasChildSource, HasSource}, 22 src::{HasChildSource, HasSource},
23 type_ref::{LifetimeRef, TypeBound, TypeRef}, 23 type_ref::{LifetimeRef, TypeBound, TypeRef},
@@ -26,27 +26,27 @@ use crate::{
26}; 26};
27 27
28/// Data about a generic type parameter (to a function, struct, impl, ...). 28/// Data about a generic type parameter (to a function, struct, impl, ...).
29#[derive(Clone, PartialEq, Eq, Debug)] 29#[derive(Clone, PartialEq, Eq, Debug, Hash)]
30pub struct TypeParamData { 30pub struct TypeParamData {
31 pub name: Option<Name>, 31 pub name: Option<Name>,
32 pub default: Option<TypeRef>, 32 pub default: Option<Interned<TypeRef>>,
33 pub provenance: TypeParamProvenance, 33 pub provenance: TypeParamProvenance,
34} 34}
35 35
36/// Data about a generic lifetime parameter (to a function, struct, impl, ...). 36/// Data about a generic lifetime parameter (to a function, struct, impl, ...).
37#[derive(Clone, PartialEq, Eq, Debug)] 37#[derive(Clone, PartialEq, Eq, Debug, Hash)]
38pub struct LifetimeParamData { 38pub struct LifetimeParamData {
39 pub name: Name, 39 pub name: Name,
40} 40}
41 41
42/// Data about a generic const parameter (to a function, struct, impl, ...). 42/// Data about a generic const parameter (to a function, struct, impl, ...).
43#[derive(Clone, PartialEq, Eq, Debug)] 43#[derive(Clone, PartialEq, Eq, Debug, Hash)]
44pub struct ConstParamData { 44pub struct ConstParamData {
45 pub name: Name, 45 pub name: Name,
46 pub ty: TypeRef, 46 pub ty: Interned<TypeRef>,
47} 47}
48 48
49#[derive(Copy, Clone, PartialEq, Eq, Debug)] 49#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
50pub enum TypeParamProvenance { 50pub enum TypeParamProvenance {
51 TypeParamList, 51 TypeParamList,
52 TraitSelf, 52 TraitSelf,
@@ -54,7 +54,7 @@ pub enum TypeParamProvenance {
54} 54}
55 55
56/// Data about the generic parameters of a function, struct, impl, etc. 56/// Data about the generic parameters of a function, struct, impl, etc.
57#[derive(Clone, PartialEq, Eq, Debug, Default)] 57#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)]
58pub struct GenericParams { 58pub struct GenericParams {
59 pub types: Arena<TypeParamData>, 59 pub types: Arena<TypeParamData>,
60 pub lifetimes: Arena<LifetimeParamData>, 60 pub lifetimes: Arena<LifetimeParamData>,
@@ -66,16 +66,16 @@ pub struct GenericParams {
66/// where clauses like `where T: Foo + Bar` are turned into multiple of these. 66/// where clauses like `where T: Foo + Bar` are turned into multiple of these.
67/// It might still result in multiple actual predicates though, because of 67/// It might still result in multiple actual predicates though, because of
68/// associated type bindings like `Iterator<Item = u32>`. 68/// associated type bindings like `Iterator<Item = u32>`.
69#[derive(Clone, PartialEq, Eq, Debug)] 69#[derive(Clone, PartialEq, Eq, Debug, Hash)]
70pub enum WherePredicate { 70pub enum WherePredicate {
71 TypeBound { target: WherePredicateTypeTarget, bound: TypeBound }, 71 TypeBound { target: WherePredicateTypeTarget, bound: TypeBound },
72 Lifetime { target: LifetimeRef, bound: LifetimeRef }, 72 Lifetime { target: LifetimeRef, bound: LifetimeRef },
73 ForLifetime { lifetimes: Box<[Name]>, target: WherePredicateTypeTarget, bound: TypeBound }, 73 ForLifetime { lifetimes: Box<[Name]>, target: WherePredicateTypeTarget, bound: TypeBound },
74} 74}
75 75
76#[derive(Clone, PartialEq, Eq, Debug)] 76#[derive(Clone, PartialEq, Eq, Debug, Hash)]
77pub enum WherePredicateTypeTarget { 77pub enum WherePredicateTypeTarget {
78 TypeRef(TypeRef), 78 TypeRef(Interned<TypeRef>),
79 /// For desugared where predicates that can directly refer to a type param. 79 /// For desugared where predicates that can directly refer to a type param.
80 TypeParam(LocalTypeParamId), 80 TypeParam(LocalTypeParamId),
81} 81}
@@ -91,7 +91,7 @@ impl GenericParams {
91 pub(crate) fn generic_params_query( 91 pub(crate) fn generic_params_query(
92 db: &dyn DefDatabase, 92 db: &dyn DefDatabase,
93 def: GenericDefId, 93 def: GenericDefId,
94 ) -> Arc<GenericParams> { 94 ) -> Interned<GenericParams> {
95 let _p = profile::span("generic_params_query"); 95 let _p = profile::span("generic_params_query");
96 96
97 let generics = match def { 97 let generics = match def {
@@ -99,47 +99,49 @@ impl GenericParams {
99 let id = id.lookup(db).id; 99 let id = id.lookup(db).id;
100 let tree = id.item_tree(db); 100 let tree = id.item_tree(db);
101 let item = &tree[id.value]; 101 let item = &tree[id.value];
102 tree[item.generic_params].clone() 102 item.generic_params.clone()
103 } 103 }
104 GenericDefId::AdtId(AdtId::StructId(id)) => { 104 GenericDefId::AdtId(AdtId::StructId(id)) => {
105 let id = id.lookup(db).id; 105 let id = id.lookup(db).id;
106 let tree = id.item_tree(db); 106 let tree = id.item_tree(db);
107 let item = &tree[id.value]; 107 let item = &tree[id.value];
108 tree[item.generic_params].clone() 108 item.generic_params.clone()
109 } 109 }
110 GenericDefId::AdtId(AdtId::EnumId(id)) => { 110 GenericDefId::AdtId(AdtId::EnumId(id)) => {
111 let id = id.lookup(db).id; 111 let id = id.lookup(db).id;
112 let tree = id.item_tree(db); 112 let tree = id.item_tree(db);
113 let item = &tree[id.value]; 113 let item = &tree[id.value];
114 tree[item.generic_params].clone() 114 item.generic_params.clone()
115 } 115 }
116 GenericDefId::AdtId(AdtId::UnionId(id)) => { 116 GenericDefId::AdtId(AdtId::UnionId(id)) => {
117 let id = id.lookup(db).id; 117 let id = id.lookup(db).id;
118 let tree = id.item_tree(db); 118 let tree = id.item_tree(db);
119 let item = &tree[id.value]; 119 let item = &tree[id.value];
120 tree[item.generic_params].clone() 120 item.generic_params.clone()
121 } 121 }
122 GenericDefId::TraitId(id) => { 122 GenericDefId::TraitId(id) => {
123 let id = id.lookup(db).id; 123 let id = id.lookup(db).id;
124 let tree = id.item_tree(db); 124 let tree = id.item_tree(db);
125 let item = &tree[id.value]; 125 let item = &tree[id.value];
126 tree[item.generic_params].clone() 126 item.generic_params.clone()
127 } 127 }
128 GenericDefId::TypeAliasId(id) => { 128 GenericDefId::TypeAliasId(id) => {
129 let id = id.lookup(db).id; 129 let id = id.lookup(db).id;
130 let tree = id.item_tree(db); 130 let tree = id.item_tree(db);
131 let item = &tree[id.value]; 131 let item = &tree[id.value];
132 tree[item.generic_params].clone() 132 item.generic_params.clone()
133 } 133 }
134 GenericDefId::ImplId(id) => { 134 GenericDefId::ImplId(id) => {
135 let id = id.lookup(db).id; 135 let id = id.lookup(db).id;
136 let tree = id.item_tree(db); 136 let tree = id.item_tree(db);
137 let item = &tree[id.value]; 137 let item = &tree[id.value];
138 tree[item.generic_params].clone() 138 item.generic_params.clone()
139 }
140 GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => {
141 Interned::new(GenericParams::default())
139 } 142 }
140 GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => GenericParams::default(),
141 }; 143 };
142 Arc::new(generics) 144 generics
143 } 145 }
144 146
145 fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) { 147 fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) {
@@ -217,6 +219,7 @@ impl GenericParams {
217 GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => FileId(!0).into(), 219 GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => FileId(!0).into(),
218 }; 220 };
219 221
222 generics.shrink_to_fit();
220 (generics, InFile::new(file_id, sm)) 223 (generics, InFile::new(file_id, sm))
221 } 224 }
222 225
@@ -256,7 +259,8 @@ impl GenericParams {
256 for type_param in params.type_params() { 259 for type_param in params.type_params() {
257 let name = type_param.name().map_or_else(Name::missing, |it| it.as_name()); 260 let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
258 // FIXME: Use `Path::from_src` 261 // FIXME: Use `Path::from_src`
259 let default = type_param.default_type().map(|it| TypeRef::from_ast(lower_ctx, it)); 262 let default =
263 type_param.default_type().map(|it| Interned::new(TypeRef::from_ast(lower_ctx, it)));
260 let param = TypeParamData { 264 let param = TypeParamData {
261 name: Some(name.clone()), 265 name: Some(name.clone()),
262 default, 266 default,
@@ -280,7 +284,7 @@ impl GenericParams {
280 for const_param in params.const_params() { 284 for const_param in params.const_params() {
281 let name = const_param.name().map_or_else(Name::missing, |it| it.as_name()); 285 let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
282 let ty = const_param.ty().map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it)); 286 let ty = const_param.ty().map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it));
283 let param = ConstParamData { name, ty }; 287 let param = ConstParamData { name, ty: Interned::new(ty) };
284 let param_id = self.consts.alloc(param); 288 let param_id = self.consts.alloc(param);
285 sm.const_params.insert(param_id, const_param.clone()); 289 sm.const_params.insert(param_id, const_param.clone());
286 } 290 }
@@ -334,11 +338,11 @@ impl GenericParams {
334 (Either::Left(type_ref), bound) => match hrtb_lifetimes { 338 (Either::Left(type_ref), bound) => match hrtb_lifetimes {
335 Some(hrtb_lifetimes) => WherePredicate::ForLifetime { 339 Some(hrtb_lifetimes) => WherePredicate::ForLifetime {
336 lifetimes: hrtb_lifetimes.clone(), 340 lifetimes: hrtb_lifetimes.clone(),
337 target: WherePredicateTypeTarget::TypeRef(type_ref), 341 target: WherePredicateTypeTarget::TypeRef(Interned::new(type_ref)),
338 bound, 342 bound,
339 }, 343 },
340 None => WherePredicate::TypeBound { 344 None => WherePredicate::TypeBound {
341 target: WherePredicateTypeTarget::TypeRef(type_ref), 345 target: WherePredicateTypeTarget::TypeRef(Interned::new(type_ref)),
342 bound, 346 bound,
343 }, 347 },
344 }, 348 },
@@ -369,6 +373,14 @@ impl GenericParams {
369 }); 373 });
370 } 374 }
371 375
376 pub(crate) fn shrink_to_fit(&mut self) {
377 let Self { consts, lifetimes, types, where_predicates } = self;
378 consts.shrink_to_fit();
379 lifetimes.shrink_to_fit();
380 types.shrink_to_fit();
381 where_predicates.shrink_to_fit();
382 }
383
372 pub fn find_type_by_name(&self, name: &Name) -> Option<LocalTypeParamId> { 384 pub fn find_type_by_name(&self, name: &Name) -> Option<LocalTypeParamId> {
373 self.types 385 self.types
374 .iter() 386 .iter()
diff --git a/crates/hir_def/src/intern.rs b/crates/hir_def/src/intern.rs
index d163f633f..abc304ef0 100644
--- a/crates/hir_def/src/intern.rs
+++ b/crates/hir_def/src/intern.rs
@@ -5,7 +5,7 @@
5use std::{ 5use std::{
6 collections::HashMap, 6 collections::HashMap,
7 fmt::{self, Debug}, 7 fmt::{self, Debug},
8 hash::{BuildHasherDefault, Hash}, 8 hash::{BuildHasherDefault, Hash, Hasher},
9 ops::Deref, 9 ops::Deref,
10 sync::Arc, 10 sync::Arc,
11}; 11};
@@ -14,11 +14,12 @@ use dashmap::{lock::RwLockWriteGuard, DashMap, SharedValue};
14use once_cell::sync::OnceCell; 14use once_cell::sync::OnceCell;
15use rustc_hash::FxHasher; 15use rustc_hash::FxHasher;
16 16
17use crate::generics::GenericParams;
18
17type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>; 19type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>;
18type Guard<T> = 20type Guard<T> =
19 RwLockWriteGuard<'static, HashMap<Arc<T>, SharedValue<()>, BuildHasherDefault<FxHasher>>>; 21 RwLockWriteGuard<'static, HashMap<Arc<T>, SharedValue<()>, BuildHasherDefault<FxHasher>>>;
20 22
21#[derive(Hash)]
22pub struct Interned<T: Internable + ?Sized> { 23pub struct Interned<T: Internable + ?Sized> {
23 arc: Arc<T>, 24 arc: Arc<T>,
24} 25}
@@ -135,6 +136,13 @@ impl PartialEq for Interned<str> {
135 136
136impl Eq for Interned<str> {} 137impl Eq for Interned<str> {}
137 138
139impl<T: Internable + ?Sized> Hash for Interned<T> {
140 fn hash<H: Hasher>(&self, state: &mut H) {
141 // NOTE: Cast disposes vtable pointer / slice/str length.
142 state.write_usize(Arc::as_ptr(&self.arc) as *const () as usize)
143 }
144}
145
138impl<T: Internable + ?Sized> AsRef<T> for Interned<T> { 146impl<T: Internable + ?Sized> AsRef<T> for Interned<T> {
139 #[inline] 147 #[inline]
140 fn as_ref(&self) -> &T { 148 fn as_ref(&self) -> &T {
@@ -183,7 +191,10 @@ pub trait Internable: Hash + Eq + 'static {
183 fn storage() -> &'static InternStorage<Self>; 191 fn storage() -> &'static InternStorage<Self>;
184} 192}
185 193
186macro_rules! impl_internable { 194/// Implements `Internable` for a given list of types, making them usable with `Interned`.
195#[macro_export]
196#[doc(hidden)]
197macro_rules! _impl_internable {
187 ( $($t:path),+ $(,)? ) => { $( 198 ( $($t:path),+ $(,)? ) => { $(
188 impl Internable for $t { 199 impl Internable for $t {
189 fn storage() -> &'static InternStorage<Self> { 200 fn storage() -> &'static InternStorage<Self> {
@@ -194,4 +205,12 @@ macro_rules! impl_internable {
194 )+ }; 205 )+ };
195} 206}
196 207
197impl_internable!(crate::type_ref::TypeRef, crate::type_ref::TraitRef, crate::path::ModPath, str); 208pub use crate::_impl_internable as impl_internable;
209
210impl_internable!(
211 crate::type_ref::TypeRef,
212 crate::type_ref::TraitRef,
213 crate::path::ModPath,
214 GenericParams,
215 str,
216);
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index a8ee5eeac..9014468ea 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -11,7 +11,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
11use stdx::format_to; 11use stdx::format_to;
12 12
13use crate::{ 13use crate::{
14 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, 14 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId,
15 LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId, 15 LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId,
16}; 16};
17 17
@@ -37,6 +37,7 @@ pub struct ItemScope {
37 37
38 defs: Vec<ModuleDefId>, 38 defs: Vec<ModuleDefId>,
39 impls: Vec<ImplId>, 39 impls: Vec<ImplId>,
40 unnamed_consts: Vec<ConstId>,
40 /// Traits imported via `use Trait as _;`. 41 /// Traits imported via `use Trait as _;`.
41 unnamed_trait_imports: FxHashMap<TraitId, Visibility>, 42 unnamed_trait_imports: FxHashMap<TraitId, Visibility>,
42 /// Macros visible in current module in legacy textual scope 43 /// Macros visible in current module in legacy textual scope
@@ -106,6 +107,10 @@ impl ItemScope {
106 .map(|(_, v)| v) 107 .map(|(_, v)| v)
107 } 108 }
108 109
110 pub fn unnamed_consts(&self) -> impl Iterator<Item = ConstId> + '_ {
111 self.unnamed_consts.iter().copied()
112 }
113
109 /// Iterate over all module scoped macros 114 /// Iterate over all module scoped macros
110 pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { 115 pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
111 self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) 116 self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_)))
@@ -156,6 +161,10 @@ impl ItemScope {
156 self.impls.push(imp) 161 self.impls.push(imp)
157 } 162 }
158 163
164 pub(crate) fn define_unnamed_const(&mut self, konst: ConstId) {
165 self.unnamed_consts.push(konst);
166 }
167
159 pub(crate) fn define_legacy_macro(&mut self, name: Name, mac: MacroDefId) { 168 pub(crate) fn define_legacy_macro(&mut self, name: Name, mac: MacroDefId) {
160 self.legacy_macros.insert(name, mac); 169 self.legacy_macros.insert(name, mac);
161 } 170 }
@@ -295,6 +304,7 @@ impl ItemScope {
295 unresolved, 304 unresolved,
296 defs, 305 defs,
297 impls, 306 impls,
307 unnamed_consts,
298 unnamed_trait_imports, 308 unnamed_trait_imports,
299 legacy_macros, 309 legacy_macros,
300 } = self; 310 } = self;
@@ -304,6 +314,7 @@ impl ItemScope {
304 unresolved.shrink_to_fit(); 314 unresolved.shrink_to_fit();
305 defs.shrink_to_fit(); 315 defs.shrink_to_fit();
306 impls.shrink_to_fit(); 316 impls.shrink_to_fit();
317 unnamed_consts.shrink_to_fit();
307 unnamed_trait_imports.shrink_to_fit(); 318 unnamed_trait_imports.shrink_to_fit();
308 legacy_macros.shrink_to_fit(); 319 legacy_macros.shrink_to_fit();
309 } 320 }
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 739906778..eaeca01bd 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -58,13 +58,6 @@ impl fmt::Debug for RawVisibilityId {
58 } 58 }
59} 59}
60 60
61#[derive(Debug, Copy, Clone, Eq, PartialEq)]
62pub struct GenericParamsId(u32);
63
64impl GenericParamsId {
65 pub const EMPTY: Self = GenericParamsId(u32::max_value());
66}
67
68/// The item tree of a source file. 61/// The item tree of a source file.
69#[derive(Debug, Default, Eq, PartialEq)] 62#[derive(Debug, Default, Eq, PartialEq)]
70pub struct ItemTree { 63pub struct ItemTree {
@@ -106,6 +99,16 @@ impl ItemTree {
106 // items. 99 // items.
107 ctx.lower_macro_stmts(stmts) 100 ctx.lower_macro_stmts(stmts)
108 }, 101 },
102 ast::Pat(_pat) => {
103 // FIXME: This occurs because macros in pattern position are treated as inner
104 // items and expanded during block DefMap computation
105 return Default::default();
106 },
107 ast::Type(ty) => {
108 // Types can contain inner items. We return an empty item tree in this case, but
109 // still need to collect inner items.
110 ctx.lower_inner_items(ty.syntax())
111 },
109 ast::Expr(e) => { 112 ast::Expr(e) => {
110 // Macros can expand to expressions. We return an empty item tree in this case, but 113 // Macros can expand to expressions. We return an empty item tree in this case, but
111 // still need to collect inner items. 114 // still need to collect inner items.
@@ -146,7 +149,6 @@ impl ItemTree {
146 macro_rules, 149 macro_rules,
147 macro_defs, 150 macro_defs,
148 vis, 151 vis,
149 generics,
150 inner_items, 152 inner_items,
151 } = &mut **data; 153 } = &mut **data;
152 154
@@ -170,7 +172,6 @@ impl ItemTree {
170 macro_defs.shrink_to_fit(); 172 macro_defs.shrink_to_fit();
171 173
172 vis.arena.shrink_to_fit(); 174 vis.arena.shrink_to_fit();
173 generics.arena.shrink_to_fit();
174 175
175 inner_items.shrink_to_fit(); 176 inner_items.shrink_to_fit();
176 } 177 }
@@ -195,13 +196,6 @@ impl ItemTree {
195 self.raw_attrs(of).clone().filter(db, krate) 196 self.raw_attrs(of).clone().filter(db, krate)
196 } 197 }
197 198
198 pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ {
199 match &self.data {
200 Some(data) => Some(data.inner_items.values().flatten().copied()).into_iter().flatten(),
201 None => None.into_iter().flatten(),
202 }
203 }
204
205 pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] { 199 pub fn inner_items_of_block(&self, block: FileAstId<ast::BlockExpr>) -> &[ModItem] {
206 match &self.data { 200 match &self.data {
207 Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]), 201 Some(data) => data.inner_items.get(&block).map(|it| &**it).unwrap_or(&[]),
@@ -242,32 +236,6 @@ static VIS_PRIV: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKi
242static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKind::Crate)); 236static VIS_PUB_CRATE: RawVisibility = RawVisibility::Module(ModPath::from_kind(PathKind::Crate));
243 237
244#[derive(Default, Debug, Eq, PartialEq)] 238#[derive(Default, Debug, Eq, PartialEq)]
245struct GenericParamsStorage {
246 arena: Arena<GenericParams>,
247}
248
249impl GenericParamsStorage {
250 fn alloc(&mut self, params: GenericParams) -> GenericParamsId {
251 if params.types.is_empty()
252 && params.lifetimes.is_empty()
253 && params.consts.is_empty()
254 && params.where_predicates.is_empty()
255 {
256 return GenericParamsId::EMPTY;
257 }
258
259 GenericParamsId(self.arena.alloc(params).into_raw().into())
260 }
261}
262
263static EMPTY_GENERICS: GenericParams = GenericParams {
264 types: Arena::new(),
265 lifetimes: Arena::new(),
266 consts: Arena::new(),
267 where_predicates: Vec::new(),
268};
269
270#[derive(Default, Debug, Eq, PartialEq)]
271struct ItemTreeData { 239struct ItemTreeData {
272 imports: Arena<Import>, 240 imports: Arena<Import>,
273 extern_crates: Arena<ExternCrate>, 241 extern_crates: Arena<ExternCrate>,
@@ -289,7 +257,6 @@ struct ItemTreeData {
289 macro_defs: Arena<MacroDef>, 257 macro_defs: Arena<MacroDef>,
290 258
291 vis: ItemVisibilities, 259 vis: ItemVisibilities,
292 generics: GenericParamsStorage,
293 260
294 inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>, 261 inner_items: FxHashMap<FileAstId<ast::BlockExpr>, SmallVec<[ModItem; 1]>>,
295} 262}
@@ -508,17 +475,6 @@ impl Index<RawVisibilityId> for ItemTree {
508 } 475 }
509} 476}
510 477
511impl Index<GenericParamsId> for ItemTree {
512 type Output = GenericParams;
513
514 fn index(&self, index: GenericParamsId) -> &Self::Output {
515 match index {
516 GenericParamsId::EMPTY => &EMPTY_GENERICS,
517 _ => &self.data().generics.arena[Idx::from_raw(index.0.into())],
518 }
519 }
520}
521
522impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree { 478impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
523 type Output = N; 479 type Output = N;
524 fn index(&self, id: FileItemTreeId<N>) -> &N { 480 fn index(&self, id: FileItemTreeId<N>) -> &N {
@@ -555,7 +511,7 @@ pub struct ExternCrate {
555pub struct Function { 511pub struct Function {
556 pub name: Name, 512 pub name: Name,
557 pub visibility: RawVisibilityId, 513 pub visibility: RawVisibilityId,
558 pub generic_params: GenericParamsId, 514 pub generic_params: Interned<GenericParams>,
559 pub abi: Option<Interned<str>>, 515 pub abi: Option<Interned<str>>,
560 pub params: IdRange<Param>, 516 pub params: IdRange<Param>,
561 pub ret_type: Interned<TypeRef>, 517 pub ret_type: Interned<TypeRef>,
@@ -590,7 +546,7 @@ impl FnFlags {
590pub struct Struct { 546pub struct Struct {
591 pub name: Name, 547 pub name: Name,
592 pub visibility: RawVisibilityId, 548 pub visibility: RawVisibilityId,
593 pub generic_params: GenericParamsId, 549 pub generic_params: Interned<GenericParams>,
594 pub fields: Fields, 550 pub fields: Fields,
595 pub ast_id: FileAstId<ast::Struct>, 551 pub ast_id: FileAstId<ast::Struct>,
596 pub kind: StructDefKind, 552 pub kind: StructDefKind,
@@ -610,7 +566,7 @@ pub enum StructDefKind {
610pub struct Union { 566pub struct Union {
611 pub name: Name, 567 pub name: Name,
612 pub visibility: RawVisibilityId, 568 pub visibility: RawVisibilityId,
613 pub generic_params: GenericParamsId, 569 pub generic_params: Interned<GenericParams>,
614 pub fields: Fields, 570 pub fields: Fields,
615 pub ast_id: FileAstId<ast::Union>, 571 pub ast_id: FileAstId<ast::Union>,
616} 572}
@@ -619,7 +575,7 @@ pub struct Union {
619pub struct Enum { 575pub struct Enum {
620 pub name: Name, 576 pub name: Name,
621 pub visibility: RawVisibilityId, 577 pub visibility: RawVisibilityId,
622 pub generic_params: GenericParamsId, 578 pub generic_params: Interned<GenericParams>,
623 pub variants: IdRange<Variant>, 579 pub variants: IdRange<Variant>,
624 pub ast_id: FileAstId<ast::Enum>, 580 pub ast_id: FileAstId<ast::Enum>,
625} 581}
@@ -648,7 +604,7 @@ pub struct Static {
648pub struct Trait { 604pub struct Trait {
649 pub name: Name, 605 pub name: Name,
650 pub visibility: RawVisibilityId, 606 pub visibility: RawVisibilityId,
651 pub generic_params: GenericParamsId, 607 pub generic_params: Interned<GenericParams>,
652 pub is_auto: bool, 608 pub is_auto: bool,
653 pub is_unsafe: bool, 609 pub is_unsafe: bool,
654 pub bounds: Box<[TypeBound]>, 610 pub bounds: Box<[TypeBound]>,
@@ -658,7 +614,7 @@ pub struct Trait {
658 614
659#[derive(Debug, Clone, Eq, PartialEq)] 615#[derive(Debug, Clone, Eq, PartialEq)]
660pub struct Impl { 616pub struct Impl {
661 pub generic_params: GenericParamsId, 617 pub generic_params: Interned<GenericParams>,
662 pub target_trait: Option<Interned<TraitRef>>, 618 pub target_trait: Option<Interned<TraitRef>>,
663 pub self_ty: Interned<TypeRef>, 619 pub self_ty: Interned<TypeRef>,
664 pub is_negative: bool, 620 pub is_negative: bool,
@@ -672,7 +628,7 @@ pub struct TypeAlias {
672 pub visibility: RawVisibilityId, 628 pub visibility: RawVisibilityId,
673 /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`. 629 /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`.
674 pub bounds: Box<[TypeBound]>, 630 pub bounds: Box<[TypeBound]>,
675 pub generic_params: GenericParamsId, 631 pub generic_params: Interned<GenericParams>,
676 pub type_ref: Option<Interned<TypeRef>>, 632 pub type_ref: Option<Interned<TypeRef>>,
677 pub is_extern: bool, 633 pub is_extern: bool,
678 pub ast_id: FileAstId<ast::TypeAlias>, 634 pub ast_id: FileAstId<ast::TypeAlias>,
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index ab7ad8310..45b099cf3 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -189,7 +189,7 @@ impl Ctx {
189 block_stack.push(self.source_ast_id_map.ast_id(&block)); 189 block_stack.push(self.source_ast_id_map.ast_id(&block));
190 }, 190 },
191 ast::Item(item) => { 191 ast::Item(item) => {
192 // FIXME: This triggers for macro calls in expression position 192 // FIXME: This triggers for macro calls in expression/pattern/type position
193 let mod_items = self.lower_mod_item(&item, true); 193 let mod_items = self.lower_mod_item(&item, true);
194 let current_block = block_stack.last(); 194 let current_block = block_stack.last();
195 if let (Some(mod_items), Some(block)) = (mod_items, current_block) { 195 if let (Some(mod_items), Some(block)) = (mod_items, current_block) {
@@ -434,7 +434,7 @@ impl Ctx {
434 let mut res = Function { 434 let mut res = Function {
435 name, 435 name,
436 visibility, 436 visibility,
437 generic_params: GenericParamsId::EMPTY, 437 generic_params: Interned::new(GenericParams::default()),
438 abi, 438 abi,
439 params, 439 params,
440 ret_type: Interned::new(ret_type), 440 ret_type: Interned::new(ret_type),
@@ -682,7 +682,7 @@ impl Ctx {
682 &mut self, 682 &mut self,
683 owner: GenericsOwner<'_>, 683 owner: GenericsOwner<'_>,
684 node: &impl ast::GenericParamsOwner, 684 node: &impl ast::GenericParamsOwner,
685 ) -> GenericParamsId { 685 ) -> Interned<GenericParams> {
686 // Generics are part of item headers and may contain inner items we need to collect. 686 // Generics are part of item headers and may contain inner items we need to collect.
687 if let Some(params) = node.generic_param_list() { 687 if let Some(params) = node.generic_param_list() {
688 self.collect_inner_items(params.syntax()); 688 self.collect_inner_items(params.syntax());
@@ -698,7 +698,7 @@ impl Ctx {
698 &mut self, 698 &mut self,
699 owner: GenericsOwner<'_>, 699 owner: GenericsOwner<'_>,
700 node: &impl ast::GenericParamsOwner, 700 node: &impl ast::GenericParamsOwner,
701 ) -> GenericParamsId { 701 ) -> Interned<GenericParams> {
702 let mut sm = &mut Default::default(); 702 let mut sm = &mut Default::default();
703 let mut generics = GenericParams::default(); 703 let mut generics = GenericParams::default();
704 match owner { 704 match owner {
@@ -739,7 +739,8 @@ impl Ctx {
739 } 739 }
740 } 740 }
741 741
742 self.data().generics.alloc(generics) 742 generics.shrink_to_fit();
743 Interned::new(generics)
743 } 744 }
744 745
745 fn lower_type_bounds(&mut self, node: &impl ast::TypeBoundsOwner) -> Vec<TypeBound> { 746 fn lower_type_bounds(&mut self, node: &impl ast::TypeBoundsOwner) -> Vec<TypeBound> {
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index f408e510a..25694f037 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -27,6 +27,7 @@ pub mod dyn_map;
27pub mod keys; 27pub mod keys;
28 28
29pub mod item_tree; 29pub mod item_tree;
30pub mod intern;
30 31
31pub mod adt; 32pub mod adt;
32pub mod data; 33pub mod data;
@@ -49,22 +50,23 @@ pub mod import_map;
49 50
50#[cfg(test)] 51#[cfg(test)]
51mod test_db; 52mod test_db;
52mod intern;
53 53
54use std::{ 54use std::{
55 hash::{Hash, Hasher}, 55 hash::{Hash, Hasher},
56 sync::Arc, 56 sync::Arc,
57}; 57};
58 58
59use adt::VariantData;
59use base_db::{impl_intern_key, salsa, CrateId}; 60use base_db::{impl_intern_key, salsa, CrateId};
60use hir_expand::{ 61use hir_expand::{
61 ast_id_map::FileAstId, 62 ast_id_map::FileAstId,
62 eager::{expand_eager_macro, ErrorEmitted, ErrorSink}, 63 eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
63 hygiene::Hygiene, 64 hygiene::Hygiene,
64 AstId, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 65 AstId, AttrId, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
65}; 66};
66use la_arena::Idx; 67use la_arena::Idx;
67use nameres::DefMap; 68use nameres::DefMap;
69use path::ModPath;
68use syntax::ast; 70use syntax::ast;
69 71
70use crate::builtin_type::BuiltinType; 72use crate::builtin_type::BuiltinType;
@@ -106,6 +108,18 @@ impl ModuleId {
106 pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> { 108 pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> {
107 self.def_map(db).containing_module(self.local_id) 109 self.def_map(db).containing_module(self.local_id)
108 } 110 }
111
112 /// Returns `true` if this module represents a block expression.
113 ///
114 /// Returns `false` if this module is a submodule *inside* a block expression
115 /// (eg. `m` in `{ mod m {} }`).
116 pub fn is_block_root(&self, db: &dyn db::DefDatabase) -> bool {
117 if self.block.is_none() {
118 return false;
119 }
120
121 self.def_map(db)[self.local_id].parent.is_none()
122 }
109} 123}
110 124
111/// An ID of a module, **local** to a specific crate 125/// An ID of a module, **local** to a specific crate
@@ -434,6 +448,16 @@ impl_from!(
434 for AttrDefId 448 for AttrDefId
435); 449);
436 450
451impl From<AssocContainerId> for AttrDefId {
452 fn from(acid: AssocContainerId) -> Self {
453 match acid {
454 AssocContainerId::ModuleId(mid) => AttrDefId::ModuleId(mid),
455 AssocContainerId::ImplId(iid) => AttrDefId::ImplId(iid),
456 AssocContainerId::TraitId(tid) => AttrDefId::TraitId(tid),
457 }
458 }
459}
460
437#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 461#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
438pub enum VariantId { 462pub enum VariantId {
439 EnumVariantId(EnumVariantId), 463 EnumVariantId(EnumVariantId),
@@ -442,6 +466,26 @@ pub enum VariantId {
442} 466}
443impl_from!(EnumVariantId, StructId, UnionId for VariantId); 467impl_from!(EnumVariantId, StructId, UnionId for VariantId);
444 468
469impl VariantId {
470 pub fn variant_data(self, db: &dyn db::DefDatabase) -> Arc<VariantData> {
471 match self {
472 VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
473 VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
474 VariantId::EnumVariantId(it) => {
475 db.enum_data(it.parent).variants[it.local_id].variant_data.clone()
476 }
477 }
478 }
479
480 pub fn file_id(self, db: &dyn db::DefDatabase) -> HirFileId {
481 match self {
482 VariantId::EnumVariantId(it) => it.parent.lookup(db).id.file_id(),
483 VariantId::StructId(it) => it.lookup(db).id.file_id(),
484 VariantId::UnionId(it) => it.lookup(db).id.file_id(),
485 }
486 }
487}
488
445trait Intern { 489trait Intern {
446 type ID; 490 type ID;
447 fn intern(self, db: &dyn db::DefDatabase) -> Self::ID; 491 fn intern(self, db: &dyn db::DefDatabase) -> Self::ID;
@@ -644,7 +688,10 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
644 } 688 }
645} 689}
646 690
647pub struct UnresolvedMacro; 691#[derive(Debug)]
692pub struct UnresolvedMacro {
693 pub path: ModPath,
694}
648 695
649fn macro_call_as_call_id( 696fn macro_call_as_call_id(
650 call: &AstIdWithPath<ast::MacroCall>, 697 call: &AstIdWithPath<ast::MacroCall>,
@@ -653,7 +700,8 @@ fn macro_call_as_call_id(
653 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 700 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
654 error_sink: &mut dyn FnMut(mbe::ExpandError), 701 error_sink: &mut dyn FnMut(mbe::ExpandError),
655) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { 702) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
656 let def: MacroDefId = resolver(call.path.clone()).ok_or(UnresolvedMacro)?; 703 let def: MacroDefId =
704 resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
657 705
658 let res = if let MacroDefKind::BuiltInEager(..) = def.kind { 706 let res = if let MacroDefKind::BuiltInEager(..) = def.kind {
659 let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast())); 707 let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast()));
@@ -669,24 +717,36 @@ fn macro_call_as_call_id(
669 ) 717 )
670 .map(MacroCallId::from) 718 .map(MacroCallId::from)
671 } else { 719 } else {
672 Ok(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(call.ast_id)).into()) 720 Ok(def
721 .as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike { ast_id: call.ast_id })
722 .into())
673 }; 723 };
674 Ok(res) 724 Ok(res)
675} 725}
676 726
677fn derive_macro_as_call_id( 727fn derive_macro_as_call_id(
678 item_attr: &AstIdWithPath<ast::Item>, 728 item_attr: &AstIdWithPath<ast::Item>,
729 derive_attr: AttrId,
679 db: &dyn db::DefDatabase, 730 db: &dyn db::DefDatabase,
680 krate: CrateId, 731 krate: CrateId,
681 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 732 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
682) -> Result<MacroCallId, UnresolvedMacro> { 733) -> Result<MacroCallId, UnresolvedMacro> {
683 let def: MacroDefId = resolver(item_attr.path.clone()).ok_or(UnresolvedMacro)?; 734 let def: MacroDefId = resolver(item_attr.path.clone())
684 let last_segment = item_attr.path.segments().last().ok_or(UnresolvedMacro)?; 735 .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
736 let last_segment = item_attr
737 .path
738 .segments()
739 .last()
740 .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
685 let res = def 741 let res = def
686 .as_lazy_macro( 742 .as_lazy_macro(
687 db.upcast(), 743 db.upcast(),
688 krate, 744 krate,
689 MacroCallKind::Derive(item_attr.ast_id, last_segment.to_string()), 745 MacroCallKind::Derive {
746 ast_id: item_attr.ast_id,
747 derive_name: last_segment.to_string(),
748 derive_attr,
749 },
690 ) 750 )
691 .into(); 751 .into();
692 Ok(res) 752 Ok(res)
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 7dd68219f..ba027c44a 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -410,6 +410,20 @@ impl DefMap {
410 } 410 }
411 } 411 }
412 412
413 pub fn dump_block_scopes(&self, db: &dyn DefDatabase) -> String {
414 let mut buf = String::new();
415 let mut arc;
416 let mut current_map = self;
417 while let Some(block) = &current_map.block {
418 format_to!(buf, "{:?} in {:?}\n", block.block, block.parent);
419 arc = block.parent.def_map(db);
420 current_map = &*arc;
421 }
422
423 format_to!(buf, "crate scope\n");
424 buf
425 }
426
413 fn shrink_to_fit(&mut self) { 427 fn shrink_to_fit(&mut self) {
414 // Exhaustive match to require handling new fields. 428 // Exhaustive match to require handling new fields.
415 let Self { 429 let Self {
@@ -481,7 +495,7 @@ mod diagnostics {
481 495
482 UnresolvedProcMacro { ast: MacroCallKind }, 496 UnresolvedProcMacro { ast: MacroCallKind },
483 497
484 UnresolvedMacroCall { ast: AstId<ast::MacroCall> }, 498 UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath },
485 499
486 MacroError { ast: MacroCallKind, message: String }, 500 MacroError { ast: MacroCallKind, message: String },
487 } 501 }
@@ -546,8 +560,9 @@ mod diagnostics {
546 pub(super) fn unresolved_macro_call( 560 pub(super) fn unresolved_macro_call(
547 container: LocalModuleId, 561 container: LocalModuleId,
548 ast: AstId<ast::MacroCall>, 562 ast: AstId<ast::MacroCall>,
563 path: ModPath,
549 ) -> Self { 564 ) -> Self {
550 Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast } } 565 Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast, path } }
551 } 566 }
552 567
553 pub(super) fn add_to( 568 pub(super) fn add_to(
@@ -613,12 +628,12 @@ mod diagnostics {
613 DiagnosticKind::UnresolvedProcMacro { ast } => { 628 DiagnosticKind::UnresolvedProcMacro { ast } => {
614 let mut precise_location = None; 629 let mut precise_location = None;
615 let (file, ast, name) = match ast { 630 let (file, ast, name) = match ast {
616 MacroCallKind::FnLike(ast) => { 631 MacroCallKind::FnLike { ast_id } => {
617 let node = ast.to_node(db.upcast()); 632 let node = ast_id.to_node(db.upcast());
618 (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) 633 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None)
619 } 634 }
620 MacroCallKind::Derive(ast, name) => { 635 MacroCallKind::Derive { ast_id, derive_name, .. } => {
621 let node = ast.to_node(db.upcast()); 636 let node = ast_id.to_node(db.upcast());
622 637
623 // Compute the precise location of the macro name's token in the derive 638 // Compute the precise location of the macro name's token in the derive
624 // list. 639 // list.
@@ -639,7 +654,7 @@ mod diagnostics {
639 }); 654 });
640 for token in tokens { 655 for token in tokens {
641 if token.kind() == SyntaxKind::IDENT 656 if token.kind() == SyntaxKind::IDENT
642 && token.text() == name.as_str() 657 && token.text() == derive_name.as_str()
643 { 658 {
644 precise_location = Some(token.text_range()); 659 precise_location = Some(token.text_range());
645 break 'outer; 660 break 'outer;
@@ -648,9 +663,9 @@ mod diagnostics {
648 } 663 }
649 664
650 ( 665 (
651 ast.file_id, 666 ast_id.file_id,
652 SyntaxNodePtr::from(AstPtr::new(&node)), 667 SyntaxNodePtr::from(AstPtr::new(&node)),
653 Some(name.clone()), 668 Some(derive_name.clone()),
654 ) 669 )
655 } 670 }
656 }; 671 };
@@ -662,20 +677,24 @@ mod diagnostics {
662 }); 677 });
663 } 678 }
664 679
665 DiagnosticKind::UnresolvedMacroCall { ast } => { 680 DiagnosticKind::UnresolvedMacroCall { ast, path } => {
666 let node = ast.to_node(db.upcast()); 681 let node = ast.to_node(db.upcast());
667 sink.push(UnresolvedMacroCall { file: ast.file_id, node: AstPtr::new(&node) }); 682 sink.push(UnresolvedMacroCall {
683 file: ast.file_id,
684 node: AstPtr::new(&node),
685 path: path.clone(),
686 });
668 } 687 }
669 688
670 DiagnosticKind::MacroError { ast, message } => { 689 DiagnosticKind::MacroError { ast, message } => {
671 let (file, ast) = match ast { 690 let (file, ast) = match ast {
672 MacroCallKind::FnLike(ast) => { 691 MacroCallKind::FnLike { ast_id, .. } => {
673 let node = ast.to_node(db.upcast()); 692 let node = ast_id.to_node(db.upcast());
674 (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) 693 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
675 } 694 }
676 MacroCallKind::Derive(ast, _) => { 695 MacroCallKind::Derive { ast_id, .. } => {
677 let node = ast.to_node(db.upcast()); 696 let node = ast_id.to_node(db.upcast());
678 (ast.file_id, SyntaxNodePtr::from(AstPtr::new(&node))) 697 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
679 } 698 }
680 }; 699 };
681 sink.push(MacroError { file, node: ast, message: message.clone() }); 700 sink.push(MacroError { file, node: ast, message: message.clone() });
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 4ddc791ce..05ceb1efb 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, MacroCallKind, MacroDefId, MacroDefKind, 16 AttrId, 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};
@@ -215,8 +215,8 @@ struct MacroDirective {
215 215
216#[derive(Clone, Debug, Eq, PartialEq)] 216#[derive(Clone, Debug, Eq, PartialEq)]
217enum MacroDirectiveKind { 217enum MacroDirectiveKind {
218 FnLike { ast_id: AstIdWithPath<ast::MacroCall>, legacy: Option<MacroCallId> }, 218 FnLike { ast_id: AstIdWithPath<ast::MacroCall> },
219 Derive { ast_id: AstIdWithPath<ast::Item> }, 219 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId },
220} 220}
221 221
222struct DefData<'a> { 222struct DefData<'a> {
@@ -478,7 +478,7 @@ impl DefCollector<'_> {
478 self.def_map.edition, 478 self.def_map.edition,
479 ); 479 );
480 480
481 let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name); 481 let res = self.def_map.resolve_name_in_extern_prelude(self.db, &extern_crate.name);
482 482
483 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { 483 if let Some(ModuleDefId::ModuleId(m)) = res.take_types() {
484 cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); 484 cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
@@ -534,6 +534,7 @@ impl DefCollector<'_> {
534 log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); 534 log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
535 if import.is_extern_crate { 535 if import.is_extern_crate {
536 let res = self.def_map.resolve_name_in_extern_prelude( 536 let res = self.def_map.resolve_name_in_extern_prelude(
537 self.db,
537 &import 538 &import
538 .path 539 .path
539 .as_ident() 540 .as_ident()
@@ -806,13 +807,7 @@ impl DefCollector<'_> {
806 let mut res = ReachedFixedPoint::Yes; 807 let mut res = ReachedFixedPoint::Yes;
807 macros.retain(|directive| { 808 macros.retain(|directive| {
808 match &directive.kind { 809 match &directive.kind {
809 MacroDirectiveKind::FnLike { ast_id, legacy } => { 810 MacroDirectiveKind::FnLike { ast_id } => {
810 if let Some(call_id) = legacy {
811 res = ReachedFixedPoint::No;
812 resolved.push((directive.module_id, *call_id, directive.depth));
813 return false;
814 }
815
816 match macro_call_as_call_id( 811 match macro_call_as_call_id(
817 ast_id, 812 ast_id,
818 self.db, 813 self.db,
@@ -834,19 +829,23 @@ impl DefCollector<'_> {
834 res = ReachedFixedPoint::No; 829 res = ReachedFixedPoint::No;
835 return false; 830 return false;
836 } 831 }
837 Err(UnresolvedMacro) | Ok(Err(_)) => {} 832 Err(UnresolvedMacro { .. }) | Ok(Err(_)) => {}
838 } 833 }
839 } 834 }
840 MacroDirectiveKind::Derive { ast_id } => { 835 MacroDirectiveKind::Derive { ast_id, derive_attr } => {
841 match derive_macro_as_call_id(ast_id, self.db, self.def_map.krate, |path| { 836 match derive_macro_as_call_id(
842 self.resolve_derive_macro(directive.module_id, &path) 837 ast_id,
843 }) { 838 *derive_attr,
839 self.db,
840 self.def_map.krate,
841 |path| self.resolve_derive_macro(directive.module_id, &path),
842 ) {
844 Ok(call_id) => { 843 Ok(call_id) => {
845 resolved.push((directive.module_id, call_id, 0)); 844 resolved.push((directive.module_id, call_id, directive.depth));
846 res = ReachedFixedPoint::No; 845 res = ReachedFixedPoint::No;
847 return false; 846 return false;
848 } 847 }
849 Err(UnresolvedMacro) => (), 848 Err(UnresolvedMacro { .. }) => (),
850 } 849 }
851 } 850 }
852 } 851 }
@@ -944,10 +943,11 @@ impl DefCollector<'_> {
944 &mut |_| (), 943 &mut |_| (),
945 ) { 944 ) {
946 Ok(_) => (), 945 Ok(_) => (),
947 Err(UnresolvedMacro) => { 946 Err(UnresolvedMacro { path }) => {
948 self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( 947 self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
949 directive.module_id, 948 directive.module_id,
950 ast_id.ast_id, 949 ast_id.ast_id,
950 path,
951 )); 951 ));
952 } 952 }
953 }, 953 },
@@ -1169,19 +1169,27 @@ impl ModCollector<'_, '_> {
1169 } 1169 }
1170 ModItem::Const(id) => { 1170 ModItem::Const(id) => {
1171 let it = &self.item_tree[id]; 1171 let it = &self.item_tree[id];
1172 1172 let const_id = ConstLoc {
1173 if let Some(name) = &it.name { 1173 container: module.into(),
1174 def = Some(DefData { 1174 id: ItemTreeId::new(self.file_id, id),
1175 id: ConstLoc { 1175 }
1176 container: module.into(), 1176 .intern(self.def_collector.db);
1177 id: ItemTreeId::new(self.file_id, id), 1177
1178 } 1178 match &it.name {
1179 .intern(self.def_collector.db) 1179 Some(name) => {
1180 .into(), 1180 def = Some(DefData {
1181 name, 1181 id: const_id.into(),
1182 visibility: &self.item_tree[it.visibility], 1182 name,
1183 has_constructor: false, 1183 visibility: &self.item_tree[it.visibility],
1184 }); 1184 has_constructor: false,
1185 });
1186 }
1187 None => {
1188 // const _: T = ...;
1189 self.def_collector.def_map.modules[self.module_id]
1190 .scope
1191 .define_unnamed_const(const_id);
1192 }
1185 } 1193 }
1186 } 1194 }
1187 ModItem::Static(id) => { 1195 ModItem::Static(id) => {
@@ -1366,7 +1374,7 @@ impl ModCollector<'_, '_> {
1366 self.def_collector.unexpanded_macros.push(MacroDirective { 1374 self.def_collector.unexpanded_macros.push(MacroDirective {
1367 module_id: self.module_id, 1375 module_id: self.module_id,
1368 depth: self.macro_depth + 1, 1376 depth: self.macro_depth + 1,
1369 kind: MacroDirectiveKind::Derive { ast_id }, 1377 kind: MacroDirectiveKind::Derive { ast_id, derive_attr: derive.id },
1370 }); 1378 });
1371 } 1379 }
1372 } 1380 }
@@ -1518,12 +1526,12 @@ impl ModCollector<'_, '_> {
1518 // Built-in macro failed eager expansion. 1526 // Built-in macro failed eager expansion.
1519 self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error( 1527 self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
1520 self.module_id, 1528 self.module_id,
1521 MacroCallKind::FnLike(ast_id.ast_id), 1529 MacroCallKind::FnLike { ast_id: ast_id.ast_id },
1522 error.unwrap().to_string(), 1530 error.unwrap().to_string(),
1523 )); 1531 ));
1524 return; 1532 return;
1525 } 1533 }
1526 Err(UnresolvedMacro) => (), 1534 Err(UnresolvedMacro { .. }) => (),
1527 } 1535 }
1528 1536
1529 // Case 2: resolve in module scope, expand during name resolution. 1537 // Case 2: resolve in module scope, expand during name resolution.
@@ -1535,7 +1543,7 @@ impl ModCollector<'_, '_> {
1535 self.def_collector.unexpanded_macros.push(MacroDirective { 1543 self.def_collector.unexpanded_macros.push(MacroDirective {
1536 module_id: self.module_id, 1544 module_id: self.module_id,
1537 depth: self.macro_depth + 1, 1545 depth: self.macro_depth + 1,
1538 kind: MacroDirectiveKind::FnLike { ast_id, legacy: None }, 1546 kind: MacroDirectiveKind::FnLike { ast_id },
1539 }); 1547 });
1540 } 1548 }
1541 1549
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
index 60471937c..c984148c3 100644
--- a/crates/hir_def/src/nameres/path_resolution.rs
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -60,12 +60,26 @@ impl ResolvePathResult {
60} 60}
61 61
62impl DefMap { 62impl DefMap {
63 pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { 63 pub(super) fn resolve_name_in_extern_prelude(
64 &self,
65 db: &dyn DefDatabase,
66 name: &Name,
67 ) -> PerNs {
64 if name == &name!(self) { 68 if name == &name!(self) {
65 cov_mark::hit!(extern_crate_self_as); 69 cov_mark::hit!(extern_crate_self_as);
66 return PerNs::types(self.module_id(self.root).into(), Visibility::Public); 70 return PerNs::types(self.module_id(self.root).into(), Visibility::Public);
67 } 71 }
68 self.extern_prelude 72
73 let arc;
74 let root = match self.block {
75 Some(_) => {
76 arc = self.crate_root(db).def_map(db);
77 &*arc
78 }
79 None => self,
80 };
81
82 root.extern_prelude
69 .get(name) 83 .get(name)
70 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)) 84 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public))
71 } 85 }
@@ -191,7 +205,7 @@ impl DefMap {
191 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), 205 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
192 }; 206 };
193 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment); 207 log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
194 self.resolve_name_in_crate_root_or_extern_prelude(&segment) 208 self.resolve_name_in_crate_root_or_extern_prelude(db, &segment)
195 } 209 }
196 PathKind::Plain => { 210 PathKind::Plain => {
197 let (_, segment) = match segments.next() { 211 let (_, segment) = match segments.next() {
@@ -373,7 +387,13 @@ impl DefMap {
373 .get_legacy_macro(name) 387 .get_legacy_macro(name)
374 .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); 388 .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
375 let from_scope = self[module].scope.get(name); 389 let from_scope = self[module].scope.get(name);
376 let from_builtin = BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none); 390 let from_builtin = match self.block {
391 Some(_) => {
392 // Only resolve to builtins in the root `DefMap`.
393 PerNs::none()
394 }
395 None => BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none),
396 };
377 let from_scope_or_builtin = match shadow { 397 let from_scope_or_builtin = match shadow {
378 BuiltinShadowMode::Module => from_scope.or(from_builtin), 398 BuiltinShadowMode::Module => from_scope.or(from_builtin),
379 BuiltinShadowMode::Other => { 399 BuiltinShadowMode::Other => {
@@ -384,24 +404,31 @@ impl DefMap {
384 } 404 }
385 } 405 }
386 }; 406 };
387 // Give precedence to names in outer `DefMap`s over the extern prelude; only check prelude 407 let from_extern_prelude = self
388 // from the crate DefMap. 408 .extern_prelude
389 let from_extern_prelude = match self.block { 409 .get(name)
390 Some(_) => PerNs::none(), 410 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public));
391 None => self
392 .extern_prelude
393 .get(name)
394 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)),
395 };
396 411
397 let from_prelude = self.resolve_in_prelude(db, name); 412 let from_prelude = self.resolve_in_prelude(db, name);
398 413
399 from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude) 414 from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude)
400 } 415 }
401 416
402 fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs { 417 fn resolve_name_in_crate_root_or_extern_prelude(
403 let from_crate_root = self[self.root].scope.get(name); 418 &self,
404 let from_extern_prelude = self.resolve_name_in_extern_prelude(name); 419 db: &dyn DefDatabase,
420 name: &Name,
421 ) -> PerNs {
422 let arc;
423 let crate_def_map = match self.block {
424 Some(_) => {
425 arc = self.crate_root(db).def_map(db);
426 &arc
427 }
428 None => self,
429 };
430 let from_crate_root = crate_def_map[crate_def_map.root].scope.get(name);
431 let from_extern_prelude = self.resolve_name_in_extern_prelude(db, name);
405 432
406 from_crate_root.or(from_extern_prelude) 433 from_crate_root.or(from_extern_prelude)
407 } 434 }
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
index fefdadb22..543975e07 100644
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -170,7 +170,7 @@ fn unresolved_legacy_scope_macro() {
170 170
171 m!(); 171 m!();
172 m2!(); 172 m2!();
173 //^^^^^^ unresolved macro call 173 //^^^^^^ unresolved macro `self::m2!`
174 "#, 174 "#,
175 ); 175 );
176} 176}
@@ -187,7 +187,7 @@ fn unresolved_module_scope_macro() {
187 187
188 self::m!(); 188 self::m!();
189 self::m2!(); 189 self::m2!();
190 //^^^^^^^^^^^^ unresolved macro call 190 //^^^^^^^^^^^^ unresolved macro `self::m2!`
191 "#, 191 "#,
192 ); 192 );
193} 193}
@@ -233,7 +233,7 @@ fn good_out_dir_diagnostic() {
233 macro_rules! concat { () => {} } 233 macro_rules! concat { () => {} }
234 234
235 include!(concat!(env!("OUT_DIR"), "/out.rs")); 235 include!(concat!(env!("OUT_DIR"), "/out.rs"));
236 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix 236 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "run build scripts" to fix
237 "#, 237 "#,
238 ); 238 );
239} 239}
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index a3e83e2cf..509f77850 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -48,7 +48,8 @@ pub enum ImportAlias {
48 48
49impl ModPath { 49impl ModPath {
50 pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> { 50 pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
51 lower::lower_path(path, hygiene).map(|it| (*it.mod_path).clone()) 51 let ctx = LowerCtx::with_hygiene(hygiene);
52 lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone())
52 } 53 }
53 54
54 pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath { 55 pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
@@ -122,7 +123,7 @@ impl ModPath {
122pub struct Path { 123pub struct Path {
123 /// Type based path like `<T>::foo`. 124 /// Type based path like `<T>::foo`.
124 /// Note that paths like `<Type as Trait>::foo` are desugard to `Trait::<Self=Type>::foo`. 125 /// Note that paths like `<Type as Trait>::foo` are desugard to `Trait::<Self=Type>::foo`.
125 type_anchor: Option<Box<TypeRef>>, 126 type_anchor: Option<Interned<TypeRef>>,
126 mod_path: Interned<ModPath>, 127 mod_path: Interned<ModPath>,
127 /// Invariant: the same len as `self.mod_path.segments` 128 /// Invariant: the same len as `self.mod_path.segments`
128 generic_args: Vec<Option<Arc<GenericArgs>>>, 129 generic_args: Vec<Option<Arc<GenericArgs>>>,
@@ -167,8 +168,8 @@ pub enum GenericArg {
167impl Path { 168impl Path {
168 /// Converts an `ast::Path` to `Path`. Works with use trees. 169 /// Converts an `ast::Path` to `Path`. Works with use trees.
169 /// It correctly handles `$crate` based path from macro call. 170 /// It correctly handles `$crate` based path from macro call.
170 pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<Path> { 171 pub fn from_src(path: ast::Path, ctx: &LowerCtx) -> Option<Path> {
171 lower::lower_path(path, hygiene) 172 lower::lower_path(path, ctx)
172 } 173 }
173 174
174 /// Converts a known mod path to `Path`. 175 /// Converts a known mod path to `Path`.
@@ -289,6 +290,12 @@ impl From<Name> for Path {
289 } 290 }
290} 291}
291 292
293impl From<Name> for Box<Path> {
294 fn from(name: Name) -> Box<Path> {
295 Box::new(Path::from(name))
296 }
297}
298
292impl From<Name> for ModPath { 299impl From<Name> for ModPath {
293 fn from(name: Name) -> ModPath { 300 fn from(name: Name) -> ModPath {
294 ModPath::from_segments(PathKind::Plain, iter::once(name)) 301 ModPath::from_segments(PathKind::Plain, iter::once(name))
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index 28f6244da..1df6db525 100644
--- a/crates/hir_def/src/path/lower.rs
+++ b/crates/hir_def/src/path/lower.rs
@@ -6,10 +6,7 @@ use crate::intern::Interned;
6use std::sync::Arc; 6use std::sync::Arc;
7 7
8use either::Either; 8use either::Either;
9use hir_expand::{ 9use hir_expand::name::{name, AsName};
10 hygiene::Hygiene,
11 name::{name, AsName},
12};
13use syntax::ast::{self, AstNode, TypeBoundsOwner}; 10use syntax::ast::{self, AstNode, TypeBoundsOwner};
14 11
15use super::AssociatedTypeBinding; 12use super::AssociatedTypeBinding;
@@ -23,12 +20,12 @@ pub(super) use lower_use::lower_use_tree;
23 20
24/// Converts an `ast::Path` to `Path`. Works with use trees. 21/// Converts an `ast::Path` to `Path`. Works with use trees.
25/// It correctly handles `$crate` based path from macro call. 22/// It correctly handles `$crate` based path from macro call.
26pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> { 23pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option<Path> {
27 let mut kind = PathKind::Plain; 24 let mut kind = PathKind::Plain;
28 let mut type_anchor = None; 25 let mut type_anchor = None;
29 let mut segments = Vec::new(); 26 let mut segments = Vec::new();
30 let mut generic_args = Vec::new(); 27 let mut generic_args = Vec::new();
31 let ctx = LowerCtx::with_hygiene(hygiene); 28 let hygiene = ctx.hygiene();
32 loop { 29 loop {
33 let segment = path.segment()?; 30 let segment = path.segment()?;
34 31
@@ -43,10 +40,10 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
43 Either::Left(name) => { 40 Either::Left(name) => {
44 let args = segment 41 let args = segment
45 .generic_arg_list() 42 .generic_arg_list()
46 .and_then(|it| lower_generic_args(&ctx, it)) 43 .and_then(|it| lower_generic_args(ctx, it))
47 .or_else(|| { 44 .or_else(|| {
48 lower_generic_args_from_fn_path( 45 lower_generic_args_from_fn_path(
49 &ctx, 46 ctx,
50 segment.param_list(), 47 segment.param_list(),
51 segment.ret_type(), 48 segment.ret_type(),
52 ) 49 )
@@ -64,17 +61,17 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
64 ast::PathSegmentKind::Type { type_ref, trait_ref } => { 61 ast::PathSegmentKind::Type { type_ref, trait_ref } => {
65 assert!(path.qualifier().is_none()); // this can only occur at the first segment 62 assert!(path.qualifier().is_none()); // this can only occur at the first segment
66 63
67 let self_type = TypeRef::from_ast(&ctx, type_ref?); 64 let self_type = TypeRef::from_ast(ctx, type_ref?);
68 65
69 match trait_ref { 66 match trait_ref {
70 // <T>::foo 67 // <T>::foo
71 None => { 68 None => {
72 type_anchor = Some(Box::new(self_type)); 69 type_anchor = Some(Interned::new(self_type));
73 kind = PathKind::Plain; 70 kind = PathKind::Plain;
74 } 71 }
75 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo 72 // <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
76 Some(trait_ref) => { 73 Some(trait_ref) => {
77 let path = Path::from_src(trait_ref.path()?, hygiene)?; 74 let path = Path::from_src(trait_ref.path()?, ctx)?;
78 let mod_path = (*path.mod_path).clone(); 75 let mod_path = (*path.mod_path).clone();
79 let num_segments = path.mod_path.segments.len(); 76 let num_segments = path.mod_path.segments.len();
80 kind = mod_path.kind; 77 kind = mod_path.kind;
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index a73585ee7..0391cc49b 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -14,6 +14,7 @@ use crate::{
14 db::DefDatabase, 14 db::DefDatabase,
15 expr::{ExprId, LabelId, PatId}, 15 expr::{ExprId, LabelId, PatId},
16 generics::GenericParams, 16 generics::GenericParams,
17 intern::Interned,
17 item_scope::{BuiltinShadowMode, BUILTIN_SCOPE}, 18 item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
18 nameres::DefMap, 19 nameres::DefMap,
19 path::{ModPath, PathKind}, 20 path::{ModPath, PathKind},
@@ -50,7 +51,7 @@ enum Scope {
50 /// All the items and imported names of a module 51 /// All the items and imported names of a module
51 ModuleScope(ModuleItemMap), 52 ModuleScope(ModuleItemMap),
52 /// Brings the generic parameters of an item into scope 53 /// Brings the generic parameters of an item into scope
53 GenericParams { def: GenericDefId, params: Arc<GenericParams> }, 54 GenericParams { def: GenericDefId, params: Interned<GenericParams> },
54 /// Brings `Self` in `impl` block into scope 55 /// Brings `Self` in `impl` block into scope
55 ImplDefScope(ImplId), 56 ImplDefScope(ImplId),
56 /// Brings `Self` in enum, struct and union definitions into scope 57 /// Brings `Self` in enum, struct and union definitions into scope
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs
index dd36106f8..8fa703a57 100644
--- a/crates/hir_def/src/test_db.rs
+++ b/crates/hir_def/src/test_db.rs
@@ -15,7 +15,12 @@ use rustc_hash::FxHashSet;
15use syntax::{algo, ast, AstNode, TextRange, TextSize}; 15use syntax::{algo, ast, AstNode, TextRange, TextSize};
16use test_utils::extract_annotations; 16use test_utils::extract_annotations;
17 17
18use crate::{db::DefDatabase, nameres::DefMap, src::HasSource, Lookup, ModuleDefId, ModuleId}; 18use crate::{
19 db::DefDatabase,
20 nameres::{DefMap, ModuleSource},
21 src::HasSource,
22 LocalModuleId, Lookup, ModuleDefId, ModuleId,
23};
19 24
20#[salsa::database( 25#[salsa::database(
21 base_db::SourceDatabaseExtStorage, 26 base_db::SourceDatabaseExtStorage,
@@ -87,10 +92,11 @@ impl TestDB {
87 pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId { 92 pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId {
88 let file_module = self.module_for_file(position.file_id); 93 let file_module = self.module_for_file(position.file_id);
89 let mut def_map = file_module.def_map(self); 94 let mut def_map = file_module.def_map(self);
95 let module = self.mod_at_position(&def_map, position);
90 96
91 def_map = match self.block_at_position(&def_map, position) { 97 def_map = match self.block_at_position(&def_map, position) {
92 Some(it) => it, 98 Some(it) => it,
93 None => return file_module, 99 None => return def_map.module_id(module),
94 }; 100 };
95 loop { 101 loop {
96 let new_map = self.block_at_position(&def_map, position); 102 let new_map = self.block_at_position(&def_map, position);
@@ -106,6 +112,47 @@ impl TestDB {
106 } 112 }
107 } 113 }
108 114
115 /// Finds the smallest/innermost module in `def_map` containing `position`.
116 fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId {
117 let mut size = None;
118 let mut res = def_map.root();
119 for (module, data) in def_map.modules() {
120 let src = data.definition_source(self);
121 if src.file_id != position.file_id.into() {
122 continue;
123 }
124
125 let range = match src.value {
126 ModuleSource::SourceFile(it) => it.syntax().text_range(),
127 ModuleSource::Module(it) => it.syntax().text_range(),
128 ModuleSource::BlockExpr(it) => it.syntax().text_range(),
129 };
130
131 if !range.contains(position.offset) {
132 continue;
133 }
134
135 let new_size = match size {
136 None => range.len(),
137 Some(size) => {
138 if range.len() < size {
139 range.len()
140 } else {
141 size
142 }
143 }
144 };
145
146 if size != Some(new_size) {
147 cov_mark::hit!(submodule_in_testdb);
148 size = Some(new_size);
149 res = module;
150 }
151 }
152
153 res
154 }
155
109 fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option<Arc<DefMap>> { 156 fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option<Arc<DefMap>> {
110 // Find the smallest (innermost) function in `def_map` containing the cursor. 157 // Find the smallest (innermost) function in `def_map` containing the cursor.
111 let mut size = None; 158 let mut size = None;
diff --git a/crates/hir_def/src/type_ref.rs b/crates/hir_def/src/type_ref.rs
index 4c24aae94..ea29da5da 100644
--- a/crates/hir_def/src/type_ref.rs
+++ b/crates/hir_def/src/type_ref.rs
@@ -1,6 +1,7 @@
1//! HIR for references to types. Paths in these are not yet resolved. They can 1//! HIR for references to types. Paths in these are not yet resolved. They can
2//! be directly created from an ast::TypeRef, without further queries. 2//! be directly created from an ast::TypeRef, without further queries.
3use hir_expand::name::Name; 3
4use hir_expand::{name::Name, AstId, InFile};
4use syntax::ast; 5use syntax::ast;
5 6
6use crate::{body::LowerCtx, path::Path}; 7use crate::{body::LowerCtx, path::Path};
@@ -68,6 +69,7 @@ impl TraitRef {
68 } 69 }
69 } 70 }
70} 71}
72
71/// Compare ty::Ty 73/// Compare ty::Ty
72#[derive(Clone, PartialEq, Eq, Hash, Debug)] 74#[derive(Clone, PartialEq, Eq, Hash, Debug)]
73pub enum TypeRef { 75pub enum TypeRef {
@@ -84,6 +86,7 @@ pub enum TypeRef {
84 // For 86 // For
85 ImplTrait(Vec<TypeBound>), 87 ImplTrait(Vec<TypeBound>),
86 DynTrait(Vec<TypeBound>), 88 DynTrait(Vec<TypeBound>),
89 Macro(AstId<ast::MacroCall>),
87 Error, 90 Error,
88} 91}
89 92
@@ -116,7 +119,7 @@ pub enum TypeBound {
116 119
117impl TypeRef { 120impl TypeRef {
118 /// Converts an `ast::TypeRef` to a `hir::TypeRef`. 121 /// Converts an `ast::TypeRef` to a `hir::TypeRef`.
119 pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self { 122 pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
120 match node { 123 match node {
121 ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()), 124 ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
122 ast::Type::TupleType(inner) => { 125 ast::Type::TupleType(inner) => {
@@ -176,8 +179,13 @@ impl TypeRef {
176 ast::Type::DynTraitType(inner) => { 179 ast::Type::DynTraitType(inner) => {
177 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list())) 180 TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
178 } 181 }
179 // FIXME: Macros in type position are not yet supported. 182 ast::Type::MacroType(mt) => match mt.macro_call() {
180 ast::Type::MacroType(_) => TypeRef::Error, 183 Some(mc) => ctx
184 .ast_id(&mc)
185 .map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc)))
186 .unwrap_or(TypeRef::Error),
187 None => TypeRef::Error,
188 },
181 } 189 }
182 } 190 }
183 191
@@ -215,7 +223,7 @@ impl TypeRef {
215 } 223 }
216 } 224 }
217 TypeRef::Path(path) => go_path(path, f), 225 TypeRef::Path(path) => go_path(path, f),
218 TypeRef::Never | TypeRef::Placeholder | TypeRef::Error => {} 226 TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
219 }; 227 };
220 } 228 }
221 229
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs
index 7d00a37c4..d4b7c9970 100644
--- a/crates/hir_def/src/visibility.rs
+++ b/crates/hir_def/src/visibility.rs
@@ -11,7 +11,7 @@ use crate::{
11 nameres::DefMap, 11 nameres::DefMap,
12 path::{ModPath, PathKind}, 12 path::{ModPath, PathKind},
13 resolver::HasResolver, 13 resolver::HasResolver,
14 FunctionId, HasModule, LocalFieldId, ModuleDefId, ModuleId, VariantId, 14 FunctionId, HasModule, LocalFieldId, ModuleId, VariantId,
15}; 15};
16 16
17/// Visibility of an item, not yet resolved. 17/// Visibility of an item, not yet resolved.
@@ -25,7 +25,7 @@ pub enum RawVisibility {
25} 25}
26 26
27impl RawVisibility { 27impl RawVisibility {
28 pub(crate) const fn private() -> RawVisibility { 28 pub(crate) fn private() -> RawVisibility {
29 RawVisibility::Module(ModPath::from_kind(PathKind::Super(0))) 29 RawVisibility::Module(ModPath::from_kind(PathKind::Super(0)))
30 } 30 }
31 31
@@ -123,11 +123,19 @@ impl Visibility {
123 def_map: &DefMap, 123 def_map: &DefMap,
124 mut from_module: crate::LocalModuleId, 124 mut from_module: crate::LocalModuleId,
125 ) -> bool { 125 ) -> bool {
126 let to_module = match self { 126 let mut to_module = match self {
127 Visibility::Module(m) => m, 127 Visibility::Module(m) => m,
128 Visibility::Public => return true, 128 Visibility::Public => return true,
129 }; 129 };
130 130
131 // `to_module` might be the root module of a block expression. Those have the same
132 // visibility as the containing module (even though no items are directly nameable from
133 // there, getting this right is important for method resolution).
134 // In that case, we adjust the visibility of `to_module` to point to the containing module.
135 if to_module.is_block_root(db) {
136 to_module = to_module.containing_module(db).unwrap();
137 }
138
131 // from_module needs to be a descendant of to_module 139 // from_module needs to be a descendant of to_module
132 let mut def_map = def_map; 140 let mut def_map = def_map;
133 let mut parent_arc; 141 let mut parent_arc;
@@ -217,6 +225,6 @@ pub(crate) fn field_visibilities_query(
217 225
218/// Resolve visibility of a function. 226/// Resolve visibility of a function.
219pub(crate) fn function_visibility_query(db: &dyn DefDatabase, def: FunctionId) -> Visibility { 227pub(crate) fn function_visibility_query(db: &dyn DefDatabase, def: FunctionId) -> Visibility {
220 let resolver = ModuleDefId::from(def).module(db).unwrap().resolver(db); 228 let resolver = def.resolver(db);
221 db.function_data(def).visibility.resolve(db, &resolver) 229 db.function_data(def).visibility.resolve(db, &resolver)
222} 230}
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index 6ece4b289..537c03028 100644
--- a/crates/hir_expand/src/builtin_derive.rs
+++ b/crates/hir_expand/src/builtin_derive.rs
@@ -269,7 +269,7 @@ mod tests {
269 use expect_test::{expect, Expect}; 269 use expect_test::{expect, Expect};
270 use name::AsName; 270 use name::AsName;
271 271
272 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc}; 272 use crate::{test_db::TestDB, AstId, AttrId, MacroCallId, MacroCallKind, MacroCallLoc};
273 273
274 use super::*; 274 use super::*;
275 275
@@ -308,7 +308,7 @@ $0
308 308
309 let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap(); 309 let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap();
310 310
311 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0])); 311 let ast_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]));
312 312
313 let loc = MacroCallLoc { 313 let loc = MacroCallLoc {
314 def: MacroDefId { 314 def: MacroDefId {
@@ -317,7 +317,11 @@ $0
317 local_inner: false, 317 local_inner: false,
318 }, 318 },
319 krate: CrateId(0), 319 krate: CrateId(0),
320 kind: MacroCallKind::Derive(attr_id, name.to_string()), 320 kind: MacroCallKind::Derive {
321 ast_id,
322 derive_name: name.to_string(),
323 derive_attr: AttrId(0),
324 },
321 }; 325 };
322 326
323 let id: MacroCallId = db.intern_macro(loc).into(); 327 let id: MacroCallId = db.intern_macro(loc).into();
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 75ec4196b..179de61f9 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -110,6 +110,7 @@ register_builtin! {
110 (format_args_nl, FormatArgsNl) => format_args_expand, 110 (format_args_nl, FormatArgsNl) => format_args_expand,
111 (llvm_asm, LlvmAsm) => asm_expand, 111 (llvm_asm, LlvmAsm) => asm_expand,
112 (asm, Asm) => asm_expand, 112 (asm, Asm) => asm_expand,
113 (global_asm, GlobalAsm) => global_asm_expand,
113 (cfg, Cfg) => cfg_expand, 114 (cfg, Cfg) => cfg_expand,
114 (core_panic, CorePanic) => panic_expand, 115 (core_panic, CorePanic) => panic_expand,
115 (std_panic, StdPanic) => panic_expand, 116 (std_panic, StdPanic) => panic_expand,
@@ -274,6 +275,15 @@ fn asm_expand(
274 ExpandResult::ok(expanded) 275 ExpandResult::ok(expanded)
275} 276}
276 277
278fn global_asm_expand(
279 _db: &dyn AstDatabase,
280 _id: LazyMacroId,
281 _tt: &tt::Subtree,
282) -> ExpandResult<tt::Subtree> {
283 // Expand to nothing (at item-level)
284 ExpandResult::ok(quote! {})
285}
286
277fn cfg_expand( 287fn cfg_expand(
278 db: &dyn AstDatabase, 288 db: &dyn AstDatabase,
279 id: LazyMacroId, 289 id: LazyMacroId,
@@ -490,7 +500,7 @@ fn env_expand(
490 // unnecessary diagnostics for eg. `CARGO_PKG_NAME`. 500 // unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
491 if key == "OUT_DIR" { 501 if key == "OUT_DIR" {
492 err = Some(mbe::ExpandError::Other( 502 err = Some(mbe::ExpandError::Other(
493 r#"`OUT_DIR` not set, enable "load out dirs from check" to fix"#.into(), 503 r#"`OUT_DIR` not set, enable "run build scripts" to fix"#.into(),
494 )); 504 ));
495 } 505 }
496 506
@@ -566,10 +576,9 @@ mod tests {
566 let loc = MacroCallLoc { 576 let loc = MacroCallLoc {
567 def, 577 def,
568 krate, 578 krate,
569 kind: MacroCallKind::FnLike(AstId::new( 579 kind: MacroCallKind::FnLike {
570 file_id.into(), 580 ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(&macro_call)),
571 ast_id_map.ast_id(&macro_call), 581 },
572 )),
573 }; 582 };
574 583
575 let id: MacroCallId = db.intern_macro(loc).into(); 584 let id: MacroCallId = db.intern_macro(loc).into();
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 10fe60821..1e4b0cc19 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -439,6 +439,8 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
439 match parent.kind() { 439 match parent.kind() {
440 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, 440 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
441 MACRO_STMTS => FragmentKind::Statements, 441 MACRO_STMTS => FragmentKind::Statements,
442 MACRO_PAT => FragmentKind::Pattern,
443 MACRO_TYPE => FragmentKind::Type,
442 ITEM_LIST => FragmentKind::Items, 444 ITEM_LIST => FragmentKind::Items,
443 LET_STMT => { 445 LET_STMT => {
444 // FIXME: Handle LHS Pattern 446 // FIXME: Handle LHS Pattern
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index 9705526fa..f12132f84 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -29,8 +29,9 @@ use base_db::CrateId;
29use mbe::ExpandResult; 29use mbe::ExpandResult;
30use parser::FragmentKind; 30use parser::FragmentKind;
31use std::sync::Arc; 31use std::sync::Arc;
32use syntax::{algo::SyntaxRewriter, SyntaxNode}; 32use syntax::{ted, SyntaxNode};
33 33
34#[derive(Debug)]
34pub struct ErrorEmitted { 35pub struct ErrorEmitted {
35 _private: (), 36 _private: (),
36} 37}
@@ -174,8 +175,9 @@ fn lazy_expand(
174) -> ExpandResult<Option<InFile<SyntaxNode>>> { 175) -> ExpandResult<Option<InFile<SyntaxNode>>> {
175 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); 176 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
176 177
177 let id: MacroCallId = 178 let id: MacroCallId = def
178 def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); 179 .as_lazy_macro(db, krate, MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id) })
180 .into();
179 181
180 let err = db.macro_expand_error(id); 182 let err = db.macro_expand_error(id);
181 let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)); 183 let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node));
@@ -190,10 +192,10 @@ fn eager_macro_recur(
190 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 192 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
191 mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError), 193 mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError),
192) -> Result<SyntaxNode, ErrorEmitted> { 194) -> Result<SyntaxNode, ErrorEmitted> {
193 let original = curr.value.clone(); 195 let original = curr.value.clone().clone_for_update();
194 196
195 let children = curr.value.descendants().filter_map(ast::MacroCall::cast); 197 let children = original.descendants().filter_map(ast::MacroCall::cast);
196 let mut rewriter = SyntaxRewriter::default(); 198 let mut replacements = Vec::new();
197 199
198 // Collect replacement 200 // Collect replacement
199 for child in children { 201 for child in children {
@@ -212,6 +214,7 @@ fn eager_macro_recur(
212 .into(); 214 .into();
213 db.parse_or_expand(id.as_file()) 215 db.parse_or_expand(id.as_file())
214 .expect("successful macro expansion should be parseable") 216 .expect("successful macro expansion should be parseable")
217 .clone_for_update()
215 } 218 }
216 MacroDefKind::Declarative(_) 219 MacroDefKind::Declarative(_)
217 | MacroDefKind::BuiltIn(..) 220 | MacroDefKind::BuiltIn(..)
@@ -225,15 +228,14 @@ fn eager_macro_recur(
225 } 228 }
226 }; 229 };
227 230
228 // check if the whole original sytnax is replaced 231 // check if the whole original syntax is replaced
229 // Note that SyntaxRewriter cannot replace the root node itself
230 if child.syntax() == &original { 232 if child.syntax() == &original {
231 return Ok(insert); 233 return Ok(insert);
232 } 234 }
233 235
234 rewriter.replace(child.syntax(), &insert); 236 replacements.push((child, insert));
235 } 237 }
236 238
237 let res = rewriter.rewrite(&original); 239 replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
238 Ok(res) 240 Ok(original)
239} 241}
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 3e332ee47..a0e6aec62 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -290,22 +290,27 @@ pub struct MacroCallLoc {
290 290
291#[derive(Debug, Clone, PartialEq, Eq, Hash)] 291#[derive(Debug, Clone, PartialEq, Eq, Hash)]
292pub enum MacroCallKind { 292pub enum MacroCallKind {
293 FnLike(AstId<ast::MacroCall>), 293 FnLike { ast_id: AstId<ast::MacroCall> },
294 Derive(AstId<ast::Item>, String), 294 Derive { ast_id: AstId<ast::Item>, derive_name: String, derive_attr: AttrId },
295} 295}
296 296
297#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
298pub struct AttrId(pub u32);
299
297impl MacroCallKind { 300impl MacroCallKind {
298 fn file_id(&self) -> HirFileId { 301 fn file_id(&self) -> HirFileId {
299 match self { 302 match self {
300 MacroCallKind::FnLike(ast_id) => ast_id.file_id, 303 MacroCallKind::FnLike { ast_id, .. } => ast_id.file_id,
301 MacroCallKind::Derive(ast_id, _) => ast_id.file_id, 304 MacroCallKind::Derive { ast_id, .. } => ast_id.file_id,
302 } 305 }
303 } 306 }
304 307
305 fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> { 308 fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
306 match self { 309 match self {
307 MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), 310 MacroCallKind::FnLike { ast_id, .. } => {
308 MacroCallKind::Derive(ast_id, _) => { 311 ast_id.with_value(ast_id.to_node(db).syntax().clone())
312 }
313 MacroCallKind::Derive { ast_id, .. } => {
309 ast_id.with_value(ast_id.to_node(db).syntax().clone()) 314 ast_id.with_value(ast_id.to_node(db).syntax().clone())
310 } 315 }
311 } 316 }
@@ -313,10 +318,10 @@ impl MacroCallKind {
313 318
314 fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> { 319 fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> {
315 match self { 320 match self {
316 MacroCallKind::FnLike(ast_id) => { 321 MacroCallKind::FnLike { ast_id, .. } => {
317 Some(ast_id.to_node(db).token_tree()?.syntax().clone()) 322 Some(ast_id.to_node(db).token_tree()?.syntax().clone())
318 } 323 }
319 MacroCallKind::Derive(ast_id, _) => Some(ast_id.to_node(db).syntax().clone()), 324 MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
320 } 325 }
321 } 326 }
322} 327}
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index a0f8766b0..bcfd3e524 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -221,6 +221,7 @@ pub mod known {
221 option_env, 221 option_env,
222 llvm_asm, 222 llvm_asm,
223 asm, 223 asm,
224 global_asm,
224 // Builtin derives 225 // Builtin derives
225 Copy, 226 Copy,
226 Clone, 227 Clone,
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 030b7eebe..66b3418f2 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -12,15 +12,15 @@ doctest = false
12[dependencies] 12[dependencies]
13cov-mark = { version = "1.1", features = ["thread-local"] } 13cov-mark = { version = "1.1", features = ["thread-local"] }
14itertools = "0.10.0" 14itertools = "0.10.0"
15arrayvec = "0.6" 15arrayvec = "0.7"
16smallvec = "1.2.0" 16smallvec = "1.2.0"
17ena = "0.14.0" 17ena = "0.14.0"
18log = "0.4.8" 18log = "0.4.8"
19rustc-hash = "1.1.0" 19rustc-hash = "1.1.0"
20scoped-tls = "1" 20scoped-tls = "1"
21chalk-solve = { version = "0.60", default-features = false } 21chalk-solve = { version = "0.64", default-features = false }
22chalk-ir = "0.60" 22chalk-ir = "0.64"
23chalk-recursive = "0.60" 23chalk-recursive = "0.64"
24la-arena = { version = "0.2.0", path = "../../lib/arena" } 24la-arena = { version = "0.2.0", path = "../../lib/arena" }
25 25
26stdx = { path = "../stdx", version = "0.0.0" } 26stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs
index 70c56cc45..2c07494a9 100644
--- a/crates/hir_ty/src/autoderef.rs
+++ b/crates/hir_ty/src/autoderef.rs
@@ -6,16 +6,15 @@
6use std::iter::successors; 6use std::iter::successors;
7 7
8use base_db::CrateId; 8use base_db::CrateId;
9use chalk_ir::cast::Cast; 9use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, VariableKind};
10use hir_def::lang_item::LangItemTarget; 10use hir_def::lang_item::LangItemTarget;
11use hir_expand::name::name; 11use hir_expand::name::name;
12use log::{info, warn}; 12use log::{info, warn};
13 13
14use crate::{ 14use crate::{
15 db::HirDatabase, 15 db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds,
16 traits::{InEnvironment, Solution}, 16 DebruijnIndex, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution, Ty, TyBuilder,
17 AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, DebruijnIndex, Interner, Ty, 17 TyKind,
18 TyBuilder, TyKind,
19}; 18};
20 19
21const AUTODEREF_RECURSION_LIMIT: usize = 10; 20const AUTODEREF_RECURSION_LIMIT: usize = 10;
@@ -37,18 +36,28 @@ pub(crate) fn deref(
37 krate: CrateId, 36 krate: CrateId,
38 ty: InEnvironment<&Canonical<Ty>>, 37 ty: InEnvironment<&Canonical<Ty>>,
39) -> Option<Canonical<Ty>> { 38) -> Option<Canonical<Ty>> {
40 if let Some(derefed) = ty.goal.value.builtin_deref() { 39 let _p = profile::span("deref");
40 if let Some(derefed) = builtin_deref(&ty.goal.value) {
41 Some(Canonical { value: derefed, binders: ty.goal.binders.clone() }) 41 Some(Canonical { value: derefed, binders: ty.goal.binders.clone() })
42 } else { 42 } else {
43 deref_by_trait(db, krate, ty) 43 deref_by_trait(db, krate, ty)
44 } 44 }
45} 45}
46 46
47fn builtin_deref(ty: &Ty) -> Option<Ty> {
48 match ty.kind(&Interner) {
49 TyKind::Ref(.., ty) => Some(ty.clone()),
50 TyKind::Raw(.., ty) => Some(ty.clone()),
51 _ => None,
52 }
53}
54
47fn deref_by_trait( 55fn deref_by_trait(
48 db: &dyn HirDatabase, 56 db: &dyn HirDatabase,
49 krate: CrateId, 57 krate: CrateId,
50 ty: InEnvironment<&Canonical<Ty>>, 58 ty: InEnvironment<&Canonical<Ty>>,
51) -> Option<Canonical<Ty>> { 59) -> Option<Canonical<Ty>> {
60 let _p = profile::span("deref_by_trait");
52 let deref_trait = match db.lang_item(krate, "deref".into())? { 61 let deref_trait = match db.lang_item(krate, "deref".into())? {
53 LangItemTarget::TraitId(it) => it, 62 LangItemTarget::TraitId(it) => it,
54 _ => return None, 63 _ => return None,
@@ -97,7 +106,7 @@ fn deref_by_trait(
97 binders: CanonicalVarKinds::from_iter( 106 binders: CanonicalVarKinds::from_iter(
98 &Interner, 107 &Interner,
99 ty.goal.binders.iter(&Interner).cloned().chain(Some(chalk_ir::WithKind::new( 108 ty.goal.binders.iter(&Interner).cloned().chain(Some(chalk_ir::WithKind::new(
100 chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), 109 VariableKind::Ty(chalk_ir::TyVariableKind::General),
101 chalk_ir::UniverseIndex::ROOT, 110 chalk_ir::UniverseIndex::ROOT,
102 ))), 111 ))),
103 ), 112 ),
@@ -122,23 +131,25 @@ fn deref_by_trait(
122 // assumptions will be broken. We would need to properly introduce 131 // assumptions will be broken. We would need to properly introduce
123 // new variables in that case 132 // new variables in that case
124 133
125 for i in 1..vars.0.binders.len(&Interner) { 134 for i in 1..vars.binders.len(&Interner) {
126 if vars.0.value.at(&Interner, i - 1).assert_ty_ref(&Interner).kind(&Interner) 135 if vars.value.subst.at(&Interner, i - 1).assert_ty_ref(&Interner).kind(&Interner)
127 != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1)) 136 != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1))
128 { 137 {
129 warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution); 138 warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution);
130 return None; 139 return None;
131 } 140 }
132 } 141 }
133 Some(Canonical { 142 // FIXME: we remove lifetime variables here since they can confuse
143 // the method resolution code later
144 Some(fixup_lifetime_variables(Canonical {
134 value: vars 145 value: vars
135 .0
136 .value 146 .value
137 .at(&Interner, vars.0.value.len(&Interner) - 1) 147 .subst
148 .at(&Interner, vars.value.subst.len(&Interner) - 1)
138 .assert_ty_ref(&Interner) 149 .assert_ty_ref(&Interner)
139 .clone(), 150 .clone(),
140 binders: vars.0.binders.clone(), 151 binders: vars.binders.clone(),
141 }) 152 }))
142 } 153 }
143 Solution::Ambig(_) => { 154 Solution::Ambig(_) => {
144 info!("Ambiguous solution for derefing {:?}: {:?}", ty.goal, solution); 155 info!("Ambiguous solution for derefing {:?}: {:?}", ty.goal, solution);
@@ -146,3 +157,32 @@ fn deref_by_trait(
146 } 157 }
147 } 158 }
148} 159}
160
161fn fixup_lifetime_variables<T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>>(
162 c: Canonical<T>,
163) -> Canonical<T> {
164 // Removes lifetime variables from the Canonical, replacing them by static lifetimes.
165 let mut i = 0;
166 let subst = Substitution::from_iter(
167 &Interner,
168 c.binders.iter(&Interner).map(|vk| match vk.kind {
169 VariableKind::Ty(_) => {
170 let index = i;
171 i += 1;
172 BoundVar::new(DebruijnIndex::INNERMOST, index).to_ty(&Interner).cast(&Interner)
173 }
174 VariableKind::Lifetime => static_lifetime().cast(&Interner),
175 VariableKind::Const(_) => unimplemented!(),
176 }),
177 );
178 let binders = CanonicalVarKinds::from_iter(
179 &Interner,
180 c.binders.iter(&Interner).filter(|vk| match vk.kind {
181 VariableKind::Ty(_) => true,
182 VariableKind::Lifetime => false,
183 VariableKind::Const(_) => true,
184 }),
185 );
186 let value = subst.apply(c.value, &Interner);
187 Canonical { binders, value }
188}
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs
index 4a9a8058f..e25ef866d 100644
--- a/crates/hir_ty/src/builder.rs
+++ b/crates/hir_ty/src/builder.rs
@@ -4,6 +4,7 @@ use std::iter;
4 4
5use chalk_ir::{ 5use chalk_ir::{
6 cast::{Cast, CastTo, Caster}, 6 cast::{Cast, CastTo, Caster},
7 fold::Fold,
7 interner::HasInterner, 8 interner::HasInterner,
8 AdtId, BoundVar, DebruijnIndex, Safety, Scalar, 9 AdtId, BoundVar, DebruijnIndex, Safety, Scalar,
9}; 10};
@@ -12,8 +13,8 @@ use smallvec::SmallVec;
12 13
13use crate::{ 14use crate::{
14 db::HirDatabase, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, 15 db::HirDatabase, primitive, to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders,
15 CallableSig, FnPointer, FnSig, GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, 16 CallableSig, FnPointer, FnSig, FnSubst, GenericArg, Interner, ProjectionTy, Substitution,
16 TyDefId, TyKind, TypeWalk, ValueTyDefId, 17 TraitRef, Ty, TyDefId, TyExt, TyKind, ValueTyDefId,
17}; 18};
18 19
19/// This is a builder for `Ty` or anything that needs a `Substitution`. 20/// This is a builder for `Ty` or anything that needs a `Substitution`.
@@ -32,8 +33,7 @@ impl<D> TyBuilder<D> {
32 33
33 fn build_internal(self) -> (D, Substitution) { 34 fn build_internal(self) -> (D, Substitution) {
34 assert_eq!(self.vec.len(), self.param_count); 35 assert_eq!(self.vec.len(), self.param_count);
35 // FIXME: would be good to have a way to construct a chalk_ir::Substitution from the interned form 36 let subst = Substitution::from_iter(&Interner, self.vec);
36 let subst = Substitution(self.vec);
37 (self.data, subst) 37 (self.data, subst)
38 } 38 }
39 39
@@ -54,7 +54,7 @@ impl<D> TyBuilder<D> {
54 } 54 }
55 55
56 pub fn fill_with_unknown(self) -> Self { 56 pub fn fill_with_unknown(self) -> Self {
57 self.fill(iter::repeat(TyKind::Unknown.intern(&Interner))) 57 self.fill(iter::repeat(TyKind::Error.intern(&Interner)))
58 } 58 }
59 59
60 pub fn fill(mut self, filler: impl Iterator<Item = impl CastTo<GenericArg>>) -> Self { 60 pub fn fill(mut self, filler: impl Iterator<Item = impl CastTo<GenericArg>>) -> Self {
@@ -78,9 +78,12 @@ impl TyBuilder<()> {
78 78
79 pub fn fn_ptr(sig: CallableSig) -> Ty { 79 pub fn fn_ptr(sig: CallableSig) -> Ty {
80 TyKind::Function(FnPointer { 80 TyKind::Function(FnPointer {
81 num_args: sig.params().len(), 81 num_binders: 0,
82 sig: FnSig { abi: (), safety: Safety::Safe, variadic: sig.is_varargs }, 82 sig: FnSig { abi: (), safety: Safety::Safe, variadic: sig.is_varargs },
83 substs: Substitution::from_iter(&Interner, sig.params_and_return.iter().cloned()), 83 substitution: FnSubst(Substitution::from_iter(
84 &Interner,
85 sig.params_and_return.iter().cloned(),
86 )),
84 }) 87 })
85 .intern(&Interner) 88 .intern(&Interner)
86 } 89 }
@@ -138,8 +141,9 @@ impl TyBuilder<hir_def::AdtId> {
138 self.vec.push(fallback().cast(&Interner)); 141 self.vec.push(fallback().cast(&Interner));
139 } else { 142 } else {
140 // each default can depend on the previous parameters 143 // each default can depend on the previous parameters
141 let subst_so_far = Substitution(self.vec.clone()); 144 let subst_so_far = Substitution::from_iter(&Interner, self.vec.clone());
142 self.vec.push(default_ty.clone().subst(&subst_so_far).cast(&Interner)); 145 self.vec
146 .push(default_ty.clone().substitute(&Interner, &subst_so_far).cast(&Interner));
143 } 147 }
144 } 148 }
145 self 149 self
@@ -192,15 +196,15 @@ impl TyBuilder<TypeAliasId> {
192 } 196 }
193} 197}
194 198
195impl<T: TypeWalk + HasInterner<Interner = Interner>> TyBuilder<Binders<T>> { 199impl<T: HasInterner<Interner = Interner> + Fold<Interner>> TyBuilder<Binders<T>> {
196 fn subst_binders(b: Binders<T>) -> Self { 200 fn subst_binders(b: Binders<T>) -> Self {
197 let param_count = b.num_binders; 201 let param_count = b.binders.len(&Interner);
198 TyBuilder::new(b, param_count) 202 TyBuilder::new(b, param_count)
199 } 203 }
200 204
201 pub fn build(self) -> T { 205 pub fn build(self) -> <T as Fold<Interner>>::Result {
202 let (b, subst) = self.build_internal(); 206 let (b, subst) = self.build_internal();
203 b.subst(&subst) 207 b.substitute(&Interner, &subst)
204 } 208 }
205} 209}
206 210
diff --git a/crates/hir_ty/src/chalk_cast.rs b/crates/hir_ty/src/chalk_cast.rs
deleted file mode 100644
index df6492113..000000000
--- a/crates/hir_ty/src/chalk_cast.rs
+++ /dev/null
@@ -1,73 +0,0 @@
1//! Implementations of the Chalk `Cast` trait for our types.
2
3use chalk_ir::{
4 cast::{Cast, CastTo},
5 interner::HasInterner,
6};
7
8use crate::{AliasEq, DomainGoal, GenericArg, GenericArgData, Interner, TraitRef, Ty, WhereClause};
9
10macro_rules! has_interner {
11 ($t:ty) => {
12 impl HasInterner for $t {
13 type Interner = crate::Interner;
14 }
15 };
16}
17
18has_interner!(WhereClause);
19has_interner!(DomainGoal);
20has_interner!(GenericArg);
21has_interner!(Ty);
22
23impl CastTo<WhereClause> for TraitRef {
24 fn cast_to(self, _interner: &Interner) -> WhereClause {
25 WhereClause::Implemented(self)
26 }
27}
28
29impl CastTo<WhereClause> for AliasEq {
30 fn cast_to(self, _interner: &Interner) -> WhereClause {
31 WhereClause::AliasEq(self)
32 }
33}
34
35impl CastTo<DomainGoal> for WhereClause {
36 fn cast_to(self, _interner: &Interner) -> DomainGoal {
37 DomainGoal::Holds(self)
38 }
39}
40
41impl CastTo<GenericArg> for Ty {
42 fn cast_to(self, interner: &Interner) -> GenericArg {
43 GenericArg::new(interner, GenericArgData::Ty(self))
44 }
45}
46
47macro_rules! transitive_impl {
48 ($a:ty, $b:ty, $c:ty) => {
49 impl CastTo<$c> for $a {
50 fn cast_to(self, interner: &Interner) -> $c {
51 self.cast::<$b>(interner).cast(interner)
52 }
53 }
54 };
55}
56
57// In Chalk, these can be done as blanket impls, but that doesn't work here
58// because of coherence
59
60transitive_impl!(TraitRef, WhereClause, DomainGoal);
61transitive_impl!(AliasEq, WhereClause, DomainGoal);
62
63macro_rules! reflexive_impl {
64 ($a:ty) => {
65 impl CastTo<$a> for $a {
66 fn cast_to(self, _interner: &Interner) -> $a {
67 self
68 }
69 }
70 };
71}
72
73reflexive_impl!(GenericArg);
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/chalk_db.rs
index 541e6082f..8f054d06b 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/chalk_db.rs
@@ -1,52 +1,47 @@
1//! Conversion code from/to Chalk. 1//! The implementation of `RustIrDatabase` for Chalk, which provides information
2//! about the code that Chalk needs.
2use std::sync::Arc; 3use std::sync::Arc;
3 4
4use log::debug; 5use log::debug;
5 6
6use chalk_ir::{fold::shift::Shift, CanonicalVarKinds}; 7use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds};
7use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; 8use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
8 9
9use base_db::{salsa::InternKey, CrateId}; 10use base_db::CrateId;
10use hir_def::{ 11use hir_def::{
11 lang_item::{lang_attr, LangItemTarget}, 12 lang_item::{lang_attr, LangItemTarget},
12 AssocContainerId, AssocItemId, HasModule, Lookup, TypeAliasId, 13 AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId,
13}; 14};
14use hir_expand::name::name; 15use hir_expand::name::name;
15 16
16use super::ChalkContext;
17use crate::{ 17use crate::{
18 db::HirDatabase, 18 db::HirDatabase,
19 display::HirDisplay, 19 display::HirDisplay,
20 from_assoc_type_id, 20 from_assoc_type_id, from_chalk_trait_id, make_only_type_binders,
21 mapping::{from_chalk, ToChalk, TypeAliasAsValue},
21 method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, 22 method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
22 to_assoc_type_id, to_chalk_trait_id, 23 to_assoc_type_id, to_chalk_trait_id,
24 traits::ChalkContext,
23 utils::generics, 25 utils::generics,
24 AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, ProjectionTy, Substitution, 26 AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, Interner, ProjectionTy,
25 TraitRef, Ty, TyBuilder, TyKind, WhereClause, 27 ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder,
28 TyExt, TyKind, WhereClause,
26}; 29};
27use mapping::{
28 convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsValue,
29};
30
31pub use self::interner::Interner;
32pub(crate) use self::interner::*;
33 30
34pub(super) mod tls; 31pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>;
35mod interner; 32pub(crate) type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>;
36mod mapping; 33pub(crate) type StructDatum = chalk_solve::rust_ir::AdtDatum<Interner>;
37 34pub(crate) type ImplDatum = chalk_solve::rust_ir::ImplDatum<Interner>;
38pub(crate) trait ToChalk { 35pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>;
39 type Chalk; 36
40 fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk; 37pub(crate) type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
41 fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self; 38pub(crate) type TraitId = chalk_ir::TraitId<Interner>;
42} 39pub(crate) type AdtId = chalk_ir::AdtId<Interner>;
43 40pub(crate) type ImplId = chalk_ir::ImplId<Interner>;
44pub(crate) fn from_chalk<T, ChalkT>(db: &dyn HirDatabase, chalk: ChalkT) -> T 41pub(crate) type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId<Interner>;
45where 42pub(crate) type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>;
46 T: ToChalk<Chalk = ChalkT>, 43pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>;
47{ 44pub(crate) type Variances = chalk_ir::Variances<Interner>;
48 T::from_chalk(db, chalk)
49}
50 45
51impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { 46impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
52 fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> { 47 fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> {
@@ -84,9 +79,9 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
84 binders: &CanonicalVarKinds<Interner>, 79 binders: &CanonicalVarKinds<Interner>,
85 ) -> Vec<ImplId> { 80 ) -> Vec<ImplId> {
86 debug!("impls_for_trait {:?}", trait_id); 81 debug!("impls_for_trait {:?}", trait_id);
87 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); 82 let trait_: hir_def::TraitId = from_chalk_trait_id(trait_id);
88 83
89 let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref(&Interner).clone()); 84 let ty: Ty = parameters[0].assert_ty_ref(&Interner).clone();
90 85
91 fn binder_kind( 86 fn binder_kind(
92 ty: &Ty, 87 ty: &Ty,
@@ -103,7 +98,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
103 None 98 None
104 } 99 }
105 100
106 let self_ty_fp = TyFingerprint::for_impl(&ty); 101 let self_ty_fp = TyFingerprint::for_trait_impl(&ty);
107 let fps: &[TyFingerprint] = match binder_kind(&ty, binders) { 102 let fps: &[TyFingerprint] = match binder_kind(&ty, binders) {
108 Some(chalk_ir::TyVariableKind::Integer) => &ALL_INT_FPS, 103 Some(chalk_ir::TyVariableKind::Integer) => &ALL_INT_FPS,
109 Some(chalk_ir::TyVariableKind::Float) => &ALL_FLOAT_FPS, 104 Some(chalk_ir::TyVariableKind::Float) => &ALL_FLOAT_FPS,
@@ -166,7 +161,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
166 Some(LangItemTarget::TraitId(trait_)) => trait_, 161 Some(LangItemTarget::TraitId(trait_)) => trait_,
167 _ => return None, 162 _ => return None,
168 }; 163 };
169 Some(trait_.to_chalk(self.db)) 164 Some(to_chalk_trait_id(trait_))
170 } 165 }
171 166
172 fn program_clauses_for_env( 167 fn program_clauses_for_env(
@@ -184,16 +179,16 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
184 .db 179 .db
185 .return_type_impl_traits(func) 180 .return_type_impl_traits(func)
186 .expect("impl trait id without impl traits"); 181 .expect("impl trait id without impl traits");
187 let data = &datas.value.impl_traits[idx as usize]; 182 let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders();
183 let data = &datas.impl_traits[idx as usize];
188 let bound = OpaqueTyDatumBound { 184 let bound = OpaqueTyDatumBound {
189 bounds: make_binders( 185 bounds: make_only_type_binders(
190 data.bounds.value.iter().cloned().map(|b| b.to_chalk(self.db)).collect(),
191 1, 186 1,
187 data.bounds.skip_binders().iter().cloned().collect(),
192 ), 188 ),
193 where_clauses: make_binders(vec![], 0), 189 where_clauses: make_only_type_binders(0, vec![]),
194 }; 190 };
195 let num_vars = datas.num_binders; 191 chalk_ir::Binders::new(binders, bound)
196 make_binders(bound, num_vars)
197 } 192 }
198 crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { 193 crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
199 if let Some((future_trait, future_output)) = self 194 if let Some((future_trait, future_output)) = self
@@ -215,7 +210,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
215 let impl_bound = WhereClause::Implemented(TraitRef { 210 let impl_bound = WhereClause::Implemented(TraitRef {
216 trait_id: to_chalk_trait_id(future_trait), 211 trait_id: to_chalk_trait_id(future_trait),
217 // Self type as the first parameter. 212 // Self type as the first parameter.
218 substitution: Substitution::single( 213 substitution: Substitution::from1(
214 &Interner,
219 TyKind::BoundVar(BoundVar { 215 TyKind::BoundVar(BoundVar {
220 debruijn: DebruijnIndex::INNERMOST, 216 debruijn: DebruijnIndex::INNERMOST,
221 index: 0, 217 index: 0,
@@ -227,7 +223,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
227 alias: AliasTy::Projection(ProjectionTy { 223 alias: AliasTy::Projection(ProjectionTy {
228 associated_ty_id: to_assoc_type_id(future_output), 224 associated_ty_id: to_assoc_type_id(future_output),
229 // Self type as the first parameter. 225 // Self type as the first parameter.
230 substitution: Substitution::single( 226 substitution: Substitution::from1(
227 &Interner,
231 TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) 228 TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
232 .intern(&Interner), 229 .intern(&Interner),
233 ), 230 ),
@@ -237,25 +234,25 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
237 .intern(&Interner), 234 .intern(&Interner),
238 }); 235 });
239 let bound = OpaqueTyDatumBound { 236 let bound = OpaqueTyDatumBound {
240 bounds: make_binders( 237 bounds: make_only_type_binders(
238 1,
241 vec![ 239 vec![
242 wrap_in_empty_binders(impl_bound).to_chalk(self.db), 240 crate::wrap_empty_binders(impl_bound),
243 wrap_in_empty_binders(proj_bound).to_chalk(self.db), 241 crate::wrap_empty_binders(proj_bound),
244 ], 242 ],
245 1,
246 ), 243 ),
247 where_clauses: make_binders(vec![], 0), 244 where_clauses: make_only_type_binders(0, vec![]),
248 }; 245 };
249 // The opaque type has 1 parameter. 246 // The opaque type has 1 parameter.
250 make_binders(bound, 1) 247 make_only_type_binders(1, bound)
251 } else { 248 } else {
252 // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback. 249 // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback.
253 let bound = OpaqueTyDatumBound { 250 let bound = OpaqueTyDatumBound {
254 bounds: make_binders(vec![], 0), 251 bounds: make_only_type_binders(0, vec![]),
255 where_clauses: make_binders(vec![], 0), 252 where_clauses: make_only_type_binders(0, vec![]),
256 }; 253 };
257 // The opaque type has 1 parameter. 254 // The opaque type has 1 parameter.
258 make_binders(bound, 1) 255 make_only_type_binders(1, bound)
259 } 256 }
260 } 257 }
261 }; 258 };
@@ -265,7 +262,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
265 262
266 fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> { 263 fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> {
267 // FIXME: actually provide the hidden type; it is relevant for auto traits 264 // FIXME: actually provide the hidden type; it is relevant for auto traits
268 TyKind::Unknown.intern(&Interner).to_chalk(self.db) 265 TyKind::Error.intern(&Interner)
269 } 266 }
270 267
271 fn is_object_safe(&self, _trait_id: chalk_ir::TraitId<Interner>) -> bool { 268 fn is_object_safe(&self, _trait_id: chalk_ir::TraitId<Interner>) -> bool {
@@ -286,33 +283,32 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
286 _closure_id: chalk_ir::ClosureId<Interner>, 283 _closure_id: chalk_ir::ClosureId<Interner>,
287 substs: &chalk_ir::Substitution<Interner>, 284 substs: &chalk_ir::Substitution<Interner>,
288 ) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> { 285 ) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> {
289 let sig_ty: Ty = 286 let sig_ty = substs.at(&Interner, 0).assert_ty_ref(&Interner).clone();
290 from_chalk(self.db, substs.at(&Interner, 0).assert_ty_ref(&Interner).clone());
291 let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr"); 287 let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr");
292 let io = rust_ir::FnDefInputsAndOutputDatum { 288 let io = rust_ir::FnDefInputsAndOutputDatum {
293 argument_types: sig.params().iter().map(|ty| ty.clone().to_chalk(self.db)).collect(), 289 argument_types: sig.params().iter().cloned().collect(),
294 return_type: sig.ret().clone().to_chalk(self.db), 290 return_type: sig.ret().clone(),
295 }; 291 };
296 make_binders(io.shifted_in(&Interner), 0) 292 make_only_type_binders(0, io.shifted_in(&Interner))
297 } 293 }
298 fn closure_upvars( 294 fn closure_upvars(
299 &self, 295 &self,
300 _closure_id: chalk_ir::ClosureId<Interner>, 296 _closure_id: chalk_ir::ClosureId<Interner>,
301 _substs: &chalk_ir::Substitution<Interner>, 297 _substs: &chalk_ir::Substitution<Interner>,
302 ) -> chalk_ir::Binders<chalk_ir::Ty<Interner>> { 298 ) -> chalk_ir::Binders<chalk_ir::Ty<Interner>> {
303 let ty = TyBuilder::unit().to_chalk(self.db); 299 let ty = TyBuilder::unit();
304 make_binders(ty, 0) 300 make_only_type_binders(0, ty)
305 } 301 }
306 fn closure_fn_substitution( 302 fn closure_fn_substitution(
307 &self, 303 &self,
308 _closure_id: chalk_ir::ClosureId<Interner>, 304 _closure_id: chalk_ir::ClosureId<Interner>,
309 _substs: &chalk_ir::Substitution<Interner>, 305 _substs: &chalk_ir::Substitution<Interner>,
310 ) -> chalk_ir::Substitution<Interner> { 306 ) -> chalk_ir::Substitution<Interner> {
311 Substitution::empty(&Interner).to_chalk(self.db) 307 Substitution::empty(&Interner)
312 } 308 }
313 309
314 fn trait_name(&self, trait_id: chalk_ir::TraitId<Interner>) -> String { 310 fn trait_name(&self, trait_id: chalk_ir::TraitId<Interner>) -> String {
315 let id = from_chalk(self.db, trait_id); 311 let id = from_chalk_trait_id(trait_id);
316 self.db.trait_data(id).name.to_string() 312 self.db.trait_data(id).name.to_string()
317 } 313 }
318 fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String { 314 fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String {
@@ -403,10 +399,10 @@ pub(crate) fn associated_ty_data_query(
403 let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); 399 let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars);
404 let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses }; 400 let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses };
405 let datum = AssociatedTyDatum { 401 let datum = AssociatedTyDatum {
406 trait_id: trait_.to_chalk(db), 402 trait_id: to_chalk_trait_id(trait_),
407 id, 403 id,
408 name: type_alias, 404 name: type_alias,
409 binders: make_binders(bound_data, generic_params.len()), 405 binders: make_only_type_binders(generic_params.len(), bound_data),
410 }; 406 };
411 Arc::new(datum) 407 Arc::new(datum)
412} 408}
@@ -417,7 +413,7 @@ pub(crate) fn trait_datum_query(
417 trait_id: TraitId, 413 trait_id: TraitId,
418) -> Arc<TraitDatum> { 414) -> Arc<TraitDatum> {
419 debug!("trait_datum {:?}", trait_id); 415 debug!("trait_datum {:?}", trait_id);
420 let trait_: hir_def::TraitId = from_chalk(db, trait_id); 416 let trait_ = from_chalk_trait_id(trait_id);
421 let trait_data = db.trait_data(trait_); 417 let trait_data = db.trait_data(trait_);
422 debug!("trait {:?} = {:?}", trait_id, trait_data.name); 418 debug!("trait {:?} = {:?}", trait_id, trait_data.name);
423 let generic_params = generics(db.upcast(), trait_.into()); 419 let generic_params = generics(db.upcast(), trait_.into());
@@ -439,7 +435,7 @@ pub(crate) fn trait_datum_query(
439 lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); 435 lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name));
440 let trait_datum = TraitDatum { 436 let trait_datum = TraitDatum {
441 id: trait_id, 437 id: trait_id,
442 binders: make_binders(trait_datum_bound, bound_vars.len(&Interner)), 438 binders: make_only_type_binders(bound_vars.len(&Interner), trait_datum_bound),
443 flags, 439 flags,
444 associated_ty_ids, 440 associated_ty_ids,
445 well_known, 441 well_known,
@@ -508,7 +504,7 @@ pub(crate) fn struct_datum_query(
508 // FIXME set ADT kind 504 // FIXME set ADT kind
509 kind: rust_ir::AdtKind::Struct, 505 kind: rust_ir::AdtKind::Struct,
510 id: struct_id, 506 id: struct_id,
511 binders: make_binders(struct_datum_bound, num_params), 507 binders: make_only_type_binders(num_params, struct_datum_bound),
512 flags, 508 flags,
513 }; 509 };
514 Arc::new(struct_datum) 510 Arc::new(struct_datum)
@@ -535,7 +531,8 @@ fn impl_def_datum(
535 .impl_trait(impl_id) 531 .impl_trait(impl_id)
536 // ImplIds for impls where the trait ref can't be resolved should never reach Chalk 532 // ImplIds for impls where the trait ref can't be resolved should never reach Chalk
537 .expect("invalid impl passed to Chalk") 533 .expect("invalid impl passed to Chalk")
538 .value; 534 .into_value_and_skipped_binders()
535 .0;
539 let impl_data = db.impl_data(impl_id); 536 let impl_data = db.impl_data(impl_id);
540 537
541 let generic_params = generics(db.upcast(), impl_id.into()); 538 let generic_params = generics(db.upcast(), impl_id.into());
@@ -555,7 +552,6 @@ fn impl_def_datum(
555 trait_ref.display(db), 552 trait_ref.display(db),
556 where_clauses 553 where_clauses
557 ); 554 );
558 let trait_ref = trait_ref.to_chalk(db);
559 555
560 let polarity = if negative { rust_ir::Polarity::Negative } else { rust_ir::Polarity::Positive }; 556 let polarity = if negative { rust_ir::Polarity::Negative } else { rust_ir::Polarity::Positive };
561 557
@@ -577,7 +573,7 @@ fn impl_def_datum(
577 .collect(); 573 .collect();
578 debug!("impl_datum: {:?}", impl_datum_bound); 574 debug!("impl_datum: {:?}", impl_datum_bound);
579 let impl_datum = ImplDatum { 575 let impl_datum = ImplDatum {
580 binders: make_binders(impl_datum_bound, bound_vars.len(&Interner)), 576 binders: make_only_type_binders(bound_vars.len(&Interner), impl_datum_bound),
581 impl_type, 577 impl_type,
582 polarity, 578 polarity,
583 associated_ty_value_ids, 579 associated_ty_value_ids,
@@ -605,18 +601,22 @@ fn type_alias_associated_ty_value(
605 _ => panic!("assoc ty value should be in impl"), 601 _ => panic!("assoc ty value should be in impl"),
606 }; 602 };
607 603
608 let trait_ref = db.impl_trait(impl_id).expect("assoc ty value should not exist").value; // we don't return any assoc ty values if the impl'd trait can't be resolved 604 let trait_ref = db
605 .impl_trait(impl_id)
606 .expect("assoc ty value should not exist")
607 .into_value_and_skipped_binders()
608 .0; // we don't return any assoc ty values if the impl'd trait can't be resolved
609 609
610 let assoc_ty = db 610 let assoc_ty = db
611 .trait_data(trait_ref.hir_trait_id()) 611 .trait_data(trait_ref.hir_trait_id())
612 .associated_type_by_name(&type_alias_data.name) 612 .associated_type_by_name(&type_alias_data.name)
613 .expect("assoc ty value should not exist"); // validated when building the impl data as well 613 .expect("assoc ty value should not exist"); // validated when building the impl data as well
614 let ty = db.ty(type_alias.into()); 614 let (ty, binders) = db.ty(type_alias.into()).into_value_and_skipped_binders();
615 let value_bound = rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) }; 615 let value_bound = rust_ir::AssociatedTyValueBound { ty };
616 let value = rust_ir::AssociatedTyValue { 616 let value = rust_ir::AssociatedTyValue {
617 impl_id: impl_id.to_chalk(db), 617 impl_id: impl_id.to_chalk(db),
618 associated_ty_id: to_assoc_type_id(assoc_ty), 618 associated_ty_id: to_assoc_type_id(assoc_ty),
619 value: make_binders(value_bound, ty.num_binders), 619 value: chalk_ir::Binders::new(binders, value_bound),
620 }; 620 };
621 Arc::new(value) 621 Arc::new(value)
622} 622}
@@ -628,34 +628,25 @@ pub(crate) fn fn_def_datum_query(
628) -> Arc<FnDefDatum> { 628) -> Arc<FnDefDatum> {
629 let callable_def: CallableDefId = from_chalk(db, fn_def_id); 629 let callable_def: CallableDefId = from_chalk(db, fn_def_id);
630 let generic_params = generics(db.upcast(), callable_def.into()); 630 let generic_params = generics(db.upcast(), callable_def.into());
631 let sig = db.callable_item_signature(callable_def); 631 let (sig, binders) = db.callable_item_signature(callable_def).into_value_and_skipped_binders();
632 let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST); 632 let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
633 let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars); 633 let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars);
634 let bound = rust_ir::FnDefDatumBound { 634 let bound = rust_ir::FnDefDatumBound {
635 // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway 635 // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway
636 inputs_and_output: make_binders( 636 inputs_and_output: make_only_type_binders(
637 0,
637 rust_ir::FnDefInputsAndOutputDatum { 638 rust_ir::FnDefInputsAndOutputDatum {
638 argument_types: sig 639 argument_types: sig.params().iter().cloned().collect(),
639 .value 640 return_type: sig.ret().clone(),
640 .params()
641 .iter()
642 .map(|ty| ty.clone().to_chalk(db))
643 .collect(),
644 return_type: sig.value.ret().clone().to_chalk(db),
645 } 641 }
646 .shifted_in(&Interner), 642 .shifted_in(&Interner),
647 0,
648 ), 643 ),
649 where_clauses, 644 where_clauses,
650 }; 645 };
651 let datum = FnDefDatum { 646 let datum = FnDefDatum {
652 id: fn_def_id, 647 id: fn_def_id,
653 sig: chalk_ir::FnSig { 648 sig: chalk_ir::FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: sig.is_varargs },
654 abi: (), 649 binders: chalk_ir::Binders::new(binders, bound),
655 safety: chalk_ir::Safety::Safe,
656 variadic: sig.value.is_varargs,
657 },
658 binders: make_binders(bound, sig.num_binders),
659 }; 650 };
660 Arc::new(datum) 651 Arc::new(datum)
661} 652}
@@ -685,42 +676,65 @@ pub(crate) fn adt_variance_query(
685 ) 676 )
686} 677}
687 678
688impl From<FnDefId> for crate::db::InternedCallableDefId { 679pub(super) fn convert_where_clauses(
689 fn from(fn_def_id: FnDefId) -> Self { 680 db: &dyn HirDatabase,
690 InternKey::from_intern_id(fn_def_id.0) 681 def: GenericDefId,
691 } 682 substs: &Substitution,
692} 683) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
693 684 let generic_predicates = db.generic_predicates(def);
694impl From<crate::db::InternedCallableDefId> for FnDefId { 685 let mut result = Vec::with_capacity(generic_predicates.len());
695 fn from(callable_def_id: crate::db::InternedCallableDefId) -> Self { 686 for pred in generic_predicates.iter() {
696 chalk_ir::FnDefId(callable_def_id.as_intern_id()) 687 result.push(pred.clone().substitute(&Interner, substs));
697 } 688 }
698} 689 result
699
700impl From<OpaqueTyId> for crate::db::InternedOpaqueTyId {
701 fn from(id: OpaqueTyId) -> Self {
702 InternKey::from_intern_id(id.0)
703 }
704}
705
706impl From<crate::db::InternedOpaqueTyId> for OpaqueTyId {
707 fn from(id: crate::db::InternedOpaqueTyId) -> Self {
708 chalk_ir::OpaqueTyId(id.as_intern_id())
709 }
710}
711
712impl From<chalk_ir::ClosureId<Interner>> for crate::db::InternedClosureId {
713 fn from(id: chalk_ir::ClosureId<Interner>) -> Self {
714 Self::from_intern_id(id.0)
715 }
716} 690}
717 691
718impl From<crate::db::InternedClosureId> for chalk_ir::ClosureId<Interner> { 692pub(super) fn generic_predicate_to_inline_bound(
719 fn from(id: crate::db::InternedClosureId) -> Self { 693 db: &dyn HirDatabase,
720 chalk_ir::ClosureId(id.as_intern_id()) 694 pred: &QuantifiedWhereClause,
695 self_ty: &Ty,
696) -> Option<chalk_ir::Binders<rust_ir::InlineBound<Interner>>> {
697 // An InlineBound is like a GenericPredicate, except the self type is left out.
698 // We don't have a special type for this, but Chalk does.
699 let self_ty_shifted_in = self_ty.clone().shifted_in_from(&Interner, DebruijnIndex::ONE);
700 let (pred, binders) = pred.as_ref().into_value_and_skipped_binders();
701 match pred {
702 WhereClause::Implemented(trait_ref) => {
703 if trait_ref.self_type_parameter(&Interner) != self_ty_shifted_in {
704 // we can only convert predicates back to type bounds if they
705 // have the expected self type
706 return None;
707 }
708 let args_no_self = trait_ref.substitution.as_slice(&Interner)[1..]
709 .iter()
710 .map(|ty| ty.clone().cast(&Interner))
711 .collect();
712 let trait_bound = rust_ir::TraitBound { trait_id: trait_ref.trait_id, args_no_self };
713 Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound)))
714 }
715 WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
716 if projection_ty.self_type_parameter(&Interner) != self_ty_shifted_in {
717 return None;
718 }
719 let trait_ = projection_ty.trait_(db);
720 let args_no_self = projection_ty.substitution.as_slice(&Interner)[1..]
721 .iter()
722 .map(|ty| ty.clone().cast(&Interner))
723 .collect();
724 let alias_eq_bound = rust_ir::AliasEqBound {
725 value: ty.clone(),
726 trait_bound: rust_ir::TraitBound {
727 trait_id: to_chalk_trait_id(trait_),
728 args_no_self,
729 },
730 associated_ty_id: projection_ty.associated_ty_id,
731 parameters: Vec::new(), // FIXME we don't support generic associated types yet
732 };
733 Some(chalk_ir::Binders::new(
734 binders,
735 rust_ir::InlineBound::AliasEqBound(alias_eq_bound),
736 ))
737 }
738 _ => None,
721 } 739 }
722} 740}
723
724fn wrap_in_empty_binders<T: crate::TypeWalk>(value: T) -> crate::Binders<T> {
725 crate::Binders::wrap_empty(value)
726}
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs
index b7463366b..8c4542956 100644
--- a/crates/hir_ty/src/chalk_ext.rs
+++ b/crates/hir_ty/src/chalk_ext.rs
@@ -1,13 +1,305 @@
1//! Various extensions traits for Chalk types. 1//! Various extensions traits for Chalk types.
2 2
3use crate::{Interner, Ty, TyKind}; 3use chalk_ir::Mutability;
4use hir_def::{
5 type_ref::Rawness, AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup, TraitId,
6};
7
8use crate::{
9 db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
10 from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId,
11 CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause,
12 Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause,
13};
4 14
5pub trait TyExt { 15pub trait TyExt {
6 fn is_unit(&self) -> bool; 16 fn is_unit(&self) -> bool;
17 fn is_never(&self) -> bool;
18 fn is_unknown(&self) -> bool;
19
20 fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
21 fn as_tuple(&self) -> Option<&Substitution>;
22 fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
23 fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
24 fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
25 fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>;
26
27 fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId>;
28 fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig>;
29
30 fn strip_references(&self) -> &Ty;
31
32 /// If this is a `dyn Trait`, returns that trait.
33 fn dyn_trait(&self) -> Option<TraitId>;
34
35 fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
36 fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>;
37
38 /// FIXME: Get rid of this, it's not a good abstraction
39 fn equals_ctor(&self, other: &Ty) -> bool;
7} 40}
8 41
9impl TyExt for Ty { 42impl TyExt for Ty {
10 fn is_unit(&self) -> bool { 43 fn is_unit(&self) -> bool {
11 matches!(self.kind(&Interner), TyKind::Tuple(0, _)) 44 matches!(self.kind(&Interner), TyKind::Tuple(0, _))
12 } 45 }
46
47 fn is_never(&self) -> bool {
48 matches!(self.kind(&Interner), TyKind::Never)
49 }
50
51 fn is_unknown(&self) -> bool {
52 matches!(self.kind(&Interner), TyKind::Error)
53 }
54
55 fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
56 match self.kind(&Interner) {
57 TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),
58 _ => None,
59 }
60 }
61
62 fn as_tuple(&self) -> Option<&Substitution> {
63 match self.kind(&Interner) {
64 TyKind::Tuple(_, substs) => Some(substs),
65 _ => None,
66 }
67 }
68
69 fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
70 if let Some(CallableDefId::FunctionId(func)) = self.callable_def(db) {
71 Some(func)
72 } else {
73 None
74 }
75 }
76 fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> {
77 match self.kind(&Interner) {
78 TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)),
79 _ => None,
80 }
81 }
82
83 fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
84 match self.kind(&Interner) {
85 TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)),
86 TyKind::Raw(mutability, ty) => Some((ty, Rawness::RawPtr, *mutability)),
87 _ => None,
88 }
89 }
90
91 fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> {
92 match *self.kind(&Interner) {
93 TyKind::Adt(AdtId(adt), ..) => Some(adt.into()),
94 TyKind::FnDef(callable, ..) => {
95 Some(db.lookup_intern_callable_def(callable.into()).into())
96 }
97 TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()),
98 TyKind::Foreign(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()),
99 _ => None,
100 }
101 }
102
103 fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> {
104 match self.kind(&Interner) {
105 &TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())),
106 _ => None,
107 }
108 }
109
110 fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> {
111 match self.kind(&Interner) {
112 TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)),
113 TyKind::FnDef(def, parameters) => {
114 let callable_def = db.lookup_intern_callable_def((*def).into());
115 let sig = db.callable_item_signature(callable_def);
116 Some(sig.substitute(&Interner, &parameters))
117 }
118 TyKind::Closure(.., substs) => {
119 let sig_param = substs.at(&Interner, 0).assert_ty_ref(&Interner);
120 sig_param.callable_sig(db)
121 }
122 _ => None,
123 }
124 }
125
126 fn dyn_trait(&self) -> Option<TraitId> {
127 let trait_ref = match self.kind(&Interner) {
128 TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
129 match b.skip_binders() {
130 WhereClause::Implemented(trait_ref) => Some(trait_ref),
131 _ => None,
132 }
133 }),
134 _ => None,
135 }?;
136 Some(from_chalk_trait_id(trait_ref.trait_id))
137 }
138
139 fn strip_references(&self) -> &Ty {
140 let mut t: &Ty = self;
141 while let TyKind::Ref(_mutability, _lifetime, ty) = t.kind(&Interner) {
142 t = ty;
143 }
144 t
145 }
146
147 fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> {
148 match self.kind(&Interner) {
149 TyKind::OpaqueType(opaque_ty_id, ..) => {
150 match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) {
151 ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => {
152 let krate = def.module(db.upcast()).krate();
153 if let Some(future_trait) = db
154 .lang_item(krate, "future_trait".into())
155 .and_then(|item| item.as_trait())
156 {
157 // This is only used by type walking.
158 // Parameters will be walked outside, and projection predicate is not used.
159 // So just provide the Future trait.
160 let impl_bound = Binders::empty(
161 &Interner,
162 WhereClause::Implemented(TraitRef {
163 trait_id: to_chalk_trait_id(future_trait),
164 substitution: Substitution::empty(&Interner),
165 }),
166 );
167 Some(vec![impl_bound])
168 } else {
169 None
170 }
171 }
172 ImplTraitId::ReturnTypeImplTrait(..) => None,
173 }
174 }
175 TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
176 let predicates = match db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into())
177 {
178 ImplTraitId::ReturnTypeImplTrait(func, idx) => {
179 db.return_type_impl_traits(func).map(|it| {
180 let data = (*it)
181 .as_ref()
182 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
183 data.substitute(&Interner, &opaque_ty.substitution)
184 })
185 }
186 // It always has an parameter for Future::Output type.
187 ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
188 };
189
190 predicates.map(|it| it.into_value_and_skipped_binders().0)
191 }
192 TyKind::Placeholder(idx) => {
193 let id = from_placeholder_idx(db, *idx);
194 let generic_params = db.generic_params(id.parent);
195 let param_data = &generic_params.types[id.local_id];
196 match param_data.provenance {
197 hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
198 let substs = TyBuilder::type_params_subst(db, id.parent);
199 let predicates = db
200 .generic_predicates(id.parent)
201 .into_iter()
202 .map(|pred| pred.clone().substitute(&Interner, &substs))
203 .filter(|wc| match &wc.skip_binders() {
204 WhereClause::Implemented(tr) => {
205 &tr.self_type_parameter(&Interner) == self
206 }
207 WhereClause::AliasEq(AliasEq {
208 alias: AliasTy::Projection(proj),
209 ty: _,
210 }) => &proj.self_type_parameter(&Interner) == self,
211 _ => false,
212 })
213 .collect::<Vec<_>>();
214
215 Some(predicates)
216 }
217 _ => None,
218 }
219 }
220 _ => None,
221 }
222 }
223
224 fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> {
225 match self.kind(&Interner) {
226 TyKind::AssociatedType(id, ..) => {
227 match from_assoc_type_id(*id).lookup(db.upcast()).container {
228 AssocContainerId::TraitId(trait_id) => Some(trait_id),
229 _ => None,
230 }
231 }
232 TyKind::Alias(AliasTy::Projection(projection_ty)) => {
233 match from_assoc_type_id(projection_ty.associated_ty_id)
234 .lookup(db.upcast())
235 .container
236 {
237 AssocContainerId::TraitId(trait_id) => Some(trait_id),
238 _ => None,
239 }
240 }
241 _ => None,
242 }
243 }
244
245 fn equals_ctor(&self, other: &Ty) -> bool {
246 match (self.kind(&Interner), other.kind(&Interner)) {
247 (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
248 (TyKind::Slice(_), TyKind::Slice(_)) | (TyKind::Array(_, _), TyKind::Array(_, _)) => {
249 true
250 }
251 (TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2,
252 (TyKind::OpaqueType(ty_id, ..), TyKind::OpaqueType(ty_id2, ..)) => ty_id == ty_id2,
253 (TyKind::AssociatedType(ty_id, ..), TyKind::AssociatedType(ty_id2, ..)) => {
254 ty_id == ty_id2
255 }
256 (TyKind::Foreign(ty_id, ..), TyKind::Foreign(ty_id2, ..)) => ty_id == ty_id2,
257 (TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2,
258 (TyKind::Ref(mutability, ..), TyKind::Ref(mutability2, ..))
259 | (TyKind::Raw(mutability, ..), TyKind::Raw(mutability2, ..)) => {
260 mutability == mutability2
261 }
262 (
263 TyKind::Function(FnPointer { num_binders, sig, .. }),
264 TyKind::Function(FnPointer { num_binders: num_binders2, sig: sig2, .. }),
265 ) => num_binders == num_binders2 && sig == sig2,
266 (TyKind::Tuple(cardinality, _), TyKind::Tuple(cardinality2, _)) => {
267 cardinality == cardinality2
268 }
269 (TyKind::Str, TyKind::Str) | (TyKind::Never, TyKind::Never) => true,
270 (TyKind::Scalar(scalar), TyKind::Scalar(scalar2)) => scalar == scalar2,
271 _ => false,
272 }
273 }
274}
275
276pub trait ProjectionTyExt {
277 fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef;
278 fn trait_(&self, db: &dyn HirDatabase) -> TraitId;
279}
280
281impl ProjectionTyExt for ProjectionTy {
282 fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
283 TraitRef {
284 trait_id: to_chalk_trait_id(self.trait_(db)),
285 substitution: self.substitution.clone(),
286 }
287 }
288
289 fn trait_(&self, db: &dyn HirDatabase) -> TraitId {
290 match from_assoc_type_id(self.associated_ty_id).lookup(db.upcast()).container {
291 AssocContainerId::TraitId(it) => it,
292 _ => panic!("projection ty without parent trait"),
293 }
294 }
295}
296
297pub trait TraitRefExt {
298 fn hir_trait_id(&self) -> TraitId;
299}
300
301impl TraitRefExt for TraitRef {
302 fn hir_trait_id(&self) -> TraitId {
303 from_chalk_trait_id(self.trait_id)
304 }
13} 305}
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index 58e4247c6..cf67d4266 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -1,18 +1,19 @@
1//! FIXME: write short doc here 1//! The home of `HirDatabase`, which is the Salsa database containing all the
2//! type inference-related queries.
2 3
3use std::sync::Arc; 4use std::sync::Arc;
4 5
5use base_db::{impl_intern_key, salsa, CrateId, Upcast}; 6use base_db::{impl_intern_key, salsa, CrateId, Upcast};
6use hir_def::{ 7use hir_def::{
7 db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId, 8 db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId,
8 LocalFieldId, TypeParamId, VariantId, 9 LifetimeParamId, LocalFieldId, TypeParamId, VariantId,
9}; 10};
10use la_arena::ArenaMap; 11use la_arena::ArenaMap;
11 12
12use crate::{ 13use crate::{
14 chalk_db,
13 method_resolution::{InherentImpls, TraitImpls}, 15 method_resolution::{InherentImpls, TraitImpls},
14 traits::chalk, 16 Binders, CallableDefId, FnDefId, ImplTraitId, InferenceResult, Interner, PolyFnSig,
15 Binders, CallableDefId, FnDefId, ImplTraitId, InferenceResult, PolyFnSig,
16 QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId, 17 QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId,
17}; 18};
18use hir_expand::name::Name; 19use hir_expand::name::Name;
@@ -86,51 +87,68 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
86 #[salsa::interned] 87 #[salsa::interned]
87 fn intern_type_param_id(&self, param_id: TypeParamId) -> InternedTypeParamId; 88 fn intern_type_param_id(&self, param_id: TypeParamId) -> InternedTypeParamId;
88 #[salsa::interned] 89 #[salsa::interned]
90 fn intern_lifetime_param_id(&self, param_id: LifetimeParamId) -> InternedLifetimeParamId;
91 #[salsa::interned]
92 fn intern_const_param_id(&self, param_id: ConstParamId) -> InternedConstParamId;
93 #[salsa::interned]
89 fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId; 94 fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
90 #[salsa::interned] 95 #[salsa::interned]
91 fn intern_closure(&self, id: (DefWithBodyId, ExprId)) -> InternedClosureId; 96 fn intern_closure(&self, id: (DefWithBodyId, ExprId)) -> InternedClosureId;
92 97
93 #[salsa::invoke(chalk::associated_ty_data_query)] 98 #[salsa::invoke(chalk_db::associated_ty_data_query)]
94 fn associated_ty_data(&self, id: chalk::AssocTypeId) -> Arc<chalk::AssociatedTyDatum>; 99 fn associated_ty_data(&self, id: chalk_db::AssocTypeId) -> Arc<chalk_db::AssociatedTyDatum>;
95 100
96 #[salsa::invoke(chalk::trait_datum_query)] 101 #[salsa::invoke(chalk_db::trait_datum_query)]
97 fn trait_datum(&self, krate: CrateId, trait_id: chalk::TraitId) -> Arc<chalk::TraitDatum>; 102 fn trait_datum(&self, krate: CrateId, trait_id: chalk_db::TraitId)
103 -> Arc<chalk_db::TraitDatum>;
98 104
99 #[salsa::invoke(chalk::struct_datum_query)] 105 #[salsa::invoke(chalk_db::struct_datum_query)]
100 fn struct_datum(&self, krate: CrateId, struct_id: chalk::AdtId) -> Arc<chalk::StructDatum>; 106 fn struct_datum(
107 &self,
108 krate: CrateId,
109 struct_id: chalk_db::AdtId,
110 ) -> Arc<chalk_db::StructDatum>;
101 111
102 #[salsa::invoke(crate::traits::chalk::impl_datum_query)] 112 #[salsa::invoke(chalk_db::impl_datum_query)]
103 fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>; 113 fn impl_datum(&self, krate: CrateId, impl_id: chalk_db::ImplId) -> Arc<chalk_db::ImplDatum>;
104 114
105 #[salsa::invoke(crate::traits::chalk::fn_def_datum_query)] 115 #[salsa::invoke(chalk_db::fn_def_datum_query)]
106 fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc<chalk::FnDefDatum>; 116 fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc<chalk_db::FnDefDatum>;
107 117
108 #[salsa::invoke(crate::traits::chalk::fn_def_variance_query)] 118 #[salsa::invoke(chalk_db::fn_def_variance_query)]
109 fn fn_def_variance(&self, krate: CrateId, fn_def_id: FnDefId) -> chalk::Variances; 119 fn fn_def_variance(&self, krate: CrateId, fn_def_id: FnDefId) -> chalk_db::Variances;
110 120
111 #[salsa::invoke(crate::traits::chalk::adt_variance_query)] 121 #[salsa::invoke(chalk_db::adt_variance_query)]
112 fn adt_variance(&self, krate: CrateId, adt_id: chalk::AdtId) -> chalk::Variances; 122 fn adt_variance(&self, krate: CrateId, adt_id: chalk_db::AdtId) -> chalk_db::Variances;
113 123
114 #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)] 124 #[salsa::invoke(chalk_db::associated_ty_value_query)]
115 fn associated_ty_value( 125 fn associated_ty_value(
116 &self, 126 &self,
117 krate: CrateId, 127 krate: CrateId,
118 id: chalk::AssociatedTyValueId, 128 id: chalk_db::AssociatedTyValueId,
119 ) -> Arc<chalk::AssociatedTyValue>; 129 ) -> Arc<chalk_db::AssociatedTyValue>;
120 130
121 #[salsa::invoke(crate::traits::trait_solve_query)] 131 #[salsa::invoke(trait_solve_wait)]
132 #[salsa::transparent]
122 fn trait_solve( 133 fn trait_solve(
123 &self, 134 &self,
124 krate: CrateId, 135 krate: CrateId,
125 goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>, 136 goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
126 ) -> Option<crate::traits::Solution>; 137 ) -> Option<crate::Solution>;
127 138
128 #[salsa::invoke(crate::traits::chalk::program_clauses_for_chalk_env_query)] 139 #[salsa::invoke(crate::traits::trait_solve_query)]
140 fn trait_solve_query(
141 &self,
142 krate: CrateId,
143 goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
144 ) -> Option<crate::Solution>;
145
146 #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)]
129 fn program_clauses_for_chalk_env( 147 fn program_clauses_for_chalk_env(
130 &self, 148 &self,
131 krate: CrateId, 149 krate: CrateId,
132 env: chalk_ir::Environment<chalk::Interner>, 150 env: chalk_ir::Environment<Interner>,
133 ) -> chalk_ir::ProgramClauses<chalk::Interner>; 151 ) -> chalk_ir::ProgramClauses<Interner>;
134} 152}
135 153
136fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> { 154fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
@@ -146,6 +164,15 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
146 db.infer_query(def) 164 db.infer_query(def)
147} 165}
148 166
167fn trait_solve_wait(
168 db: &dyn HirDatabase,
169 krate: CrateId,
170 goal: crate::Canonical<crate::InEnvironment<crate::DomainGoal>>,
171) -> Option<crate::Solution> {
172 let _p = profile::span("trait_solve::wait");
173 db.trait_solve_query(krate, goal)
174}
175
149#[test] 176#[test]
150fn hir_database_is_object_safe() { 177fn hir_database_is_object_safe() {
151 fn _assert_object_safe(_: &dyn HirDatabase) {} 178 fn _assert_object_safe(_: &dyn HirDatabase) {}
@@ -156,6 +183,14 @@ pub struct InternedTypeParamId(salsa::InternId);
156impl_intern_key!(InternedTypeParamId); 183impl_intern_key!(InternedTypeParamId);
157 184
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 185#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
186pub struct InternedLifetimeParamId(salsa::InternId);
187impl_intern_key!(InternedLifetimeParamId);
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
190pub struct InternedConstParamId(salsa::InternId);
191impl_intern_key!(InternedConstParamId);
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
159pub struct InternedOpaqueTyId(salsa::InternId); 194pub struct InternedOpaqueTyId(salsa::InternId);
160impl_intern_key!(InternedOpaqueTyId); 195impl_intern_key!(InternedOpaqueTyId);
161 196
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index 86f937e1d..84fc8ce14 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -1,4 +1,4 @@
1//! FIXME: write short doc here 1//! Type inference-based diagnostics.
2mod expr; 2mod expr;
3mod match_check; 3mod match_check;
4mod unsafe_check; 4mod unsafe_check;
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index 1c9f9ede7..075dc4131 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -35,6 +35,8 @@ use crate::{
35}; 35};
36 36
37mod allow { 37mod allow {
38 pub(super) const BAD_STYLE: &str = "bad_style";
39 pub(super) const NONSTANDARD_STYLE: &str = "nonstandard_style";
38 pub(super) const NON_SNAKE_CASE: &str = "non_snake_case"; 40 pub(super) const NON_SNAKE_CASE: &str = "non_snake_case";
39 pub(super) const NON_UPPER_CASE_GLOBAL: &str = "non_upper_case_globals"; 41 pub(super) const NON_UPPER_CASE_GLOBAL: &str = "non_upper_case_globals";
40 pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; 42 pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types";
@@ -83,10 +85,39 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
83 } 85 }
84 86
85 /// Checks whether not following the convention is allowed for this item. 87 /// Checks whether not following the convention is allowed for this item.
86 /// 88 fn allowed(&self, id: AttrDefId, allow_name: &str, recursing: bool) -> bool {
87 /// Currently this method doesn't check parent attributes. 89 let is_allowed = |def_id| {
88 fn allowed(&self, id: AttrDefId, allow_name: &str) -> bool { 90 let attrs = self.db.attrs(def_id);
89 self.db.attrs(id).by_key("allow").tt_values().any(|tt| tt.to_string().contains(allow_name)) 91 // don't bug the user about directly no_mangle annotated stuff, they can't do anything about it
92 (!recursing && attrs.by_key("no_mangle").exists())
93 || attrs.by_key("allow").tt_values().any(|tt| {
94 let allows = tt.to_string();
95 allows.contains(allow_name)
96 || allows.contains(allow::BAD_STYLE)
97 || allows.contains(allow::NONSTANDARD_STYLE)
98 })
99 };
100
101 is_allowed(id)
102 // go upwards one step or give up
103 || match id {
104 AttrDefId::ModuleId(m) => m.containing_module(self.db.upcast()).map(|v| v.into()),
105 AttrDefId::FunctionId(f) => Some(f.lookup(self.db.upcast()).container.into()),
106 AttrDefId::StaticId(sid) => Some(sid.lookup(self.db.upcast()).container.into()),
107 AttrDefId::ConstId(cid) => Some(cid.lookup(self.db.upcast()).container.into()),
108 AttrDefId::TraitId(tid) => Some(tid.lookup(self.db.upcast()).container.into()),
109 AttrDefId::ImplId(iid) => Some(iid.lookup(self.db.upcast()).container.into()),
110 // These warnings should not explore macro definitions at all
111 AttrDefId::MacroDefId(_) => None,
112 // Will never occur under an enum/struct/union/type alias
113 AttrDefId::AdtId(_) => None,
114 AttrDefId::FieldId(_) => None,
115 AttrDefId::EnumVariantId(_) => None,
116 AttrDefId::TypeAliasId(_) => None,
117 AttrDefId::GenericParamId(_) => None,
118 }
119 .map(|mid| self.allowed(mid, allow_name, true))
120 .unwrap_or(false)
90 } 121 }
91 122
92 fn validate_func(&mut self, func: FunctionId) { 123 fn validate_func(&mut self, func: FunctionId) {
@@ -109,7 +140,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
109 } 140 }
110 141
111 // Check whether non-snake case identifiers are allowed for this function. 142 // Check whether non-snake case identifiers are allowed for this function.
112 if self.allowed(func.into(), allow::NON_SNAKE_CASE) { 143 if self.allowed(func.into(), allow::NON_SNAKE_CASE, false) {
113 return; 144 return;
114 } 145 }
115 146
@@ -328,8 +359,9 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
328 fn validate_struct(&mut self, struct_id: StructId) { 359 fn validate_struct(&mut self, struct_id: StructId) {
329 let data = self.db.struct_data(struct_id); 360 let data = self.db.struct_data(struct_id);
330 361
331 let non_camel_case_allowed = self.allowed(struct_id.into(), allow::NON_CAMEL_CASE_TYPES); 362 let non_camel_case_allowed =
332 let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE); 363 self.allowed(struct_id.into(), allow::NON_CAMEL_CASE_TYPES, false);
364 let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false);
333 365
334 // Check the structure name. 366 // Check the structure name.
335 let struct_name = data.name.to_string(); 367 let struct_name = data.name.to_string();
@@ -461,7 +493,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
461 let data = self.db.enum_data(enum_id); 493 let data = self.db.enum_data(enum_id);
462 494
463 // Check whether non-camel case names are allowed for this enum. 495 // Check whether non-camel case names are allowed for this enum.
464 if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES) { 496 if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES, false) {
465 return; 497 return;
466 } 498 }
467 499
@@ -584,7 +616,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
584 fn validate_const(&mut self, const_id: ConstId) { 616 fn validate_const(&mut self, const_id: ConstId) {
585 let data = self.db.const_data(const_id); 617 let data = self.db.const_data(const_id);
586 618
587 if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL) { 619 if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) {
588 return; 620 return;
589 } 621 }
590 622
@@ -632,7 +664,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
632 return; 664 return;
633 } 665 }
634 666
635 if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL) { 667 if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) {
636 return; 668 return;
637 } 669 }
638 670
@@ -867,23 +899,116 @@ fn main() {
867 fn allow_attributes() { 899 fn allow_attributes() {
868 check_diagnostics( 900 check_diagnostics(
869 r#" 901 r#"
870 #[allow(non_snake_case)] 902#[allow(non_snake_case)]
871 fn NonSnakeCaseName(SOME_VAR: u8) -> u8{ 903fn NonSnakeCaseName(SOME_VAR: u8) -> u8{
872 let OtherVar = SOME_VAR + 1; 904 // cov_flags generated output from elsewhere in this file
873 OtherVar 905 extern "C" {
906 #[no_mangle]
907 static lower_case: u8;
874 } 908 }
875 909
876 #[allow(non_snake_case, non_camel_case_types)] 910 let OtherVar = SOME_VAR + 1;
877 pub struct some_type { 911 OtherVar
878 SOME_FIELD: u8, 912}
879 SomeField: u16, 913
914#[allow(nonstandard_style)]
915mod CheckNonstandardStyle {
916 fn HiImABadFnName() {}
917}
918
919#[allow(bad_style)]
920mod CheckBadStyle {
921 fn HiImABadFnName() {}
922}
923
924mod F {
925 #![allow(non_snake_case)]
926 fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {}
927}
928
929#[allow(non_snake_case, non_camel_case_types)]
930pub struct some_type {
931 SOME_FIELD: u8,
932 SomeField: u16,
933}
934
935#[allow(non_upper_case_globals)]
936pub const some_const: u8 = 10;
937
938#[allow(non_upper_case_globals)]
939pub static SomeStatic: u8 = 10;
940 "#,
941 );
880 } 942 }
881 943
882 #[allow(non_upper_case_globals)] 944 #[test]
883 pub const some_const: u8 = 10; 945 fn allow_attributes_crate_attr() {
946 check_diagnostics(
947 r#"
948#![allow(non_snake_case)]
884 949
885 #[allow(non_upper_case_globals)] 950mod F {
886 pub static SomeStatic: u8 = 10; 951 fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {}
952}
953 "#,
954 );
955 }
956
957 #[test]
958 #[ignore]
959 fn bug_trait_inside_fn() {
960 // FIXME:
961 // This is broken, and in fact, should not even be looked at by this
962 // lint in the first place. There's weird stuff going on in the
963 // collection phase.
964 // It's currently being brought in by:
965 // * validate_func on `a` recursing into modules
966 // * then it finds the trait and then the function while iterating
967 // through modules
968 // * then validate_func is called on Dirty
969 // * ... which then proceeds to look at some unknown module taking no
970 // attrs from either the impl or the fn a, and then finally to the root
971 // module
972 //
973 // It should find the attribute on the trait, but it *doesn't even see
974 // the trait* as far as I can tell.
975
976 check_diagnostics(
977 r#"
978trait T { fn a(); }
979struct U {}
980impl T for U {
981 fn a() {
982 // this comes out of bitflags, mostly
983 #[allow(non_snake_case)]
984 trait __BitFlags {
985 const HiImAlsoBad: u8 = 2;
986 #[inline]
987 fn Dirty(&self) -> bool {
988 false
989 }
990 }
991
992 }
993}
994 "#,
995 );
996 }
997
998 #[test]
999 #[ignore]
1000 fn bug_traits_arent_checked() {
1001 // FIXME: Traits and functions in traits aren't currently checked by
1002 // r-a, even though rustc will complain about them.
1003 check_diagnostics(
1004 r#"
1005trait BAD_TRAIT {
1006 // ^^^^^^^^^ Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait`
1007 fn BAD_FUNCTION();
1008 // ^^^^^^^^^^^^ Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function`
1009 fn BadFunction();
1010 // ^^^^^^^^^^^^ Function `BadFunction` should have snake_case name, e.g. `bad_function`
1011}
887 "#, 1012 "#,
888 ); 1013 );
889 } 1014 }
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 8169b759f..79602c3dd 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -14,7 +14,6 @@ use crate::{
14 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, 14 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
15 MissingPatFields, RemoveThisSemicolon, 15 MissingPatFields, RemoveThisSemicolon,
16 }, 16 },
17 utils::variant_data,
18 AdtId, InferenceResult, Interner, TyExt, TyKind, 17 AdtId, InferenceResult, Interner, TyExt, TyKind,
19}; 18};
20 19
@@ -104,7 +103,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
104 let root = source_ptr.file_syntax(db.upcast()); 103 let root = source_ptr.file_syntax(db.upcast());
105 if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) { 104 if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) {
106 if let Some(_) = record_expr.record_expr_field_list() { 105 if let Some(_) = record_expr.record_expr_field_list() {
107 let variant_data = variant_data(db.upcast(), variant_def); 106 let variant_data = variant_def.variant_data(db.upcast());
108 let missed_fields = missed_fields 107 let missed_fields = missed_fields
109 .into_iter() 108 .into_iter()
110 .map(|idx| variant_data.fields()[idx].name.clone()) 109 .map(|idx| variant_data.fields()[idx].name.clone())
@@ -135,7 +134,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
135 let root = source_ptr.file_syntax(db.upcast()); 134 let root = source_ptr.file_syntax(db.upcast());
136 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { 135 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
137 if let Some(_) = record_pat.record_pat_field_list() { 136 if let Some(_) = record_pat.record_pat_field_list() {
138 let variant_data = variant_data(db.upcast(), variant_def); 137 let variant_data = variant_def.variant_data(db.upcast());
139 let missed_fields = missed_fields 138 let missed_fields = missed_fields
140 .into_iter() 139 .into_iter()
141 .map(|idx| variant_data.fields()[idx].name.clone()) 140 .map(|idx| variant_data.fields()[idx].name.clone())
@@ -245,7 +244,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
245 Some(callee) => callee, 244 Some(callee) => callee,
246 None => return, 245 None => return,
247 }; 246 };
248 let sig = db.callable_item_signature(callee.into()).value; 247 let sig =
248 db.callable_item_signature(callee.into()).into_value_and_skipped_binders().0;
249 249
250 (sig, args) 250 (sig, args)
251 } 251 }
@@ -314,7 +314,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
314 if pat_ty == match_expr_ty 314 if pat_ty == match_expr_ty
315 || match_expr_ty 315 || match_expr_ty
316 .as_reference() 316 .as_reference()
317 .map(|(match_expr_ty, _)| match_expr_ty == pat_ty) 317 .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
318 .unwrap_or(false) 318 .unwrap_or(false)
319 { 319 {
320 // If we had a NotUsefulMatchArm diagnostic, we could 320 // If we had a NotUsefulMatchArm diagnostic, we could
@@ -452,7 +452,7 @@ pub fn record_literal_missing_fields(
452 return None; 452 return None;
453 } 453 }
454 454
455 let variant_data = variant_data(db.upcast(), variant_def); 455 let variant_data = variant_def.variant_data(db.upcast());
456 456
457 let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); 457 let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
458 let missed_fields: Vec<LocalFieldId> = variant_data 458 let missed_fields: Vec<LocalFieldId> = variant_data
@@ -482,7 +482,7 @@ pub fn record_pattern_missing_fields(
482 return None; 482 return None;
483 } 483 }
484 484
485 let variant_data = variant_data(db.upcast(), variant_def); 485 let variant_data = variant_def.variant_data(db.upcast());
486 486
487 let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect(); 487 let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
488 let missed_fields: Vec<LocalFieldId> = variant_data 488 let missed_fields: Vec<LocalFieldId> = variant_data
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index 34291578a..e9762622f 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -227,7 +227,7 @@ use hir_def::{
227use la_arena::Idx; 227use la_arena::Idx;
228use smallvec::{smallvec, SmallVec}; 228use smallvec::{smallvec, SmallVec};
229 229
230use crate::{db::HirDatabase, AdtId, InferenceResult, Interner, TyKind}; 230use crate::{db::HirDatabase, AdtId, InferenceResult, Interner, TyExt, TyKind};
231 231
232#[derive(Debug, Clone, Copy)] 232#[derive(Debug, Clone, Copy)]
233/// Either a pattern from the source code being analyzed, represented as 233/// Either a pattern from the source code being analyzed, represented as
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index b5efe9df5..ed97dc0e3 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -11,7 +11,9 @@ use hir_def::{
11}; 11};
12use hir_expand::diagnostics::DiagnosticSink; 12use hir_expand::diagnostics::DiagnosticSink;
13 13
14use crate::{db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Interner, TyKind}; 14use crate::{
15 db::HirDatabase, diagnostics::MissingUnsafe, InferenceResult, Interner, TyExt, TyKind,
16};
15 17
16pub(super) struct UnsafeValidator<'a, 'b: 'a> { 18pub(super) struct UnsafeValidator<'a, 'b: 'a> {
17 owner: DefWithBodyId, 19 owner: DefWithBodyId,
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index 385bd9405..4fb7d9cf2 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -1,9 +1,15 @@
1//! FIXME: write short doc here 1//! The `HirDisplay` trait, which serves two purposes: Turning various bits from
2//! HIR back into source code, and just displaying them for debugging/testing
3//! purposes.
2 4
3use std::{array, fmt}; 5use std::{
6 array,
7 fmt::{self, Debug},
8};
4 9
5use chalk_ir::Mutability; 10use chalk_ir::BoundVar;
6use hir_def::{ 11use hir_def::{
12 body,
7 db::DefDatabase, 13 db::DefDatabase,
8 find_path, 14 find_path,
9 generics::TypeParamProvenance, 15 generics::TypeParamProvenance,
@@ -13,13 +19,15 @@ use hir_def::{
13 visibility::Visibility, 19 visibility::Visibility,
14 AssocContainerId, Lookup, ModuleId, TraitId, 20 AssocContainerId, Lookup, ModuleId, TraitId,
15}; 21};
16use hir_expand::name::Name; 22use hir_expand::{hygiene::Hygiene, name::Name};
17 23
18use crate::{ 24use crate::{
19 db::HirDatabase, from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, primitive, 25 const_from_placeholder_idx, db::HirDatabase, from_assoc_type_id, from_foreign_def_id,
20 to_assoc_type_id, traits::chalk::from_chalk, utils::generics, AdtId, AliasEq, AliasTy, 26 from_placeholder_idx, lt_from_placeholder_idx, mapping::from_chalk, primitive, subst_prefix,
21 CallableDefId, CallableSig, DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, OpaqueTy, 27 to_assoc_type_id, utils::generics, AdtId, AliasEq, AliasTy, CallableDefId, CallableSig, Const,
22 ProjectionTy, QuantifiedWhereClause, Scalar, TraitRef, Ty, TyExt, TyKind, WhereClause, 28 ConstValue, DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData,
29 LifetimeOutlives, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause,
30 Scalar, TraitRef, TraitRefExt, Ty, TyExt, TyKind, WhereClause,
23}; 31};
24 32
25pub struct HirFormatter<'a> { 33pub struct HirFormatter<'a> {
@@ -46,6 +54,10 @@ pub trait HirDisplay {
46 where 54 where
47 Self: Sized, 55 Self: Sized,
48 { 56 {
57 assert!(
58 !matches!(display_target, DisplayTarget::SourceCode { .. }),
59 "HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead"
60 );
49 HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target } 61 HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target }
50 } 62 }
51 63
@@ -230,7 +242,7 @@ where
230 Err(HirDisplayError::FmtError) => Err(fmt::Error), 242 Err(HirDisplayError::FmtError) => Err(fmt::Error),
231 Err(HirDisplayError::DisplaySourceCodeError(_)) => { 243 Err(HirDisplayError::DisplaySourceCodeError(_)) => {
232 // This should never happen 244 // This should never happen
233 panic!("HirDisplay failed when calling Display::fmt!") 245 panic!("HirDisplay::hir_fmt failed with DisplaySourceCodeError when calling Display::fmt!")
234 } 246 }
235 } 247 }
236 } 248 }
@@ -251,16 +263,12 @@ impl HirDisplay for ProjectionTy {
251 } 263 }
252 264
253 let trait_ = f.db.trait_data(self.trait_(f.db)); 265 let trait_ = f.db.trait_data(self.trait_(f.db));
254 let first_parameter = self.self_type_parameter().into_displayable( 266 write!(f, "<")?;
255 f.db, 267 self.self_type_parameter(&Interner).hir_fmt(f)?;
256 f.max_size, 268 write!(f, " as {}", trait_.name)?;
257 f.omit_verbose_types,
258 f.display_target,
259 );
260 write!(f, "<{} as {}", first_parameter, trait_.name)?;
261 if self.substitution.len(&Interner) > 1 { 269 if self.substitution.len(&Interner) > 1 {
262 write!(f, "<")?; 270 write!(f, "<")?;
263 f.write_joined(&self.substitution.interned(&Interner)[1..], ", ")?; 271 f.write_joined(&self.substitution.as_slice(&Interner)[1..], ", ")?;
264 write!(f, ">")?; 272 write!(f, ">")?;
265 } 273 }
266 write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?; 274 write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?;
@@ -282,10 +290,35 @@ impl HirDisplay for GenericArg {
282 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { 290 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
283 match self.interned() { 291 match self.interned() {
284 crate::GenericArgData::Ty(ty) => ty.hir_fmt(f), 292 crate::GenericArgData::Ty(ty) => ty.hir_fmt(f),
293 crate::GenericArgData::Lifetime(lt) => lt.hir_fmt(f),
294 crate::GenericArgData::Const(c) => c.hir_fmt(f),
295 }
296 }
297}
298
299impl HirDisplay for Const {
300 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
301 let data = self.interned();
302 match data.value {
303 ConstValue::BoundVar(idx) => idx.hir_fmt(f),
304 ConstValue::InferenceVar(..) => write!(f, "_"),
305 ConstValue::Placeholder(idx) => {
306 let id = const_from_placeholder_idx(f.db, idx);
307 let generics = generics(f.db.upcast(), id.parent);
308 let param_data = &generics.params.consts[id.local_id];
309 write!(f, "{}", param_data.name)
310 }
311 ConstValue::Concrete(_) => write!(f, "_"),
285 } 312 }
286 } 313 }
287} 314}
288 315
316impl HirDisplay for BoundVar {
317 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
318 write!(f, "?{}.{}", self.debruijn.depth(), self.index)
319 }
320}
321
289impl HirDisplay for Ty { 322impl HirDisplay for Ty {
290 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { 323 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
291 if f.should_truncate() { 324 if f.should_truncate() {
@@ -305,15 +338,14 @@ impl HirDisplay for Ty {
305 t.hir_fmt(f)?; 338 t.hir_fmt(f)?;
306 write!(f, "]")?; 339 write!(f, "]")?;
307 } 340 }
308 TyKind::Array(t) => { 341 TyKind::Array(t, c) => {
309 write!(f, "[")?; 342 write!(f, "[")?;
310 t.hir_fmt(f)?; 343 t.hir_fmt(f)?;
311 write!(f, "; _]")?; 344 write!(f, "; ")?;
345 c.hir_fmt(f)?;
346 write!(f, "]")?;
312 } 347 }
313 TyKind::Raw(m, t) | TyKind::Ref(m, t) => { 348 TyKind::Raw(m, t) | TyKind::Ref(m, _, t) => {
314 let ty_display =
315 t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
316
317 if matches!(self.kind(&Interner), TyKind::Raw(..)) { 349 if matches!(self.kind(&Interner), TyKind::Raw(..)) {
318 write!( 350 write!(
319 f, 351 f,
@@ -352,8 +384,8 @@ impl HirDisplay for Ty {
352 let data = (*datas) 384 let data = (*datas)
353 .as_ref() 385 .as_ref()
354 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); 386 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
355 let bounds = data.subst(parameters); 387 let bounds = data.substitute(&Interner, parameters);
356 bounds.value 388 bounds.into_value_and_skipped_binders().0
357 } else { 389 } else {
358 Vec::new() 390 Vec::new()
359 } 391 }
@@ -368,16 +400,16 @@ impl HirDisplay for Ty {
368 if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) 400 if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_)
369 && predicates.len() <= 2 401 && predicates.len() <= 2
370 { 402 {
371 return write!(f, "{}", ty_display); 403 return t.hir_fmt(f);
372 } 404 }
373 } 405 }
374 406
375 if predicates.len() > 1 { 407 if predicates.len() > 1 {
376 write!(f, "(")?; 408 write!(f, "(")?;
377 write!(f, "{}", ty_display)?; 409 t.hir_fmt(f)?;
378 write!(f, ")")?; 410 write!(f, ")")?;
379 } else { 411 } else {
380 write!(f, "{}", ty_display)?; 412 t.hir_fmt(f)?;
381 } 413 }
382 } 414 }
383 TyKind::Tuple(_, substs) => { 415 TyKind::Tuple(_, substs) => {
@@ -387,7 +419,7 @@ impl HirDisplay for Ty {
387 write!(f, ",)")?; 419 write!(f, ",)")?;
388 } else { 420 } else {
389 write!(f, "(")?; 421 write!(f, "(")?;
390 f.write_joined(&*substs.0, ", ")?; 422 f.write_joined(&*substs.as_slice(&Interner), ", ")?;
391 write!(f, ")")?; 423 write!(f, ")")?;
392 } 424 }
393 } 425 }
@@ -397,7 +429,7 @@ impl HirDisplay for Ty {
397 } 429 }
398 TyKind::FnDef(def, parameters) => { 430 TyKind::FnDef(def, parameters) => {
399 let def = from_chalk(f.db, *def); 431 let def = from_chalk(f.db, *def);
400 let sig = f.db.callable_item_signature(def).subst(parameters); 432 let sig = f.db.callable_item_signature(def).substitute(&Interner, parameters);
401 match def { 433 match def {
402 CallableDefId::FunctionId(ff) => { 434 CallableDefId::FunctionId(ff) => {
403 write!(f, "fn {}", f.db.function_data(ff).name)? 435 write!(f, "fn {}", f.db.function_data(ff).name)?
@@ -415,7 +447,7 @@ impl HirDisplay for Ty {
415 // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self? 447 // We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
416 if total_len > 0 { 448 if total_len > 0 {
417 write!(f, "<")?; 449 write!(f, "<")?;
418 f.write_joined(&parameters.0[..total_len], ", ")?; 450 f.write_joined(&parameters.as_slice(&Interner)[..total_len], ", ")?;
419 write!(f, ">")?; 451 write!(f, ">")?;
420 } 452 }
421 } 453 }
@@ -424,14 +456,8 @@ impl HirDisplay for Ty {
424 write!(f, ")")?; 456 write!(f, ")")?;
425 let ret = sig.ret(); 457 let ret = sig.ret();
426 if !ret.is_unit() { 458 if !ret.is_unit() {
427 let ret_display = ret.into_displayable( 459 write!(f, " -> ")?;
428 f.db, 460 ret.hir_fmt(f)?;
429 f.max_size,
430 f.omit_verbose_types,
431 f.display_target,
432 );
433
434 write!(f, " -> {}", ret_display)?;
435 } 461 }
436 } 462 }
437 TyKind::Adt(AdtId(def_id), parameters) => { 463 TyKind::Adt(AdtId(def_id), parameters) => {
@@ -468,7 +494,7 @@ impl HirDisplay for Ty {
468 .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) 494 .map(|generic_def_id| f.db.generic_defaults(generic_def_id))
469 .filter(|defaults| !defaults.is_empty()) 495 .filter(|defaults| !defaults.is_empty())
470 { 496 {
471 None => parameters.0.as_ref(), 497 None => parameters.as_slice(&Interner),
472 Some(default_parameters) => { 498 Some(default_parameters) => {
473 let mut default_from = 0; 499 let mut default_from = 0;
474 for (i, parameter) in parameters.iter(&Interner).enumerate() { 500 for (i, parameter) in parameters.iter(&Interner).enumerate() {
@@ -476,13 +502,15 @@ impl HirDisplay for Ty {
476 parameter.assert_ty_ref(&Interner).kind(&Interner), 502 parameter.assert_ty_ref(&Interner).kind(&Interner),
477 default_parameters.get(i), 503 default_parameters.get(i),
478 ) { 504 ) {
479 (&TyKind::Unknown, _) | (_, None) => { 505 (&TyKind::Error, _) | (_, None) => {
480 default_from = i + 1; 506 default_from = i + 1;
481 } 507 }
482 (_, Some(default_parameter)) => { 508 (_, Some(default_parameter)) => {
483 let actual_default = default_parameter 509 let actual_default =
484 .clone() 510 default_parameter.clone().substitute(
485 .subst(&parameters.prefix(i)); 511 &Interner,
512 &subst_prefix(parameters, i),
513 );
486 if parameter.assert_ty_ref(&Interner) != &actual_default 514 if parameter.assert_ty_ref(&Interner) != &actual_default
487 { 515 {
488 default_from = i + 1; 516 default_from = i + 1;
@@ -490,11 +518,11 @@ impl HirDisplay for Ty {
490 } 518 }
491 } 519 }
492 } 520 }
493 &parameters.0[0..default_from] 521 &parameters.as_slice(&Interner)[0..default_from]
494 } 522 }
495 } 523 }
496 } else { 524 } else {
497 parameters.0.as_ref() 525 parameters.as_slice(&Interner)
498 }; 526 };
499 if !parameters_to_write.is_empty() { 527 if !parameters_to_write.is_empty() {
500 write!(f, "<")?; 528 write!(f, "<")?;
@@ -517,7 +545,7 @@ impl HirDisplay for Ty {
517 write!(f, "{}::{}", trait_.name, type_alias_data.name)?; 545 write!(f, "{}::{}", trait_.name, type_alias_data.name)?;
518 if parameters.len(&Interner) > 0 { 546 if parameters.len(&Interner) > 0 {
519 write!(f, "<")?; 547 write!(f, "<")?;
520 f.write_joined(&*parameters.0, ", ")?; 548 f.write_joined(&*parameters.as_slice(&Interner), ", ")?;
521 write!(f, ">")?; 549 write!(f, ">")?;
522 } 550 }
523 } else { 551 } else {
@@ -529,7 +557,7 @@ impl HirDisplay for Ty {
529 projection_ty.hir_fmt(f)?; 557 projection_ty.hir_fmt(f)?;
530 } 558 }
531 } 559 }
532 TyKind::ForeignType(type_alias) => { 560 TyKind::Foreign(type_alias) => {
533 let type_alias = f.db.type_alias_data(from_foreign_def_id(*type_alias)); 561 let type_alias = f.db.type_alias_data(from_foreign_def_id(*type_alias));
534 write!(f, "{}", type_alias.name)?; 562 write!(f, "{}", type_alias.name)?;
535 } 563 }
@@ -542,8 +570,8 @@ impl HirDisplay for Ty {
542 let data = (*datas) 570 let data = (*datas)
543 .as_ref() 571 .as_ref()
544 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); 572 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
545 let bounds = data.subst(&parameters); 573 let bounds = data.substitute(&Interner, &parameters);
546 write_bounds_like_dyn_trait_with_prefix("impl", &bounds.value, f)?; 574 write_bounds_like_dyn_trait_with_prefix("impl", bounds.skip_binders(), f)?;
547 // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution 575 // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
548 } 576 }
549 ImplTraitId::AsyncBlockTypeImplTrait(..) => { 577 ImplTraitId::AsyncBlockTypeImplTrait(..) => {
@@ -571,13 +599,8 @@ impl HirDisplay for Ty {
571 write!(f, "|")?; 599 write!(f, "|")?;
572 }; 600 };
573 601
574 let ret_display = sig.ret().into_displayable( 602 write!(f, " -> ")?;
575 f.db, 603 sig.ret().hir_fmt(f)?;
576 f.max_size,
577 f.omit_verbose_types,
578 f.display_target,
579 );
580 write!(f, " -> {}", ret_display)?;
581 } else { 604 } else {
582 write!(f, "{{closure}}")?; 605 write!(f, "{{closure}}")?;
583 } 606 }
@@ -592,25 +615,26 @@ impl HirDisplay for Ty {
592 } 615 }
593 TypeParamProvenance::ArgumentImplTrait => { 616 TypeParamProvenance::ArgumentImplTrait => {
594 let substs = generics.type_params_subst(f.db); 617 let substs = generics.type_params_subst(f.db);
595 let bounds = f 618 let bounds =
596 .db 619 f.db.generic_predicates(id.parent)
597 .generic_predicates(id.parent) 620 .into_iter()
598 .into_iter() 621 .map(|pred| pred.clone().substitute(&Interner, &substs))
599 .map(|pred| pred.clone().subst(&substs)) 622 .filter(|wc| match &wc.skip_binders() {
600 .filter(|wc| match &wc.skip_binders() { 623 WhereClause::Implemented(tr) => {
601 WhereClause::Implemented(tr) => tr.self_type_parameter() == self, 624 &tr.self_type_parameter(&Interner) == self
602 WhereClause::AliasEq(AliasEq { 625 }
603 alias: AliasTy::Projection(proj), 626 WhereClause::AliasEq(AliasEq {
604 ty: _, 627 alias: AliasTy::Projection(proj),
605 }) => proj.self_type_parameter() == self, 628 ty: _,
606 _ => false, 629 }) => &proj.self_type_parameter(&Interner) == self,
607 }) 630 _ => false,
608 .collect::<Vec<_>>(); 631 })
632 .collect::<Vec<_>>();
609 write_bounds_like_dyn_trait_with_prefix("impl", &bounds, f)?; 633 write_bounds_like_dyn_trait_with_prefix("impl", &bounds, f)?;
610 } 634 }
611 } 635 }
612 } 636 }
613 TyKind::BoundVar(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?, 637 TyKind::BoundVar(idx) => idx.hir_fmt(f)?,
614 TyKind::Dyn(dyn_ty) => { 638 TyKind::Dyn(dyn_ty) => {
615 write_bounds_like_dyn_trait_with_prefix( 639 write_bounds_like_dyn_trait_with_prefix(
616 "dyn", 640 "dyn",
@@ -628,15 +652,15 @@ impl HirDisplay for Ty {
628 let data = (*datas) 652 let data = (*datas)
629 .as_ref() 653 .as_ref()
630 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); 654 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
631 let bounds = data.subst(&opaque_ty.substitution); 655 let bounds = data.substitute(&Interner, &opaque_ty.substitution);
632 write_bounds_like_dyn_trait_with_prefix("impl", &bounds.value, f)?; 656 write_bounds_like_dyn_trait_with_prefix("impl", bounds.skip_binders(), f)?;
633 } 657 }
634 ImplTraitId::AsyncBlockTypeImplTrait(..) => { 658 ImplTraitId::AsyncBlockTypeImplTrait(..) => {
635 write!(f, "{{async block}}")?; 659 write!(f, "{{async block}}")?;
636 } 660 }
637 }; 661 };
638 } 662 }
639 TyKind::Unknown => { 663 TyKind::Error => {
640 if f.display_target.is_source_code() { 664 if f.display_target.is_source_code() {
641 return Err(HirDisplayError::DisplaySourceCodeError( 665 return Err(HirDisplayError::DisplaySourceCodeError(
642 DisplaySourceCodeError::UnknownType, 666 DisplaySourceCodeError::UnknownType,
@@ -645,6 +669,8 @@ impl HirDisplay for Ty {
645 write!(f, "{{unknown}}")?; 669 write!(f, "{{unknown}}")?;
646 } 670 }
647 TyKind::InferenceVar(..) => write!(f, "_")?, 671 TyKind::InferenceVar(..) => write!(f, "_")?,
672 TyKind::Generator(..) => write!(f, "{{generator}}")?,
673 TyKind::GeneratorWitness(..) => write!(f, "{{generator witness}}")?,
648 } 674 }
649 Ok(()) 675 Ok(())
650 } 676 }
@@ -664,9 +690,8 @@ impl HirDisplay for CallableSig {
664 write!(f, ")")?; 690 write!(f, ")")?;
665 let ret = self.ret(); 691 let ret = self.ret();
666 if !ret.is_unit() { 692 if !ret.is_unit() {
667 let ret_display = 693 write!(f, " -> ")?;
668 ret.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target); 694 ret.hir_fmt(f)?;
669 write!(f, " -> {}", ret_display)?;
670 } 695 }
671 Ok(()) 696 Ok(())
672 } 697 }
@@ -723,17 +748,17 @@ fn write_bounds_like_dyn_trait(
723 if !first { 748 if !first {
724 write!(f, " + ")?; 749 write!(f, " + ")?;
725 } 750 }
726 // We assume that the self type is $0 (i.e. the 751 // We assume that the self type is ^0.0 (i.e. the
727 // existential) here, which is the only thing that's 752 // existential) here, which is the only thing that's
728 // possible in actual Rust, and hence don't print it 753 // possible in actual Rust, and hence don't print it
729 write!(f, "{}", f.db.trait_data(trait_).name)?; 754 write!(f, "{}", f.db.trait_data(trait_).name)?;
730 if let [_, params @ ..] = &*trait_ref.substitution.0 { 755 if let [_, params @ ..] = &*trait_ref.substitution.as_slice(&Interner) {
731 if is_fn_trait { 756 if is_fn_trait {
732 if let Some(args) = 757 if let Some(args) =
733 params.first().and_then(|it| it.assert_ty_ref(&Interner).as_tuple()) 758 params.first().and_then(|it| it.assert_ty_ref(&Interner).as_tuple())
734 { 759 {
735 write!(f, "(")?; 760 write!(f, "(")?;
736 f.write_joined(&*args.0, ", ")?; 761 f.write_joined(args.as_slice(&Interner), ", ")?;
737 write!(f, ")")?; 762 write!(f, ")")?;
738 } 763 }
739 } else if !params.is_empty() { 764 } else if !params.is_empty() {
@@ -765,6 +790,10 @@ fn write_bounds_like_dyn_trait(
765 } 790 }
766 ty.hir_fmt(f)?; 791 ty.hir_fmt(f)?;
767 } 792 }
793
794 // FIXME implement these
795 WhereClause::LifetimeOutlives(_) => {}
796 WhereClause::TypeOutlives(_) => {}
768 } 797 }
769 first = false; 798 first = false;
770 } 799 }
@@ -774,31 +803,29 @@ fn write_bounds_like_dyn_trait(
774 Ok(()) 803 Ok(())
775} 804}
776 805
777impl TraitRef { 806fn fmt_trait_ref(tr: &TraitRef, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> {
778 fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> { 807 if f.should_truncate() {
779 if f.should_truncate() { 808 return write!(f, "{}", TYPE_HINT_TRUNCATION);
780 return write!(f, "{}", TYPE_HINT_TRUNCATION); 809 }
781 }
782 810
783 self.self_type_parameter().hir_fmt(f)?; 811 tr.self_type_parameter(&Interner).hir_fmt(f)?;
784 if use_as { 812 if use_as {
785 write!(f, " as ")?; 813 write!(f, " as ")?;
786 } else { 814 } else {
787 write!(f, ": ")?; 815 write!(f, ": ")?;
788 } 816 }
789 write!(f, "{}", f.db.trait_data(self.hir_trait_id()).name)?; 817 write!(f, "{}", f.db.trait_data(tr.hir_trait_id()).name)?;
790 if self.substitution.len(&Interner) > 1 { 818 if tr.substitution.len(&Interner) > 1 {
791 write!(f, "<")?; 819 write!(f, "<")?;
792 f.write_joined(&self.substitution.interned(&Interner)[1..], ", ")?; 820 f.write_joined(&tr.substitution.as_slice(&Interner)[1..], ", ")?;
793 write!(f, ">")?; 821 write!(f, ">")?;
794 }
795 Ok(())
796 } 822 }
823 Ok(())
797} 824}
798 825
799impl HirDisplay for TraitRef { 826impl HirDisplay for TraitRef {
800 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { 827 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
801 self.hir_fmt_ext(f, false) 828 fmt_trait_ref(self, f, false)
802 } 829 }
803} 830}
804 831
@@ -812,7 +839,7 @@ impl HirDisplay for WhereClause {
812 WhereClause::Implemented(trait_ref) => trait_ref.hir_fmt(f)?, 839 WhereClause::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
813 WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { 840 WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
814 write!(f, "<")?; 841 write!(f, "<")?;
815 projection_ty.trait_ref(f.db).hir_fmt_ext(f, true)?; 842 fmt_trait_ref(&projection_ty.trait_ref(f.db), f, true)?;
816 write!( 843 write!(
817 f, 844 f,
818 ">::{} = ", 845 ">::{} = ",
@@ -821,20 +848,44 @@ impl HirDisplay for WhereClause {
821 ty.hir_fmt(f)?; 848 ty.hir_fmt(f)?;
822 } 849 }
823 WhereClause::AliasEq(_) => write!(f, "{{error}}")?, 850 WhereClause::AliasEq(_) => write!(f, "{{error}}")?,
851
852 // FIXME implement these
853 WhereClause::TypeOutlives(..) => {}
854 WhereClause::LifetimeOutlives(..) => {}
824 } 855 }
825 Ok(()) 856 Ok(())
826 } 857 }
827} 858}
828 859
860impl HirDisplay for LifetimeOutlives {
861 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
862 self.a.hir_fmt(f)?;
863 write!(f, ": ")?;
864 self.b.hir_fmt(f)
865 }
866}
867
829impl HirDisplay for Lifetime { 868impl HirDisplay for Lifetime {
830 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { 869 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
870 self.interned().hir_fmt(f)
871 }
872}
873
874impl HirDisplay for LifetimeData {
875 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
831 match self { 876 match self {
832 Lifetime::Parameter(id) => { 877 LifetimeData::BoundVar(idx) => idx.hir_fmt(f),
878 LifetimeData::InferenceVar(_) => write!(f, "_"),
879 LifetimeData::Placeholder(idx) => {
880 let id = lt_from_placeholder_idx(f.db, *idx);
833 let generics = generics(f.db.upcast(), id.parent); 881 let generics = generics(f.db.upcast(), id.parent);
834 let param_data = &generics.params.lifetimes[id.local_id]; 882 let param_data = &generics.params.lifetimes[id.local_id];
835 write!(f, "{}", &param_data.name) 883 write!(f, "{}", param_data.name)
836 } 884 }
837 Lifetime::Static => write!(f, "'static"), 885 LifetimeData::Static => write!(f, "'static"),
886 LifetimeData::Empty(_) => Ok(()),
887 LifetimeData::Erased => Ok(()),
888 LifetimeData::Phantom(_, _) => Ok(()),
838 } 889 }
839 } 890 }
840} 891}
@@ -845,9 +896,11 @@ impl HirDisplay for DomainGoal {
845 DomainGoal::Holds(wc) => { 896 DomainGoal::Holds(wc) => {
846 write!(f, "Holds(")?; 897 write!(f, "Holds(")?;
847 wc.hir_fmt(f)?; 898 wc.hir_fmt(f)?;
848 write!(f, ")") 899 write!(f, ")")?;
849 } 900 }
901 _ => write!(f, "?")?,
850 } 902 }
903 Ok(())
851 } 904 }
852} 905}
853 906
@@ -945,6 +998,18 @@ impl HirDisplay for TypeRef {
945 write!(f, "dyn ")?; 998 write!(f, "dyn ")?;
946 f.write_joined(bounds, " + ")?; 999 f.write_joined(bounds, " + ")?;
947 } 1000 }
1001 TypeRef::Macro(macro_call) => {
1002 let macro_call = macro_call.to_node(f.db.upcast());
1003 let ctx = body::LowerCtx::with_hygiene(&Hygiene::new_unhygienic());
1004 match macro_call.path() {
1005 Some(path) => match Path::from_src(path, &ctx) {
1006 Some(path) => path.hir_fmt(f)?,
1007 None => write!(f, "{{macro}}")?,
1008 },
1009 None => write!(f, "{{macro}}")?,
1010 }
1011 write!(f, "!(..)")?;
1012 }
948 TypeRef::Error => write!(f, "{{error}}")?, 1013 TypeRef::Error => write!(f, "{{error}}")?,
949 } 1014 }
950 Ok(()) 1015 Ok(())
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 1b1d4458c..bf2da2d4a 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -18,7 +18,7 @@ use std::mem;
18use std::ops::Index; 18use std::ops::Index;
19use std::sync::Arc; 19use std::sync::Arc;
20 20
21use chalk_ir::{cast::Cast, Mutability}; 21use chalk_ir::{cast::Cast, DebruijnIndex, Mutability};
22use hir_def::{ 22use hir_def::{
23 body::Body, 23 body::Body,
24 data::{ConstData, FunctionData, StaticData}, 24 data::{ConstData, FunctionData, StaticData},
@@ -37,12 +37,12 @@ use stdx::impl_from;
37use syntax::SmolStr; 37use syntax::SmolStr;
38 38
39use super::{ 39use super::{
40 traits::{DomainGoal, Guidance, Solution}, 40 DomainGoal, Guidance, InEnvironment, ProjectionTy, Solution, TraitEnvironment, TraitRef, Ty,
41 InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty, TypeWalk,
42}; 41};
43use crate::{ 42use crate::{
44 db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode, 43 db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic,
45 to_assoc_type_id, AliasEq, AliasTy, Interner, TyBuilder, TyKind, 44 lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Canonical, Interner,
45 TyBuilder, TyExt, TyKind,
46}; 46};
47 47
48// This lint has a false positive here. See the link below for details. 48// This lint has a false positive here. See the link below for details.
@@ -120,7 +120,7 @@ struct InternedStandardTypes {
120 120
121impl Default for InternedStandardTypes { 121impl Default for InternedStandardTypes {
122 fn default() -> Self { 122 fn default() -> Self {
123 InternedStandardTypes { unknown: TyKind::Unknown.intern(&Interner) } 123 InternedStandardTypes { unknown: TyKind::Error.intern(&Interner) }
124 } 124 }
125} 125}
126 126
@@ -131,10 +131,7 @@ pub struct InferenceResult {
131 method_resolutions: FxHashMap<ExprId, FunctionId>, 131 method_resolutions: FxHashMap<ExprId, FunctionId>,
132 /// For each field access expr, records the field it resolves to. 132 /// For each field access expr, records the field it resolves to.
133 field_resolutions: FxHashMap<ExprId, FieldId>, 133 field_resolutions: FxHashMap<ExprId, FieldId>,
134 /// For each field in record literal, records the field it resolves to. 134 /// For each struct literal or pattern, records the variant it resolves to.
135 record_field_resolutions: FxHashMap<ExprId, FieldId>,
136 record_pat_field_resolutions: FxHashMap<PatId, FieldId>,
137 /// For each struct literal, records the variant it resolves to.
138 variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, 135 variant_resolutions: FxHashMap<ExprOrPatId, VariantId>,
139 /// For each associated item record what it resolves to 136 /// For each associated item record what it resolves to
140 assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, 137 assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>,
@@ -153,12 +150,6 @@ impl InferenceResult {
153 pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> { 150 pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> {
154 self.field_resolutions.get(&expr).copied() 151 self.field_resolutions.get(&expr).copied()
155 } 152 }
156 pub fn record_field_resolution(&self, expr: ExprId) -> Option<FieldId> {
157 self.record_field_resolutions.get(&expr).copied()
158 }
159 pub fn record_pat_field_resolution(&self, pat: PatId) -> Option<FieldId> {
160 self.record_pat_field_resolutions.get(&pat).copied()
161 }
162 pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> { 153 pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option<VariantId> {
163 self.variant_resolutions.get(&id.into()).copied() 154 self.variant_resolutions.get(&id.into()).copied()
164 } 155 }
@@ -247,7 +238,7 @@ impl<'a> InferenceContext<'a> {
247 table: unify::InferenceTable::new(), 238 table: unify::InferenceTable::new(),
248 obligations: Vec::default(), 239 obligations: Vec::default(),
249 last_obligations_check: None, 240 last_obligations_check: None,
250 return_ty: TyKind::Unknown.intern(&Interner), // set in collect_fn_signature 241 return_ty: TyKind::Error.intern(&Interner), // set in collect_fn_signature
251 trait_env: owner 242 trait_env: owner
252 .as_generic_def_id() 243 .as_generic_def_id()
253 .map_or_else(Default::default, |d| db.trait_environment(d)), 244 .map_or_else(Default::default, |d| db.trait_environment(d)),
@@ -261,7 +252,7 @@ impl<'a> InferenceContext<'a> {
261 } 252 }
262 253
263 fn err_ty(&self) -> Ty { 254 fn err_ty(&self) -> Ty {
264 TyKind::Unknown.intern(&Interner) 255 TyKind::Error.intern(&Interner)
265 } 256 }
266 257
267 fn resolve_all(mut self) -> InferenceResult { 258 fn resolve_all(mut self) -> InferenceResult {
@@ -326,13 +317,13 @@ impl<'a> InferenceContext<'a> {
326 /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it. 317 /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it.
327 fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { 318 fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
328 match ty.kind(&Interner) { 319 match ty.kind(&Interner) {
329 TyKind::Unknown => self.table.new_type_var(), 320 TyKind::Error => self.table.new_type_var(),
330 _ => ty, 321 _ => ty,
331 } 322 }
332 } 323 }
333 324
334 fn insert_type_vars(&mut self, ty: Ty) -> Ty { 325 fn insert_type_vars(&mut self, ty: Ty) -> Ty {
335 ty.fold(&mut |ty| self.insert_type_vars_shallow(ty)) 326 fold_tys(ty, |ty, _| self.insert_type_vars_shallow(ty), DebruijnIndex::INNERMOST)
336 } 327 }
337 328
338 fn resolve_obligations_as_possible(&mut self) { 329 fn resolve_obligations_as_possible(&mut self) {
@@ -345,17 +336,24 @@ impl<'a> InferenceContext<'a> {
345 self.last_obligations_check = Some(self.table.revision); 336 self.last_obligations_check = Some(self.table.revision);
346 let obligations = mem::replace(&mut self.obligations, Vec::new()); 337 let obligations = mem::replace(&mut self.obligations, Vec::new());
347 for obligation in obligations { 338 for obligation in obligations {
348 let in_env = InEnvironment::new(self.trait_env.env.clone(), obligation.clone()); 339 let in_env = InEnvironment::new(&self.trait_env.env, obligation.clone());
349 let canonicalized = self.canonicalizer().canonicalize_obligation(in_env); 340 let canonicalized = self.canonicalizer().canonicalize_obligation(in_env);
350 let solution = 341 let solution =
351 self.db.trait_solve(self.resolver.krate().unwrap(), canonicalized.value.clone()); 342 self.db.trait_solve(self.resolver.krate().unwrap(), canonicalized.value.clone());
352 343
353 match solution { 344 match solution {
354 Some(Solution::Unique(substs)) => { 345 Some(Solution::Unique(canonical_subst)) => {
355 canonicalized.apply_solution(self, substs.0); 346 canonicalized.apply_solution(
347 self,
348 Canonical {
349 binders: canonical_subst.binders,
350 // FIXME: handle constraints
351 value: canonical_subst.value.subst,
352 },
353 );
356 } 354 }
357 Some(Solution::Ambig(Guidance::Definite(substs))) => { 355 Some(Solution::Ambig(Guidance::Definite(substs))) => {
358 canonicalized.apply_solution(self, substs.0); 356 canonicalized.apply_solution(self, substs);
359 self.obligations.push(obligation); 357 self.obligations.push(obligation);
360 } 358 }
361 Some(_) => { 359 Some(_) => {
@@ -436,12 +434,16 @@ impl<'a> InferenceContext<'a> {
436 /// to do it as well. 434 /// to do it as well.
437 fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { 435 fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
438 let ty = self.resolve_ty_as_possible(ty); 436 let ty = self.resolve_ty_as_possible(ty);
439 ty.fold(&mut |ty| match ty.kind(&Interner) { 437 fold_tys(
440 TyKind::Alias(AliasTy::Projection(proj_ty)) => { 438 ty,
441 self.normalize_projection_ty(proj_ty.clone()) 439 |ty, _| match ty.kind(&Interner) {
442 } 440 TyKind::Alias(AliasTy::Projection(proj_ty)) => {
443 _ => ty, 441 self.normalize_projection_ty(proj_ty.clone())
444 }) 442 }
443 _ => ty,
444 },
445 DebruijnIndex::INNERMOST,
446 )
445 } 447 }
446 448
447 fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { 449 fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
@@ -470,55 +472,32 @@ impl<'a> InferenceContext<'a> {
470 TypeNs::AdtId(AdtId::StructId(strukt)) => { 472 TypeNs::AdtId(AdtId::StructId(strukt)) => {
471 let substs = ctx.substs_from_path(path, strukt.into(), true); 473 let substs = ctx.substs_from_path(path, strukt.into(), true);
472 let ty = self.db.ty(strukt.into()); 474 let ty = self.db.ty(strukt.into());
473 let ty = self.insert_type_vars(ty.subst(&substs)); 475 let ty = self.insert_type_vars(ty.substitute(&Interner, &substs));
474 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) 476 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
475 } 477 }
476 TypeNs::AdtId(AdtId::UnionId(u)) => { 478 TypeNs::AdtId(AdtId::UnionId(u)) => {
477 let substs = ctx.substs_from_path(path, u.into(), true); 479 let substs = ctx.substs_from_path(path, u.into(), true);
478 let ty = self.db.ty(u.into()); 480 let ty = self.db.ty(u.into());
479 let ty = self.insert_type_vars(ty.subst(&substs)); 481 let ty = self.insert_type_vars(ty.substitute(&Interner, &substs));
480 forbid_unresolved_segments((ty, Some(u.into())), unresolved) 482 forbid_unresolved_segments((ty, Some(u.into())), unresolved)
481 } 483 }
482 TypeNs::EnumVariantId(var) => { 484 TypeNs::EnumVariantId(var) => {
483 let substs = ctx.substs_from_path(path, var.into(), true); 485 let substs = ctx.substs_from_path(path, var.into(), true);
484 let ty = self.db.ty(var.parent.into()); 486 let ty = self.db.ty(var.parent.into());
485 let ty = self.insert_type_vars(ty.subst(&substs)); 487 let ty = self.insert_type_vars(ty.substitute(&Interner, &substs));
486 forbid_unresolved_segments((ty, Some(var.into())), unresolved) 488 forbid_unresolved_segments((ty, Some(var.into())), unresolved)
487 } 489 }
488 TypeNs::SelfType(impl_id) => { 490 TypeNs::SelfType(impl_id) => {
489 let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); 491 let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
490 let substs = generics.type_params_subst(self.db); 492 let substs = generics.type_params_subst(self.db);
491 let ty = self.db.impl_self_ty(impl_id).subst(&substs); 493 let ty = self.db.impl_self_ty(impl_id).substitute(&Interner, &substs);
492 match unresolved { 494 self.resolve_variant_on_alias(ty, unresolved, path)
493 None => {
494 let variant = ty_variant(&ty);
495 (ty, variant)
496 }
497 Some(1) => {
498 let segment = path.mod_path().segments().last().unwrap();
499 // this could be an enum variant or associated type
500 if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
501 let enum_data = self.db.enum_data(enum_id);
502 if let Some(local_id) = enum_data.variant(segment) {
503 let variant = EnumVariantId { parent: enum_id, local_id };
504 return (ty, Some(variant.into()));
505 }
506 }
507 // FIXME potentially resolve assoc type
508 (self.err_ty(), None)
509 }
510 Some(_) => {
511 // FIXME diagnostic
512 (self.err_ty(), None)
513 }
514 }
515 } 495 }
516 TypeNs::TypeAliasId(it) => { 496 TypeNs::TypeAliasId(it) => {
517 let ty = TyBuilder::def_ty(self.db, it.into()) 497 let ty = TyBuilder::def_ty(self.db, it.into())
518 .fill(std::iter::repeat_with(|| self.table.new_type_var())) 498 .fill(std::iter::repeat_with(|| self.table.new_type_var()))
519 .build(); 499 .build();
520 let variant = ty_variant(&ty); 500 self.resolve_variant_on_alias(ty, unresolved, path)
521 forbid_unresolved_segments((ty, variant), unresolved)
522 } 501 }
523 TypeNs::AdtSelfType(_) => { 502 TypeNs::AdtSelfType(_) => {
524 // FIXME this could happen in array size expressions, once we're checking them 503 // FIXME this could happen in array size expressions, once we're checking them
@@ -542,19 +521,46 @@ impl<'a> InferenceContext<'a> {
542 result 521 result
543 } else { 522 } else {
544 // FIXME diagnostic 523 // FIXME diagnostic
545 (TyKind::Unknown.intern(&Interner), None) 524 (TyKind::Error.intern(&Interner), None)
546 } 525 }
547 } 526 }
527 }
548 528
549 fn ty_variant(ty: &Ty) -> Option<VariantId> { 529 fn resolve_variant_on_alias(
550 ty.as_adt().and_then(|(adt_id, _)| match adt_id { 530 &mut self,
551 AdtId::StructId(s) => Some(VariantId::StructId(s)), 531 ty: Ty,
552 AdtId::UnionId(u) => Some(VariantId::UnionId(u)), 532 unresolved: Option<usize>,
553 AdtId::EnumId(_) => { 533 path: &Path,
554 // FIXME Error E0071, expected struct, variant or union type, found enum `Foo` 534 ) -> (Ty, Option<VariantId>) {
555 None 535 match unresolved {
536 None => {
537 let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
538 AdtId::StructId(s) => Some(VariantId::StructId(s)),
539 AdtId::UnionId(u) => Some(VariantId::UnionId(u)),
540 AdtId::EnumId(_) => {
541 // FIXME Error E0071, expected struct, variant or union type, found enum `Foo`
542 None
543 }
544 });
545 (ty, variant)
546 }
547 Some(1) => {
548 let segment = path.mod_path().segments().last().unwrap();
549 // this could be an enum variant or associated type
550 if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
551 let enum_data = self.db.enum_data(enum_id);
552 if let Some(local_id) = enum_data.variant(segment) {
553 let variant = EnumVariantId { parent: enum_id, local_id };
554 return (ty, Some(variant.into()));
555 }
556 } 556 }
557 }) 557 // FIXME potentially resolve assoc type
558 (self.err_ty(), None)
559 }
560 Some(_) => {
561 // FIXME diagnostic
562 (self.err_ty(), None)
563 }
558 } 564 }
559 } 565 }
560 566
@@ -692,25 +698,6 @@ impl<'a> InferenceContext<'a> {
692 } 698 }
693} 699}
694 700
695/// The kinds of placeholders we need during type inference. There's separate
696/// values for general types, and for integer and float variables. The latter
697/// two are used for inference of literal values (e.g. `100` could be one of
698/// several integer types).
699#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
700pub struct InferenceVar {
701 index: u32,
702}
703
704impl InferenceVar {
705 fn to_inner(self) -> unify::TypeVarId {
706 unify::TypeVarId(self.index)
707 }
708
709 fn from_inner(unify::TypeVarId(index): unify::TypeVarId) -> Self {
710 InferenceVar { index }
711 }
712}
713
714/// When inferring an expression, we propagate downward whatever type hint we 701/// When inferring an expression, we propagate downward whatever type hint we
715/// are able in the form of an `Expectation`. 702/// are able in the form of an `Expectation`.
716#[derive(Clone, PartialEq, Eq, Debug)] 703#[derive(Clone, PartialEq, Eq, Debug)]
@@ -755,7 +742,7 @@ impl Expectation {
755 fn none() -> Self { 742 fn none() -> Self {
756 Expectation { 743 Expectation {
757 // FIXME 744 // FIXME
758 ty: TyKind::Unknown.intern(&Interner), 745 ty: TyKind::Error.intern(&Interner),
759 rvalue_hint: false, 746 rvalue_hint: false,
760 } 747 }
761 } 748 }
@@ -763,7 +750,7 @@ impl Expectation {
763 fn coercion_target(&self) -> Ty { 750 fn coercion_target(&self) -> Ty {
764 if self.rvalue_hint { 751 if self.rvalue_hint {
765 // FIXME 752 // FIXME
766 TyKind::Unknown.intern(&Interner) 753 TyKind::Error.intern(&Interner)
767 } else { 754 } else {
768 self.ty.clone() 755 self.ty.clone()
769 } 756 }
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs
index 028a4d568..1f463a425 100644
--- a/crates/hir_ty/src/infer/coerce.rs
+++ b/crates/hir_ty/src/infer/coerce.rs
@@ -7,7 +7,7 @@
7use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; 7use chalk_ir::{cast::Cast, Mutability, TyVariableKind};
8use hir_def::lang_item::LangItemTarget; 8use hir_def::lang_item::LangItemTarget;
9 9
10use crate::{autoderef, traits::Solution, Interner, Ty, TyBuilder, TyKind}; 10use crate::{autoderef, Canonical, Interner, Solution, Ty, TyBuilder, TyExt, TyKind};
11 11
12use super::{InEnvironment, InferenceContext}; 12use super::{InEnvironment, InferenceContext};
13 13
@@ -71,17 +71,19 @@ impl<'a> InferenceContext<'a> {
71 } 71 }
72 72
73 // Pointer weakening and function to pointer 73 // Pointer weakening and function to pointer
74 match (from_ty.interned_mut(), to_ty.kind(&Interner)) { 74 match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
75 // `*mut T` -> `*const T` 75 // `*mut T` -> `*const T`
76 (TyKind::Raw(_, inner), TyKind::Raw(m2 @ Mutability::Not, ..)) => {
77 from_ty = TyKind::Raw(*m2, inner.clone()).intern(&Interner);
78 }
76 // `&mut T` -> `&T` 79 // `&mut T` -> `&T`
77 (TyKind::Raw(m1, ..), TyKind::Raw(m2 @ Mutability::Not, ..)) 80 (TyKind::Ref(_, lt, inner), TyKind::Ref(m2 @ Mutability::Not, ..)) => {
78 | (TyKind::Ref(m1, ..), TyKind::Ref(m2 @ Mutability::Not, ..)) => { 81 from_ty = TyKind::Ref(*m2, lt.clone(), inner.clone()).intern(&Interner);
79 *m1 = *m2;
80 } 82 }
81 // `&T` -> `*const T` 83 // `&T` -> `*const T`
82 // `&mut T` -> `*mut T`/`*const T` 84 // `&mut T` -> `*mut T`/`*const T`
83 (TyKind::Ref(.., substs), &TyKind::Raw(m2 @ Mutability::Not, ..)) 85 (TyKind::Ref(.., substs), &TyKind::Raw(m2 @ Mutability::Not, ..))
84 | (TyKind::Ref(Mutability::Mut, substs), &TyKind::Raw(m2, ..)) => { 86 | (TyKind::Ref(Mutability::Mut, _, substs), &TyKind::Raw(m2, ..)) => {
85 from_ty = TyKind::Raw(m2, substs.clone()).intern(&Interner); 87 from_ty = TyKind::Raw(m2, substs.clone()).intern(&Interner);
86 } 88 }
87 89
@@ -111,7 +113,9 @@ impl<'a> InferenceContext<'a> {
111 // Auto Deref if cannot coerce 113 // Auto Deref if cannot coerce
112 match (from_ty.kind(&Interner), to_ty.kind(&Interner)) { 114 match (from_ty.kind(&Interner), to_ty.kind(&Interner)) {
113 // FIXME: DerefMut 115 // FIXME: DerefMut
114 (TyKind::Ref(_, st1), TyKind::Ref(_, st2)) => self.unify_autoderef_behind_ref(st1, st2), 116 (TyKind::Ref(.., st1), TyKind::Ref(.., st2)) => {
117 self.unify_autoderef_behind_ref(st1, st2)
118 }
115 119
116 // Otherwise, normal unify 120 // Otherwise, normal unify
117 _ => self.unify(&from_ty, to_ty), 121 _ => self.unify(&from_ty, to_ty),
@@ -137,7 +141,7 @@ impl<'a> InferenceContext<'a> {
137 b.push(from_ty.clone()).push(to_ty.clone()).build() 141 b.push(from_ty.clone()).push(to_ty.clone()).build()
138 }; 142 };
139 143
140 let goal = InEnvironment::new(self.trait_env.env.clone(), trait_ref.cast(&Interner)); 144 let goal = InEnvironment::new(&self.trait_env.env, trait_ref.cast(&Interner));
141 145
142 let canonicalizer = self.canonicalizer(); 146 let canonicalizer = self.canonicalizer();
143 let canonicalized = canonicalizer.canonicalize_obligation(goal); 147 let canonicalized = canonicalizer.canonicalize_obligation(goal);
@@ -146,7 +150,14 @@ impl<'a> InferenceContext<'a> {
146 150
147 match solution { 151 match solution {
148 Solution::Unique(v) => { 152 Solution::Unique(v) => {
149 canonicalized.apply_solution(self, v.0); 153 canonicalized.apply_solution(
154 self,
155 Canonical {
156 binders: v.binders,
157 // FIXME handle constraints
158 value: v.value.subst,
159 },
160 );
150 } 161 }
151 _ => return None, 162 _ => return None,
152 }; 163 };
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index c584a2c08..50497eecb 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -3,7 +3,7 @@
3use std::iter::{repeat, repeat_with}; 3use std::iter::{repeat, repeat_with};
4use std::{mem, sync::Arc}; 4use std::{mem, sync::Arc};
5 5
6use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; 6use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
7use hir_def::{ 7use hir_def::{
8 expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, 8 expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
9 path::{GenericArg, GenericArgs}, 9 path::{GenericArg, GenericArgs},
@@ -15,15 +15,16 @@ use stdx::always;
15use syntax::ast::RangeOp; 15use syntax::ast::RangeOp;
16 16
17use crate::{ 17use crate::{
18 autoderef, 18 autoderef, dummy_usize_const,
19 lower::lower_to_chalk_mutability, 19 lower::lower_to_chalk_mutability,
20 mapping::from_chalk,
20 method_resolution, op, 21 method_resolution, op,
21 primitive::{self, UintTy}, 22 primitive::{self, UintTy},
22 to_chalk_trait_id, 23 static_lifetime, to_chalk_trait_id,
23 traits::{chalk::from_chalk, FnTrait, InEnvironment}, 24 traits::FnTrait,
24 utils::{generics, variant_data, Generics}, 25 utils::{generics, Generics},
25 AdtId, Binders, CallableDefId, FnPointer, FnSig, Interner, Rawness, Scalar, Substitution, 26 AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
26 TraitRef, Ty, TyBuilder, TyKind, 27 ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
27}; 28};
28 29
29use super::{ 30use super::{
@@ -180,7 +181,8 @@ impl<'a> InferenceContext<'a> {
180 let inner_ty = self.infer_expr(*body, &Expectation::none()); 181 let inner_ty = self.infer_expr(*body, &Expectation::none());
181 let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body); 182 let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body);
182 let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); 183 let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
183 TyKind::OpaqueType(opaque_ty_id, Substitution::single(inner_ty)).intern(&Interner) 184 TyKind::OpaqueType(opaque_ty_id, Substitution::from1(&Interner, inner_ty))
185 .intern(&Interner)
184 } 186 }
185 Expr::Loop { body, label } => { 187 Expr::Loop { body, label } => {
186 self.breakables.push(BreakableContext { 188 self.breakables.push(BreakableContext {
@@ -259,14 +261,17 @@ impl<'a> InferenceContext<'a> {
259 }; 261 };
260 sig_tys.push(ret_ty.clone()); 262 sig_tys.push(ret_ty.clone());
261 let sig_ty = TyKind::Function(FnPointer { 263 let sig_ty = TyKind::Function(FnPointer {
262 num_args: sig_tys.len() - 1, 264 num_binders: 0,
263 sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false }, 265 sig: FnSig { abi: (), safety: chalk_ir::Safety::Safe, variadic: false },
264 substs: Substitution::from_iter(&Interner, sig_tys.clone()), 266 substitution: FnSubst(
267 Substitution::from_iter(&Interner, sig_tys.clone()).shifted_in(&Interner),
268 ),
265 }) 269 })
266 .intern(&Interner); 270 .intern(&Interner);
267 let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into(); 271 let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
268 let closure_ty = 272 let closure_ty =
269 TyKind::Closure(closure_id, Substitution::single(sig_ty)).intern(&Interner); 273 TyKind::Closure(closure_id, Substitution::from1(&Interner, sig_ty))
274 .intern(&Interner);
270 275
271 // Eagerly try to relate the closure type with the expected 276 // Eagerly try to relate the closure type with the expected
272 // type, otherwise we often won't have enough information to 277 // type, otherwise we often won't have enough information to
@@ -313,7 +318,13 @@ impl<'a> InferenceContext<'a> {
313 self.normalize_associated_types_in(ret_ty) 318 self.normalize_associated_types_in(ret_ty)
314 } 319 }
315 Expr::MethodCall { receiver, args, method_name, generic_args } => self 320 Expr::MethodCall { receiver, args, method_name, generic_args } => self
316 .infer_method_call(tgt_expr, *receiver, &args, &method_name, generic_args.as_ref()), 321 .infer_method_call(
322 tgt_expr,
323 *receiver,
324 &args,
325 &method_name,
326 generic_args.as_deref(),
327 ),
317 Expr::Match { expr, arms } => { 328 Expr::Match { expr, arms } => {
318 let input_ty = self.infer_expr(*expr, &Expectation::none()); 329 let input_ty = self.infer_expr(*expr, &Expectation::none());
319 330
@@ -394,16 +405,19 @@ impl<'a> InferenceContext<'a> {
394 TyKind::Never.intern(&Interner) 405 TyKind::Never.intern(&Interner)
395 } 406 }
396 Expr::RecordLit { path, fields, spread } => { 407 Expr::RecordLit { path, fields, spread } => {
397 let (ty, def_id) = self.resolve_variant(path.as_ref()); 408 let (ty, def_id) = self.resolve_variant(path.as_deref());
398 if let Some(variant) = def_id { 409 if let Some(variant) = def_id {
399 self.write_variant_resolution(tgt_expr.into(), variant); 410 self.write_variant_resolution(tgt_expr.into(), variant);
400 } 411 }
401 412
402 self.unify(&ty, &expected.ty); 413 self.unify(&ty, &expected.ty);
403 414
404 let substs = ty.substs().cloned().unwrap_or_else(|| Substitution::empty(&Interner)); 415 let substs = ty
416 .as_adt()
417 .map(|(_, s)| s.clone())
418 .unwrap_or_else(|| Substitution::empty(&Interner));
405 let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default(); 419 let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default();
406 let variant_data = def_id.map(|it| variant_data(self.db.upcast(), it)); 420 let variant_data = def_id.map(|it| it.variant_data(self.db.upcast()));
407 for field in fields.iter() { 421 for field in fields.iter() {
408 let field_def = 422 let field_def =
409 variant_data.as_ref().and_then(|it| match it.field(&field.name) { 423 variant_data.as_ref().and_then(|it| match it.field(&field.name) {
@@ -415,11 +429,8 @@ impl<'a> InferenceContext<'a> {
415 None 429 None
416 } 430 }
417 }); 431 });
418 if let Some(field_def) = field_def {
419 self.result.record_field_resolutions.insert(field.expr, field_def);
420 }
421 let field_ty = field_def.map_or(self.err_ty(), |it| { 432 let field_ty = field_def.map_or(self.err_ty(), |it| {
422 field_types[it.local_id].clone().subst(&substs) 433 field_types[it.local_id].clone().substitute(&Interner, &substs)
423 }); 434 });
424 self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty)); 435 self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty));
425 } 436 }
@@ -453,7 +464,7 @@ impl<'a> InferenceContext<'a> {
453 match canonicalized.decanonicalize_ty(derefed_ty.value).kind(&Interner) { 464 match canonicalized.decanonicalize_ty(derefed_ty.value).kind(&Interner) {
454 TyKind::Tuple(_, substs) => name.as_tuple_index().and_then(|idx| { 465 TyKind::Tuple(_, substs) => name.as_tuple_index().and_then(|idx| {
455 substs 466 substs
456 .interned(&Interner) 467 .as_slice(&Interner)
457 .get(idx) 468 .get(idx)
458 .map(|a| a.assert_ty_ref(&Interner)) 469 .map(|a| a.assert_ty_ref(&Interner))
459 .cloned() 470 .cloned()
@@ -466,7 +477,7 @@ impl<'a> InferenceContext<'a> {
466 Some( 477 Some(
467 self.db.field_types((*s).into())[field.local_id] 478 self.db.field_types((*s).into())[field.local_id]
468 .clone() 479 .clone()
469 .subst(&parameters), 480 .substitute(&Interner, &parameters),
470 ) 481 )
471 } else { 482 } else {
472 None 483 None
@@ -480,7 +491,7 @@ impl<'a> InferenceContext<'a> {
480 Some( 491 Some(
481 self.db.field_types((*u).into())[field.local_id] 492 self.db.field_types((*u).into())[field.local_id]
482 .clone() 493 .clone()
483 .subst(&parameters), 494 .substitute(&Interner, &parameters),
484 ) 495 )
485 } else { 496 } else {
486 None 497 None
@@ -527,7 +538,7 @@ impl<'a> InferenceContext<'a> {
527 let inner_ty = self.infer_expr_inner(*expr, &expectation); 538 let inner_ty = self.infer_expr_inner(*expr, &expectation);
528 match rawness { 539 match rawness {
529 Rawness::RawPtr => TyKind::Raw(mutability, inner_ty), 540 Rawness::RawPtr => TyKind::Raw(mutability, inner_ty),
530 Rawness::Ref => TyKind::Ref(mutability, inner_ty), 541 Rawness::Ref => TyKind::Ref(mutability, static_lifetime(), inner_ty),
531 } 542 }
532 .intern(&Interner) 543 .intern(&Interner)
533 } 544 }
@@ -702,7 +713,7 @@ impl<'a> InferenceContext<'a> {
702 } 713 }
703 Expr::Array(array) => { 714 Expr::Array(array) => {
704 let elem_ty = match expected.ty.kind(&Interner) { 715 let elem_ty = match expected.ty.kind(&Interner) {
705 TyKind::Array(st) | TyKind::Slice(st) => st.clone(), 716 TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
706 _ => self.table.new_type_var(), 717 _ => self.table.new_type_var(),
707 }; 718 };
708 719
@@ -726,17 +737,19 @@ impl<'a> InferenceContext<'a> {
726 } 737 }
727 } 738 }
728 739
729 TyKind::Array(elem_ty).intern(&Interner) 740 TyKind::Array(elem_ty, dummy_usize_const()).intern(&Interner)
730 } 741 }
731 Expr::Literal(lit) => match lit { 742 Expr::Literal(lit) => match lit {
732 Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner), 743 Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
733 Literal::String(..) => { 744 Literal::String(..) => {
734 TyKind::Ref(Mutability::Not, TyKind::Str.intern(&Interner)).intern(&Interner) 745 TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(&Interner))
746 .intern(&Interner)
735 } 747 }
736 Literal::ByteString(..) => { 748 Literal::ByteString(..) => {
737 let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner); 749 let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner);
738 let array_type = TyKind::Array(byte_type).intern(&Interner); 750 let array_type =
739 TyKind::Ref(Mutability::Not, array_type).intern(&Interner) 751 TyKind::Array(byte_type, dummy_usize_const()).intern(&Interner);
752 TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner)
740 } 753 }
741 Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner), 754 Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner),
742 Literal::Int(_v, ty) => match ty { 755 Literal::Int(_v, ty) => match ty {
@@ -853,10 +866,10 @@ impl<'a> InferenceContext<'a> {
853 self.write_method_resolution(tgt_expr, func); 866 self.write_method_resolution(tgt_expr, func);
854 (ty, self.db.value_ty(func.into()), Some(generics(self.db.upcast(), func.into()))) 867 (ty, self.db.value_ty(func.into()), Some(generics(self.db.upcast(), func.into())))
855 } 868 }
856 None => (receiver_ty, Binders::new(0, self.err_ty()), None), 869 None => (receiver_ty, Binders::empty(&Interner, self.err_ty()), None),
857 }; 870 };
858 let substs = self.substs_for_method_call(def_generics, generic_args, &derefed_receiver_ty); 871 let substs = self.substs_for_method_call(def_generics, generic_args, &derefed_receiver_ty);
859 let method_ty = method_ty.subst(&substs); 872 let method_ty = method_ty.substitute(&Interner, &substs);
860 let method_ty = self.insert_type_vars(method_ty); 873 let method_ty = self.insert_type_vars(method_ty);
861 self.register_obligations_for_call(&method_ty); 874 self.register_obligations_for_call(&method_ty);
862 let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) { 875 let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) {
@@ -872,7 +885,9 @@ impl<'a> InferenceContext<'a> {
872 // Apply autoref so the below unification works correctly 885 // Apply autoref so the below unification works correctly
873 // FIXME: return correct autorefs from lookup_method 886 // FIXME: return correct autorefs from lookup_method
874 let actual_receiver_ty = match expected_receiver_ty.as_reference() { 887 let actual_receiver_ty = match expected_receiver_ty.as_reference() {
875 Some((_, mutability)) => TyKind::Ref(mutability, derefed_receiver_ty).intern(&Interner), 888 Some((_, lifetime, mutability)) => {
889 TyKind::Ref(mutability, lifetime, derefed_receiver_ty).intern(&Interner)
890 }
876 _ => derefed_receiver_ty, 891 _ => derefed_receiver_ty,
877 }; 892 };
878 self.unify(&expected_receiver_ty, &actual_receiver_ty); 893 self.unify(&expected_receiver_ty, &actual_receiver_ty);
@@ -953,9 +968,11 @@ impl<'a> InferenceContext<'a> {
953 let def: CallableDefId = from_chalk(self.db, *fn_def); 968 let def: CallableDefId = from_chalk(self.db, *fn_def);
954 let generic_predicates = self.db.generic_predicates(def.into()); 969 let generic_predicates = self.db.generic_predicates(def.into());
955 for predicate in generic_predicates.iter() { 970 for predicate in generic_predicates.iter() {
956 let (predicate, binders) = 971 let (predicate, binders) = predicate
957 predicate.clone().subst(parameters).into_value_and_skipped_binders(); 972 .clone()
958 always!(binders == 0); // quantified where clauses not yet handled 973 .substitute(&Interner, parameters)
974 .into_value_and_skipped_binders();
975 always!(binders.len(&Interner) == 0); // quantified where clauses not yet handled
959 self.push_obligation(predicate.cast(&Interner)); 976 self.push_obligation(predicate.cast(&Interner));
960 } 977 }
961 // add obligation for trait implementation, if this is a trait method 978 // add obligation for trait implementation, if this is a trait method
@@ -964,8 +981,10 @@ impl<'a> InferenceContext<'a> {
964 if let AssocContainerId::TraitId(trait_) = f.lookup(self.db.upcast()).container 981 if let AssocContainerId::TraitId(trait_) = f.lookup(self.db.upcast()).container
965 { 982 {
966 // construct a TraitRef 983 // construct a TraitRef
967 let substs = 984 let substs = crate::subst_prefix(
968 parameters.prefix(generics(self.db.upcast(), trait_.into()).len()); 985 &*parameters,
986 generics(self.db.upcast(), trait_.into()).len(),
987 );
969 self.push_obligation( 988 self.push_obligation(
970 TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs } 989 TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs }
971 .cast(&Interner), 990 .cast(&Interner),
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs
index 5b70d5e5a..aea354cde 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -7,14 +7,13 @@ use chalk_ir::Mutability;
7use hir_def::{ 7use hir_def::{
8 expr::{BindingAnnotation, Expr, Literal, Pat, PatId, RecordFieldPat}, 8 expr::{BindingAnnotation, Expr, Literal, Pat, PatId, RecordFieldPat},
9 path::Path, 9 path::Path,
10 FieldId,
11}; 10};
12use hir_expand::name::Name; 11use hir_expand::name::Name;
13 12
14use super::{BindingMode, Expectation, InferenceContext}; 13use super::{BindingMode, Expectation, InferenceContext};
15use crate::{ 14use crate::{
16 lower::lower_to_chalk_mutability, utils::variant_data, Interner, Substitution, Ty, TyBuilder, 15 lower::lower_to_chalk_mutability, static_lifetime, Interner, Substitution, Ty, TyBuilder,
17 TyKind, 16 TyExt, TyKind,
18}; 17};
19 18
20impl<'a> InferenceContext<'a> { 19impl<'a> InferenceContext<'a> {
@@ -28,13 +27,14 @@ impl<'a> InferenceContext<'a> {
28 ellipsis: Option<usize>, 27 ellipsis: Option<usize>,
29 ) -> Ty { 28 ) -> Ty {
30 let (ty, def) = self.resolve_variant(path); 29 let (ty, def) = self.resolve_variant(path);
31 let var_data = def.map(|it| variant_data(self.db.upcast(), it)); 30 let var_data = def.map(|it| it.variant_data(self.db.upcast()));
32 if let Some(variant) = def { 31 if let Some(variant) = def {
33 self.write_variant_resolution(id.into(), variant); 32 self.write_variant_resolution(id.into(), variant);
34 } 33 }
35 self.unify(&ty, expected); 34 self.unify(&ty, expected);
36 35
37 let substs = ty.substs().cloned().unwrap_or_else(|| Substitution::empty(&Interner)); 36 let substs =
37 ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(&Interner));
38 38
39 let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); 39 let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
40 let (pre, post) = match ellipsis { 40 let (pre, post) = match ellipsis {
@@ -49,7 +49,9 @@ impl<'a> InferenceContext<'a> {
49 let expected_ty = var_data 49 let expected_ty = var_data
50 .as_ref() 50 .as_ref()
51 .and_then(|d| d.field(&Name::new_tuple_field(i))) 51 .and_then(|d| d.field(&Name::new_tuple_field(i)))
52 .map_or(self.err_ty(), |field| field_tys[field].clone().subst(&substs)); 52 .map_or(self.err_ty(), |field| {
53 field_tys[field].clone().substitute(&Interner, &substs)
54 });
53 let expected_ty = self.normalize_associated_types_in(expected_ty); 55 let expected_ty = self.normalize_associated_types_in(expected_ty);
54 self.infer_pat(subpat, &expected_ty, default_bm); 56 self.infer_pat(subpat, &expected_ty, default_bm);
55 } 57 }
@@ -66,25 +68,22 @@ impl<'a> InferenceContext<'a> {
66 id: PatId, 68 id: PatId,
67 ) -> Ty { 69 ) -> Ty {
68 let (ty, def) = self.resolve_variant(path); 70 let (ty, def) = self.resolve_variant(path);
69 let var_data = def.map(|it| variant_data(self.db.upcast(), it)); 71 let var_data = def.map(|it| it.variant_data(self.db.upcast()));
70 if let Some(variant) = def { 72 if let Some(variant) = def {
71 self.write_variant_resolution(id.into(), variant); 73 self.write_variant_resolution(id.into(), variant);
72 } 74 }
73 75
74 self.unify(&ty, expected); 76 self.unify(&ty, expected);
75 77
76 let substs = ty.substs().cloned().unwrap_or_else(|| Substitution::empty(&Interner)); 78 let substs =
79 ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(&Interner));
77 80
78 let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); 81 let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
79 for subpat in subpats { 82 for subpat in subpats {
80 let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name)); 83 let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name));
81 if let Some(local_id) = matching_field { 84 let expected_ty = matching_field.map_or(self.err_ty(), |field| {
82 let field_def = FieldId { parent: def.unwrap(), local_id }; 85 field_tys[field].clone().substitute(&Interner, &substs)
83 self.result.record_pat_field_resolutions.insert(subpat.pat, field_def); 86 });
84 }
85
86 let expected_ty = matching_field
87 .map_or(self.err_ty(), |field| field_tys[field].clone().subst(&substs));
88 let expected_ty = self.normalize_associated_types_in(expected_ty); 87 let expected_ty = self.normalize_associated_types_in(expected_ty);
89 self.infer_pat(subpat.pat, &expected_ty, default_bm); 88 self.infer_pat(subpat.pat, &expected_ty, default_bm);
90 } 89 }
@@ -101,7 +100,7 @@ impl<'a> InferenceContext<'a> {
101 let body = Arc::clone(&self.body); // avoid borrow checker problem 100 let body = Arc::clone(&self.body); // avoid borrow checker problem
102 101
103 if is_non_ref_pat(&body, pat) { 102 if is_non_ref_pat(&body, pat) {
104 while let Some((inner, mutability)) = expected.as_reference() { 103 while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
105 expected = inner; 104 expected = inner;
106 default_bm = match default_bm { 105 default_bm = match default_bm {
107 BindingMode::Move => BindingMode::Ref(mutability), 106 BindingMode::Move => BindingMode::Ref(mutability),
@@ -123,7 +122,7 @@ impl<'a> InferenceContext<'a> {
123 let ty = match &body[pat] { 122 let ty = match &body[pat] {
124 &Pat::Tuple { ref args, ellipsis } => { 123 &Pat::Tuple { ref args, ellipsis } => {
125 let expectations = match expected.as_tuple() { 124 let expectations = match expected.as_tuple() {
126 Some(parameters) => &*parameters.0, 125 Some(parameters) => &*parameters.as_slice(&Interner),
127 _ => &[], 126 _ => &[],
128 }; 127 };
129 128
@@ -159,7 +158,7 @@ impl<'a> InferenceContext<'a> {
159 Pat::Ref { pat, mutability } => { 158 Pat::Ref { pat, mutability } => {
160 let mutability = lower_to_chalk_mutability(*mutability); 159 let mutability = lower_to_chalk_mutability(*mutability);
161 let expectation = match expected.as_reference() { 160 let expectation = match expected.as_reference() {
162 Some((inner_ty, exp_mut)) => { 161 Some((inner_ty, _lifetime, exp_mut)) => {
163 if mutability != exp_mut { 162 if mutability != exp_mut {
164 // FIXME: emit type error? 163 // FIXME: emit type error?
165 } 164 }
@@ -168,10 +167,10 @@ impl<'a> InferenceContext<'a> {
168 _ => self.result.standard_types.unknown.clone(), 167 _ => self.result.standard_types.unknown.clone(),
169 }; 168 };
170 let subty = self.infer_pat(*pat, &expectation, default_bm); 169 let subty = self.infer_pat(*pat, &expectation, default_bm);
171 TyKind::Ref(mutability, subty).intern(&Interner) 170 TyKind::Ref(mutability, static_lifetime(), subty).intern(&Interner)
172 } 171 }
173 Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat( 172 Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
174 p.as_ref(), 173 p.as_deref(),
175 subpats, 174 subpats,
176 expected, 175 expected,
177 default_bm, 176 default_bm,
@@ -179,7 +178,7 @@ impl<'a> InferenceContext<'a> {
179 *ellipsis, 178 *ellipsis,
180 ), 179 ),
181 Pat::Record { path: p, args: fields, ellipsis: _ } => { 180 Pat::Record { path: p, args: fields, ellipsis: _ } => {
182 self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) 181 self.infer_record_pat(p.as_deref(), fields, expected, default_bm, pat)
183 } 182 }
184 Pat::Path(path) => { 183 Pat::Path(path) => {
185 // FIXME use correct resolver for the surrounding expression 184 // FIXME use correct resolver for the surrounding expression
@@ -201,7 +200,8 @@ impl<'a> InferenceContext<'a> {
201 200
202 let bound_ty = match mode { 201 let bound_ty = match mode {
203 BindingMode::Ref(mutability) => { 202 BindingMode::Ref(mutability) => {
204 TyKind::Ref(mutability, inner_ty.clone()).intern(&Interner) 203 TyKind::Ref(mutability, static_lifetime(), inner_ty.clone())
204 .intern(&Interner)
205 } 205 }
206 BindingMode::Move => inner_ty.clone(), 206 BindingMode::Move => inner_ty.clone(),
207 }; 207 };
@@ -210,17 +210,20 @@ impl<'a> InferenceContext<'a> {
210 return inner_ty; 210 return inner_ty;
211 } 211 }
212 Pat::Slice { prefix, slice, suffix } => { 212 Pat::Slice { prefix, slice, suffix } => {
213 let (container_ty, elem_ty): (fn(_) -> _, _) = match expected.kind(&Interner) { 213 let elem_ty = match expected.kind(&Interner) {
214 TyKind::Array(st) => (TyKind::Array, st.clone()), 214 TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(),
215 TyKind::Slice(st) => (TyKind::Slice, st.clone()), 215 _ => self.err_ty(),
216 _ => (TyKind::Slice, self.err_ty()),
217 }; 216 };
218 217
219 for pat_id in prefix.iter().chain(suffix) { 218 for pat_id in prefix.iter().chain(suffix) {
220 self.infer_pat(*pat_id, &elem_ty, default_bm); 219 self.infer_pat(*pat_id, &elem_ty, default_bm);
221 } 220 }
222 221
223 let pat_ty = container_ty(elem_ty).intern(&Interner); 222 let pat_ty = match expected.kind(&Interner) {
223 TyKind::Array(_, const_) => TyKind::Array(elem_ty, const_.clone()),
224 _ => TyKind::Slice(elem_ty),
225 }
226 .intern(&Interner);
224 if let Some(slice_pat_id) = slice { 227 if let Some(slice_pat_id) = slice {
225 self.infer_pat(*slice_pat_id, &pat_ty, default_bm); 228 self.infer_pat(*slice_pat_id, &pat_ty, default_bm);
226 } 229 }
@@ -239,7 +242,7 @@ impl<'a> InferenceContext<'a> {
239 let (inner_ty, alloc_ty) = match expected.as_adt() { 242 let (inner_ty, alloc_ty) = match expected.as_adt() {
240 Some((adt, subst)) if adt == box_adt => ( 243 Some((adt, subst)) if adt == box_adt => (
241 subst.at(&Interner, 0).assert_ty_ref(&Interner).clone(), 244 subst.at(&Interner, 0).assert_ty_ref(&Interner).clone(),
242 subst.interned(&Interner).get(1).and_then(|a| a.ty(&Interner).cloned()), 245 subst.as_slice(&Interner).get(1).and_then(|a| a.ty(&Interner).cloned()),
243 ), 246 ),
244 _ => (self.result.standard_types.unknown.clone(), None), 247 _ => (self.result.standard_types.unknown.clone(), None),
245 }; 248 };
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 671ea355f..495282eba 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -10,7 +10,10 @@ use hir_def::{
10}; 10};
11use hir_expand::name::Name; 11use hir_expand::name::Name;
12 12
13use crate::{method_resolution, Interner, Substitution, Ty, TyBuilder, TyKind, ValueTyDefId}; 13use crate::{
14 method_resolution, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
15 ValueTyDefId,
16};
14 17
15use super::{ExprOrPatId, InferenceContext, TraitRef}; 18use super::{ExprOrPatId, InferenceContext, TraitRef};
16 19
@@ -81,9 +84,9 @@ impl<'a> InferenceContext<'a> {
81 ValueNs::ImplSelf(impl_id) => { 84 ValueNs::ImplSelf(impl_id) => {
82 let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); 85 let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
83 let substs = generics.type_params_subst(self.db); 86 let substs = generics.type_params_subst(self.db);
84 let ty = self.db.impl_self_ty(impl_id).subst(&substs); 87 let ty = self.db.impl_self_ty(impl_id).substitute(&Interner, &substs);
85 if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() { 88 if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() {
86 let ty = self.db.value_ty(struct_id.into()).subst(&substs); 89 let ty = self.db.value_ty(struct_id.into()).substitute(&Interner, &substs);
87 return Some(ty); 90 return Some(ty);
88 } else { 91 } else {
89 // FIXME: diagnostic, invalid Self reference 92 // FIXME: diagnostic, invalid Self reference
@@ -98,7 +101,7 @@ impl<'a> InferenceContext<'a> {
98 let substs = ctx.substs_from_path(path, typable, true); 101 let substs = ctx.substs_from_path(path, typable, true);
99 let ty = TyBuilder::value_ty(self.db, typable) 102 let ty = TyBuilder::value_ty(self.db, typable)
100 .use_parent_substs(&parent_substs) 103 .use_parent_substs(&parent_substs)
101 .fill(substs.interned(&Interner)[parent_substs.len(&Interner)..].iter().cloned()) 104 .fill(substs.as_slice(&Interner)[parent_substs.len(&Interner)..].iter().cloned())
102 .build(); 105 .build();
103 Some(ty) 106 Some(ty)
104 } 107 }
@@ -142,7 +145,7 @@ impl<'a> InferenceContext<'a> {
142 remaining_segments_for_ty, 145 remaining_segments_for_ty,
143 true, 146 true,
144 ); 147 );
145 if let TyKind::Unknown = ty.kind(&Interner) { 148 if let TyKind::Error = ty.kind(&Interner) {
146 return None; 149 return None;
147 } 150 }
148 151
@@ -207,7 +210,7 @@ impl<'a> InferenceContext<'a> {
207 name: &Name, 210 name: &Name,
208 id: ExprOrPatId, 211 id: ExprOrPatId,
209 ) -> Option<(ValueNs, Option<Substitution>)> { 212 ) -> Option<(ValueNs, Option<Substitution>)> {
210 if let TyKind::Unknown = ty.kind(&Interner) { 213 if let TyKind::Error = ty.kind(&Interner) {
211 return None; 214 return None;
212 } 215 }
213 216
@@ -243,7 +246,8 @@ impl<'a> InferenceContext<'a> {
243 let impl_substs = TyBuilder::subst_for_def(self.db, impl_id) 246 let impl_substs = TyBuilder::subst_for_def(self.db, impl_id)
244 .fill(iter::repeat_with(|| self.table.new_type_var())) 247 .fill(iter::repeat_with(|| self.table.new_type_var()))
245 .build(); 248 .build();
246 let impl_self_ty = self.db.impl_self_ty(impl_id).subst(&impl_substs); 249 let impl_self_ty =
250 self.db.impl_self_ty(impl_id).substitute(&Interner, &impl_substs);
247 self.unify(&impl_self_ty, &ty); 251 self.unify(&impl_self_ty, &ty);
248 Some(impl_substs) 252 Some(impl_substs)
249 } 253 }
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index a04b935ef..a887e20b0 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -2,13 +2,17 @@
2 2
3use std::borrow::Cow; 3use std::borrow::Cow;
4 4
5use chalk_ir::{FloatTy, IntTy, TyVariableKind, UniverseIndex, VariableKind}; 5use chalk_ir::{
6 cast::Cast, fold::Fold, interner::HasInterner, FloatTy, IntTy, TyVariableKind, UniverseIndex,
7 VariableKind,
8};
6use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; 9use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue};
7 10
8use super::{DomainGoal, InferenceContext}; 11use super::{DomainGoal, InferenceContext};
9use crate::{ 12use crate::{
10 AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, DebruijnIndex, FnPointer, 13 fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds,
11 InEnvironment, InferenceVar, Interner, Scalar, Substitution, Ty, TyKind, TypeWalk, WhereClause, 14 DebruijnIndex, FnPointer, FnSubst, InEnvironment, InferenceVar, Interner, Scalar, Substitution,
15 Ty, TyExt, TyKind, WhereClause,
12}; 16};
13 17
14impl<'a> InferenceContext<'a> { 18impl<'a> InferenceContext<'a> {
@@ -33,7 +37,10 @@ where
33} 37}
34 38
35#[derive(Debug)] 39#[derive(Debug)]
36pub(super) struct Canonicalized<T> { 40pub(super) struct Canonicalized<T>
41where
42 T: HasInterner<Interner = Interner>,
43{
37 pub(super) value: Canonical<T>, 44 pub(super) value: Canonical<T>,
38 free_vars: Vec<(InferenceVar, TyVariableKind)>, 45 free_vars: Vec<(InferenceVar, TyVariableKind)>,
39} 46}
@@ -47,11 +54,16 @@ impl<'a, 'b> Canonicalizer<'a, 'b> {
47 }) 54 })
48 } 55 }
49 56
50 fn do_canonicalize<T: TypeWalk>(&mut self, t: T, binders: DebruijnIndex) -> T { 57 fn do_canonicalize<T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>>(
51 t.fold_binders( 58 &mut self,
52 &mut |ty, binders| match ty.kind(&Interner) { 59 t: T,
60 binders: DebruijnIndex,
61 ) -> T {
62 fold_tys(
63 t,
64 |ty, binders| match ty.kind(&Interner) {
53 &TyKind::InferenceVar(var, kind) => { 65 &TyKind::InferenceVar(var, kind) => {
54 let inner = var.to_inner(); 66 let inner = from_inference_var(var);
55 if self.var_stack.contains(&inner) { 67 if self.var_stack.contains(&inner) {
56 // recursive type 68 // recursive type
57 return self.ctx.table.type_variable_table.fallback_value(var, kind); 69 return self.ctx.table.type_variable_table.fallback_value(var, kind);
@@ -65,7 +77,7 @@ impl<'a, 'b> Canonicalizer<'a, 'b> {
65 result 77 result
66 } else { 78 } else {
67 let root = self.ctx.table.var_unification_table.find(inner); 79 let root = self.ctx.table.var_unification_table.find(inner);
68 let position = self.add(InferenceVar::from_inner(root), kind); 80 let position = self.add(to_inference_var(root), kind);
69 TyKind::BoundVar(BoundVar::new(binders, position)).intern(&Interner) 81 TyKind::BoundVar(BoundVar::new(binders, position)).intern(&Interner)
70 } 82 }
71 } 83 }
@@ -75,7 +87,10 @@ impl<'a, 'b> Canonicalizer<'a, 'b> {
75 ) 87 )
76 } 88 }
77 89
78 fn into_canonicalized<T>(self, result: T) -> Canonicalized<T> { 90 fn into_canonicalized<T: HasInterner<Interner = Interner>>(
91 self,
92 result: T,
93 ) -> Canonicalized<T> {
79 let kinds = self 94 let kinds = self
80 .free_vars 95 .free_vars
81 .iter() 96 .iter()
@@ -102,25 +117,18 @@ impl<'a, 'b> Canonicalizer<'a, 'b> {
102 DomainGoal::Holds(wc) => { 117 DomainGoal::Holds(wc) => {
103 DomainGoal::Holds(self.do_canonicalize(wc, DebruijnIndex::INNERMOST)) 118 DomainGoal::Holds(self.do_canonicalize(wc, DebruijnIndex::INNERMOST))
104 } 119 }
120 _ => unimplemented!(),
105 }; 121 };
106 self.into_canonicalized(InEnvironment { goal: result, environment: obligation.environment }) 122 self.into_canonicalized(InEnvironment { goal: result, environment: obligation.environment })
107 } 123 }
108} 124}
109 125
110impl<T> Canonicalized<T> { 126impl<T: HasInterner<Interner = Interner>> Canonicalized<T> {
111 pub(super) fn decanonicalize_ty(&self, mut ty: Ty) -> Ty { 127 pub(super) fn decanonicalize_ty(&self, ty: Ty) -> Ty {
112 ty.walk_mut_binders( 128 crate::fold_free_vars(ty, |bound, _binders| {
113 &mut |ty, binders| { 129 let (v, k) = self.free_vars[bound.index];
114 if let &mut TyKind::BoundVar(bound) = ty.interned_mut() { 130 TyKind::InferenceVar(v, k).intern(&Interner)
115 if bound.debruijn >= binders { 131 })
116 let (v, k) = self.free_vars[bound.index];
117 *ty = TyKind::InferenceVar(v, k).intern(&Interner);
118 }
119 }
120 },
121 DebruijnIndex::INNERMOST,
122 );
123 ty
124 } 132 }
125 133
126 pub(super) fn apply_solution( 134 pub(super) fn apply_solution(
@@ -132,15 +140,17 @@ impl<T> Canonicalized<T> {
132 let new_vars = Substitution::from_iter( 140 let new_vars = Substitution::from_iter(
133 &Interner, 141 &Interner,
134 solution.binders.iter(&Interner).map(|k| match k.kind { 142 solution.binders.iter(&Interner).map(|k| match k.kind {
135 VariableKind::Ty(TyVariableKind::General) => ctx.table.new_type_var(), 143 VariableKind::Ty(TyVariableKind::General) => {
136 VariableKind::Ty(TyVariableKind::Integer) => ctx.table.new_integer_var(), 144 ctx.table.new_type_var().cast(&Interner)
137 VariableKind::Ty(TyVariableKind::Float) => ctx.table.new_float_var(), 145 }
138 // HACK: Chalk can sometimes return new lifetime variables. We 146 VariableKind::Ty(TyVariableKind::Integer) => {
139 // want to just skip them, but to not mess up the indices of 147 ctx.table.new_integer_var().cast(&Interner)
140 // other variables, we'll just create a new type variable in 148 }
141 // their place instead. This should not matter (we never see the 149 VariableKind::Ty(TyVariableKind::Float) => {
142 // actual *uses* of the lifetime variable). 150 ctx.table.new_float_var().cast(&Interner)
143 VariableKind::Lifetime => ctx.table.new_type_var(), 151 }
152 // Chalk can sometimes return new lifetime variables. We just use the static lifetime everywhere
153 VariableKind::Lifetime => static_lifetime().cast(&Interner),
144 _ => panic!("const variable in solution"), 154 _ => panic!("const variable in solution"),
145 }), 155 }),
146 ); 156 );
@@ -149,7 +159,7 @@ impl<T> Canonicalized<T> {
149 // eagerly replace projections in the type; we may be getting types 159 // eagerly replace projections in the type; we may be getting types
150 // e.g. from where clauses where this hasn't happened yet 160 // e.g. from where clauses where this hasn't happened yet
151 let ty = ctx.normalize_associated_types_in( 161 let ty = ctx.normalize_associated_types_in(
152 ty.assert_ty_ref(&Interner).clone().subst_bound_vars(&new_vars), 162 new_vars.apply(ty.assert_ty_ref(&Interner).clone(), &Interner),
153 ); 163 );
154 ctx.table.unify(&TyKind::InferenceVar(v, k).intern(&Interner), &ty); 164 ctx.table.unify(&TyKind::InferenceVar(v, k).intern(&Interner), &ty);
155 } 165 }
@@ -170,8 +180,8 @@ pub(crate) fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substitution> {
170 // fallback to Unknown in the end (kind of hacky, as below) 180 // fallback to Unknown in the end (kind of hacky, as below)
171 .map(|_| table.new_type_var()), 181 .map(|_| table.new_type_var()),
172 ); 182 );
173 let ty1_with_vars = tys.value.0.clone().subst_bound_vars(&vars); 183 let ty1_with_vars = vars.apply(tys.value.0.clone(), &Interner);
174 let ty2_with_vars = tys.value.1.clone().subst_bound_vars(&vars); 184 let ty2_with_vars = vars.apply(tys.value.1.clone(), &Interner);
175 if !table.unify(&ty1_with_vars, &ty2_with_vars) { 185 if !table.unify(&ty1_with_vars, &ty2_with_vars) {
176 return None; 186 return None;
177 } 187 }
@@ -204,17 +214,17 @@ impl TypeVariableTable {
204 } 214 }
205 215
206 pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { 216 pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) {
207 self.inner[iv.to_inner().0 as usize].diverging = diverging; 217 self.inner[from_inference_var(iv).0 as usize].diverging = diverging;
208 } 218 }
209 219
210 fn is_diverging(&mut self, iv: InferenceVar) -> bool { 220 fn is_diverging(&mut self, iv: InferenceVar) -> bool {
211 self.inner[iv.to_inner().0 as usize].diverging 221 self.inner[from_inference_var(iv).0 as usize].diverging
212 } 222 }
213 223
214 fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { 224 fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty {
215 match kind { 225 match kind {
216 _ if self.inner[iv.to_inner().0 as usize].diverging => TyKind::Never, 226 _ if self.inner[from_inference_var(iv).0 as usize].diverging => TyKind::Never,
217 TyVariableKind::General => TyKind::Unknown, 227 TyVariableKind::General => TyKind::Error,
218 TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)), 228 TyVariableKind::Integer => TyKind::Scalar(Scalar::Int(IntTy::I32)),
219 TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)), 229 TyVariableKind::Float => TyKind::Scalar(Scalar::Float(FloatTy::F64)),
220 } 230 }
@@ -247,7 +257,7 @@ impl InferenceTable {
247 self.type_variable_table.push(TypeVariableData { diverging }); 257 self.type_variable_table.push(TypeVariableData { diverging });
248 let key = self.var_unification_table.new_key(TypeVarValue::Unknown); 258 let key = self.var_unification_table.new_key(TypeVarValue::Unknown);
249 assert_eq!(key.0 as usize, self.type_variable_table.inner.len() - 1); 259 assert_eq!(key.0 as usize, self.type_variable_table.inner.len() - 1);
250 TyKind::InferenceVar(InferenceVar::from_inner(key), kind).intern(&Interner) 260 TyKind::InferenceVar(to_inference_var(key), kind).intern(&Interner)
251 } 261 }
252 262
253 pub(crate) fn new_type_var(&mut self) -> Ty { 263 pub(crate) fn new_type_var(&mut self) -> Ty {
@@ -284,7 +294,7 @@ impl InferenceTable {
284 substs2: &Substitution, 294 substs2: &Substitution,
285 depth: usize, 295 depth: usize,
286 ) -> bool { 296 ) -> bool {
287 substs1.0.iter().zip(substs2.0.iter()).all(|(t1, t2)| { 297 substs1.iter(&Interner).zip(substs2.iter(&Interner)).all(|(t1, t2)| {
288 self.unify_inner(t1.assert_ty_ref(&Interner), t2.assert_ty_ref(&Interner), depth) 298 self.unify_inner(t1.assert_ty_ref(&Interner), t2.assert_ty_ref(&Interner), depth)
289 }) 299 })
290 } 300 }
@@ -305,8 +315,8 @@ impl InferenceTable {
305 (TyKind::Adt(_, substs1), TyKind::Adt(_, substs2)) 315 (TyKind::Adt(_, substs1), TyKind::Adt(_, substs2))
306 | (TyKind::FnDef(_, substs1), TyKind::FnDef(_, substs2)) 316 | (TyKind::FnDef(_, substs1), TyKind::FnDef(_, substs2))
307 | ( 317 | (
308 TyKind::Function(FnPointer { substs: substs1, .. }), 318 TyKind::Function(FnPointer { substitution: FnSubst(substs1), .. }),
309 TyKind::Function(FnPointer { substs: substs2, .. }), 319 TyKind::Function(FnPointer { substitution: FnSubst(substs2), .. }),
310 ) 320 )
311 | (TyKind::Tuple(_, substs1), TyKind::Tuple(_, substs2)) 321 | (TyKind::Tuple(_, substs1), TyKind::Tuple(_, substs2))
312 | (TyKind::OpaqueType(_, substs1), TyKind::OpaqueType(_, substs2)) 322 | (TyKind::OpaqueType(_, substs1), TyKind::OpaqueType(_, substs2))
@@ -314,9 +324,11 @@ impl InferenceTable {
314 | (TyKind::Closure(.., substs1), TyKind::Closure(.., substs2)) => { 324 | (TyKind::Closure(.., substs1), TyKind::Closure(.., substs2)) => {
315 self.unify_substs(substs1, substs2, depth + 1) 325 self.unify_substs(substs1, substs2, depth + 1)
316 } 326 }
317 (TyKind::Ref(_, ty1), TyKind::Ref(_, ty2)) 327 (TyKind::Array(ty1, c1), TyKind::Array(ty2, c2)) if c1 == c2 => {
328 self.unify_inner(ty1, ty2, depth + 1)
329 }
330 (TyKind::Ref(_, _, ty1), TyKind::Ref(_, _, ty2))
318 | (TyKind::Raw(_, ty1), TyKind::Raw(_, ty2)) 331 | (TyKind::Raw(_, ty1), TyKind::Raw(_, ty2))
319 | (TyKind::Array(ty1), TyKind::Array(ty2))
320 | (TyKind::Slice(ty1), TyKind::Slice(ty2)) => self.unify_inner(ty1, ty2, depth + 1), 332 | (TyKind::Slice(ty1), TyKind::Slice(ty2)) => self.unify_inner(ty1, ty2, depth + 1),
321 _ => true, /* we checked equals_ctor already */ 333 _ => true, /* we checked equals_ctor already */
322 } 334 }
@@ -327,7 +339,7 @@ impl InferenceTable {
327 339
328 pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool { 340 pub(super) fn unify_inner_trivial(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool {
329 match (ty1.kind(&Interner), ty2.kind(&Interner)) { 341 match (ty1.kind(&Interner), ty2.kind(&Interner)) {
330 (TyKind::Unknown, _) | (_, TyKind::Unknown) => true, 342 (TyKind::Error, _) | (_, TyKind::Error) => true,
331 343
332 (TyKind::Placeholder(p1), TyKind::Placeholder(p2)) if *p1 == *p2 => true, 344 (TyKind::Placeholder(p1), TyKind::Placeholder(p2)) if *p1 == *p2 => true,
333 345
@@ -364,8 +376,12 @@ impl InferenceTable {
364 == self.type_variable_table.is_diverging(*tv2) => 376 == self.type_variable_table.is_diverging(*tv2) =>
365 { 377 {
366 // both type vars are unknown since we tried to resolve them 378 // both type vars are unknown since we tried to resolve them
367 if !self.var_unification_table.unioned(tv1.to_inner(), tv2.to_inner()) { 379 if !self
368 self.var_unification_table.union(tv1.to_inner(), tv2.to_inner()); 380 .var_unification_table
381 .unioned(from_inference_var(*tv1), from_inference_var(*tv2))
382 {
383 self.var_unification_table
384 .union(from_inference_var(*tv1), from_inference_var(*tv2));
369 self.revision += 1; 385 self.revision += 1;
370 } 386 }
371 true 387 true
@@ -402,7 +418,7 @@ impl InferenceTable {
402 ) => { 418 ) => {
403 // the type var is unknown since we tried to resolve it 419 // the type var is unknown since we tried to resolve it
404 self.var_unification_table.union_value( 420 self.var_unification_table.union_value(
405 tv.to_inner(), 421 from_inference_var(*tv),
406 TypeVarValue::Known(other.clone().intern(&Interner)), 422 TypeVarValue::Known(other.clone().intern(&Interner)),
407 ); 423 );
408 self.revision += 1; 424 self.revision += 1;
@@ -457,7 +473,7 @@ impl InferenceTable {
457 } 473 }
458 match ty.kind(&Interner) { 474 match ty.kind(&Interner) {
459 TyKind::InferenceVar(tv, _) => { 475 TyKind::InferenceVar(tv, _) => {
460 let inner = tv.to_inner(); 476 let inner = from_inference_var(*tv);
461 match self.var_unification_table.inlined_probe_value(inner).known() { 477 match self.var_unification_table.inlined_probe_value(inner).known() {
462 Some(known_ty) => { 478 Some(known_ty) => {
463 // The known_ty can't be a type var itself 479 // The known_ty can't be a type var itself
@@ -478,55 +494,63 @@ impl InferenceTable {
478 /// be resolved as far as possible, i.e. contain no type variables with 494 /// be resolved as far as possible, i.e. contain no type variables with
479 /// known type. 495 /// known type.
480 fn resolve_ty_as_possible_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty { 496 fn resolve_ty_as_possible_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
481 ty.fold(&mut |ty| match ty.kind(&Interner) { 497 fold_tys(
482 &TyKind::InferenceVar(tv, kind) => { 498 ty,
483 let inner = tv.to_inner(); 499 |ty, _| match ty.kind(&Interner) {
484 if tv_stack.contains(&inner) { 500 &TyKind::InferenceVar(tv, kind) => {
485 cov_mark::hit!(type_var_cycles_resolve_as_possible); 501 let inner = from_inference_var(tv);
486 // recursive type 502 if tv_stack.contains(&inner) {
487 return self.type_variable_table.fallback_value(tv, kind); 503 cov_mark::hit!(type_var_cycles_resolve_as_possible);
488 } 504 // recursive type
489 if let Some(known_ty) = 505 return self.type_variable_table.fallback_value(tv, kind);
490 self.var_unification_table.inlined_probe_value(inner).known() 506 }
491 { 507 if let Some(known_ty) =
492 // known_ty may contain other variables that are known by now 508 self.var_unification_table.inlined_probe_value(inner).known()
493 tv_stack.push(inner); 509 {
494 let result = self.resolve_ty_as_possible_inner(tv_stack, known_ty.clone()); 510 // known_ty may contain other variables that are known by now
495 tv_stack.pop(); 511 tv_stack.push(inner);
496 result 512 let result = self.resolve_ty_as_possible_inner(tv_stack, known_ty.clone());
497 } else { 513 tv_stack.pop();
498 ty 514 result
515 } else {
516 ty
517 }
499 } 518 }
500 } 519 _ => ty,
501 _ => ty, 520 },
502 }) 521 DebruijnIndex::INNERMOST,
522 )
503 } 523 }
504 524
505 /// Resolves the type completely; type variables without known type are 525 /// Resolves the type completely; type variables without known type are
506 /// replaced by TyKind::Unknown. 526 /// replaced by TyKind::Unknown.
507 fn resolve_ty_completely_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty { 527 fn resolve_ty_completely_inner(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
508 ty.fold(&mut |ty| match ty.kind(&Interner) { 528 fold_tys(
509 &TyKind::InferenceVar(tv, kind) => { 529 ty,
510 let inner = tv.to_inner(); 530 |ty, _| match ty.kind(&Interner) {
511 if tv_stack.contains(&inner) { 531 &TyKind::InferenceVar(tv, kind) => {
512 cov_mark::hit!(type_var_cycles_resolve_completely); 532 let inner = from_inference_var(tv);
513 // recursive type 533 if tv_stack.contains(&inner) {
514 return self.type_variable_table.fallback_value(tv, kind); 534 cov_mark::hit!(type_var_cycles_resolve_completely);
515 } 535 // recursive type
516 if let Some(known_ty) = 536 return self.type_variable_table.fallback_value(tv, kind);
517 self.var_unification_table.inlined_probe_value(inner).known() 537 }
518 { 538 if let Some(known_ty) =
519 // known_ty may contain other variables that are known by now 539 self.var_unification_table.inlined_probe_value(inner).known()
520 tv_stack.push(inner); 540 {
521 let result = self.resolve_ty_completely_inner(tv_stack, known_ty.clone()); 541 // known_ty may contain other variables that are known by now
522 tv_stack.pop(); 542 tv_stack.push(inner);
523 result 543 let result = self.resolve_ty_completely_inner(tv_stack, known_ty.clone());
524 } else { 544 tv_stack.pop();
525 self.type_variable_table.fallback_value(tv, kind) 545 result
546 } else {
547 self.type_variable_table.fallback_value(tv, kind)
548 }
526 } 549 }
527 } 550 _ => ty,
528 _ => ty, 551 },
529 }) 552 DebruijnIndex::INNERMOST,
553 )
530 } 554 }
531} 555}
532 556
@@ -550,6 +574,14 @@ impl UnifyKey for TypeVarId {
550 } 574 }
551} 575}
552 576
577fn from_inference_var(var: InferenceVar) -> TypeVarId {
578 TypeVarId(var.index())
579}
580
581fn to_inference_var(TypeVarId(index): TypeVarId) -> InferenceVar {
582 index.into()
583}
584
553/// The value of a type variable: either we already know the type, or we don't 585/// The value of a type variable: either we already know the type, or we don't
554/// know it yet. 586/// know it yet.
555#[derive(Clone, PartialEq, Eq, Debug)] 587#[derive(Clone, PartialEq, Eq, Debug)]
diff --git a/crates/hir_ty/src/traits/chalk/interner.rs b/crates/hir_ty/src/interner.rs
index 94e94a26d..a1656115d 100644
--- a/crates/hir_ty/src/traits/chalk/interner.rs
+++ b/crates/hir_ty/src/interner.rs
@@ -1,61 +1,83 @@
1//! Implementation of the Chalk `Interner` trait, which allows customizing the 1//! Implementation of the Chalk `Interner` trait, which allows customizing the
2//! representation of the various objects Chalk deals with (types, goals etc.). 2//! representation of the various objects Chalk deals with (types, goals etc.).
3 3
4use super::tls; 4use crate::{chalk_db, tls, GenericArg};
5use base_db::salsa::InternId; 5use base_db::salsa::InternId;
6use chalk_ir::{GenericArg, Goal, GoalData}; 6use chalk_ir::{Goal, GoalData};
7use hir_def::TypeAliasId; 7use hir_def::{
8 intern::{impl_internable, InternStorage, Internable, Interned},
9 TypeAliasId,
10};
8use smallvec::SmallVec; 11use smallvec::SmallVec;
9use std::{fmt, sync::Arc}; 12use std::{fmt, sync::Arc};
10 13
11#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] 14#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
12pub struct Interner; 15pub struct Interner;
13 16
14pub(crate) type AssocTypeId = chalk_ir::AssocTypeId<Interner>; 17#[derive(PartialEq, Eq, Hash, Debug)]
15pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>; 18pub struct InternedWrapper<T>(T);
16pub(crate) type TraitId = chalk_ir::TraitId<Interner>; 19
17pub(crate) type TraitDatum = chalk_solve::rust_ir::TraitDatum<Interner>; 20impl<T> std::ops::Deref for InternedWrapper<T> {
18pub(crate) type AdtId = chalk_ir::AdtId<Interner>; 21 type Target = T;
19pub(crate) type StructDatum = chalk_solve::rust_ir::AdtDatum<Interner>; 22
20pub(crate) type ImplId = chalk_ir::ImplId<Interner>; 23 fn deref(&self) -> &Self::Target {
21pub(crate) type ImplDatum = chalk_solve::rust_ir::ImplDatum<Interner>; 24 &self.0
22pub(crate) type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId<Interner>; 25 }
23pub(crate) type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>; 26}
24pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; 27
25pub(crate) type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>; 28impl_internable!(
26pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>; 29 InternedWrapper<Vec<chalk_ir::VariableKind<Interner>>>,
27pub(crate) type Variances = chalk_ir::Variances<Interner>; 30 InternedWrapper<SmallVec<[GenericArg; 2]>>,
31 InternedWrapper<chalk_ir::TyData<Interner>>,
32 InternedWrapper<chalk_ir::LifetimeData<Interner>>,
33 InternedWrapper<chalk_ir::ConstData<Interner>>,
34 InternedWrapper<Vec<chalk_ir::CanonicalVarKind<Interner>>>,
35 InternedWrapper<Vec<chalk_ir::ProgramClause<Interner>>>,
36 InternedWrapper<Vec<chalk_ir::QuantifiedWhereClause<Interner>>>,
37 InternedWrapper<Vec<chalk_ir::Variance>>,
38);
28 39
29impl chalk_ir::interner::Interner for Interner { 40impl chalk_ir::interner::Interner for Interner {
30 type InternedType = Arc<chalk_ir::TyData<Self>>; 41 type InternedType = Interned<InternedWrapper<chalk_ir::TyData<Interner>>>;
31 type InternedLifetime = chalk_ir::LifetimeData<Self>; 42 type InternedLifetime = Interned<InternedWrapper<chalk_ir::LifetimeData<Self>>>;
32 type InternedConst = Arc<chalk_ir::ConstData<Self>>; 43 type InternedConst = Interned<InternedWrapper<chalk_ir::ConstData<Self>>>;
33 type InternedConcreteConst = (); 44 type InternedConcreteConst = ();
34 type InternedGenericArg = chalk_ir::GenericArgData<Self>; 45 type InternedGenericArg = chalk_ir::GenericArgData<Self>;
35 type InternedGoal = Arc<GoalData<Self>>; 46 type InternedGoal = Arc<GoalData<Self>>;
36 type InternedGoals = Vec<Goal<Self>>; 47 type InternedGoals = Vec<Goal<Self>>;
37 type InternedSubstitution = SmallVec<[GenericArg<Self>; 2]>; 48 type InternedSubstitution = Interned<InternedWrapper<SmallVec<[GenericArg; 2]>>>;
38 type InternedProgramClause = Arc<chalk_ir::ProgramClauseData<Self>>; 49 type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
39 type InternedProgramClauses = Arc<[chalk_ir::ProgramClause<Self>]>; 50 type InternedProgramClauses = Interned<InternedWrapper<Vec<chalk_ir::ProgramClause<Self>>>>;
40 type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>; 51 type InternedQuantifiedWhereClauses =
41 type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>; 52 Interned<InternedWrapper<Vec<chalk_ir::QuantifiedWhereClause<Self>>>>;
42 type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>; 53 type InternedVariableKinds = Interned<InternedWrapper<Vec<chalk_ir::VariableKind<Interner>>>>;
54 type InternedCanonicalVarKinds =
55 Interned<InternedWrapper<Vec<chalk_ir::CanonicalVarKind<Self>>>>;
43 type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>; 56 type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>;
44 type InternedVariances = Arc<[chalk_ir::Variance]>; 57 type InternedVariances = Interned<InternedWrapper<Vec<chalk_ir::Variance>>>;
45 type DefId = InternId; 58 type DefId = InternId;
46 type InternedAdtId = hir_def::AdtId; 59 type InternedAdtId = hir_def::AdtId;
47 type Identifier = TypeAliasId; 60 type Identifier = TypeAliasId;
48 type FnAbi = (); 61 type FnAbi = ();
49 62
50 fn debug_adt_id(type_kind_id: AdtId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { 63 fn debug_adt_id(
64 type_kind_id: chalk_db::AdtId,
65 fmt: &mut fmt::Formatter<'_>,
66 ) -> Option<fmt::Result> {
51 tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt))) 67 tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt)))
52 } 68 }
53 69
54 fn debug_trait_id(type_kind_id: TraitId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { 70 fn debug_trait_id(
71 type_kind_id: chalk_db::TraitId,
72 fmt: &mut fmt::Formatter<'_>,
73 ) -> Option<fmt::Result> {
55 tls::with_current_program(|prog| Some(prog?.debug_trait_id(type_kind_id, fmt))) 74 tls::with_current_program(|prog| Some(prog?.debug_trait_id(type_kind_id, fmt)))
56 } 75 }
57 76
58 fn debug_assoc_type_id(id: AssocTypeId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { 77 fn debug_assoc_type_id(
78 id: chalk_db::AssocTypeId,
79 fmt: &mut fmt::Formatter<'_>,
80 ) -> Option<fmt::Result> {
59 tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt))) 81 tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt)))
60 } 82 }
61 83
@@ -99,7 +121,7 @@ impl chalk_ir::interner::Interner for Interner {
99 } 121 }
100 122
101 fn debug_generic_arg( 123 fn debug_generic_arg(
102 parameter: &GenericArg<Interner>, 124 parameter: &GenericArg,
103 fmt: &mut fmt::Formatter<'_>, 125 fmt: &mut fmt::Formatter<'_>,
104 ) -> Option<fmt::Result> { 126 ) -> Option<fmt::Result> {
105 tls::with_current_program(|prog| Some(prog?.debug_generic_arg(parameter, fmt))) 127 tls::with_current_program(|prog| Some(prog?.debug_generic_arg(parameter, fmt)))
@@ -192,59 +214,58 @@ impl chalk_ir::interner::Interner for Interner {
192 tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt))) 214 tls::with_current_program(|prog| Some(prog?.debug_quantified_where_clauses(clauses, fmt)))
193 } 215 }
194 216
195 fn intern_ty(&self, kind: chalk_ir::TyKind<Self>) -> Arc<chalk_ir::TyData<Self>> { 217 fn intern_ty(&self, kind: chalk_ir::TyKind<Self>) -> Self::InternedType {
196 let flags = kind.compute_flags(self); 218 let flags = kind.compute_flags(self);
197 Arc::new(chalk_ir::TyData { kind, flags }) 219 Interned::new(InternedWrapper(chalk_ir::TyData { kind, flags }))
198 } 220 }
199 221
200 fn ty_data<'a>(&self, ty: &'a Arc<chalk_ir::TyData<Self>>) -> &'a chalk_ir::TyData<Self> { 222 fn ty_data<'a>(&self, ty: &'a Self::InternedType) -> &'a chalk_ir::TyData<Self> {
201 ty 223 &ty.0
202 } 224 }
203 225
204 fn intern_lifetime( 226 fn intern_lifetime(&self, lifetime: chalk_ir::LifetimeData<Self>) -> Self::InternedLifetime {
205 &self, 227 Interned::new(InternedWrapper(lifetime))
206 lifetime: chalk_ir::LifetimeData<Self>,
207 ) -> chalk_ir::LifetimeData<Self> {
208 lifetime
209 } 228 }
210 229
211 fn lifetime_data<'a>( 230 fn lifetime_data<'a>(
212 &self, 231 &self,
213 lifetime: &'a chalk_ir::LifetimeData<Self>, 232 lifetime: &'a Self::InternedLifetime,
214 ) -> &'a chalk_ir::LifetimeData<Self> { 233 ) -> &'a chalk_ir::LifetimeData<Self> {
215 lifetime 234 &lifetime.0
216 } 235 }
217 236
218 fn intern_const(&self, constant: chalk_ir::ConstData<Self>) -> Arc<chalk_ir::ConstData<Self>> { 237 fn intern_const(&self, constant: chalk_ir::ConstData<Self>) -> Self::InternedConst {
219 Arc::new(constant) 238 Interned::new(InternedWrapper(constant))
220 } 239 }
221 240
222 fn const_data<'a>( 241 fn const_data<'a>(&self, constant: &'a Self::InternedConst) -> &'a chalk_ir::ConstData<Self> {
223 &self, 242 &constant.0
224 constant: &'a Arc<chalk_ir::ConstData<Self>>,
225 ) -> &'a chalk_ir::ConstData<Self> {
226 constant
227 } 243 }
228 244
229 fn const_eq(&self, _ty: &Arc<chalk_ir::TyData<Self>>, _c1: &(), _c2: &()) -> bool { 245 fn const_eq(
246 &self,
247 _ty: &Self::InternedType,
248 _c1: &Self::InternedConcreteConst,
249 _c2: &Self::InternedConcreteConst,
250 ) -> bool {
230 true 251 true
231 } 252 }
232 253
233 fn intern_generic_arg( 254 fn intern_generic_arg(
234 &self, 255 &self,
235 parameter: chalk_ir::GenericArgData<Self>, 256 parameter: chalk_ir::GenericArgData<Self>,
236 ) -> chalk_ir::GenericArgData<Self> { 257 ) -> Self::InternedGenericArg {
237 parameter 258 parameter
238 } 259 }
239 260
240 fn generic_arg_data<'a>( 261 fn generic_arg_data<'a>(
241 &self, 262 &self,
242 parameter: &'a chalk_ir::GenericArgData<Self>, 263 parameter: &'a Self::InternedGenericArg,
243 ) -> &'a chalk_ir::GenericArgData<Self> { 264 ) -> &'a chalk_ir::GenericArgData<Self> {
244 parameter 265 parameter
245 } 266 }
246 267
247 fn intern_goal(&self, goal: GoalData<Self>) -> Arc<GoalData<Self>> { 268 fn intern_goal(&self, goal: GoalData<Self>) -> Self::InternedGoal {
248 Arc::new(goal) 269 Arc::new(goal)
249 } 270 }
250 271
@@ -255,38 +276,38 @@ impl chalk_ir::interner::Interner for Interner {
255 data.into_iter().collect() 276 data.into_iter().collect()
256 } 277 }
257 278
258 fn goal_data<'a>(&self, goal: &'a Arc<GoalData<Self>>) -> &'a GoalData<Self> { 279 fn goal_data<'a>(&self, goal: &'a Self::InternedGoal) -> &'a GoalData<Self> {
259 goal 280 goal
260 } 281 }
261 282
262 fn goals_data<'a>(&self, goals: &'a Vec<Goal<Interner>>) -> &'a [Goal<Interner>] { 283 fn goals_data<'a>(&self, goals: &'a Self::InternedGoals) -> &'a [Goal<Interner>] {
263 goals 284 goals
264 } 285 }
265 286
266 fn intern_substitution<E>( 287 fn intern_substitution<E>(
267 &self, 288 &self,
268 data: impl IntoIterator<Item = Result<GenericArg<Self>, E>>, 289 data: impl IntoIterator<Item = Result<GenericArg, E>>,
269 ) -> Result<Self::InternedSubstitution, E> { 290 ) -> Result<Self::InternedSubstitution, E> {
270 data.into_iter().collect() 291 Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?)))
271 } 292 }
272 293
273 fn substitution_data<'a>( 294 fn substitution_data<'a>(
274 &self, 295 &self,
275 substitution: &'a Self::InternedSubstitution, 296 substitution: &'a Self::InternedSubstitution,
276 ) -> &'a [GenericArg<Self>] { 297 ) -> &'a [GenericArg] {
277 substitution 298 &substitution.as_ref().0
278 } 299 }
279 300
280 fn intern_program_clause( 301 fn intern_program_clause(
281 &self, 302 &self,
282 data: chalk_ir::ProgramClauseData<Self>, 303 data: chalk_ir::ProgramClauseData<Self>,
283 ) -> Arc<chalk_ir::ProgramClauseData<Self>> { 304 ) -> Self::InternedProgramClause {
284 Arc::new(data) 305 data
285 } 306 }
286 307
287 fn program_clause_data<'a>( 308 fn program_clause_data<'a>(
288 &self, 309 &self,
289 clause: &'a Arc<chalk_ir::ProgramClauseData<Self>>, 310 clause: &'a Self::InternedProgramClause,
290 ) -> &'a chalk_ir::ProgramClauseData<Self> { 311 ) -> &'a chalk_ir::ProgramClauseData<Self> {
291 clause 312 clause
292 } 313 }
@@ -294,13 +315,13 @@ impl chalk_ir::interner::Interner for Interner {
294 fn intern_program_clauses<E>( 315 fn intern_program_clauses<E>(
295 &self, 316 &self,
296 data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>, 317 data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>,
297 ) -> Result<Arc<[chalk_ir::ProgramClause<Self>]>, E> { 318 ) -> Result<Self::InternedProgramClauses, E> {
298 data.into_iter().collect() 319 Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?)))
299 } 320 }
300 321
301 fn program_clauses_data<'a>( 322 fn program_clauses_data<'a>(
302 &self, 323 &self,
303 clauses: &'a Arc<[chalk_ir::ProgramClause<Self>]>, 324 clauses: &'a Self::InternedProgramClauses,
304 ) -> &'a [chalk_ir::ProgramClause<Self>] { 325 ) -> &'a [chalk_ir::ProgramClause<Self>] {
305 &clauses 326 &clauses
306 } 327 }
@@ -309,7 +330,7 @@ impl chalk_ir::interner::Interner for Interner {
309 &self, 330 &self,
310 data: impl IntoIterator<Item = Result<chalk_ir::QuantifiedWhereClause<Self>, E>>, 331 data: impl IntoIterator<Item = Result<chalk_ir::QuantifiedWhereClause<Self>, E>>,
311 ) -> Result<Self::InternedQuantifiedWhereClauses, E> { 332 ) -> Result<Self::InternedQuantifiedWhereClauses, E> {
312 data.into_iter().collect() 333 Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?)))
313 } 334 }
314 335
315 fn quantified_where_clauses_data<'a>( 336 fn quantified_where_clauses_data<'a>(
@@ -323,21 +344,21 @@ impl chalk_ir::interner::Interner for Interner {
323 &self, 344 &self,
324 data: impl IntoIterator<Item = Result<chalk_ir::VariableKind<Self>, E>>, 345 data: impl IntoIterator<Item = Result<chalk_ir::VariableKind<Self>, E>>,
325 ) -> Result<Self::InternedVariableKinds, E> { 346 ) -> Result<Self::InternedVariableKinds, E> {
326 data.into_iter().collect() 347 Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?)))
327 } 348 }
328 349
329 fn variable_kinds_data<'a>( 350 fn variable_kinds_data<'a>(
330 &self, 351 &self,
331 parameter_kinds: &'a Self::InternedVariableKinds, 352 parameter_kinds: &'a Self::InternedVariableKinds,
332 ) -> &'a [chalk_ir::VariableKind<Self>] { 353 ) -> &'a [chalk_ir::VariableKind<Self>] {
333 &parameter_kinds 354 &parameter_kinds.as_ref().0
334 } 355 }
335 356
336 fn intern_canonical_var_kinds<E>( 357 fn intern_canonical_var_kinds<E>(
337 &self, 358 &self,
338 data: impl IntoIterator<Item = Result<chalk_ir::CanonicalVarKind<Self>, E>>, 359 data: impl IntoIterator<Item = Result<chalk_ir::CanonicalVarKind<Self>, E>>,
339 ) -> Result<Self::InternedCanonicalVarKinds, E> { 360 ) -> Result<Self::InternedCanonicalVarKinds, E> {
340 data.into_iter().collect() 361 Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?)))
341 } 362 }
342 363
343 fn canonical_var_kinds_data<'a>( 364 fn canonical_var_kinds_data<'a>(
@@ -377,7 +398,7 @@ impl chalk_ir::interner::Interner for Interner {
377 &self, 398 &self,
378 data: impl IntoIterator<Item = Result<chalk_ir::Variance, E>>, 399 data: impl IntoIterator<Item = Result<chalk_ir::Variance, E>>,
379 ) -> Result<Self::InternedVariances, E> { 400 ) -> Result<Self::InternedVariances, E> {
380 data.into_iter().collect() 401 Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?)))
381 } 402 }
382 403
383 fn variances_data<'a>( 404 fn variances_data<'a>(
@@ -391,3 +412,12 @@ impl chalk_ir::interner::Interner for Interner {
391impl chalk_ir::interner::HasInterner for Interner { 412impl chalk_ir::interner::HasInterner for Interner {
392 type Interner = Self; 413 type Interner = Self;
393} 414}
415
416#[macro_export]
417macro_rules! has_interner {
418 ($t:ty) => {
419 impl HasInterner for $t {
420 type Interner = crate::Interner;
421 }
422 };
423}
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index a8c87eadf..0505fa4ae 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -1,65 +1,68 @@
1//! The type system. We currently use this to infer types for completion, hover 1//! The type system. We currently use this to infer types for completion, hover
2//! information and various assists. 2//! information and various assists.
3
3#[allow(unused)] 4#[allow(unused)]
4macro_rules! eprintln { 5macro_rules! eprintln {
5 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; 6 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
6} 7}
7 8
8mod autoderef; 9mod autoderef;
9pub mod primitive;
10pub mod traits;
11pub mod method_resolution;
12mod op;
13mod lower;
14pub(crate) mod infer;
15pub(crate) mod utils;
16mod chalk_cast;
17mod chalk_ext;
18mod builder; 10mod builder;
19 11mod chalk_db;
20pub mod display; 12mod chalk_ext;
13mod infer;
14mod interner;
15mod lower;
16mod mapping;
17mod op;
18mod tls;
19mod utils;
20mod walk;
21pub mod db; 21pub mod db;
22pub mod diagnostics; 22pub mod diagnostics;
23pub mod display;
24pub mod method_resolution;
25pub mod primitive;
26pub mod traits;
23 27
24#[cfg(test)] 28#[cfg(test)]
25mod tests; 29mod tests;
26#[cfg(test)] 30#[cfg(test)]
27mod test_db; 31mod test_db;
28 32
29use std::{mem, sync::Arc}; 33use std::sync::Arc;
30
31use chalk_ir::cast::{CastTo, Caster};
32use itertools::Itertools;
33use smallvec::SmallVec;
34 34
35use base_db::salsa; 35use chalk_ir::{
36use hir_def::{ 36 fold::{Fold, Shift},
37 expr::ExprId, type_ref::Rawness, AssocContainerId, FunctionId, GenericDefId, HasModule, 37 interner::HasInterner,
38 LifetimeParamId, Lookup, TraitId, TypeAliasId, TypeParamId, 38 UintTy,
39}; 39};
40use hir_def::{expr::ExprId, type_ref::Rawness, TypeParamId};
40 41
41use crate::{ 42use crate::{db::HirDatabase, display::HirDisplay, utils::generics};
42 db::HirDatabase,
43 display::HirDisplay,
44 utils::{generics, make_mut_slice},
45};
46 43
47pub use autoderef::autoderef; 44pub use autoderef::autoderef;
48pub use builder::TyBuilder; 45pub use builder::TyBuilder;
49pub use chalk_ext::TyExt; 46pub use chalk_ext::*;
50pub use infer::{could_unify, InferenceResult, InferenceVar}; 47pub use infer::{could_unify, InferenceResult};
48pub use interner::Interner;
51pub use lower::{ 49pub use lower::{
52 associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, 50 associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,
53 TyDefId, TyLoweringContext, ValueTyDefId, 51 TyDefId, TyLoweringContext, ValueTyDefId,
54}; 52};
55pub use traits::{AliasEq, DomainGoal, InEnvironment, TraitEnvironment}; 53pub use mapping::{
54 const_from_placeholder_idx, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
55 from_placeholder_idx, lt_from_placeholder_idx, to_assoc_type_id, to_chalk_trait_id,
56 to_foreign_def_id, to_placeholder_idx,
57};
58pub use traits::TraitEnvironment;
59pub use utils::all_super_traits;
60pub use walk::TypeWalk;
56 61
57pub use chalk_ir::{ 62pub use chalk_ir::{
58 cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind, 63 cast::Cast, AdtId, BoundVar, DebruijnIndex, Mutability, Safety, Scalar, TyVariableKind,
59}; 64};
60 65
61pub use crate::traits::chalk::Interner;
62
63pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>; 66pub type ForeignDefId = chalk_ir::ForeignDefId<Interner>;
64pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>; 67pub type AssocTypeId = chalk_ir::AssocTypeId<Interner>;
65pub type FnDefId = chalk_ir::FnDefId<Interner>; 68pub type FnDefId = chalk_ir::FnDefId<Interner>;
@@ -67,401 +70,56 @@ pub type ClosureId = chalk_ir::ClosureId<Interner>;
67pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>; 70pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>;
68pub type PlaceholderIndex = chalk_ir::PlaceholderIndex; 71pub type PlaceholderIndex = chalk_ir::PlaceholderIndex;
69 72
73pub type VariableKind = chalk_ir::VariableKind<Interner>;
74pub type VariableKinds = chalk_ir::VariableKinds<Interner>;
70pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds<Interner>; 75pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds<Interner>;
76pub type Binders<T> = chalk_ir::Binders<T>;
77pub type Substitution = chalk_ir::Substitution<Interner>;
78pub type GenericArg = chalk_ir::GenericArg<Interner>;
79pub type GenericArgData = chalk_ir::GenericArgData<Interner>;
80
81pub type Ty = chalk_ir::Ty<Interner>;
82pub type TyKind = chalk_ir::TyKind<Interner>;
83pub type DynTy = chalk_ir::DynTy<Interner>;
84pub type FnPointer = chalk_ir::FnPointer<Interner>;
85// pub type FnSubst = chalk_ir::FnSubst<Interner>;
86pub use chalk_ir::FnSubst;
87pub type ProjectionTy = chalk_ir::ProjectionTy<Interner>;
88pub type AliasTy = chalk_ir::AliasTy<Interner>;
89pub type OpaqueTy = chalk_ir::OpaqueTy<Interner>;
90pub type InferenceVar = chalk_ir::InferenceVar;
91
92pub type Lifetime = chalk_ir::Lifetime<Interner>;
93pub type LifetimeData = chalk_ir::LifetimeData<Interner>;
94pub type LifetimeOutlives = chalk_ir::LifetimeOutlives<Interner>;
95
96pub type Const = chalk_ir::Const<Interner>;
97pub type ConstData = chalk_ir::ConstData<Interner>;
98pub type ConstValue = chalk_ir::ConstValue<Interner>;
99pub type ConcreteConst = chalk_ir::ConcreteConst<Interner>;
71 100
72pub type ChalkTraitId = chalk_ir::TraitId<Interner>; 101pub type ChalkTraitId = chalk_ir::TraitId<Interner>;
73 102pub type TraitRef = chalk_ir::TraitRef<Interner>;
74#[derive(Clone, PartialEq, Eq, Debug, Hash)] 103pub type QuantifiedWhereClause = Binders<WhereClause>;
75pub enum Lifetime { 104pub type QuantifiedWhereClauses = chalk_ir::QuantifiedWhereClauses<Interner>;
76 Parameter(LifetimeParamId), 105pub type Canonical<T> = chalk_ir::Canonical<T>;
77 Static,
78}
79
80#[derive(Clone, PartialEq, Eq, Debug, Hash)]
81pub struct OpaqueTy {
82 pub opaque_ty_id: OpaqueTyId,
83 pub substitution: Substitution,
84}
85
86impl TypeWalk for OpaqueTy {
87 fn walk(&self, f: &mut impl FnMut(&Ty)) {
88 self.substitution.walk(f);
89 }
90
91 fn walk_mut_binders(
92 &mut self,
93 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
94 binders: DebruijnIndex,
95 ) {
96 self.substitution.walk_mut_binders(f, binders);
97 }
98}
99
100/// A "projection" type corresponds to an (unnormalized)
101/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
102/// trait and all its parameters are fully known.
103#[derive(Clone, PartialEq, Eq, Debug, Hash)]
104pub struct ProjectionTy {
105 pub associated_ty_id: AssocTypeId,
106 pub substitution: Substitution,
107}
108
109impl ProjectionTy {
110 pub fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef {
111 TraitRef {
112 trait_id: to_chalk_trait_id(self.trait_(db)),
113 substitution: self.substitution.clone(),
114 }
115 }
116
117 pub fn self_type_parameter(&self) -> &Ty {
118 &self.substitution.interned(&Interner)[0].assert_ty_ref(&Interner)
119 }
120
121 fn trait_(&self, db: &dyn HirDatabase) -> TraitId {
122 match from_assoc_type_id(self.associated_ty_id).lookup(db.upcast()).container {
123 AssocContainerId::TraitId(it) => it,
124 _ => panic!("projection ty without parent trait"),
125 }
126 }
127}
128
129impl TypeWalk for ProjectionTy {
130 fn walk(&self, f: &mut impl FnMut(&Ty)) {
131 self.substitution.walk(f);
132 }
133
134 fn walk_mut_binders(
135 &mut self,
136 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
137 binders: DebruijnIndex,
138 ) {
139 self.substitution.walk_mut_binders(f, binders);
140 }
141}
142
143#[derive(Clone, PartialEq, Eq, Debug, Hash)]
144pub struct DynTy {
145 /// The unknown self type.
146 pub bounds: Binders<QuantifiedWhereClauses>,
147}
148 106
149pub type FnSig = chalk_ir::FnSig<Interner>; 107pub type FnSig = chalk_ir::FnSig<Interner>;
150 108
151#[derive(Clone, PartialEq, Eq, Debug, Hash)] 109pub type InEnvironment<T> = chalk_ir::InEnvironment<T>;
152pub struct FnPointer { 110pub type DomainGoal = chalk_ir::DomainGoal<Interner>;
153 pub num_args: usize, 111pub type AliasEq = chalk_ir::AliasEq<Interner>;
154 pub sig: FnSig, 112pub type Solution = chalk_solve::Solution<Interner>;
155 pub substs: Substitution, 113pub type ConstrainedSubst = chalk_ir::ConstrainedSubst<Interner>;
156} 114pub type Guidance = chalk_solve::Guidance<Interner>;
157 115pub type WhereClause = chalk_ir::WhereClause<Interner>;
158#[derive(Clone, PartialEq, Eq, Debug, Hash)]
159pub enum AliasTy {
160 /// A "projection" type corresponds to an (unnormalized)
161 /// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
162 /// trait and all its parameters are fully known.
163 Projection(ProjectionTy),
164 /// An opaque type (`impl Trait`).
165 ///
166 /// This is currently only used for return type impl trait; each instance of
167 /// `impl Trait` in a return type gets its own ID.
168 Opaque(OpaqueTy),
169}
170
171impl TypeWalk for AliasTy {
172 fn walk(&self, f: &mut impl FnMut(&Ty)) {
173 match self {
174 AliasTy::Projection(it) => it.walk(f),
175 AliasTy::Opaque(it) => it.walk(f),
176 }
177 }
178 116
179 fn walk_mut_binders( 117// FIXME: get rid of this
180 &mut self, 118pub fn subst_prefix(s: &Substitution, n: usize) -> Substitution {
181 f: &mut impl FnMut(&mut Ty, DebruijnIndex), 119 Substitution::from_iter(
182 binders: DebruijnIndex, 120 &Interner,
183 ) { 121 s.as_slice(&Interner)[..std::cmp::min(s.len(&Interner), n)].iter().cloned(),
184 match self { 122 )
185 AliasTy::Projection(it) => it.walk_mut_binders(f, binders),
186 AliasTy::Opaque(it) => it.walk_mut_binders(f, binders),
187 }
188 }
189}
190/// A type.
191///
192/// See also the `TyKind` enum in rustc (librustc/ty/sty.rs), which represents
193/// the same thing (but in a different way).
194///
195/// This should be cheap to clone.
196#[derive(Clone, PartialEq, Eq, Debug, Hash)]
197pub enum TyKind {
198 /// Structures, enumerations and unions.
199 Adt(AdtId<Interner>, Substitution),
200
201 /// Represents an associated item like `Iterator::Item`. This is used
202 /// when we have tried to normalize a projection like `T::Item` but
203 /// couldn't find a better representation. In that case, we generate
204 /// an **application type** like `(Iterator::Item)<T>`.
205 AssociatedType(AssocTypeId, Substitution),
206
207 /// a scalar type like `bool` or `u32`
208 Scalar(Scalar),
209
210 /// A tuple type. For example, `(i32, bool)`.
211 Tuple(usize, Substitution),
212
213 /// An array with the given length. Written as `[T; n]`.
214 Array(Ty),
215
216 /// The pointee of an array slice. Written as `[T]`.
217 Slice(Ty),
218
219 /// A raw pointer. Written as `*mut T` or `*const T`
220 Raw(Mutability, Ty),
221
222 /// A reference; a pointer with an associated lifetime. Written as
223 /// `&'a mut T` or `&'a T`.
224 Ref(Mutability, Ty),
225
226 /// This represents a placeholder for an opaque type in situations where we
227 /// don't know the hidden type (i.e. currently almost always). This is
228 /// analogous to the `AssociatedType` type constructor.
229 /// It is also used as the type of async block, with one type parameter
230 /// representing the Future::Output type.
231 OpaqueType(OpaqueTyId, Substitution),
232
233 /// The anonymous type of a function declaration/definition. Each
234 /// function has a unique type, which is output (for a function
235 /// named `foo` returning an `i32`) as `fn() -> i32 {foo}`.
236 ///
237 /// This includes tuple struct / enum variant constructors as well.
238 ///
239 /// For example the type of `bar` here:
240 ///
241 /// ```
242 /// fn foo() -> i32 { 1 }
243 /// let bar = foo; // bar: fn() -> i32 {foo}
244 /// ```
245 FnDef(FnDefId, Substitution),
246
247 /// The pointee of a string slice. Written as `str`.
248 Str,
249
250 /// The never type `!`.
251 Never,
252
253 /// The type of a specific closure.
254 ///
255 /// The closure signature is stored in a `FnPtr` type in the first type
256 /// parameter.
257 Closure(ClosureId, Substitution),
258
259 /// Represents a foreign type declared in external blocks.
260 ForeignType(ForeignDefId),
261
262 /// A pointer to a function. Written as `fn() -> i32`.
263 ///
264 /// For example the type of `bar` here:
265 ///
266 /// ```
267 /// fn foo() -> i32 { 1 }
268 /// let bar: fn() -> i32 = foo;
269 /// ```
270 Function(FnPointer),
271
272 /// An "alias" type represents some form of type alias, such as:
273 /// - An associated type projection like `<T as Iterator>::Item`
274 /// - `impl Trait` types
275 /// - Named type aliases like `type Foo<X> = Vec<X>`
276 Alias(AliasTy),
277
278 /// A placeholder for a type parameter; for example, `T` in `fn f<T>(x: T)
279 /// {}` when we're type-checking the body of that function. In this
280 /// situation, we know this stands for *some* type, but don't know the exact
281 /// type.
282 Placeholder(PlaceholderIndex),
283
284 /// A bound type variable. This is used in various places: when representing
285 /// some polymorphic type like the type of function `fn f<T>`, the type
286 /// parameters get turned into variables; during trait resolution, inference
287 /// variables get turned into bound variables and back; and in `Dyn` the
288 /// `Self` type is represented with a bound variable as well.
289 BoundVar(BoundVar),
290
291 /// A type variable used during type checking.
292 InferenceVar(InferenceVar, TyVariableKind),
293
294 /// A trait object (`dyn Trait` or bare `Trait` in pre-2018 Rust).
295 ///
296 /// The predicates are quantified over the `Self` type, i.e. `Ty::Bound(0)`
297 /// represents the `Self` type inside the bounds. This is currently
298 /// implicit; Chalk has the `Binders` struct to make it explicit, but it
299 /// didn't seem worth the overhead yet.
300 Dyn(DynTy),
301
302 /// A placeholder for a type which could not be computed; this is propagated
303 /// to avoid useless error messages. Doubles as a placeholder where type
304 /// variables are inserted before type checking, since we want to try to
305 /// infer a better type here anyway -- for the IDE use case, we want to try
306 /// to infer as much as possible even in the presence of type errors.
307 Unknown,
308}
309
310#[derive(Clone, PartialEq, Eq, Debug, Hash)]
311pub struct Ty(Arc<TyKind>);
312
313impl TyKind {
314 pub fn intern(self, _interner: &Interner) -> Ty {
315 Ty(Arc::new(self))
316 }
317}
318
319impl Ty {
320 pub fn kind(&self, _interner: &Interner) -> &TyKind {
321 &self.0
322 }
323
324 pub fn interned_mut(&mut self) -> &mut TyKind {
325 Arc::make_mut(&mut self.0)
326 }
327
328 pub fn into_inner(self) -> TyKind {
329 Arc::try_unwrap(self.0).unwrap_or_else(|a| (*a).clone())
330 }
331}
332
333#[derive(Clone, PartialEq, Eq, Debug, Hash)]
334pub struct GenericArg {
335 interned: GenericArgData,
336}
337
338#[derive(Clone, PartialEq, Eq, Debug, Hash)]
339pub enum GenericArgData {
340 Ty(Ty),
341}
342
343impl GenericArg {
344 /// Constructs a generic argument using `GenericArgData`.
345 pub fn new(_interner: &Interner, data: GenericArgData) -> Self {
346 GenericArg { interned: data }
347 }
348
349 /// Gets the interned value.
350 pub fn interned(&self) -> &GenericArgData {
351 &self.interned
352 }
353
354 /// Asserts that this is a type argument.
355 pub fn assert_ty_ref(&self, interner: &Interner) -> &Ty {
356 self.ty(interner).unwrap()
357 }
358
359 /// Checks whether the generic argument is a type.
360 pub fn is_ty(&self, _interner: &Interner) -> bool {
361 match self.interned() {
362 GenericArgData::Ty(_) => true,
363 }
364 }
365
366 /// Returns the type if it is one, `None` otherwise.
367 pub fn ty(&self, _interner: &Interner) -> Option<&Ty> {
368 match self.interned() {
369 GenericArgData::Ty(t) => Some(t),
370 }
371 }
372}
373
374impl TypeWalk for GenericArg {
375 fn walk(&self, f: &mut impl FnMut(&Ty)) {
376 match &self.interned {
377 GenericArgData::Ty(ty) => {
378 ty.walk(f);
379 }
380 }
381 }
382
383 fn walk_mut_binders(
384 &mut self,
385 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
386 binders: DebruijnIndex,
387 ) {
388 match &mut self.interned {
389 GenericArgData::Ty(ty) => {
390 ty.walk_mut_binders(f, binders);
391 }
392 }
393 }
394}
395
396/// A list of substitutions for generic parameters.
397#[derive(Clone, PartialEq, Eq, Debug, Hash)]
398pub struct Substitution(SmallVec<[GenericArg; 2]>);
399
400impl TypeWalk for Substitution {
401 fn walk(&self, f: &mut impl FnMut(&Ty)) {
402 for t in self.0.iter() {
403 t.walk(f);
404 }
405 }
406
407 fn walk_mut_binders(
408 &mut self,
409 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
410 binders: DebruijnIndex,
411 ) {
412 for t in &mut self.0 {
413 t.walk_mut_binders(f, binders);
414 }
415 }
416}
417
418impl Substitution {
419 pub fn interned(&self, _: &Interner) -> &[GenericArg] {
420 &self.0
421 }
422
423 pub fn len(&self, _: &Interner) -> usize {
424 self.0.len()
425 }
426
427 pub fn is_empty(&self, _: &Interner) -> bool {
428 self.0.is_empty()
429 }
430
431 pub fn at(&self, _: &Interner, i: usize) -> &GenericArg {
432 &self.0[i]
433 }
434
435 pub fn empty(_: &Interner) -> Substitution {
436 Substitution(SmallVec::new())
437 }
438
439 pub fn iter(&self, _: &Interner) -> std::slice::Iter<'_, GenericArg> {
440 self.0.iter()
441 }
442
443 pub fn single(ty: Ty) -> Substitution {
444 Substitution({
445 let mut v = SmallVec::new();
446 v.push(ty.cast(&Interner));
447 v
448 })
449 }
450
451 pub fn prefix(&self, n: usize) -> Substitution {
452 Substitution(self.0[..std::cmp::min(self.0.len(), n)].into())
453 }
454
455 pub fn suffix(&self, n: usize) -> Substitution {
456 Substitution(self.0[self.0.len() - std::cmp::min(self.0.len(), n)..].into())
457 }
458
459 pub fn from_iter(
460 interner: &Interner,
461 elements: impl IntoIterator<Item = impl CastTo<GenericArg>>,
462 ) -> Self {
463 Substitution(elements.into_iter().casted(interner).collect())
464 }
465} 123}
466 124
467/// Return an index of a parameter in the generic type parameter list by it's id. 125/// Return an index of a parameter in the generic type parameter list by it's id.
@@ -469,190 +127,39 @@ pub fn param_idx(db: &dyn HirDatabase, id: TypeParamId) -> Option<usize> {
469 generics(db.upcast(), id.parent).param_idx(id) 127 generics(db.upcast(), id.parent).param_idx(id)
470} 128}
471 129
472#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] 130pub(crate) fn wrap_empty_binders<T>(value: T) -> Binders<T>
473pub struct Binders<T> { 131where
474 pub num_binders: usize, 132 T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>,
475 pub value: T, 133{
476} 134 Binders::empty(&Interner, value.shifted_in_from(&Interner, DebruijnIndex::ONE))
477 135}
478impl<T> Binders<T> { 136
479 pub fn new(num_binders: usize, value: T) -> Self { 137pub(crate) fn make_only_type_binders<T: HasInterner<Interner = Interner>>(
480 Self { num_binders, value } 138 num_vars: usize,
481 } 139 value: T,
482 140) -> Binders<T> {
483 pub fn wrap_empty(value: T) -> Self 141 Binders::new(
484 where 142 VariableKinds::from_iter(
485 T: TypeWalk, 143 &Interner,
486 { 144 std::iter::repeat(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General))
487 Self { num_binders: 0, value: value.shift_bound_vars(DebruijnIndex::ONE) } 145 .take(num_vars),
488 } 146 ),
489 147 value,
490 pub fn as_ref(&self) -> Binders<&T> { 148 )
491 Binders { num_binders: self.num_binders, value: &self.value } 149}
492 } 150
493 151// FIXME: get rid of this
494 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Binders<U> { 152pub fn make_canonical<T: HasInterner<Interner = Interner>>(
495 Binders { num_binders: self.num_binders, value: f(self.value) } 153 value: T,
496 } 154 kinds: impl IntoIterator<Item = TyVariableKind>,
497 155) -> Canonical<T> {
498 pub fn filter_map<U>(self, f: impl FnOnce(T) -> Option<U>) -> Option<Binders<U>> { 156 let kinds = kinds.into_iter().map(|tk| {
499 Some(Binders { num_binders: self.num_binders, value: f(self.value)? }) 157 chalk_ir::CanonicalVarKind::new(
500 } 158 chalk_ir::VariableKind::Ty(tk),
501 159 chalk_ir::UniverseIndex::ROOT,
502 pub fn skip_binders(&self) -> &T { 160 )
503 &self.value 161 });
504 } 162 Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) }
505
506 pub fn into_value_and_skipped_binders(self) -> (T, usize) {
507 (self.value, self.num_binders)
508 }
509}
510
511impl<T: Clone> Binders<&T> {
512 pub fn cloned(&self) -> Binders<T> {
513 Binders { num_binders: self.num_binders, value: self.value.clone() }
514 }
515}
516
517impl<T: TypeWalk> Binders<T> {
518 /// Substitutes all variables.
519 pub fn subst(self, subst: &Substitution) -> T {
520 assert_eq!(subst.len(&Interner), self.num_binders);
521 self.value.subst_bound_vars(subst)
522 }
523}
524
525impl<T: TypeWalk> TypeWalk for Binders<T> {
526 fn walk(&self, f: &mut impl FnMut(&Ty)) {
527 self.value.walk(f);
528 }
529
530 fn walk_mut_binders(
531 &mut self,
532 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
533 binders: DebruijnIndex,
534 ) {
535 self.value.walk_mut_binders(f, binders.shifted_in())
536 }
537}
538
539/// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait.
540#[derive(Clone, PartialEq, Eq, Debug, Hash)]
541pub struct TraitRef {
542 pub trait_id: ChalkTraitId,
543 pub substitution: Substitution,
544}
545
546impl TraitRef {
547 pub fn self_type_parameter(&self) -> &Ty {
548 &self.substitution.at(&Interner, 0).assert_ty_ref(&Interner)
549 }
550
551 pub fn hir_trait_id(&self) -> TraitId {
552 from_chalk_trait_id(self.trait_id)
553 }
554}
555
556impl TypeWalk for TraitRef {
557 fn walk(&self, f: &mut impl FnMut(&Ty)) {
558 self.substitution.walk(f);
559 }
560
561 fn walk_mut_binders(
562 &mut self,
563 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
564 binders: DebruijnIndex,
565 ) {
566 self.substitution.walk_mut_binders(f, binders);
567 }
568}
569
570/// Like `generics::WherePredicate`, but with resolved types: A condition on the
571/// parameters of a generic item.
572#[derive(Debug, Clone, PartialEq, Eq, Hash)]
573pub enum WhereClause {
574 /// The given trait needs to be implemented for its type parameters.
575 Implemented(TraitRef),
576 /// An associated type bindings like in `Iterator<Item = T>`.
577 AliasEq(AliasEq),
578}
579
580impl WhereClause {
581 pub fn is_implemented(&self) -> bool {
582 matches!(self, WhereClause::Implemented(_))
583 }
584
585 pub fn trait_ref(&self, db: &dyn HirDatabase) -> Option<TraitRef> {
586 match self {
587 WhereClause::Implemented(tr) => Some(tr.clone()),
588 WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), .. }) => {
589 Some(proj.trait_ref(db))
590 }
591 WhereClause::AliasEq(_) => None,
592 }
593 }
594}
595
596impl TypeWalk for WhereClause {
597 fn walk(&self, f: &mut impl FnMut(&Ty)) {
598 match self {
599 WhereClause::Implemented(trait_ref) => trait_ref.walk(f),
600 WhereClause::AliasEq(alias_eq) => alias_eq.walk(f),
601 }
602 }
603
604 fn walk_mut_binders(
605 &mut self,
606 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
607 binders: DebruijnIndex,
608 ) {
609 match self {
610 WhereClause::Implemented(trait_ref) => trait_ref.walk_mut_binders(f, binders),
611 WhereClause::AliasEq(alias_eq) => alias_eq.walk_mut_binders(f, binders),
612 }
613 }
614}
615
616pub type QuantifiedWhereClause = Binders<WhereClause>;
617
618#[derive(Debug, Clone, PartialEq, Eq, Hash)]
619pub struct QuantifiedWhereClauses(Arc<[QuantifiedWhereClause]>);
620
621impl QuantifiedWhereClauses {
622 pub fn from_iter(
623 _interner: &Interner,
624 elements: impl IntoIterator<Item = QuantifiedWhereClause>,
625 ) -> Self {
626 QuantifiedWhereClauses(elements.into_iter().collect())
627 }
628
629 pub fn interned(&self) -> &Arc<[QuantifiedWhereClause]> {
630 &self.0
631 }
632}
633
634/// Basically a claim (currently not validated / checked) that the contained
635/// type / trait ref contains no inference variables; any inference variables it
636/// contained have been replaced by bound variables, and `kinds` tells us how
637/// many there are and whether they were normal or float/int variables. This is
638/// used to erase irrelevant differences between types before using them in
639/// queries.
640#[derive(Debug, Clone, PartialEq, Eq, Hash)]
641pub struct Canonical<T> {
642 pub value: T,
643 pub binders: CanonicalVarKinds,
644}
645
646impl<T> Canonical<T> {
647 pub fn new(value: T, kinds: impl IntoIterator<Item = TyVariableKind>) -> Self {
648 let kinds = kinds.into_iter().map(|tk| {
649 chalk_ir::CanonicalVarKind::new(
650 chalk_ir::VariableKind::Ty(tk),
651 chalk_ir::UniverseIndex::ROOT,
652 )
653 });
654 Self { value, binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds) }
655 }
656} 163}
657 164
658/// A function signature as seen by type inference: Several parameter types and 165/// A function signature as seen by type inference: Several parameter types and
@@ -663,6 +170,8 @@ pub struct CallableSig {
663 is_varargs: bool, 170 is_varargs: bool,
664} 171}
665 172
173has_interner!(CallableSig);
174
666/// A polymorphic function signature. 175/// A polymorphic function signature.
667pub type PolyFnSig = Binders<CallableSig>; 176pub type PolyFnSig = Binders<CallableSig>;
668 177
@@ -676,10 +185,12 @@ impl CallableSig {
676 CallableSig { 185 CallableSig {
677 // FIXME: what to do about lifetime params? -> return PolyFnSig 186 // FIXME: what to do about lifetime params? -> return PolyFnSig
678 params_and_return: fn_ptr 187 params_and_return: fn_ptr
679 .substs 188 .substitution
680 .clone() 189 .clone()
681 .shift_bound_vars_out(DebruijnIndex::ONE) 190 .shifted_out_to(&Interner, DebruijnIndex::ONE)
682 .interned(&Interner) 191 .expect("unexpected lifetime vars in fn ptr")
192 .0
193 .as_slice(&Interner)
683 .iter() 194 .iter()
684 .map(|arg| arg.assert_ty_ref(&Interner).clone()) 195 .map(|arg| arg.assert_ty_ref(&Interner).clone())
685 .collect(), 196 .collect(),
@@ -696,485 +207,20 @@ impl CallableSig {
696 } 207 }
697} 208}
698 209
699impl TypeWalk for CallableSig { 210impl Fold<Interner> for CallableSig {
700 fn walk(&self, f: &mut impl FnMut(&Ty)) { 211 type Result = CallableSig;
701 for t in self.params_and_return.iter() {
702 t.walk(f);
703 }
704 }
705
706 fn walk_mut_binders(
707 &mut self,
708 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
709 binders: DebruijnIndex,
710 ) {
711 for t in make_mut_slice(&mut self.params_and_return) {
712 t.walk_mut_binders(f, binders);
713 }
714 }
715}
716
717impl Ty {
718 pub fn as_reference(&self) -> Option<(&Ty, Mutability)> {
719 match self.kind(&Interner) {
720 TyKind::Ref(mutability, ty) => Some((ty, *mutability)),
721 _ => None,
722 }
723 }
724
725 pub fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
726 match self.kind(&Interner) {
727 TyKind::Ref(mutability, ty) => Some((ty, Rawness::Ref, *mutability)),
728 TyKind::Raw(mutability, ty) => Some((ty, Rawness::RawPtr, *mutability)),
729 _ => None,
730 }
731 }
732
733 pub fn strip_references(&self) -> &Ty {
734 let mut t: &Ty = self;
735
736 while let TyKind::Ref(_mutability, ty) = t.kind(&Interner) {
737 t = ty;
738 }
739
740 t
741 }
742
743 pub fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> {
744 match self.kind(&Interner) {
745 TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)),
746 _ => None,
747 }
748 }
749
750 pub fn as_tuple(&self) -> Option<&Substitution> {
751 match self.kind(&Interner) {
752 TyKind::Tuple(_, substs) => Some(substs),
753 _ => None,
754 }
755 }
756
757 pub fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId> {
758 match *self.kind(&Interner) {
759 TyKind::Adt(AdtId(adt), ..) => Some(adt.into()),
760 TyKind::FnDef(callable, ..) => {
761 Some(db.lookup_intern_callable_def(callable.into()).into())
762 }
763 TyKind::AssociatedType(type_alias, ..) => Some(from_assoc_type_id(type_alias).into()),
764 TyKind::ForeignType(type_alias, ..) => Some(from_foreign_def_id(type_alias).into()),
765 _ => None,
766 }
767 }
768
769 pub fn is_never(&self) -> bool {
770 matches!(self.kind(&Interner), TyKind::Never)
771 }
772
773 pub fn is_unknown(&self) -> bool {
774 matches!(self.kind(&Interner), TyKind::Unknown)
775 }
776
777 pub fn equals_ctor(&self, other: &Ty) -> bool {
778 match (self.kind(&Interner), other.kind(&Interner)) {
779 (TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
780 (TyKind::Slice(_), TyKind::Slice(_)) | (TyKind::Array(_), TyKind::Array(_)) => true,
781 (TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2,
782 (TyKind::OpaqueType(ty_id, ..), TyKind::OpaqueType(ty_id2, ..)) => ty_id == ty_id2,
783 (TyKind::AssociatedType(ty_id, ..), TyKind::AssociatedType(ty_id2, ..)) => {
784 ty_id == ty_id2
785 }
786 (TyKind::ForeignType(ty_id, ..), TyKind::ForeignType(ty_id2, ..)) => ty_id == ty_id2,
787 (TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2,
788 (TyKind::Ref(mutability, ..), TyKind::Ref(mutability2, ..))
789 | (TyKind::Raw(mutability, ..), TyKind::Raw(mutability2, ..)) => {
790 mutability == mutability2
791 }
792 (
793 TyKind::Function(FnPointer { num_args, sig, .. }),
794 TyKind::Function(FnPointer { num_args: num_args2, sig: sig2, .. }),
795 ) => num_args == num_args2 && sig == sig2,
796 (TyKind::Tuple(cardinality, _), TyKind::Tuple(cardinality2, _)) => {
797 cardinality == cardinality2
798 }
799 (TyKind::Str, TyKind::Str) | (TyKind::Never, TyKind::Never) => true,
800 (TyKind::Scalar(scalar), TyKind::Scalar(scalar2)) => scalar == scalar2,
801 _ => false,
802 }
803 }
804
805 /// If this is a `dyn Trait` type, this returns the `Trait` part.
806 fn dyn_trait_ref(&self) -> Option<&TraitRef> {
807 match self.kind(&Interner) {
808 TyKind::Dyn(dyn_ty) => {
809 dyn_ty.bounds.value.interned().get(0).and_then(|b| match b.skip_binders() {
810 WhereClause::Implemented(trait_ref) => Some(trait_ref),
811 _ => None,
812 })
813 }
814 _ => None,
815 }
816 }
817
818 /// If this is a `dyn Trait`, returns that trait.
819 pub fn dyn_trait(&self) -> Option<TraitId> {
820 self.dyn_trait_ref().map(|it| it.trait_id).map(from_chalk_trait_id)
821 }
822
823 fn builtin_deref(&self) -> Option<Ty> {
824 match self.kind(&Interner) {
825 TyKind::Ref(.., ty) => Some(ty.clone()),
826 TyKind::Raw(.., ty) => Some(ty.clone()),
827 _ => None,
828 }
829 }
830
831 pub fn callable_def(&self, db: &dyn HirDatabase) -> Option<CallableDefId> {
832 match self.kind(&Interner) {
833 &TyKind::FnDef(def, ..) => Some(db.lookup_intern_callable_def(def.into())),
834 _ => None,
835 }
836 }
837
838 pub fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
839 if let Some(CallableDefId::FunctionId(func)) = self.callable_def(db) {
840 Some(func)
841 } else {
842 None
843 }
844 }
845
846 pub fn callable_sig(&self, db: &dyn HirDatabase) -> Option<CallableSig> {
847 match self.kind(&Interner) {
848 TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)),
849 TyKind::FnDef(def, parameters) => {
850 let callable_def = db.lookup_intern_callable_def((*def).into());
851 let sig = db.callable_item_signature(callable_def);
852 Some(sig.subst(&parameters))
853 }
854 TyKind::Closure(.., substs) => {
855 let sig_param = substs.at(&Interner, 0).assert_ty_ref(&Interner);
856 sig_param.callable_sig(db)
857 }
858 _ => None,
859 }
860 }
861
862 /// Returns the type parameters of this type if it has some (i.e. is an ADT
863 /// or function); so if `self` is `Option<u32>`, this returns the `u32`.
864 pub fn substs(&self) -> Option<&Substitution> {
865 match self.kind(&Interner) {
866 TyKind::Adt(_, substs)
867 | TyKind::FnDef(_, substs)
868 | TyKind::Function(FnPointer { substs, .. })
869 | TyKind::Tuple(_, substs)
870 | TyKind::OpaqueType(_, substs)
871 | TyKind::AssociatedType(_, substs)
872 | TyKind::Closure(.., substs) => Some(substs),
873 _ => None,
874 }
875 }
876
877 fn substs_mut(&mut self) -> Option<&mut Substitution> {
878 match self.interned_mut() {
879 TyKind::Adt(_, substs)
880 | TyKind::FnDef(_, substs)
881 | TyKind::Function(FnPointer { substs, .. })
882 | TyKind::Tuple(_, substs)
883 | TyKind::OpaqueType(_, substs)
884 | TyKind::AssociatedType(_, substs)
885 | TyKind::Closure(.., substs) => Some(substs),
886 _ => None,
887 }
888 }
889
890 pub fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>> {
891 match self.kind(&Interner) {
892 TyKind::OpaqueType(opaque_ty_id, ..) => {
893 match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) {
894 ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => {
895 let krate = def.module(db.upcast()).krate();
896 if let Some(future_trait) = db
897 .lang_item(krate, "future_trait".into())
898 .and_then(|item| item.as_trait())
899 {
900 // This is only used by type walking.
901 // Parameters will be walked outside, and projection predicate is not used.
902 // So just provide the Future trait.
903 let impl_bound = Binders::new(
904 0,
905 WhereClause::Implemented(TraitRef {
906 trait_id: to_chalk_trait_id(future_trait),
907 substitution: Substitution::empty(&Interner),
908 }),
909 );
910 Some(vec![impl_bound])
911 } else {
912 None
913 }
914 }
915 ImplTraitId::ReturnTypeImplTrait(..) => None,
916 }
917 }
918 TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
919 let predicates = match db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into())
920 {
921 ImplTraitId::ReturnTypeImplTrait(func, idx) => {
922 db.return_type_impl_traits(func).map(|it| {
923 let data = (*it)
924 .as_ref()
925 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
926 data.subst(&opaque_ty.substitution)
927 })
928 }
929 // It always has an parameter for Future::Output type.
930 ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
931 };
932
933 predicates.map(|it| it.value)
934 }
935 TyKind::Placeholder(idx) => {
936 let id = from_placeholder_idx(db, *idx);
937 let generic_params = db.generic_params(id.parent);
938 let param_data = &generic_params.types[id.local_id];
939 match param_data.provenance {
940 hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
941 let substs = TyBuilder::type_params_subst(db, id.parent);
942 let predicates = db
943 .generic_predicates(id.parent)
944 .into_iter()
945 .map(|pred| pred.clone().subst(&substs))
946 .filter(|wc| match &wc.skip_binders() {
947 WhereClause::Implemented(tr) => tr.self_type_parameter() == self,
948 WhereClause::AliasEq(AliasEq {
949 alias: AliasTy::Projection(proj),
950 ty: _,
951 }) => proj.self_type_parameter() == self,
952 _ => false,
953 })
954 .collect_vec();
955
956 Some(predicates)
957 }
958 _ => None,
959 }
960 }
961 _ => None,
962 }
963 }
964
965 pub fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> {
966 match self.kind(&Interner) {
967 TyKind::AssociatedType(id, ..) => {
968 match from_assoc_type_id(*id).lookup(db.upcast()).container {
969 AssocContainerId::TraitId(trait_id) => Some(trait_id),
970 _ => None,
971 }
972 }
973 TyKind::Alias(AliasTy::Projection(projection_ty)) => {
974 match from_assoc_type_id(projection_ty.associated_ty_id)
975 .lookup(db.upcast())
976 .container
977 {
978 AssocContainerId::TraitId(trait_id) => Some(trait_id),
979 _ => None,
980 }
981 }
982 _ => None,
983 }
984 }
985}
986
987/// This allows walking structures that contain types to do something with those
988/// types, similar to Chalk's `Fold` trait.
989pub trait TypeWalk {
990 fn walk(&self, f: &mut impl FnMut(&Ty));
991 fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
992 self.walk_mut_binders(&mut |ty, _binders| f(ty), DebruijnIndex::INNERMOST);
993 }
994 /// Walk the type, counting entered binders.
995 ///
996 /// `TyKind::Bound` variables use DeBruijn indexing, which means that 0 refers
997 /// to the innermost binder, 1 to the next, etc.. So when we want to
998 /// substitute a certain bound variable, we can't just walk the whole type
999 /// and blindly replace each instance of a certain index; when we 'enter'
1000 /// things that introduce new bound variables, we have to keep track of
1001 /// that. Currently, the only thing that introduces bound variables on our
1002 /// side are `TyKind::Dyn` and `TyKind::Opaque`, which each introduce a bound
1003 /// variable for the self type.
1004 fn walk_mut_binders(
1005 &mut self,
1006 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
1007 binders: DebruijnIndex,
1008 );
1009
1010 fn fold_binders(
1011 mut self,
1012 f: &mut impl FnMut(Ty, DebruijnIndex) -> Ty,
1013 binders: DebruijnIndex,
1014 ) -> Self
1015 where
1016 Self: Sized,
1017 {
1018 self.walk_mut_binders(
1019 &mut |ty_mut, binders| {
1020 let ty = mem::replace(ty_mut, TyKind::Unknown.intern(&Interner));
1021 *ty_mut = f(ty, binders);
1022 },
1023 binders,
1024 );
1025 self
1026 }
1027
1028 fn fold(mut self, f: &mut impl FnMut(Ty) -> Ty) -> Self
1029 where
1030 Self: Sized,
1031 {
1032 self.walk_mut(&mut |ty_mut| {
1033 let ty = mem::replace(ty_mut, TyKind::Unknown.intern(&Interner));
1034 *ty_mut = f(ty);
1035 });
1036 self
1037 }
1038 212
1039 /// Substitutes `TyKind::Bound` vars with the given substitution. 213 fn fold_with<'i>(
1040 fn subst_bound_vars(self, substs: &Substitution) -> Self 214 self,
215 folder: &mut dyn chalk_ir::fold::Folder<'i, Interner>,
216 outer_binder: DebruijnIndex,
217 ) -> chalk_ir::Fallible<Self::Result>
1041 where 218 where
1042 Self: Sized, 219 Interner: 'i,
1043 { 220 {
1044 self.subst_bound_vars_at_depth(substs, DebruijnIndex::INNERMOST) 221 let vec = self.params_and_return.to_vec();
1045 } 222 let folded = vec.fold_with(folder, outer_binder)?;
1046 223 Ok(CallableSig { params_and_return: folded.into(), is_varargs: self.is_varargs })
1047 /// Substitutes `TyKind::Bound` vars with the given substitution.
1048 fn subst_bound_vars_at_depth(mut self, substs: &Substitution, depth: DebruijnIndex) -> Self
1049 where
1050 Self: Sized,
1051 {
1052 self.walk_mut_binders(
1053 &mut |ty, binders| {
1054 if let &mut TyKind::BoundVar(bound) = ty.interned_mut() {
1055 if bound.debruijn >= binders {
1056 *ty = substs.0[bound.index]
1057 .assert_ty_ref(&Interner)
1058 .clone()
1059 .shift_bound_vars(binders);
1060 }
1061 }
1062 },
1063 depth,
1064 );
1065 self
1066 }
1067
1068 /// Shifts up debruijn indices of `TyKind::Bound` vars by `n`.
1069 fn shift_bound_vars(self, n: DebruijnIndex) -> Self
1070 where
1071 Self: Sized,
1072 {
1073 self.fold_binders(
1074 &mut |ty, binders| match ty.kind(&Interner) {
1075 TyKind::BoundVar(bound) if bound.debruijn >= binders => {
1076 TyKind::BoundVar(bound.shifted_in_from(n)).intern(&Interner)
1077 }
1078 _ => ty,
1079 },
1080 DebruijnIndex::INNERMOST,
1081 )
1082 }
1083
1084 /// Shifts debruijn indices of `TyKind::Bound` vars out (down) by `n`.
1085 fn shift_bound_vars_out(self, n: DebruijnIndex) -> Self
1086 where
1087 Self: Sized + std::fmt::Debug,
1088 {
1089 self.fold_binders(
1090 &mut |ty, binders| match ty.kind(&Interner) {
1091 TyKind::BoundVar(bound) if bound.debruijn >= binders => {
1092 TyKind::BoundVar(bound.shifted_out_to(n).unwrap_or(bound.clone()))
1093 .intern(&Interner)
1094 }
1095 _ => ty,
1096 },
1097 DebruijnIndex::INNERMOST,
1098 )
1099 }
1100}
1101
1102impl TypeWalk for Ty {
1103 fn walk(&self, f: &mut impl FnMut(&Ty)) {
1104 match self.kind(&Interner) {
1105 TyKind::Alias(AliasTy::Projection(p_ty)) => {
1106 for t in p_ty.substitution.iter(&Interner) {
1107 t.walk(f);
1108 }
1109 }
1110 TyKind::Alias(AliasTy::Opaque(o_ty)) => {
1111 for t in o_ty.substitution.iter(&Interner) {
1112 t.walk(f);
1113 }
1114 }
1115 TyKind::Dyn(dyn_ty) => {
1116 for p in dyn_ty.bounds.value.interned().iter() {
1117 p.walk(f);
1118 }
1119 }
1120 TyKind::Slice(ty) | TyKind::Array(ty) | TyKind::Ref(_, ty) | TyKind::Raw(_, ty) => {
1121 ty.walk(f);
1122 }
1123 _ => {
1124 if let Some(substs) = self.substs() {
1125 for t in substs.iter(&Interner) {
1126 t.walk(f);
1127 }
1128 }
1129 }
1130 }
1131 f(self);
1132 }
1133
1134 fn walk_mut_binders(
1135 &mut self,
1136 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
1137 binders: DebruijnIndex,
1138 ) {
1139 match self.interned_mut() {
1140 TyKind::Alias(AliasTy::Projection(p_ty)) => {
1141 p_ty.substitution.walk_mut_binders(f, binders);
1142 }
1143 TyKind::Dyn(dyn_ty) => {
1144 for p in make_mut_slice(&mut dyn_ty.bounds.value.0) {
1145 p.walk_mut_binders(f, binders.shifted_in());
1146 }
1147 }
1148 TyKind::Alias(AliasTy::Opaque(o_ty)) => {
1149 o_ty.substitution.walk_mut_binders(f, binders);
1150 }
1151 TyKind::Slice(ty) | TyKind::Array(ty) | TyKind::Ref(_, ty) | TyKind::Raw(_, ty) => {
1152 ty.walk_mut_binders(f, binders);
1153 }
1154 _ => {
1155 if let Some(substs) = self.substs_mut() {
1156 substs.walk_mut_binders(f, binders);
1157 }
1158 }
1159 }
1160 f(self, binders);
1161 }
1162}
1163
1164impl<T: TypeWalk> TypeWalk for Vec<T> {
1165 fn walk(&self, f: &mut impl FnMut(&Ty)) {
1166 for t in self {
1167 t.walk(f);
1168 }
1169 }
1170 fn walk_mut_binders(
1171 &mut self,
1172 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
1173 binders: DebruijnIndex,
1174 ) {
1175 for t in self {
1176 t.walk_mut_binders(f, binders);
1177 }
1178 } 224 }
1179} 225}
1180 226
@@ -1189,45 +235,75 @@ pub struct ReturnTypeImplTraits {
1189 pub(crate) impl_traits: Vec<ReturnTypeImplTrait>, 235 pub(crate) impl_traits: Vec<ReturnTypeImplTrait>,
1190} 236}
1191 237
238has_interner!(ReturnTypeImplTraits);
239
1192#[derive(Clone, PartialEq, Eq, Debug, Hash)] 240#[derive(Clone, PartialEq, Eq, Debug, Hash)]
1193pub(crate) struct ReturnTypeImplTrait { 241pub(crate) struct ReturnTypeImplTrait {
1194 pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>, 242 pub(crate) bounds: Binders<Vec<QuantifiedWhereClause>>,
1195} 243}
1196 244
1197pub fn to_foreign_def_id(id: TypeAliasId) -> ForeignDefId { 245pub fn static_lifetime() -> Lifetime {
1198 chalk_ir::ForeignDefId(salsa::InternKey::as_intern_id(&id)) 246 LifetimeData::Static.intern(&Interner)
1199}
1200
1201pub fn from_foreign_def_id(id: ForeignDefId) -> TypeAliasId {
1202 salsa::InternKey::from_intern_id(id.0)
1203} 247}
1204 248
1205pub fn to_assoc_type_id(id: TypeAliasId) -> AssocTypeId { 249pub fn dummy_usize_const() -> Const {
1206 chalk_ir::AssocTypeId(salsa::InternKey::as_intern_id(&id)) 250 let usize_ty = chalk_ir::TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner);
251 chalk_ir::ConstData {
252 ty: usize_ty,
253 value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: () }),
254 }
255 .intern(&Interner)
1207} 256}
1208 257
1209pub fn from_assoc_type_id(id: AssocTypeId) -> TypeAliasId { 258pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + Fold<Interner>>(
1210 salsa::InternKey::from_intern_id(id.0) 259 t: T,
1211} 260 f: impl FnMut(BoundVar, DebruijnIndex) -> Ty,
261) -> T::Result {
262 use chalk_ir::{fold::Folder, Fallible};
263 struct FreeVarFolder<F>(F);
264 impl<'i, F: FnMut(BoundVar, DebruijnIndex) -> Ty + 'i> Folder<'i, Interner> for FreeVarFolder<F> {
265 fn as_dyn(&mut self) -> &mut dyn Folder<'i, Interner> {
266 self
267 }
1212 268
1213pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> TypeParamId { 269 fn interner(&self) -> &'i Interner {
1214 assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); 270 &Interner
1215 let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx)); 271 }
1216 db.lookup_intern_type_param_id(interned_id)
1217}
1218 272
1219pub fn to_placeholder_idx(db: &dyn HirDatabase, id: TypeParamId) -> PlaceholderIndex { 273 fn fold_free_var_ty(
1220 let interned_id = db.intern_type_param_id(id); 274 &mut self,
1221 PlaceholderIndex { 275 bound_var: BoundVar,
1222 ui: chalk_ir::UniverseIndex::ROOT, 276 outer_binder: DebruijnIndex,
1223 idx: salsa::InternKey::as_intern_id(&interned_id).as_usize(), 277 ) -> Fallible<Ty> {
278 Ok(self.0(bound_var, outer_binder))
279 }
1224 } 280 }
281 t.fold_with(&mut FreeVarFolder(f), DebruijnIndex::INNERMOST).expect("fold failed unexpectedly")
1225} 282}
1226 283
1227pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId { 284pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + Fold<Interner>>(
1228 chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id)) 285 t: T,
1229} 286 f: impl FnMut(Ty, DebruijnIndex) -> Ty,
287 binders: DebruijnIndex,
288) -> T::Result {
289 use chalk_ir::{
290 fold::{Folder, SuperFold},
291 Fallible,
292 };
293 struct TyFolder<F>(F);
294 impl<'i, F: FnMut(Ty, DebruijnIndex) -> Ty + 'i> Folder<'i, Interner> for TyFolder<F> {
295 fn as_dyn(&mut self) -> &mut dyn Folder<'i, Interner> {
296 self
297 }
298
299 fn interner(&self) -> &'i Interner {
300 &Interner
301 }
1230 302
1231pub fn from_chalk_trait_id(id: ChalkTraitId) -> TraitId { 303 fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
1232 salsa::InternKey::from_intern_id(id.0) 304 let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?;
305 Ok(self.0(ty, outer_binder))
306 }
307 }
308 t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly")
1233} 309}
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 214655807..7fd46becd 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -5,12 +5,14 @@
5//! - Building the type for an item: This happens through the `type_for_def` query. 5//! - Building the type for an item: This happens through the `type_for_def` query.
6//! 6//!
7//! This usually involves resolving names, collecting generic arguments etc. 7//! This usually involves resolving names, collecting generic arguments etc.
8use std::cell::{Cell, RefCell};
8use std::{iter, sync::Arc}; 9use std::{iter, sync::Arc};
9 10
10use base_db::CrateId; 11use base_db::CrateId;
11use chalk_ir::{cast::Cast, Mutability, Safety}; 12use chalk_ir::{cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety};
12use hir_def::{ 13use hir_def::{
13 adt::StructKind, 14 adt::StructKind,
15 body::{Expander, LowerCtx},
14 builtin_type::BuiltinType, 16 builtin_type::BuiltinType,
15 generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, 17 generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
16 path::{GenericArg, Path, PathSegment, PathSegments}, 18 path::{GenericArg, Path, PathSegment, PathSegments},
@@ -20,23 +22,24 @@ use hir_def::{
20 GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, 22 GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
21 TypeAliasId, TypeParamId, UnionId, VariantId, 23 TypeAliasId, TypeParamId, UnionId, VariantId,
22}; 24};
23use hir_expand::name::Name; 25use hir_expand::{name::Name, ExpandResult};
24use la_arena::ArenaMap; 26use la_arena::ArenaMap;
25use smallvec::SmallVec; 27use smallvec::SmallVec;
26use stdx::impl_from; 28use stdx::impl_from;
29use syntax::ast;
27 30
28use crate::{ 31use crate::{
29 db::HirDatabase, 32 db::HirDatabase,
30 to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, 33 dummy_usize_const,
31 traits::chalk::{Interner, ToChalk}, 34 mapping::ToChalk,
35 static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
32 utils::{ 36 utils::{
33 all_super_trait_refs, associated_type_by_name_including_super_traits, generics, 37 all_super_trait_refs, associated_type_by_name_including_super_traits, generics, Generics,
34 variant_data,
35 }, 38 },
36 AliasEq, AliasTy, Binders, BoundVar, CallableSig, DebruijnIndex, DynTy, FnPointer, FnSig, 39 AliasEq, AliasTy, Binders, BoundVar, CallableSig, DebruijnIndex, DynTy, FnPointer, FnSig,
37 ImplTraitId, OpaqueTy, PolyFnSig, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, 40 FnSubst, ImplTraitId, Interner, OpaqueTy, PolyFnSig, ProjectionTy, QuantifiedWhereClause,
38 ReturnTypeImplTrait, ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, Ty, 41 QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, Substitution,
39 TyBuilder, TyKind, TypeWalk, WhereClause, 42 TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
40}; 43};
41 44
42#[derive(Debug)] 45#[derive(Debug)]
@@ -50,7 +53,7 @@ pub struct TyLoweringContext<'a> {
50 /// possible currently, so this should be fine for now. 53 /// possible currently, so this should be fine for now.
51 pub type_param_mode: TypeParamLoweringMode, 54 pub type_param_mode: TypeParamLoweringMode,
52 pub impl_trait_mode: ImplTraitLoweringMode, 55 pub impl_trait_mode: ImplTraitLoweringMode,
53 impl_trait_counter: std::cell::Cell<u16>, 56 impl_trait_counter: Cell<u16>,
54 /// When turning `impl Trait` into opaque types, we have to collect the 57 /// When turning `impl Trait` into opaque types, we have to collect the
55 /// bounds at the same time to get the IDs correct (without becoming too 58 /// bounds at the same time to get the IDs correct (without becoming too
56 /// complicated). I don't like using interior mutability (as for the 59 /// complicated). I don't like using interior mutability (as for the
@@ -59,16 +62,17 @@ pub struct TyLoweringContext<'a> {
59 /// we're grouping the mutable data (the counter and this field) together 62 /// we're grouping the mutable data (the counter and this field) together
60 /// with the immutable context (the references to the DB and resolver). 63 /// with the immutable context (the references to the DB and resolver).
61 /// Splitting this up would be a possible fix. 64 /// Splitting this up would be a possible fix.
62 opaque_type_data: std::cell::RefCell<Vec<ReturnTypeImplTrait>>, 65 opaque_type_data: RefCell<Vec<ReturnTypeImplTrait>>,
66 expander: RefCell<Option<Expander>>,
63} 67}
64 68
65impl<'a> TyLoweringContext<'a> { 69impl<'a> TyLoweringContext<'a> {
66 pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self { 70 pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self {
67 let impl_trait_counter = std::cell::Cell::new(0); 71 let impl_trait_counter = Cell::new(0);
68 let impl_trait_mode = ImplTraitLoweringMode::Disallowed; 72 let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
69 let type_param_mode = TypeParamLoweringMode::Placeholder; 73 let type_param_mode = TypeParamLoweringMode::Placeholder;
70 let in_binders = DebruijnIndex::INNERMOST; 74 let in_binders = DebruijnIndex::INNERMOST;
71 let opaque_type_data = std::cell::RefCell::new(Vec::new()); 75 let opaque_type_data = RefCell::new(Vec::new());
72 Self { 76 Self {
73 db, 77 db,
74 resolver, 78 resolver,
@@ -77,6 +81,7 @@ impl<'a> TyLoweringContext<'a> {
77 impl_trait_counter, 81 impl_trait_counter,
78 type_param_mode, 82 type_param_mode,
79 opaque_type_data, 83 opaque_type_data,
84 expander: RefCell::new(None),
80 } 85 }
81 } 86 }
82 87
@@ -86,15 +91,18 @@ impl<'a> TyLoweringContext<'a> {
86 f: impl FnOnce(&TyLoweringContext) -> T, 91 f: impl FnOnce(&TyLoweringContext) -> T,
87 ) -> T { 92 ) -> T {
88 let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new()); 93 let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new());
94 let expander = self.expander.replace(None);
89 let new_ctx = Self { 95 let new_ctx = Self {
90 in_binders: debruijn, 96 in_binders: debruijn,
91 impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), 97 impl_trait_counter: Cell::new(self.impl_trait_counter.get()),
92 opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec), 98 opaque_type_data: RefCell::new(opaque_ty_data_vec),
99 expander: RefCell::new(expander),
93 ..*self 100 ..*self
94 }; 101 };
95 let result = f(&new_ctx); 102 let result = f(&new_ctx);
96 self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); 103 self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
97 self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner()); 104 self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner());
105 self.expander.replace(new_ctx.expander.into_inner());
98 result 106 result
99 } 107 }
100 108
@@ -166,7 +174,7 @@ impl<'a> TyLoweringContext<'a> {
166 } 174 }
167 TypeRef::Array(inner) => { 175 TypeRef::Array(inner) => {
168 let inner_ty = self.lower_ty(inner); 176 let inner_ty = self.lower_ty(inner);
169 TyKind::Array(inner_ty).intern(&Interner) 177 TyKind::Array(inner_ty, dummy_usize_const()).intern(&Interner)
170 } 178 }
171 TypeRef::Slice(inner) => { 179 TypeRef::Slice(inner) => {
172 let inner_ty = self.lower_ty(inner); 180 let inner_ty = self.lower_ty(inner);
@@ -174,16 +182,19 @@ impl<'a> TyLoweringContext<'a> {
174 } 182 }
175 TypeRef::Reference(inner, _, mutability) => { 183 TypeRef::Reference(inner, _, mutability) => {
176 let inner_ty = self.lower_ty(inner); 184 let inner_ty = self.lower_ty(inner);
177 TyKind::Ref(lower_to_chalk_mutability(*mutability), inner_ty).intern(&Interner) 185 let lifetime = static_lifetime();
186 TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty)
187 .intern(&Interner)
178 } 188 }
179 TypeRef::Placeholder => TyKind::Unknown.intern(&Interner), 189 TypeRef::Placeholder => TyKind::Error.intern(&Interner),
180 TypeRef::Fn(params, is_varargs) => { 190 TypeRef::Fn(params, is_varargs) => {
181 let substs = 191 let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
182 Substitution::from_iter(&Interner, params.iter().map(|tr| self.lower_ty(tr))); 192 Substitution::from_iter(&Interner, params.iter().map(|tr| ctx.lower_ty(tr)))
193 });
183 TyKind::Function(FnPointer { 194 TyKind::Function(FnPointer {
184 num_args: substs.len(&Interner) - 1, 195 num_binders: 0, // FIXME lower `for<'a> fn()` correctly
185 sig: FnSig { abi: (), safety: Safety::Safe, variadic: *is_varargs }, 196 sig: FnSig { abi: (), safety: Safety::Safe, variadic: *is_varargs },
186 substs, 197 substitution: FnSubst(substs),
187 }) 198 })
188 .intern(&Interner) 199 .intern(&Interner)
189 } 200 }
@@ -196,8 +207,8 @@ impl<'a> TyLoweringContext<'a> {
196 bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)), 207 bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)),
197 ) 208 )
198 }); 209 });
199 let bounds = Binders::new(1, bounds); 210 let bounds = crate::make_only_type_binders(1, bounds);
200 TyKind::Dyn(DynTy { bounds }).intern(&Interner) 211 TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(&Interner)
201 } 212 }
202 TypeRef::ImplTrait(bounds) => { 213 TypeRef::ImplTrait(bounds) => {
203 match self.impl_trait_mode { 214 match self.impl_trait_mode {
@@ -209,9 +220,9 @@ impl<'a> TyLoweringContext<'a> {
209 // this dance is to make sure the data is in the right 220 // this dance is to make sure the data is in the right
210 // place even if we encounter more opaque types while 221 // place even if we encounter more opaque types while
211 // lowering the bounds 222 // lowering the bounds
212 self.opaque_type_data 223 self.opaque_type_data.borrow_mut().push(ReturnTypeImplTrait {
213 .borrow_mut() 224 bounds: crate::make_only_type_binders(1, Vec::new()),
214 .push(ReturnTypeImplTrait { bounds: Binders::new(1, Vec::new()) }); 225 });
215 // We don't want to lower the bounds inside the binders 226 // We don't want to lower the bounds inside the binders
216 // we're currently in, because they don't end up inside 227 // we're currently in, because they don't end up inside
217 // those binders. E.g. when we have `impl Trait<impl 228 // those binders. E.g. when we have `impl Trait<impl
@@ -253,12 +264,12 @@ impl<'a> TyLoweringContext<'a> {
253 data.provenance == TypeParamProvenance::ArgumentImplTrait 264 data.provenance == TypeParamProvenance::ArgumentImplTrait
254 }) 265 })
255 .nth(idx as usize) 266 .nth(idx as usize)
256 .map_or(TyKind::Unknown, |(id, _)| { 267 .map_or(TyKind::Error, |(id, _)| {
257 TyKind::Placeholder(to_placeholder_idx(self.db, id)) 268 TyKind::Placeholder(to_placeholder_idx(self.db, id))
258 }); 269 });
259 param.intern(&Interner) 270 param.intern(&Interner)
260 } else { 271 } else {
261 TyKind::Unknown.intern(&Interner) 272 TyKind::Error.intern(&Interner)
262 } 273 }
263 } 274 }
264 ImplTraitLoweringMode::Variable => { 275 ImplTraitLoweringMode::Variable => {
@@ -280,11 +291,58 @@ impl<'a> TyLoweringContext<'a> {
280 } 291 }
281 ImplTraitLoweringMode::Disallowed => { 292 ImplTraitLoweringMode::Disallowed => {
282 // FIXME: report error 293 // FIXME: report error
283 TyKind::Unknown.intern(&Interner) 294 TyKind::Error.intern(&Interner)
295 }
296 }
297 }
298 TypeRef::Macro(macro_call) => {
299 let (expander, recursion_start) = {
300 let mut expander = self.expander.borrow_mut();
301 if expander.is_some() {
302 (Some(expander), false)
303 } else {
304 if let Some(module_id) = self.resolver.module() {
305 *expander = Some(Expander::new(
306 self.db.upcast(),
307 macro_call.file_id,
308 module_id,
309 ));
310 (Some(expander), true)
311 } else {
312 (None, false)
313 }
284 } 314 }
315 };
316 let ty = if let Some(mut expander) = expander {
317 let expander_mut = expander.as_mut().unwrap();
318 let macro_call = macro_call.to_node(self.db.upcast());
319 match expander_mut.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
320 Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
321 let ctx =
322 LowerCtx::new(self.db.upcast(), expander_mut.current_file_id());
323 let type_ref = TypeRef::from_ast(&ctx, expanded);
324
325 drop(expander);
326 let ty = self.lower_ty(&type_ref);
327
328 self.expander
329 .borrow_mut()
330 .as_mut()
331 .unwrap()
332 .exit(self.db.upcast(), mark);
333 Some(ty)
334 }
335 _ => None,
336 }
337 } else {
338 None
339 };
340 if recursion_start {
341 *self.expander.borrow_mut() = None;
285 } 342 }
343 ty.unwrap_or_else(|| TyKind::Error.intern(&Interner))
286 } 344 }
287 TypeRef::Error => TyKind::Unknown.intern(&Interner), 345 TypeRef::Error => TyKind::Error.intern(&Interner),
288 }; 346 };
289 (ty, res) 347 (ty, res)
290 } 348 }
@@ -328,7 +386,7 @@ impl<'a> TyLoweringContext<'a> {
328 (self.select_associated_type(res, segment), None) 386 (self.select_associated_type(res, segment), None)
329 } else if remaining_segments.len() > 1 { 387 } else if remaining_segments.len() > 1 {
330 // FIXME report error (ambiguous associated type) 388 // FIXME report error (ambiguous associated type)
331 (TyKind::Unknown.intern(&Interner), None) 389 (TyKind::Error.intern(&Interner), None)
332 } else { 390 } else {
333 (ty, res) 391 (ty, res)
334 } 392 }
@@ -372,21 +430,24 @@ impl<'a> TyLoweringContext<'a> {
372 } 430 }
373 None => { 431 None => {
374 // FIXME: report error (associated type not found) 432 // FIXME: report error (associated type not found)
375 TyKind::Unknown.intern(&Interner) 433 TyKind::Error.intern(&Interner)
376 } 434 }
377 } 435 }
378 } else if remaining_segments.len() > 1 { 436 } else if remaining_segments.len() > 1 {
379 // FIXME report error (ambiguous associated type) 437 // FIXME report error (ambiguous associated type)
380 TyKind::Unknown.intern(&Interner) 438 TyKind::Error.intern(&Interner)
381 } else { 439 } else {
382 let dyn_ty = DynTy { 440 let dyn_ty = DynTy {
383 bounds: Binders::new( 441 bounds: crate::make_only_type_binders(
384 1, 442 1,
385 QuantifiedWhereClauses::from_iter( 443 QuantifiedWhereClauses::from_iter(
386 &Interner, 444 &Interner,
387 Some(Binders::wrap_empty(WhereClause::Implemented(trait_ref))), 445 Some(crate::wrap_empty_binders(WhereClause::Implemented(
446 trait_ref,
447 ))),
388 ), 448 ),
389 ), 449 ),
450 lifetime: static_lifetime(),
390 }; 451 };
391 TyKind::Dyn(dyn_ty).intern(&Interner) 452 TyKind::Dyn(dyn_ty).intern(&Interner)
392 }; 453 };
@@ -414,7 +475,7 @@ impl<'a> TyLoweringContext<'a> {
414 TypeParamLoweringMode::Placeholder => generics.type_params_subst(self.db), 475 TypeParamLoweringMode::Placeholder => generics.type_params_subst(self.db),
415 TypeParamLoweringMode::Variable => generics.bound_vars_subst(self.in_binders), 476 TypeParamLoweringMode::Variable => generics.bound_vars_subst(self.in_binders),
416 }; 477 };
417 self.db.impl_self_ty(impl_id).subst(&substs) 478 self.db.impl_self_ty(impl_id).substitute(&Interner, &substs)
418 } 479 }
419 TypeNs::AdtSelfType(adt) => { 480 TypeNs::AdtSelfType(adt) => {
420 let generics = generics(self.db.upcast(), adt.into()); 481 let generics = generics(self.db.upcast(), adt.into());
@@ -422,7 +483,7 @@ impl<'a> TyLoweringContext<'a> {
422 TypeParamLoweringMode::Placeholder => generics.type_params_subst(self.db), 483 TypeParamLoweringMode::Placeholder => generics.type_params_subst(self.db),
423 TypeParamLoweringMode::Variable => generics.bound_vars_subst(self.in_binders), 484 TypeParamLoweringMode::Variable => generics.bound_vars_subst(self.in_binders),
424 }; 485 };
425 self.db.ty(adt.into()).subst(&substs) 486 self.db.ty(adt.into()).substitute(&Interner, &substs)
426 } 487 }
427 488
428 TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args), 489 TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args),
@@ -433,7 +494,7 @@ impl<'a> TyLoweringContext<'a> {
433 self.lower_path_inner(resolved_segment, it.into(), infer_args) 494 self.lower_path_inner(resolved_segment, it.into(), infer_args)
434 } 495 }
435 // FIXME: report error 496 // FIXME: report error
436 TypeNs::EnumVariantId(_) => return (TyKind::Unknown.intern(&Interner), None), 497 TypeNs::EnumVariantId(_) => return (TyKind::Error.intern(&Interner), None),
437 }; 498 };
438 self.lower_ty_relative_path(ty, Some(resolution), remaining_segments) 499 self.lower_ty_relative_path(ty, Some(resolution), remaining_segments)
439 } 500 }
@@ -447,7 +508,7 @@ impl<'a> TyLoweringContext<'a> {
447 let (resolution, remaining_index) = 508 let (resolution, remaining_index) =
448 match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { 509 match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
449 Some(it) => it, 510 Some(it) => it,
450 None => return (TyKind::Unknown.intern(&Interner), None), 511 None => return (TyKind::Error.intern(&Interner), None),
451 }; 512 };
452 let (resolved_segment, remaining_segments) = match remaining_index { 513 let (resolved_segment, remaining_segments) = match remaining_index {
453 None => ( 514 None => (
@@ -477,13 +538,13 @@ impl<'a> TyLoweringContext<'a> {
477 ), 538 ),
478 ); 539 );
479 let s = generics.type_params_subst(self.db); 540 let s = generics.type_params_subst(self.db);
480 t.substitution.clone().subst_bound_vars(&s) 541 s.apply(t.substitution.clone(), &Interner)
481 } 542 }
482 TypeParamLoweringMode::Variable => t.substitution.clone(), 543 TypeParamLoweringMode::Variable => t.substitution.clone(),
483 }; 544 };
484 // We need to shift in the bound vars, since 545 // We need to shift in the bound vars, since
485 // associated_type_shorthand_candidates does not do that 546 // associated_type_shorthand_candidates does not do that
486 let substs = substs.shift_bound_vars(self.in_binders); 547 let substs = substs.shifted_in_from(&Interner, self.in_binders);
487 // FIXME handle type parameters on the segment 548 // FIXME handle type parameters on the segment
488 return Some( 549 return Some(
489 TyKind::Alias(AliasTy::Projection(ProjectionTy { 550 TyKind::Alias(AliasTy::Projection(ProjectionTy {
@@ -498,9 +559,9 @@ impl<'a> TyLoweringContext<'a> {
498 }, 559 },
499 ); 560 );
500 561
501 ty.unwrap_or(TyKind::Unknown.intern(&Interner)) 562 ty.unwrap_or(TyKind::Error.intern(&Interner))
502 } else { 563 } else {
503 TyKind::Unknown.intern(&Interner) 564 TyKind::Error.intern(&Interner)
504 } 565 }
505 } 566 }
506 567
@@ -516,7 +577,7 @@ impl<'a> TyLoweringContext<'a> {
516 TyDefId::TypeAliasId(it) => Some(it.into()), 577 TyDefId::TypeAliasId(it) => Some(it.into()),
517 }; 578 };
518 let substs = self.substs_from_path_segment(segment, generic_def, infer_args, None); 579 let substs = self.substs_from_path_segment(segment, generic_def, infer_args, None);
519 self.db.ty(typeable).subst(&substs) 580 self.db.ty(typeable).substitute(&Interner, &substs)
520 } 581 }
521 582
522 /// Collect generic arguments from a path into a `Substs`. See also 583 /// Collect generic arguments from a path into a `Substs`. See also
@@ -569,13 +630,13 @@ impl<'a> TyLoweringContext<'a> {
569 def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split()); 630 def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split());
570 let total_len = parent_params + self_params + type_params + impl_trait_params; 631 let total_len = parent_params + self_params + type_params + impl_trait_params;
571 632
572 substs.extend(iter::repeat(TyKind::Unknown.intern(&Interner)).take(parent_params)); 633 substs.extend(iter::repeat(TyKind::Error.intern(&Interner)).take(parent_params));
573 634
574 let fill_self_params = || { 635 let fill_self_params = || {
575 substs.extend( 636 substs.extend(
576 explicit_self_ty 637 explicit_self_ty
577 .into_iter() 638 .into_iter()
578 .chain(iter::repeat(TyKind::Unknown.intern(&Interner))) 639 .chain(iter::repeat(TyKind::Error.intern(&Interner)))
579 .take(self_params), 640 .take(self_params),
580 ) 641 )
581 }; 642 };
@@ -620,7 +681,7 @@ impl<'a> TyLoweringContext<'a> {
620 for default_ty in defaults.iter().skip(substs.len()) { 681 for default_ty in defaults.iter().skip(substs.len()) {
621 // each default can depend on the previous parameters 682 // each default can depend on the previous parameters
622 let substs_so_far = Substitution::from_iter(&Interner, substs.clone()); 683 let substs_so_far = Substitution::from_iter(&Interner, substs.clone());
623 substs.push(default_ty.clone().subst(&substs_so_far)); 684 substs.push(default_ty.clone().substitute(&Interner, &substs_so_far));
624 } 685 }
625 } 686 }
626 } 687 }
@@ -628,7 +689,7 @@ impl<'a> TyLoweringContext<'a> {
628 // add placeholders for args that were not provided 689 // add placeholders for args that were not provided
629 // FIXME: emit diagnostics in contexts where this is not allowed 690 // FIXME: emit diagnostics in contexts where this is not allowed
630 for _ in substs.len()..total_len { 691 for _ in substs.len()..total_len {
631 substs.push(TyKind::Unknown.intern(&Interner)); 692 substs.push(TyKind::Error.intern(&Interner));
632 } 693 }
633 assert_eq!(substs.len(), total_len); 694 assert_eq!(substs.len(), total_len);
634 695
@@ -720,7 +781,7 @@ impl<'a> TyLoweringContext<'a> {
720 let trait_ref = match bound { 781 let trait_ref = match bound {
721 TypeBound::Path(path) => { 782 TypeBound::Path(path) => {
722 bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); 783 bindings = self.lower_trait_ref_from_path(path, Some(self_ty));
723 bindings.clone().map(WhereClause::Implemented).map(|b| Binders::wrap_empty(b)) 784 bindings.clone().map(WhereClause::Implemented).map(|b| crate::wrap_empty_binders(b))
724 } 785 }
725 TypeBound::Lifetime(_) => None, 786 TypeBound::Lifetime(_) => None,
726 TypeBound::Error => None, 787 TypeBound::Error => None,
@@ -767,7 +828,7 @@ impl<'a> TyLoweringContext<'a> {
767 let ty = self.lower_ty(type_ref); 828 let ty = self.lower_ty(type_ref);
768 let alias_eq = 829 let alias_eq =
769 AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; 830 AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
770 preds.push(Binders::wrap_empty(WhereClause::AliasEq(alias_eq))); 831 preds.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
771 } 832 }
772 for bound in &binding.bounds { 833 for bound in &binding.bounds {
773 preds.extend(self.lower_type_bound( 834 preds.extend(self.lower_type_bound(
@@ -787,7 +848,7 @@ impl<'a> TyLoweringContext<'a> {
787 let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { 848 let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
788 bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)).collect() 849 bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)).collect()
789 }); 850 });
790 ReturnTypeImplTrait { bounds: Binders::new(1, predicates) } 851 ReturnTypeImplTrait { bounds: crate::make_only_type_binders(1, predicates) }
791 } 852 }
792} 853}
793 854
@@ -831,17 +892,20 @@ pub fn associated_type_shorthand_candidates<R>(
831 }; 892 };
832 893
833 match res { 894 match res {
834 // FIXME: how to correctly handle higher-ranked bounds here? 895 TypeNs::SelfType(impl_id) => search(
835 TypeNs::SelfType(impl_id) => { 896 // we're _in_ the impl -- the binders get added back later. Correct,
836 search(db.impl_trait(impl_id)?.value.shift_bound_vars_out(DebruijnIndex::ONE)) 897 // but it would be nice to make this more explicit
837 } 898 db.impl_trait(impl_id)?.into_value_and_skipped_binders().0,
899 ),
838 TypeNs::GenericParam(param_id) => { 900 TypeNs::GenericParam(param_id) => {
839 let predicates = db.generic_predicates_for_param(param_id); 901 let predicates = db.generic_predicates_for_param(param_id);
840 let res = predicates.iter().find_map(|pred| match &pred.value.value { 902 let res = predicates.iter().find_map(|pred| match pred.skip_binders().skip_binders() {
841 // FIXME: how to correctly handle higher-ranked bounds here? 903 // FIXME: how to correctly handle higher-ranked bounds here?
842 WhereClause::Implemented(tr) => { 904 WhereClause::Implemented(tr) => search(
843 search(tr.clone().shift_bound_vars_out(DebruijnIndex::ONE)) 905 tr.clone()
844 } 906 .shifted_out_to(&Interner, DebruijnIndex::ONE)
907 .expect("FIXME unexpected higher-ranked trait bound"),
908 ),
845 _ => None, 909 _ => None,
846 }); 910 });
847 if let res @ Some(_) = res { 911 if let res @ Some(_) = res {
@@ -870,7 +934,7 @@ pub(crate) fn field_types_query(
870 db: &dyn HirDatabase, 934 db: &dyn HirDatabase,
871 variant_id: VariantId, 935 variant_id: VariantId,
872) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>> { 936) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>> {
873 let var_data = variant_data(db.upcast(), variant_id); 937 let var_data = variant_id.variant_data(db.upcast());
874 let (resolver, def): (_, GenericDefId) = match variant_id { 938 let (resolver, def): (_, GenericDefId) = match variant_id {
875 VariantId::StructId(it) => (it.resolver(db.upcast()), it.into()), 939 VariantId::StructId(it) => (it.resolver(db.upcast()), it.into()),
876 VariantId::UnionId(it) => (it.resolver(db.upcast()), it.into()), 940 VariantId::UnionId(it) => (it.resolver(db.upcast()), it.into()),
@@ -881,7 +945,7 @@ pub(crate) fn field_types_query(
881 let ctx = 945 let ctx =
882 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); 946 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
883 for (field_id, field_data) in var_data.fields().iter() { 947 for (field_id, field_data) in var_data.fields().iter() {
884 res.insert(field_id, Binders::new(generics.len(), ctx.lower_ty(&field_data.type_ref))) 948 res.insert(field_id, make_binders(&generics, ctx.lower_ty(&field_data.type_ref)))
885 } 949 }
886 Arc::new(res) 950 Arc::new(res)
887} 951}
@@ -915,9 +979,7 @@ pub(crate) fn generic_predicates_for_param_query(
915 }, 979 },
916 WherePredicate::Lifetime { .. } => false, 980 WherePredicate::Lifetime { .. } => false,
917 }) 981 })
918 .flat_map(|pred| { 982 .flat_map(|pred| ctx.lower_where_predicate(pred, true).map(|p| make_binders(&generics, p)))
919 ctx.lower_where_predicate(pred, true).map(|p| Binders::new(generics.len(), p))
920 })
921 .collect() 983 .collect()
922} 984}
923 985
@@ -941,10 +1003,10 @@ pub(crate) fn trait_environment_query(
941 for pred in resolver.where_predicates_in_scope() { 1003 for pred in resolver.where_predicates_in_scope() {
942 for pred in ctx.lower_where_predicate(pred, false) { 1004 for pred in ctx.lower_where_predicate(pred, false) {
943 if let WhereClause::Implemented(tr) = &pred.skip_binders() { 1005 if let WhereClause::Implemented(tr) = &pred.skip_binders() {
944 traits_in_scope.push((tr.self_type_parameter().clone(), tr.hir_trait_id())); 1006 traits_in_scope
1007 .push((tr.self_type_parameter(&Interner).clone(), tr.hir_trait_id()));
945 } 1008 }
946 let program_clause: chalk_ir::ProgramClause<Interner> = 1009 let program_clause: chalk_ir::ProgramClause<Interner> = pred.clone().cast(&Interner);
947 pred.clone().to_chalk(db).cast(&Interner);
948 clauses.push(program_clause.into_from_env_clause(&Interner)); 1010 clauses.push(program_clause.into_from_env_clause(&Interner));
949 } 1011 }
950 } 1012 }
@@ -967,7 +1029,7 @@ pub(crate) fn trait_environment_query(
967 let substs = TyBuilder::type_params_subst(db, trait_id); 1029 let substs = TyBuilder::type_params_subst(db, trait_id);
968 let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs }; 1030 let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs };
969 let pred = WhereClause::Implemented(trait_ref); 1031 let pred = WhereClause::Implemented(trait_ref);
970 let program_clause: chalk_ir::ProgramClause<Interner> = pred.to_chalk(db).cast(&Interner); 1032 let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(&Interner);
971 clauses.push(program_clause.into_from_env_clause(&Interner)); 1033 clauses.push(program_clause.into_from_env_clause(&Interner));
972 } 1034 }
973 1035
@@ -987,9 +1049,7 @@ pub(crate) fn generic_predicates_query(
987 let generics = generics(db.upcast(), def); 1049 let generics = generics(db.upcast(), def);
988 resolver 1050 resolver
989 .where_predicates_in_scope() 1051 .where_predicates_in_scope()
990 .flat_map(|pred| { 1052 .flat_map(|pred| ctx.lower_where_predicate(pred, false).map(|p| make_binders(&generics, p)))
991 ctx.lower_where_predicate(pred, false).map(|p| Binders::new(generics.len(), p))
992 })
993 .collect() 1053 .collect()
994} 1054}
995 1055
@@ -1008,25 +1068,21 @@ pub(crate) fn generic_defaults_query(
1008 .enumerate() 1068 .enumerate()
1009 .map(|(idx, (_, p))| { 1069 .map(|(idx, (_, p))| {
1010 let mut ty = 1070 let mut ty =
1011 p.default.as_ref().map_or(TyKind::Unknown.intern(&Interner), |t| ctx.lower_ty(t)); 1071 p.default.as_ref().map_or(TyKind::Error.intern(&Interner), |t| ctx.lower_ty(t));
1012 1072
1013 // Each default can only refer to previous parameters. 1073 // Each default can only refer to previous parameters.
1014 ty.walk_mut_binders( 1074 ty = crate::fold_free_vars(ty, |bound, binders| {
1015 &mut |ty, binders| match ty.interned_mut() { 1075 if bound.index >= idx && bound.debruijn == DebruijnIndex::INNERMOST {
1016 TyKind::BoundVar(BoundVar { debruijn, index }) if *debruijn == binders => { 1076 // type variable default referring to parameter coming
1017 if *index >= idx { 1077 // after it. This is forbidden (FIXME: report
1018 // type variable default referring to parameter coming 1078 // diagnostic)
1019 // after it. This is forbidden (FIXME: report 1079 TyKind::Error.intern(&Interner)
1020 // diagnostic) 1080 } else {
1021 *ty = TyKind::Unknown.intern(&Interner); 1081 bound.shifted_in_from(binders).to_ty(&Interner)
1022 } 1082 }
1023 } 1083 });
1024 _ => {}
1025 },
1026 DebruijnIndex::INNERMOST,
1027 );
1028 1084
1029 Binders::new(idx, ty) 1085 crate::make_only_type_binders(idx, ty)
1030 }) 1086 })
1031 .collect(); 1087 .collect();
1032 1088
@@ -1039,14 +1095,13 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
1039 let ctx_params = TyLoweringContext::new(db, &resolver) 1095 let ctx_params = TyLoweringContext::new(db, &resolver)
1040 .with_impl_trait_mode(ImplTraitLoweringMode::Variable) 1096 .with_impl_trait_mode(ImplTraitLoweringMode::Variable)
1041 .with_type_param_mode(TypeParamLoweringMode::Variable); 1097 .with_type_param_mode(TypeParamLoweringMode::Variable);
1042 let params = data.params.iter().map(|tr| (&ctx_params).lower_ty(tr)).collect::<Vec<_>>(); 1098 let params = data.params.iter().map(|tr| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
1043 let ctx_ret = TyLoweringContext::new(db, &resolver) 1099 let ctx_ret = TyLoweringContext::new(db, &resolver)
1044 .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) 1100 .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
1045 .with_type_param_mode(TypeParamLoweringMode::Variable); 1101 .with_type_param_mode(TypeParamLoweringMode::Variable);
1046 let ret = (&ctx_ret).lower_ty(&data.ret_type); 1102 let ret = ctx_ret.lower_ty(&data.ret_type);
1047 let generics = generics(db.upcast(), def.into()); 1103 let generics = generics(db.upcast(), def.into());
1048 let num_binders = generics.len(); 1104 make_binders(&generics, CallableSig::from_params_and_return(params, ret, data.is_varargs()))
1049 Binders::new(num_binders, CallableSig::from_params_and_return(params, ret, data.is_varargs()))
1050} 1105}
1051 1106
1052/// Build the declared type of a function. This should not need to look at the 1107/// Build the declared type of a function. This should not need to look at the
@@ -1054,8 +1109,8 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
1054fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> { 1109fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> Binders<Ty> {
1055 let generics = generics(db.upcast(), def.into()); 1110 let generics = generics(db.upcast(), def.into());
1056 let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST); 1111 let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST);
1057 Binders::new( 1112 make_binders(
1058 substs.len(&Interner), 1113 &generics,
1059 TyKind::FnDef(CallableDefId::FunctionId(def).to_chalk(db), substs).intern(&Interner), 1114 TyKind::FnDef(CallableDefId::FunctionId(def).to_chalk(db), substs).intern(&Interner),
1060 ) 1115 )
1061} 1116}
@@ -1068,7 +1123,7 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
1068 let ctx = 1123 let ctx =
1069 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); 1124 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
1070 1125
1071 Binders::new(generics.len(), ctx.lower_ty(&data.type_ref)) 1126 make_binders(&generics, ctx.lower_ty(&data.type_ref))
1072} 1127}
1073 1128
1074/// Build the declared type of a static. 1129/// Build the declared type of a static.
@@ -1077,7 +1132,7 @@ fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders<Ty> {
1077 let resolver = def.resolver(db.upcast()); 1132 let resolver = def.resolver(db.upcast());
1078 let ctx = TyLoweringContext::new(db, &resolver); 1133 let ctx = TyLoweringContext::new(db, &resolver);
1079 1134
1080 Binders::new(0, ctx.lower_ty(&data.type_ref)) 1135 Binders::empty(&Interner, ctx.lower_ty(&data.type_ref))
1081} 1136}
1082 1137
1083fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnSig { 1138fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnSig {
@@ -1087,8 +1142,8 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS
1087 let ctx = 1142 let ctx =
1088 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); 1143 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
1089 let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>(); 1144 let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
1090 let ret = type_for_adt(db, def.into()); 1145 let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
1091 Binders::new(ret.num_binders, CallableSig::from_params_and_return(params, ret.value, false)) 1146 Binders::new(binders, CallableSig::from_params_and_return(params, ret, false))
1092} 1147}
1093 1148
1094/// Build the type of a tuple struct constructor. 1149/// Build the type of a tuple struct constructor.
@@ -1099,8 +1154,8 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders<T
1099 } 1154 }
1100 let generics = generics(db.upcast(), def.into()); 1155 let generics = generics(db.upcast(), def.into());
1101 let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST); 1156 let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST);
1102 Binders::new( 1157 make_binders(
1103 substs.len(&Interner), 1158 &generics,
1104 TyKind::FnDef(CallableDefId::StructId(def).to_chalk(db), substs).intern(&Interner), 1159 TyKind::FnDef(CallableDefId::StructId(def).to_chalk(db), substs).intern(&Interner),
1105 ) 1160 )
1106} 1161}
@@ -1113,8 +1168,8 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
1113 let ctx = 1168 let ctx =
1114 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); 1169 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
1115 let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>(); 1170 let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
1116 let ret = type_for_adt(db, def.parent.into()); 1171 let (ret, binders) = type_for_adt(db, def.parent.into()).into_value_and_skipped_binders();
1117 Binders::new(ret.num_binders, CallableSig::from_params_and_return(params, ret.value, false)) 1172 Binders::new(binders, CallableSig::from_params_and_return(params, ret, false))
1118} 1173}
1119 1174
1120/// Build the type of a tuple enum variant constructor. 1175/// Build the type of a tuple enum variant constructor.
@@ -1126,17 +1181,17 @@ fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -
1126 } 1181 }
1127 let generics = generics(db.upcast(), def.parent.into()); 1182 let generics = generics(db.upcast(), def.parent.into());
1128 let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST); 1183 let substs = generics.bound_vars_subst(DebruijnIndex::INNERMOST);
1129 Binders::new( 1184 make_binders(
1130 substs.len(&Interner), 1185 &generics,
1131 TyKind::FnDef(CallableDefId::EnumVariantId(def).to_chalk(db), substs).intern(&Interner), 1186 TyKind::FnDef(CallableDefId::EnumVariantId(def).to_chalk(db), substs).intern(&Interner),
1132 ) 1187 )
1133} 1188}
1134 1189
1135fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> { 1190fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
1191 let generics = generics(db.upcast(), adt.into());
1136 let b = TyBuilder::adt(db, adt); 1192 let b = TyBuilder::adt(db, adt);
1137 let num_binders = b.remaining();
1138 let ty = b.fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build(); 1193 let ty = b.fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build();
1139 Binders::new(num_binders, ty) 1194 make_binders(&generics, ty)
1140} 1195}
1141 1196
1142fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { 1197fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
@@ -1145,11 +1200,11 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
1145 let ctx = 1200 let ctx =
1146 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); 1201 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
1147 if db.type_alias_data(t).is_extern { 1202 if db.type_alias_data(t).is_extern {
1148 Binders::new(0, TyKind::ForeignType(crate::to_foreign_def_id(t)).intern(&Interner)) 1203 Binders::empty(&Interner, TyKind::Foreign(crate::to_foreign_def_id(t)).intern(&Interner))
1149 } else { 1204 } else {
1150 let type_ref = &db.type_alias_data(t).type_ref; 1205 let type_ref = &db.type_alias_data(t).type_ref;
1151 let inner = ctx.lower_ty(type_ref.as_deref().unwrap_or(&TypeRef::Error)); 1206 let inner = ctx.lower_ty(type_ref.as_deref().unwrap_or(&TypeRef::Error));
1152 Binders::new(generics.len(), inner) 1207 make_binders(&generics, inner)
1153 } 1208 }
1154} 1209}
1155 1210
@@ -1208,19 +1263,21 @@ impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for V
1208/// namespace. 1263/// namespace.
1209pub(crate) fn ty_query(db: &dyn HirDatabase, def: TyDefId) -> Binders<Ty> { 1264pub(crate) fn ty_query(db: &dyn HirDatabase, def: TyDefId) -> Binders<Ty> {
1210 match def { 1265 match def {
1211 TyDefId::BuiltinType(it) => Binders::new(0, TyBuilder::builtin(it)), 1266 TyDefId::BuiltinType(it) => Binders::empty(&Interner, TyBuilder::builtin(it)),
1212 TyDefId::AdtId(it) => type_for_adt(db, it), 1267 TyDefId::AdtId(it) => type_for_adt(db, it),
1213 TyDefId::TypeAliasId(it) => type_for_type_alias(db, it), 1268 TyDefId::TypeAliasId(it) => type_for_type_alias(db, it),
1214 } 1269 }
1215} 1270}
1216 1271
1217pub(crate) fn ty_recover(db: &dyn HirDatabase, _cycle: &[String], def: &TyDefId) -> Binders<Ty> { 1272pub(crate) fn ty_recover(db: &dyn HirDatabase, _cycle: &[String], def: &TyDefId) -> Binders<Ty> {
1218 let num_binders = match *def { 1273 let generics = match *def {
1219 TyDefId::BuiltinType(_) => 0, 1274 TyDefId::BuiltinType(_) => {
1220 TyDefId::AdtId(it) => generics(db.upcast(), it.into()).len(), 1275 return Binders::empty(&Interner, TyKind::Error.intern(&Interner))
1221 TyDefId::TypeAliasId(it) => generics(db.upcast(), it.into()).len(), 1276 }
1277 TyDefId::AdtId(it) => generics(db.upcast(), it.into()),
1278 TyDefId::TypeAliasId(it) => generics(db.upcast(), it.into()),
1222 }; 1279 };
1223 Binders::new(num_binders, TyKind::Unknown.intern(&Interner)) 1280 make_binders(&generics, TyKind::Error.intern(&Interner))
1224} 1281}
1225 1282
1226pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Binders<Ty> { 1283pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Binders<Ty> {
@@ -1240,7 +1297,7 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde
1240 let generics = generics(db.upcast(), impl_id.into()); 1297 let generics = generics(db.upcast(), impl_id.into());
1241 let ctx = 1298 let ctx =
1242 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); 1299 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
1243 Binders::new(generics.len(), ctx.lower_ty(&impl_data.self_ty)) 1300 make_binders(&generics, ctx.lower_ty(&impl_data.self_ty))
1244} 1301}
1245 1302
1246pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty { 1303pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
@@ -1258,7 +1315,7 @@ pub(crate) fn impl_self_ty_recover(
1258 impl_id: &ImplId, 1315 impl_id: &ImplId,
1259) -> Binders<Ty> { 1316) -> Binders<Ty> {
1260 let generics = generics(db.upcast(), (*impl_id).into()); 1317 let generics = generics(db.upcast(), (*impl_id).into());
1261 Binders::new(generics.len(), TyKind::Unknown.intern(&Interner)) 1318 make_binders(&generics, TyKind::Error.intern(&Interner))
1262} 1319}
1263 1320
1264pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> { 1321pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> {
@@ -1266,9 +1323,9 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<
1266 let resolver = impl_id.resolver(db.upcast()); 1323 let resolver = impl_id.resolver(db.upcast());
1267 let ctx = 1324 let ctx =
1268 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); 1325 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
1269 let self_ty = db.impl_self_ty(impl_id); 1326 let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders();
1270 let target_trait = impl_data.target_trait.as_ref()?; 1327 let target_trait = impl_data.target_trait.as_ref()?;
1271 Some(Binders::new(self_ty.num_binders, ctx.lower_trait_ref(target_trait, Some(self_ty.value))?)) 1328 Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, Some(self_ty))?))
1272} 1329}
1273 1330
1274pub(crate) fn return_type_impl_traits( 1331pub(crate) fn return_type_impl_traits(
@@ -1283,13 +1340,12 @@ pub(crate) fn return_type_impl_traits(
1283 .with_type_param_mode(TypeParamLoweringMode::Variable); 1340 .with_type_param_mode(TypeParamLoweringMode::Variable);
1284 let _ret = (&ctx_ret).lower_ty(&data.ret_type); 1341 let _ret = (&ctx_ret).lower_ty(&data.ret_type);
1285 let generics = generics(db.upcast(), def.into()); 1342 let generics = generics(db.upcast(), def.into());
1286 let num_binders = generics.len();
1287 let return_type_impl_traits = 1343 let return_type_impl_traits =
1288 ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() }; 1344 ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() };
1289 if return_type_impl_traits.impl_traits.is_empty() { 1345 if return_type_impl_traits.impl_traits.is_empty() {
1290 None 1346 None
1291 } else { 1347 } else {
1292 Some(Arc::new(Binders::new(num_binders, return_type_impl_traits))) 1348 Some(Arc::new(make_binders(&generics, return_type_impl_traits)))
1293 } 1349 }
1294} 1350}
1295 1351
@@ -1299,3 +1355,7 @@ pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mut
1299 hir_def::type_ref::Mutability::Mut => Mutability::Mut, 1355 hir_def::type_ref::Mutability::Mut => Mutability::Mut,
1300 } 1356 }
1301} 1357}
1358
1359fn make_binders<T: HasInterner<Interner = Interner>>(generics: &Generics, value: T) -> Binders<T> {
1360 crate::make_only_type_binders(generics.len(), value)
1361}
diff --git a/crates/hir_ty/src/mapping.rs b/crates/hir_ty/src/mapping.rs
new file mode 100644
index 000000000..5e86fafe5
--- /dev/null
+++ b/crates/hir_ty/src/mapping.rs
@@ -0,0 +1,154 @@
1//! This module contains the implementations of the `ToChalk` trait, which
2//! handles conversion between our data types and their corresponding types in
3//! Chalk (in both directions); plus some helper functions for more specialized
4//! conversions.
5
6use chalk_solve::rust_ir;
7
8use base_db::salsa::{self, InternKey};
9use hir_def::{ConstParamId, LifetimeParamId, TraitId, TypeAliasId, TypeParamId};
10
11use crate::{
12 chalk_db, db::HirDatabase, AssocTypeId, CallableDefId, ChalkTraitId, FnDefId, ForeignDefId,
13 Interner, OpaqueTyId, PlaceholderIndex,
14};
15
16pub(crate) trait ToChalk {
17 type Chalk;
18 fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk;
19 fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self;
20}
21
22pub(crate) fn from_chalk<T, ChalkT>(db: &dyn HirDatabase, chalk: ChalkT) -> T
23where
24 T: ToChalk<Chalk = ChalkT>,
25{
26 T::from_chalk(db, chalk)
27}
28
29impl ToChalk for hir_def::ImplId {
30 type Chalk = chalk_db::ImplId;
31
32 fn to_chalk(self, _db: &dyn HirDatabase) -> chalk_db::ImplId {
33 chalk_ir::ImplId(self.as_intern_id())
34 }
35
36 fn from_chalk(_db: &dyn HirDatabase, impl_id: chalk_db::ImplId) -> hir_def::ImplId {
37 InternKey::from_intern_id(impl_id.0)
38 }
39}
40
41impl ToChalk for CallableDefId {
42 type Chalk = FnDefId;
43
44 fn to_chalk(self, db: &dyn HirDatabase) -> FnDefId {
45 db.intern_callable_def(self).into()
46 }
47
48 fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDefId {
49 db.lookup_intern_callable_def(fn_def_id.into())
50 }
51}
52
53pub(crate) struct TypeAliasAsValue(pub(crate) TypeAliasId);
54
55impl ToChalk for TypeAliasAsValue {
56 type Chalk = chalk_db::AssociatedTyValueId;
57
58 fn to_chalk(self, _db: &dyn HirDatabase) -> chalk_db::AssociatedTyValueId {
59 rust_ir::AssociatedTyValueId(self.0.as_intern_id())
60 }
61
62 fn from_chalk(
63 _db: &dyn HirDatabase,
64 assoc_ty_value_id: chalk_db::AssociatedTyValueId,
65 ) -> TypeAliasAsValue {
66 TypeAliasAsValue(TypeAliasId::from_intern_id(assoc_ty_value_id.0))
67 }
68}
69
70impl From<FnDefId> for crate::db::InternedCallableDefId {
71 fn from(fn_def_id: FnDefId) -> Self {
72 InternKey::from_intern_id(fn_def_id.0)
73 }
74}
75
76impl From<crate::db::InternedCallableDefId> for FnDefId {
77 fn from(callable_def_id: crate::db::InternedCallableDefId) -> Self {
78 chalk_ir::FnDefId(callable_def_id.as_intern_id())
79 }
80}
81
82impl From<OpaqueTyId> for crate::db::InternedOpaqueTyId {
83 fn from(id: OpaqueTyId) -> Self {
84 InternKey::from_intern_id(id.0)
85 }
86}
87
88impl From<crate::db::InternedOpaqueTyId> for OpaqueTyId {
89 fn from(id: crate::db::InternedOpaqueTyId) -> Self {
90 chalk_ir::OpaqueTyId(id.as_intern_id())
91 }
92}
93
94impl From<chalk_ir::ClosureId<Interner>> for crate::db::InternedClosureId {
95 fn from(id: chalk_ir::ClosureId<Interner>) -> Self {
96 Self::from_intern_id(id.0)
97 }
98}
99
100impl From<crate::db::InternedClosureId> for chalk_ir::ClosureId<Interner> {
101 fn from(id: crate::db::InternedClosureId) -> Self {
102 chalk_ir::ClosureId(id.as_intern_id())
103 }
104}
105
106pub fn to_foreign_def_id(id: TypeAliasId) -> ForeignDefId {
107 chalk_ir::ForeignDefId(salsa::InternKey::as_intern_id(&id))
108}
109
110pub fn from_foreign_def_id(id: ForeignDefId) -> TypeAliasId {
111 salsa::InternKey::from_intern_id(id.0)
112}
113
114pub fn to_assoc_type_id(id: TypeAliasId) -> AssocTypeId {
115 chalk_ir::AssocTypeId(salsa::InternKey::as_intern_id(&id))
116}
117
118pub fn from_assoc_type_id(id: AssocTypeId) -> TypeAliasId {
119 salsa::InternKey::from_intern_id(id.0)
120}
121
122pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> TypeParamId {
123 assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT);
124 let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx));
125 db.lookup_intern_type_param_id(interned_id)
126}
127
128pub fn to_placeholder_idx(db: &dyn HirDatabase, id: TypeParamId) -> PlaceholderIndex {
129 let interned_id = db.intern_type_param_id(id);
130 PlaceholderIndex {
131 ui: chalk_ir::UniverseIndex::ROOT,
132 idx: salsa::InternKey::as_intern_id(&interned_id).as_usize(),
133 }
134}
135
136pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> LifetimeParamId {
137 assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT);
138 let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx));
139 db.lookup_intern_lifetime_param_id(interned_id)
140}
141
142pub fn const_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> ConstParamId {
143 assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT);
144 let interned_id = salsa::InternKey::from_intern_id(salsa::InternId::from(idx.idx));
145 db.lookup_intern_const_param_id(interned_id)
146}
147
148pub fn to_chalk_trait_id(id: TraitId) -> ChalkTraitId {
149 chalk_ir::TraitId(salsa::InternKey::as_intern_id(&id))
150}
151
152pub fn from_chalk_trait_id(id: ChalkTraitId) -> TraitId {
153 salsa::InternKey::from_intern_id(id.0)
154}
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index a76586f0c..48bbcfd9f 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -8,8 +8,8 @@ use arrayvec::ArrayVec;
8use base_db::CrateId; 8use base_db::CrateId;
9use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; 9use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
10use hir_def::{ 10use hir_def::{
11 lang_item::LangItemTarget, AssocContainerId, AssocItemId, FunctionId, GenericDefId, HasModule, 11 lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId,
12 ImplId, Lookup, ModuleId, TraitId, 12 GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId,
13}; 13};
14use hir_expand::name::Name; 14use hir_expand::name::Name;
15use rustc_hash::{FxHashMap, FxHashSet}; 15use rustc_hash::{FxHashMap, FxHashSet};
@@ -19,51 +19,91 @@ use crate::{
19 db::HirDatabase, 19 db::HirDatabase,
20 from_foreign_def_id, 20 from_foreign_def_id,
21 primitive::{self, FloatTy, IntTy, UintTy}, 21 primitive::{self, FloatTy, IntTy, UintTy},
22 static_lifetime,
22 utils::all_super_traits, 23 utils::all_super_traits,
23 AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, FnPointer, FnSig, ForeignDefId, 24 AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner,
24 InEnvironment, Interner, Scalar, Substitution, TraitEnvironment, Ty, TyBuilder, TyKind, 25 Scalar, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
25 TypeWalk,
26}; 26};
27 27
28/// This is used as a key for indexing impls. 28/// This is used as a key for indexing impls.
29#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 29#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
30pub enum TyFingerprint { 30pub enum TyFingerprint {
31 // These are lang item impls:
31 Str, 32 Str,
32 Slice, 33 Slice,
33 Array, 34 Array,
34 Never, 35 Never,
35 RawPtr(Mutability), 36 RawPtr(Mutability),
36 Scalar(Scalar), 37 Scalar(Scalar),
38 // These can have user-defined impls:
37 Adt(hir_def::AdtId), 39 Adt(hir_def::AdtId),
38 Dyn(TraitId), 40 Dyn(TraitId),
39 Tuple(usize),
40 ForeignType(ForeignDefId), 41 ForeignType(ForeignDefId),
41 FnPtr(usize, FnSig), 42 // These only exist for trait impls
43 Unit,
44 Unnameable,
45 Function(u32),
42} 46}
43 47
44impl TyFingerprint { 48impl TyFingerprint {
45 /// Creates a TyFingerprint for looking up an impl. Only certain types can 49 /// Creates a TyFingerprint for looking up an inherent impl. Only certain
46 /// have impls: if we have some `struct S`, we can have an `impl S`, but not 50 /// types can have inherent impls: if we have some `struct S`, we can have
47 /// `impl &S`. Hence, this will return `None` for reference types and such. 51 /// an `impl S`, but not `impl &S`. Hence, this will return `None` for
48 pub fn for_impl(ty: &Ty) -> Option<TyFingerprint> { 52 /// reference types and such.
49 let fp = match *ty.kind(&Interner) { 53 pub fn for_inherent_impl(ty: &Ty) -> Option<TyFingerprint> {
54 let fp = match ty.kind(&Interner) {
50 TyKind::Str => TyFingerprint::Str, 55 TyKind::Str => TyFingerprint::Str,
51 TyKind::Never => TyFingerprint::Never, 56 TyKind::Never => TyFingerprint::Never,
52 TyKind::Slice(..) => TyFingerprint::Slice, 57 TyKind::Slice(..) => TyFingerprint::Slice,
53 TyKind::Array(..) => TyFingerprint::Array, 58 TyKind::Array(..) => TyFingerprint::Array,
54 TyKind::Scalar(scalar) => TyFingerprint::Scalar(scalar), 59 TyKind::Scalar(scalar) => TyFingerprint::Scalar(*scalar),
55 TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(adt), 60 TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt),
56 TyKind::Tuple(cardinality, _) => TyFingerprint::Tuple(cardinality), 61 TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability),
57 TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(mutability), 62 TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id),
58 TyKind::ForeignType(alias_id, ..) => TyFingerprint::ForeignType(alias_id),
59 TyKind::Function(FnPointer { num_args, sig, .. }) => {
60 TyFingerprint::FnPtr(num_args, sig)
61 }
62 TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?, 63 TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?,
63 _ => return None, 64 _ => return None,
64 }; 65 };
65 Some(fp) 66 Some(fp)
66 } 67 }
68
69 /// Creates a TyFingerprint for looking up a trait impl.
70 pub fn for_trait_impl(ty: &Ty) -> Option<TyFingerprint> {
71 let fp = match ty.kind(&Interner) {
72 TyKind::Str => TyFingerprint::Str,
73 TyKind::Never => TyFingerprint::Never,
74 TyKind::Slice(..) => TyFingerprint::Slice,
75 TyKind::Array(..) => TyFingerprint::Array,
76 TyKind::Scalar(scalar) => TyFingerprint::Scalar(*scalar),
77 TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt),
78 TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability),
79 TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id),
80 TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?,
81 TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty),
82 TyKind::Tuple(_, subst) => {
83 let first_ty = subst.interned().get(0).map(|arg| arg.assert_ty_ref(&Interner));
84 if let Some(ty) = first_ty {
85 return TyFingerprint::for_trait_impl(ty);
86 } else {
87 TyFingerprint::Unit
88 }
89 }
90 TyKind::AssociatedType(_, _)
91 | TyKind::OpaqueType(_, _)
92 | TyKind::FnDef(_, _)
93 | TyKind::Closure(_, _)
94 | TyKind::Generator(..)
95 | TyKind::GeneratorWitness(..) => TyFingerprint::Unnameable,
96 TyKind::Function(fn_ptr) => {
97 TyFingerprint::Function(fn_ptr.substitution.0.len(&Interner) as u32)
98 }
99 TyKind::Alias(_)
100 | TyKind::Placeholder(_)
101 | TyKind::BoundVar(_)
102 | TyKind::InferenceVar(_, _)
103 | TyKind::Error => return None,
104 };
105 Some(fp)
106 }
67} 107}
68 108
69pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [ 109pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [
@@ -99,25 +139,38 @@ impl TraitImpls {
99 let mut impls = Self { map: FxHashMap::default() }; 139 let mut impls = Self { map: FxHashMap::default() };
100 140
101 let crate_def_map = db.crate_def_map(krate); 141 let crate_def_map = db.crate_def_map(krate);
102 for (_module_id, module_data) in crate_def_map.modules() { 142 collect_def_map(db, &crate_def_map, &mut impls);
103 for impl_id in module_data.scope.impls() { 143
104 let target_trait = match db.impl_trait(impl_id) { 144 return Arc::new(impls);
105 Some(tr) => tr.value.hir_trait_id(), 145
106 None => continue, 146 fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut TraitImpls) {
107 }; 147 for (_module_id, module_data) in def_map.modules() {
108 let self_ty = db.impl_self_ty(impl_id); 148 for impl_id in module_data.scope.impls() {
109 let self_ty_fp = TyFingerprint::for_impl(&self_ty.value); 149 let target_trait = match db.impl_trait(impl_id) {
110 impls 150 Some(tr) => tr.skip_binders().hir_trait_id(),
111 .map 151 None => continue,
112 .entry(target_trait) 152 };
113 .or_default() 153 let self_ty = db.impl_self_ty(impl_id);
114 .entry(self_ty_fp) 154 let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders());
115 .or_default() 155 impls
116 .push(impl_id); 156 .map
157 .entry(target_trait)
158 .or_default()
159 .entry(self_ty_fp)
160 .or_default()
161 .push(impl_id);
162 }
163
164 // To better support custom derives, collect impls in all unnamed const items.
165 // const _: () = { ... };
166 for konst in module_data.scope.unnamed_consts() {
167 let body = db.body(konst.into());
168 for (_, block_def_map) in body.blocks(db.upcast()) {
169 collect_def_map(db, &block_def_map, impls);
170 }
171 }
117 } 172 }
118 } 173 }
119
120 Arc::new(impls)
121 } 174 }
122 175
123 pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { 176 pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
@@ -143,10 +196,13 @@ impl TraitImpls {
143 } 196 }
144 197
145 /// Queries all trait impls for the given type. 198 /// Queries all trait impls for the given type.
146 pub fn for_self_ty(&self, fp: TyFingerprint) -> impl Iterator<Item = ImplId> + '_ { 199 pub fn for_self_ty_without_blanket_impls(
200 &self,
201 fp: TyFingerprint,
202 ) -> impl Iterator<Item = ImplId> + '_ {
147 self.map 203 self.map
148 .values() 204 .values()
149 .flat_map(move |impls| impls.get(&None).into_iter().chain(impls.get(&Some(fp)))) 205 .flat_map(move |impls| impls.get(&Some(fp)).into_iter())
150 .flat_map(|it| it.iter().copied()) 206 .flat_map(|it| it.iter().copied())
151 } 207 }
152 208
@@ -190,28 +246,43 @@ pub struct InherentImpls {
190 246
191impl InherentImpls { 247impl InherentImpls {
192 pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { 248 pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
193 let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default(); 249 let mut impls = Self { map: FxHashMap::default() };
194 250
195 let crate_def_map = db.crate_def_map(krate); 251 let crate_def_map = db.crate_def_map(krate);
196 for (_module_id, module_data) in crate_def_map.modules() { 252 collect_def_map(db, &crate_def_map, &mut impls);
197 for impl_id in module_data.scope.impls() { 253
198 let data = db.impl_data(impl_id); 254 return Arc::new(impls);
199 if data.target_trait.is_some() { 255
200 continue; 256 fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut InherentImpls) {
257 for (_module_id, module_data) in def_map.modules() {
258 for impl_id in module_data.scope.impls() {
259 let data = db.impl_data(impl_id);
260 if data.target_trait.is_some() {
261 continue;
262 }
263
264 let self_ty = db.impl_self_ty(impl_id);
265 let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders());
266 if let Some(fp) = fp {
267 impls.map.entry(fp).or_default().push(impl_id);
268 }
269 // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
201 } 270 }
202 271
203 let self_ty = db.impl_self_ty(impl_id); 272 // To better support custom derives, collect impls in all unnamed const items.
204 if let Some(fp) = TyFingerprint::for_impl(&self_ty.value) { 273 // const _: () = { ... };
205 map.entry(fp).or_default().push(impl_id); 274 for konst in module_data.scope.unnamed_consts() {
275 let body = db.body(konst.into());
276 for (_, block_def_map) in body.blocks(db.upcast()) {
277 collect_def_map(db, &block_def_map, impls);
278 }
206 } 279 }
207 } 280 }
208 } 281 }
209
210 Arc::new(Self { map })
211 } 282 }
212 283
213 pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] { 284 pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] {
214 match TyFingerprint::for_impl(self_ty) { 285 match TyFingerprint::for_inherent_impl(self_ty) {
215 Some(fp) => self.map.get(&fp).map(|vec| vec.as_ref()).unwrap_or(&[]), 286 Some(fp) => self.map.get(&fp).map(|vec| vec.as_ref()).unwrap_or(&[]),
216 None => &[], 287 None => &[],
217 } 288 }
@@ -222,15 +293,14 @@ impl InherentImpls {
222 } 293 }
223} 294}
224 295
225impl Ty { 296pub fn def_crates(
226 pub fn def_crates( 297 db: &dyn HirDatabase,
227 &self, 298 ty: &Ty,
228 db: &dyn HirDatabase, 299 cur_crate: CrateId,
229 cur_crate: CrateId, 300) -> Option<ArrayVec<CrateId, 2>> {
230 ) -> Option<ArrayVec<CrateId, 2>> { 301 // Types like slice can have inherent impls in several crates, (core and alloc).
231 // Types like slice can have inherent impls in several crates, (core and alloc). 302 // The corresponding impls are marked with lang items, so we can use them to find the required crates.
232 // The corresponding impls are marked with lang items, so we can use them to find the required crates. 303 macro_rules! lang_item_crate {
233 macro_rules! lang_item_crate {
234 ($($name:expr),+ $(,)?) => {{ 304 ($($name:expr),+ $(,)?) => {{
235 let mut v = ArrayVec::<LangItemTarget, 2>::new(); 305 let mut v = ArrayVec::<LangItemTarget, 2>::new();
236 $( 306 $(
@@ -240,51 +310,50 @@ impl Ty {
240 }}; 310 }};
241 } 311 }
242 312
243 let mod_to_crate_ids = |module: ModuleId| Some(std::iter::once(module.krate()).collect()); 313 let mod_to_crate_ids = |module: ModuleId| Some(std::iter::once(module.krate()).collect());
244 314
245 let lang_item_targets = match self.kind(&Interner) { 315 let lang_item_targets = match ty.kind(&Interner) {
246 TyKind::Adt(AdtId(def_id), _) => { 316 TyKind::Adt(AdtId(def_id), _) => {
247 return mod_to_crate_ids(def_id.module(db.upcast())); 317 return mod_to_crate_ids(def_id.module(db.upcast()));
248 } 318 }
249 TyKind::ForeignType(id) => { 319 TyKind::Foreign(id) => {
250 return mod_to_crate_ids( 320 return mod_to_crate_ids(
251 from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast()), 321 from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast()),
252 ); 322 );
253 } 323 }
254 TyKind::Scalar(Scalar::Bool) => lang_item_crate!("bool"), 324 TyKind::Scalar(Scalar::Bool) => lang_item_crate!("bool"),
255 TyKind::Scalar(Scalar::Char) => lang_item_crate!("char"), 325 TyKind::Scalar(Scalar::Char) => lang_item_crate!("char"),
256 TyKind::Scalar(Scalar::Float(f)) => match f { 326 TyKind::Scalar(Scalar::Float(f)) => match f {
257 // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime) 327 // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime)
258 FloatTy::F32 => lang_item_crate!("f32", "f32_runtime"), 328 FloatTy::F32 => lang_item_crate!("f32", "f32_runtime"),
259 FloatTy::F64 => lang_item_crate!("f64", "f64_runtime"), 329 FloatTy::F64 => lang_item_crate!("f64", "f64_runtime"),
260 }, 330 },
261 &TyKind::Scalar(Scalar::Int(t)) => { 331 &TyKind::Scalar(Scalar::Int(t)) => {
262 lang_item_crate!(primitive::int_ty_to_string(t)) 332 lang_item_crate!(primitive::int_ty_to_string(t))
263 } 333 }
264 &TyKind::Scalar(Scalar::Uint(t)) => { 334 &TyKind::Scalar(Scalar::Uint(t)) => {
265 lang_item_crate!(primitive::uint_ty_to_string(t)) 335 lang_item_crate!(primitive::uint_ty_to_string(t))
266 } 336 }
267 TyKind::Str => lang_item_crate!("str_alloc", "str"), 337 TyKind::Str => lang_item_crate!("str_alloc", "str"),
268 TyKind::Slice(_) => lang_item_crate!("slice_alloc", "slice"), 338 TyKind::Slice(_) => lang_item_crate!("slice_alloc", "slice"),
269 TyKind::Raw(Mutability::Not, _) => lang_item_crate!("const_ptr"), 339 TyKind::Raw(Mutability::Not, _) => lang_item_crate!("const_ptr"),
270 TyKind::Raw(Mutability::Mut, _) => lang_item_crate!("mut_ptr"), 340 TyKind::Raw(Mutability::Mut, _) => lang_item_crate!("mut_ptr"),
271 TyKind::Dyn(_) => { 341 TyKind::Dyn(_) => {
272 return self.dyn_trait().and_then(|trait_| { 342 return ty.dyn_trait().and_then(|trait_| {
273 mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast())) 343 mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast()))
274 }); 344 });
275 } 345 }
276 _ => return None, 346 _ => return None,
277 }; 347 };
278 let res = lang_item_targets 348 let res = lang_item_targets
279 .into_iter() 349 .into_iter()
280 .filter_map(|it| match it { 350 .filter_map(|it| match it {
281 LangItemTarget::ImplDefId(it) => Some(it), 351 LangItemTarget::ImplDefId(it) => Some(it),
282 _ => None, 352 _ => None,
283 }) 353 })
284 .map(|it| it.lookup(db.upcast()).container.krate()) 354 .map(|it| it.lookup(db.upcast()).container.krate())
285 .collect(); 355 .collect();
286 Some(res) 356 Some(res)
287 }
288} 357}
289 358
290/// Look up the method with the given name, returning the actual autoderefed 359/// Look up the method with the given name, returning the actual autoderefed
@@ -453,7 +522,8 @@ fn iterate_method_candidates_with_autoref(
453 } 522 }
454 let refed = Canonical { 523 let refed = Canonical {
455 binders: deref_chain[0].binders.clone(), 524 binders: deref_chain[0].binders.clone(),
456 value: TyKind::Ref(Mutability::Not, deref_chain[0].value.clone()).intern(&Interner), 525 value: TyKind::Ref(Mutability::Not, static_lifetime(), deref_chain[0].value.clone())
526 .intern(&Interner),
457 }; 527 };
458 if iterate_method_candidates_by_receiver( 528 if iterate_method_candidates_by_receiver(
459 &refed, 529 &refed,
@@ -470,7 +540,8 @@ fn iterate_method_candidates_with_autoref(
470 } 540 }
471 let ref_muted = Canonical { 541 let ref_muted = Canonical {
472 binders: deref_chain[0].binders.clone(), 542 binders: deref_chain[0].binders.clone(),
473 value: TyKind::Ref(Mutability::Mut, deref_chain[0].value.clone()).intern(&Interner), 543 value: TyKind::Ref(Mutability::Mut, static_lifetime(), deref_chain[0].value.clone())
544 .intern(&Interner),
474 }; 545 };
475 if iterate_method_candidates_by_receiver( 546 if iterate_method_candidates_by_receiver(
476 &ref_muted, 547 &ref_muted,
@@ -592,6 +663,7 @@ fn iterate_trait_method_candidates(
592 } 663 }
593 } 664 }
594 known_implemented = true; 665 known_implemented = true;
666 // FIXME: we shouldn't be ignoring the binders here
595 if callback(&self_ty.value, *item) { 667 if callback(&self_ty.value, *item) {
596 return true; 668 return true;
597 } 669 }
@@ -609,7 +681,7 @@ fn iterate_inherent_methods(
609 visible_from_module: Option<ModuleId>, 681 visible_from_module: Option<ModuleId>,
610 callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, 682 callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
611) -> bool { 683) -> bool {
612 let def_crates = match self_ty.value.def_crates(db, krate) { 684 let def_crates = match def_crates(db, &self_ty.value, krate) {
613 Some(k) => k, 685 Some(k) => k,
614 None => return false, 686 None => return false,
615 }; 687 };
@@ -709,10 +781,11 @@ pub(crate) fn inherent_impl_substs(
709) -> Option<Substitution> { 781) -> Option<Substitution> {
710 // we create a var for each type parameter of the impl; we need to keep in 782 // we create a var for each type parameter of the impl; we need to keep in
711 // mind here that `self_ty` might have vars of its own 783 // mind here that `self_ty` might have vars of its own
784 let self_ty_vars = self_ty.binders.len(&Interner);
712 let vars = TyBuilder::subst_for_def(db, impl_id) 785 let vars = TyBuilder::subst_for_def(db, impl_id)
713 .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.binders.len(&Interner)) 786 .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty_vars)
714 .build(); 787 .build();
715 let self_ty_with_vars = db.impl_self_ty(impl_id).subst(&vars); 788 let self_ty_with_vars = db.impl_self_ty(impl_id).substitute(&Interner, &vars);
716 let mut kinds = self_ty.binders.interned().to_vec(); 789 let mut kinds = self_ty.binders.interned().to_vec();
717 kinds.extend( 790 kinds.extend(
718 iter::repeat(chalk_ir::WithKind::new( 791 iter::repeat(chalk_ir::WithKind::new(
@@ -725,33 +798,27 @@ pub(crate) fn inherent_impl_substs(
725 binders: CanonicalVarKinds::from_iter(&Interner, kinds), 798 binders: CanonicalVarKinds::from_iter(&Interner, kinds),
726 value: (self_ty_with_vars, self_ty.value.clone()), 799 value: (self_ty_with_vars, self_ty.value.clone()),
727 }; 800 };
728 let substs = super::infer::unify(&tys); 801 let substs = super::infer::unify(&tys)?;
729 // We only want the substs for the vars we added, not the ones from self_ty. 802 // We only want the substs for the vars we added, not the ones from self_ty.
730 // Also, if any of the vars we added are still in there, we replace them by 803 // Also, if any of the vars we added are still in there, we replace them by
731 // Unknown. I think this can only really happen if self_ty contained 804 // Unknown. I think this can only really happen if self_ty contained
732 // Unknown, and in that case we want the result to contain Unknown in those 805 // Unknown, and in that case we want the result to contain Unknown in those
733 // places again. 806 // places again.
734 substs 807 let suffix =
735 .map(|s| fallback_bound_vars(s.suffix(vars.len(&Interner)), self_ty.binders.len(&Interner))) 808 Substitution::from_iter(&Interner, substs.iter(&Interner).cloned().skip(self_ty_vars));
809 Some(fallback_bound_vars(suffix, self_ty_vars))
736} 810}
737 811
738/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past 812/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
739/// num_vars_to_keep) by `TyKind::Unknown`. 813/// num_vars_to_keep) by `TyKind::Unknown`.
740fn fallback_bound_vars(s: Substitution, num_vars_to_keep: usize) -> Substitution { 814fn fallback_bound_vars(s: Substitution, num_vars_to_keep: usize) -> Substitution {
741 s.fold_binders( 815 crate::fold_free_vars(s, |bound, binders| {
742 &mut |ty, binders| { 816 if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
743 if let TyKind::BoundVar(bound) = ty.kind(&Interner) { 817 TyKind::Error.intern(&Interner)
744 if bound.index >= num_vars_to_keep && bound.debruijn >= binders { 818 } else {
745 TyKind::Unknown.intern(&Interner) 819 bound.shifted_in_from(binders).to_ty(&Interner)
746 } else { 820 }
747 ty 821 })
748 }
749 } else {
750 ty
751 }
752 },
753 DebruijnIndex::INNERMOST,
754 )
755} 822}
756 823
757fn transform_receiver_ty( 824fn transform_receiver_ty(
@@ -774,7 +841,7 @@ fn transform_receiver_ty(
774 AssocContainerId::ModuleId(_) => unreachable!(), 841 AssocContainerId::ModuleId(_) => unreachable!(),
775 }; 842 };
776 let sig = db.callable_item_signature(function_id.into()); 843 let sig = db.callable_item_signature(function_id.into());
777 Some(sig.value.params()[0].clone().subst_bound_vars(&substs)) 844 Some(sig.map(|s| s.params()[0].clone()).substitute(&Interner, &substs))
778} 845}
779 846
780pub fn implements_trait( 847pub fn implements_trait(
@@ -800,7 +867,7 @@ pub fn implements_trait_unique(
800 let goal = generic_implements_goal(db, env, trait_, ty.clone()); 867 let goal = generic_implements_goal(db, env, trait_, ty.clone());
801 let solution = db.trait_solve(krate, goal); 868 let solution = db.trait_solve(krate, goal);
802 869
803 matches!(solution, Some(crate::traits::Solution::Unique(_))) 870 matches!(solution, Some(crate::Solution::Unique(_)))
804} 871}
805 872
806/// This creates Substs for a trait with the given Self type and type variables 873/// This creates Substs for a trait with the given Self type and type variables
@@ -826,7 +893,7 @@ fn generic_implements_goal(
826 let obligation = trait_ref.cast(&Interner); 893 let obligation = trait_ref.cast(&Interner);
827 Canonical { 894 Canonical {
828 binders: CanonicalVarKinds::from_iter(&Interner, kinds), 895 binders: CanonicalVarKinds::from_iter(&Interner, kinds),
829 value: InEnvironment::new(env.env.clone(), obligation), 896 value: InEnvironment::new(&env.env, obligation),
830 } 897 }
831} 898}
832 899
@@ -837,7 +904,9 @@ fn autoderef_method_receiver(
837) -> Vec<Canonical<Ty>> { 904) -> Vec<Canonical<Ty>> {
838 let mut deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect(); 905 let mut deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect();
839 // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) 906 // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!)
840 if let Some(TyKind::Array(parameters)) = deref_chain.last().map(|ty| ty.value.kind(&Interner)) { 907 if let Some(TyKind::Array(parameters, _)) =
908 deref_chain.last().map(|ty| ty.value.kind(&Interner))
909 {
841 let kinds = deref_chain.last().unwrap().binders.clone(); 910 let kinds = deref_chain.last().unwrap().binders.clone();
842 let unsized_ty = TyKind::Slice(parameters.clone()).intern(&Interner); 911 let unsized_ty = TyKind::Slice(parameters.clone()).intern(&Interner);
843 deref_chain.push(Canonical { value: unsized_ty, binders: kinds }) 912 deref_chain.push(Canonical { value: unsized_ty, binders: kinds })
diff --git a/crates/hir_ty/src/op.rs b/crates/hir_ty/src/op.rs
index 90dd31a35..0222de2bc 100644
--- a/crates/hir_ty/src/op.rs
+++ b/crates/hir_ty/src/op.rs
@@ -9,22 +9,56 @@ pub(super) fn binary_op_return_ty(op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Ty {
9 BinaryOp::LogicOp(_) | BinaryOp::CmpOp(_) => TyKind::Scalar(Scalar::Bool).intern(&Interner), 9 BinaryOp::LogicOp(_) | BinaryOp::CmpOp(_) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
10 BinaryOp::Assignment { .. } => TyBuilder::unit(), 10 BinaryOp::Assignment { .. } => TyBuilder::unit(),
11 BinaryOp::ArithOp(ArithOp::Shl) | BinaryOp::ArithOp(ArithOp::Shr) => { 11 BinaryOp::ArithOp(ArithOp::Shl) | BinaryOp::ArithOp(ArithOp::Shr) => {
12 match lhs_ty.kind(&Interner) { 12 // all integer combinations are valid here
13 if matches!(
14 lhs_ty.kind(&Interner),
13 TyKind::Scalar(Scalar::Int(_)) 15 TyKind::Scalar(Scalar::Int(_))
14 | TyKind::Scalar(Scalar::Uint(_)) 16 | TyKind::Scalar(Scalar::Uint(_))
15 | TyKind::Scalar(Scalar::Float(_)) => lhs_ty, 17 | TyKind::InferenceVar(_, TyVariableKind::Integer)
16 TyKind::InferenceVar(_, TyVariableKind::Integer) 18 ) && matches!(
17 | TyKind::InferenceVar(_, TyVariableKind::Float) => lhs_ty, 19 rhs_ty.kind(&Interner),
18 _ => TyKind::Unknown.intern(&Interner), 20 TyKind::Scalar(Scalar::Int(_))
21 | TyKind::Scalar(Scalar::Uint(_))
22 | TyKind::InferenceVar(_, TyVariableKind::Integer)
23 ) {
24 lhs_ty
25 } else {
26 TyKind::Error.intern(&Interner)
19 } 27 }
20 } 28 }
21 BinaryOp::ArithOp(_) => match rhs_ty.kind(&Interner) { 29 BinaryOp::ArithOp(_) => match (lhs_ty.kind(&Interner), rhs_ty.kind(&Interner)) {
22 TyKind::Scalar(Scalar::Int(_)) 30 // (int, int) | (uint, uint) | (float, float)
23 | TyKind::Scalar(Scalar::Uint(_)) 31 (TyKind::Scalar(Scalar::Int(_)), TyKind::Scalar(Scalar::Int(_)))
24 | TyKind::Scalar(Scalar::Float(_)) => rhs_ty, 32 | (TyKind::Scalar(Scalar::Uint(_)), TyKind::Scalar(Scalar::Uint(_)))
25 TyKind::InferenceVar(_, TyVariableKind::Integer) 33 | (TyKind::Scalar(Scalar::Float(_)), TyKind::Scalar(Scalar::Float(_))) => rhs_ty,
26 | TyKind::InferenceVar(_, TyVariableKind::Float) => rhs_ty, 34 // ({int}, int) | ({int}, uint)
27 _ => TyKind::Unknown.intern(&Interner), 35 (TyKind::InferenceVar(_, TyVariableKind::Integer), TyKind::Scalar(Scalar::Int(_)))
36 | (TyKind::InferenceVar(_, TyVariableKind::Integer), TyKind::Scalar(Scalar::Uint(_))) => {
37 rhs_ty
38 }
39 // (int, {int}) | (uint, {int})
40 (TyKind::Scalar(Scalar::Int(_)), TyKind::InferenceVar(_, TyVariableKind::Integer))
41 | (TyKind::Scalar(Scalar::Uint(_)), TyKind::InferenceVar(_, TyVariableKind::Integer)) => {
42 lhs_ty
43 }
44 // ({float} | float)
45 (TyKind::InferenceVar(_, TyVariableKind::Float), TyKind::Scalar(Scalar::Float(_))) => {
46 rhs_ty
47 }
48 // (float, {float})
49 (TyKind::Scalar(Scalar::Float(_)), TyKind::InferenceVar(_, TyVariableKind::Float)) => {
50 lhs_ty
51 }
52 // ({int}, {int}) | ({float}, {float})
53 (
54 TyKind::InferenceVar(_, TyVariableKind::Integer),
55 TyKind::InferenceVar(_, TyVariableKind::Integer),
56 )
57 | (
58 TyKind::InferenceVar(_, TyVariableKind::Float),
59 TyKind::InferenceVar(_, TyVariableKind::Float),
60 ) => rhs_ty,
61 _ => TyKind::Error.intern(&Interner),
28 }, 62 },
29 } 63 }
30} 64}
@@ -37,10 +71,10 @@ pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty {
37 TyKind::Scalar(_) | TyKind::Str => lhs_ty, 71 TyKind::Scalar(_) | TyKind::Str => lhs_ty,
38 TyKind::InferenceVar(_, TyVariableKind::Integer) 72 TyKind::InferenceVar(_, TyVariableKind::Integer)
39 | TyKind::InferenceVar(_, TyVariableKind::Float) => lhs_ty, 73 | TyKind::InferenceVar(_, TyVariableKind::Float) => lhs_ty,
40 _ => TyKind::Unknown.intern(&Interner), 74 _ => TyKind::Error.intern(&Interner),
41 }, 75 },
42 BinaryOp::ArithOp(ArithOp::Shl) | BinaryOp::ArithOp(ArithOp::Shr) => { 76 BinaryOp::ArithOp(ArithOp::Shl) | BinaryOp::ArithOp(ArithOp::Shr) => {
43 TyKind::Unknown.intern(&Interner) 77 TyKind::Error.intern(&Interner)
44 } 78 }
45 BinaryOp::CmpOp(CmpOp::Ord { .. }) 79 BinaryOp::CmpOp(CmpOp::Ord { .. })
46 | BinaryOp::Assignment { op: Some(_) } 80 | BinaryOp::Assignment { op: Some(_) }
@@ -50,7 +84,7 @@ pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty {
50 | TyKind::Scalar(Scalar::Float(_)) => lhs_ty, 84 | TyKind::Scalar(Scalar::Float(_)) => lhs_ty,
51 TyKind::InferenceVar(_, TyVariableKind::Integer) 85 TyKind::InferenceVar(_, TyVariableKind::Integer)
52 | TyKind::InferenceVar(_, TyVariableKind::Float) => lhs_ty, 86 | TyKind::InferenceVar(_, TyVariableKind::Float) => lhs_ty,
53 _ => TyKind::Unknown.intern(&Interner), 87 _ => TyKind::Error.intern(&Interner),
54 }, 88 },
55 } 89 }
56} 90}
diff --git a/crates/hir_ty/src/primitive.rs b/crates/hir_ty/src/primitive.rs
index 2449addfb..d7f48c69a 100644
--- a/crates/hir_ty/src/primitive.rs
+++ b/crates/hir_ty/src/primitive.rs
@@ -1,7 +1,4 @@
1//! Defines primitive types, which have a couple of peculiarities: 1//! A few helper functions for dealing with primitives.
2//!
3//! * during type inference, they can be uncertain (ie, `let x = 92;`)
4//! * they don't belong to any particular crate.
5 2
6pub use chalk_ir::{FloatTy, IntTy, UintTy}; 3pub use chalk_ir::{FloatTy, IntTy, UintTy};
7pub use hir_def::builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}; 4pub use hir_def::builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint};
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index 86e3d8b86..6588aa46c 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -1065,12 +1065,211 @@ fn macro_in_arm() {
1065 } 1065 }
1066 "#, 1066 "#,
1067 expect![[r#" 1067 expect![[r#"
1068 !0..2 '()': ()
1068 51..110 '{ ... }; }': () 1069 51..110 '{ ... }; }': ()
1069 61..62 'x': u32 1070 61..62 'x': u32
1070 65..107 'match ... }': u32 1071 65..107 'match ... }': u32
1071 71..73 '()': () 1072 71..73 '()': ()
1072 84..91 'unit!()': ()
1073 95..100 '92u32': u32 1073 95..100 '92u32': u32
1074 "#]], 1074 "#]],
1075 ); 1075 );
1076} 1076}
1077
1078#[test]
1079fn macro_in_type_alias_position() {
1080 check_infer(
1081 r#"
1082 macro_rules! U32 {
1083 () => { u32 };
1084 }
1085
1086 trait Foo {
1087 type Ty;
1088 }
1089
1090 impl<T> Foo for T {
1091 type Ty = U32!();
1092 }
1093
1094 type TayTo = U32!();
1095
1096 fn testy() {
1097 let a: <() as Foo>::Ty;
1098 let b: TayTo;
1099 }
1100 "#,
1101 expect![[r#"
1102 147..196 '{ ...yTo; }': ()
1103 157..158 'a': u32
1104 185..186 'b': u32
1105 "#]],
1106 );
1107}
1108
1109#[test]
1110fn nested_macro_in_type_alias_position() {
1111 check_infer(
1112 r#"
1113 macro_rules! U32Inner2 {
1114 () => { u32 };
1115 }
1116
1117 macro_rules! U32Inner1 {
1118 () => { U32Inner2!() };
1119 }
1120
1121 macro_rules! U32 {
1122 () => { U32Inner1!() };
1123 }
1124
1125 trait Foo {
1126 type Ty;
1127 }
1128
1129 impl<T> Foo for T {
1130 type Ty = U32!();
1131 }
1132
1133 type TayTo = U32!();
1134
1135 fn testy() {
1136 let a: <() as Foo>::Ty;
1137 let b: TayTo;
1138 }
1139 "#,
1140 expect![[r#"
1141 259..308 '{ ...yTo; }': ()
1142 269..270 'a': u32
1143 297..298 'b': u32
1144 "#]],
1145 );
1146}
1147
1148#[test]
1149fn macros_in_type_alias_position_generics() {
1150 check_infer(
1151 r#"
1152 struct Foo<A, B>(A, B);
1153
1154 macro_rules! U32 {
1155 () => { u32 };
1156 }
1157
1158 macro_rules! Bar {
1159 () => { Foo<U32!(), U32!()> };
1160 }
1161
1162 trait Moo {
1163 type Ty;
1164 }
1165
1166 impl<T> Moo for T {
1167 type Ty = Bar!();
1168 }
1169
1170 type TayTo = Bar!();
1171
1172 fn main() {
1173 let a: <() as Moo>::Ty;
1174 let b: TayTo;
1175 }
1176 "#,
1177 expect![[r#"
1178 228..277 '{ ...yTo; }': ()
1179 238..239 'a': Foo<u32, u32>
1180 266..267 'b': Foo<u32, u32>
1181 "#]],
1182 );
1183}
1184
1185#[test]
1186fn macros_in_type_position() {
1187 check_infer(
1188 r#"
1189 struct Foo<A, B>(A, B);
1190
1191 macro_rules! U32 {
1192 () => { u32 };
1193 }
1194
1195 macro_rules! Bar {
1196 () => { Foo<U32!(), U32!()> };
1197 }
1198
1199 fn main() {
1200 let a: Bar!();
1201 }
1202 "#,
1203 expect![[r#"
1204 133..155 '{ ...!(); }': ()
1205 143..144 'a': Foo<u32, u32>
1206 "#]],
1207 );
1208}
1209
1210#[test]
1211fn macros_in_type_generics() {
1212 check_infer(
1213 r#"
1214 struct Foo<A, B>(A, B);
1215
1216 macro_rules! U32 {
1217 () => { u32 };
1218 }
1219
1220 macro_rules! Bar {
1221 () => { Foo<U32!(), U32!()> };
1222 }
1223
1224 trait Moo {
1225 type Ty;
1226 }
1227
1228 impl<T> Moo for T {
1229 type Ty = Foo<Bar!(), Bar!()>;
1230 }
1231
1232 type TayTo = Foo<Bar!(), U32!()>;
1233
1234 fn main() {
1235 let a: <() as Moo>::Ty;
1236 let b: TayTo;
1237 }
1238 "#,
1239 expect![[r#"
1240 254..303 '{ ...yTo; }': ()
1241 264..265 'a': Foo<Foo<u32, u32>, Foo<u32, u32>>
1242 292..293 'b': Foo<Foo<u32, u32>, u32>
1243 "#]],
1244 );
1245}
1246
1247#[test]
1248fn infinitely_recursive_macro_type() {
1249 check_infer(
1250 r#"
1251 struct Bar<T, X>(T, X);
1252
1253 macro_rules! Foo {
1254 () => { Foo!() }
1255 }
1256
1257 macro_rules! U32 {
1258 () => { u32 }
1259 }
1260
1261 type A = Foo!();
1262 type B = Bar<Foo!(), U32!()>;
1263
1264 fn main() {
1265 let a: A;
1266 let b: B;
1267 }
1268 "#,
1269 expect![[r#"
1270 166..197 '{ ...: B; }': ()
1271 176..177 'a': {unknown}
1272 190..191 'b': Bar<{unknown}, u32>
1273 "#]],
1274 );
1275}
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index 61f18b0d2..a4c132bc5 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1292,3 +1292,60 @@ mod b {
1292 "#]], 1292 "#]],
1293 ) 1293 )
1294} 1294}
1295
1296#[test]
1297fn trait_impl_in_unnamed_const() {
1298 check_types(
1299 r#"
1300struct S;
1301
1302trait Tr {
1303 fn method(&self) -> u16;
1304}
1305
1306const _: () = {
1307 impl Tr for S {}
1308};
1309
1310fn f() {
1311 S.method();
1312 //^^^^^^^^^^ u16
1313}
1314 "#,
1315 );
1316}
1317
1318#[test]
1319fn inherent_impl_in_unnamed_const() {
1320 check_types(
1321 r#"
1322struct S;
1323
1324const _: () = {
1325 impl S {
1326 fn method(&self) -> u16 { 0 }
1327
1328 pub(super) fn super_method(&self) -> u16 { 0 }
1329
1330 pub(crate) fn crate_method(&self) -> u16 { 0 }
1331
1332 pub fn pub_method(&self) -> u16 { 0 }
1333 }
1334};
1335
1336fn f() {
1337 S.method();
1338 //^^^^^^^^^^ u16
1339
1340 S.super_method();
1341 //^^^^^^^^^^^^^^^^ u16
1342
1343 S.crate_method();
1344 //^^^^^^^^^^^^^^^^ u16
1345
1346 S.pub_method();
1347 //^^^^^^^^^^^^^^ u16
1348}
1349 "#,
1350 );
1351}
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 85a28e76b..f514b3efe 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -1,6 +1,6 @@
1use expect_test::expect; 1use expect_test::expect;
2 2
3use super::{check_infer, check_infer_with_mismatches}; 3use super::{check_infer, check_infer_with_mismatches, check_types};
4 4
5#[test] 5#[test]
6fn infer_pattern() { 6fn infer_pattern() {
@@ -825,3 +825,29 @@ fn foo(foo: Foo) {
825 "#]], 825 "#]],
826 ); 826 );
827} 827}
828
829#[test]
830fn macro_pat() {
831 check_types(
832 r#"
833macro_rules! pat {
834 ($name:ident) => { Enum::Variant1($name) }
835}
836
837enum Enum {
838 Variant1(u8),
839 Variant2,
840}
841
842fn f(e: Enum) {
843 match e {
844 pat!(bind) => {
845 bind;
846 //^^^^ u8
847 }
848 Enum::Variant2 => {}
849 }
850}
851 "#,
852 )
853}
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index b69f86050..9cd9f473d 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -974,3 +974,41 @@ fn param_overrides_fn() {
974 "#, 974 "#,
975 ) 975 )
976} 976}
977
978#[test]
979fn lifetime_from_chalk_during_deref() {
980 check_types(
981 r#"
982 #[lang = "deref"]
983 pub trait Deref {
984 type Target;
985 }
986
987 struct Box<T: ?Sized> {}
988 impl<T> Deref for Box<T> {
989 type Target = T;
990
991 fn deref(&self) -> &Self::Target {
992 loop {}
993 }
994 }
995
996 trait Iterator {
997 type Item;
998 }
999
1000 pub struct Iter<'a, T: 'a> {
1001 inner: Box<dyn IterTrait<'a, T, Item = &'a T> + 'a>,
1002 }
1003
1004 trait IterTrait<'a, T: 'a>: Iterator<Item = &'a T> {
1005 fn clone_box(&self);
1006 }
1007
1008 fn clone_iter<T>(s: Iter<T>) {
1009 s.inner.clone_box();
1010 //^^^^^^^^^^^^^^^^^^^ ()
1011 }
1012 "#,
1013 )
1014}
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 361cd6302..5948d0bc2 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -1765,6 +1765,24 @@ fn main() {
1765} 1765}
1766 1766
1767#[test] 1767#[test]
1768fn shadowing_primitive_with_inner_items() {
1769 check_types(
1770 r#"
1771struct i32;
1772struct Foo;
1773
1774impl i32 { fn foo(&self) -> Foo { Foo } }
1775
1776fn main() {
1777 fn inner() {}
1778 let x: i32 = i32;
1779 x.foo();
1780 //^ Foo
1781}"#,
1782 );
1783}
1784
1785#[test]
1768fn not_shadowing_primitive_by_module() { 1786fn not_shadowing_primitive_by_module() {
1769 check_types( 1787 check_types(
1770 r#" 1788 r#"
@@ -2564,3 +2582,36 @@ fn f() {
2564 "#, 2582 "#,
2565 ) 2583 )
2566} 2584}
2585
2586#[test]
2587fn infer_type_alias_variant() {
2588 check_infer(
2589 r#"
2590type Qux = Foo;
2591enum Foo {
2592 Bar(i32),
2593 Baz { baz: f32 }
2594}
2595
2596fn f() {
2597 match Foo::Bar(3) {
2598 Qux::Bar(bar) => (),
2599 Qux::Baz { baz } => (),
2600 }
2601}
2602 "#,
2603 expect![[r#"
2604 72..166 '{ ... } }': ()
2605 78..164 'match ... }': ()
2606 84..92 'Foo::Bar': Bar(i32) -> Foo
2607 84..95 'Foo::Bar(3)': Foo
2608 93..94 '3': i32
2609 106..119 'Qux::Bar(bar)': Foo
2610 115..118 'bar': i32
2611 123..125 '()': ()
2612 135..151 'Qux::B... baz }': Foo
2613 146..149 'baz': f32
2614 155..157 '()': ()
2615 "#]],
2616 )
2617}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 65b71fdfa..ffc7c8ef4 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -263,15 +263,14 @@ mod ops {
263fn infer_from_bound_1() { 263fn infer_from_bound_1() {
264 check_infer( 264 check_infer(
265 r#" 265 r#"
266 trait Trait<T> {} 266trait Trait<T> {}
267 struct S<T>(T); 267struct S<T>(T);
268 impl<U> Trait<U> for S<U> {} 268impl<U> Trait<U> for S<U> {}
269 fn foo<T: Trait<u32>>(t: T) {} 269fn foo<T: Trait<u32>>(t: T) {}
270 fn test() { 270fn test() {
271 let s = S(unknown); 271 let s = S(unknown);
272 foo(s); 272 foo(s);
273 } 273}"#,
274 "#,
275 expect![[r#" 274 expect![[r#"
276 85..86 't': T 275 85..86 't': T
277 91..93 '{}': () 276 91..93 '{}': ()
@@ -291,15 +290,14 @@ fn infer_from_bound_1() {
291fn infer_from_bound_2() { 290fn infer_from_bound_2() {
292 check_infer( 291 check_infer(
293 r#" 292 r#"
294 trait Trait<T> {} 293trait Trait<T> {}
295 struct S<T>(T); 294struct S<T>(T);
296 impl<U> Trait<U> for S<U> {} 295impl<U> Trait<U> for S<U> {}
297 fn foo<U, T: Trait<U>>(t: T) -> U {} 296fn foo<U, T: Trait<U>>(t: T) -> U {}
298 fn test() { 297fn test() {
299 let s = S(unknown); 298 let s = S(unknown);
300 let x: u32 = foo(s); 299 let x: u32 = foo(s);
301 } 300}"#,
302 "#,
303 expect![[r#" 301 expect![[r#"
304 86..87 't': T 302 86..87 't': T
305 97..99 '{}': () 303 97..99 '{}': ()
@@ -321,13 +319,12 @@ fn trait_default_method_self_bound_implements_trait() {
321 cov_mark::check!(trait_self_implements_self); 319 cov_mark::check!(trait_self_implements_self);
322 check_infer( 320 check_infer(
323 r#" 321 r#"
324 trait Trait { 322trait Trait {
325 fn foo(&self) -> i64; 323 fn foo(&self) -> i64;
326 fn bar(&self) -> { 324 fn bar(&self) -> {
327 let x = self.foo(); 325 let x = self.foo();
328 } 326 }
329 } 327}"#,
330 "#,
331 expect![[r#" 328 expect![[r#"
332 26..30 'self': &Self 329 26..30 'self': &Self
333 52..56 'self': &Self 330 52..56 'self': &Self
@@ -343,15 +340,14 @@ fn trait_default_method_self_bound_implements_trait() {
343fn trait_default_method_self_bound_implements_super_trait() { 340fn trait_default_method_self_bound_implements_super_trait() {
344 check_infer( 341 check_infer(
345 r#" 342 r#"
346 trait SuperTrait { 343trait SuperTrait {
347 fn foo(&self) -> i64; 344 fn foo(&self) -> i64;
348 } 345}
349 trait Trait: SuperTrait { 346trait Trait: SuperTrait {
350 fn bar(&self) -> { 347 fn bar(&self) -> {
351 let x = self.foo(); 348 let x = self.foo();
352 } 349 }
353 } 350}"#,
354 "#,
355 expect![[r#" 351 expect![[r#"
356 31..35 'self': &Self 352 31..35 'self': &Self
357 85..89 'self': &Self 353 85..89 'self': &Self
@@ -367,18 +363,17 @@ fn trait_default_method_self_bound_implements_super_trait() {
367fn infer_project_associated_type() { 363fn infer_project_associated_type() {
368 check_infer( 364 check_infer(
369 r#" 365 r#"
370 trait Iterable { 366trait Iterable {
371 type Item; 367 type Item;
372 } 368}
373 struct S; 369struct S;
374 impl Iterable for S { type Item = u32; } 370impl Iterable for S { type Item = u32; }
375 fn test<T: Iterable>() { 371fn test<T: Iterable>() {
376 let x: <S as Iterable>::Item = 1; 372 let x: <S as Iterable>::Item = 1;
377 let y: <T as Iterable>::Item = no_matter; 373 let y: <T as Iterable>::Item = no_matter;
378 let z: T::Item = no_matter; 374 let z: T::Item = no_matter;
379 let a: <T>::Item = no_matter; 375 let a: <T>::Item = no_matter;
380 } 376}"#,
381 "#,
382 expect![[r#" 377 expect![[r#"
383 108..261 '{ ...ter; }': () 378 108..261 '{ ...ter; }': ()
384 118..119 'x': u32 379 118..119 'x': u32
@@ -397,20 +392,19 @@ fn infer_project_associated_type() {
397fn infer_return_associated_type() { 392fn infer_return_associated_type() {
398 check_infer( 393 check_infer(
399 r#" 394 r#"
400 trait Iterable { 395trait Iterable {
401 type Item; 396 type Item;
402 } 397}
403 struct S; 398struct S;
404 impl Iterable for S { type Item = u32; } 399impl Iterable for S { type Item = u32; }
405 fn foo1<T: Iterable>(t: T) -> T::Item {} 400fn foo1<T: Iterable>(t: T) -> T::Item {}
406 fn foo2<T: Iterable>(t: T) -> <T as Iterable>::Item {} 401fn foo2<T: Iterable>(t: T) -> <T as Iterable>::Item {}
407 fn foo3<T: Iterable>(t: T) -> <T>::Item {} 402fn foo3<T: Iterable>(t: T) -> <T>::Item {}
408 fn test() { 403fn test() {
409 let x = foo1(S); 404 let x = foo1(S);
410 let y = foo2(S); 405 let y = foo2(S);
411 let z = foo3(S); 406 let z = foo3(S);
412 } 407}"#,
413 "#,
414 expect![[r#" 408 expect![[r#"
415 106..107 't': T 409 106..107 't': T
416 123..125 '{}': () 410 123..125 '{}': ()
@@ -439,13 +433,12 @@ fn infer_return_associated_type() {
439fn infer_associated_type_bound() { 433fn infer_associated_type_bound() {
440 check_infer( 434 check_infer(
441 r#" 435 r#"
442 trait Iterable { 436trait Iterable {
443 type Item; 437 type Item;
444 } 438}
445 fn test<T: Iterable<Item=u32>>() { 439fn test<T: Iterable<Item=u32>>() {
446 let y: T::Item = unknown; 440 let y: T::Item = unknown;
447 } 441}"#,
448 "#,
449 expect![[r#" 442 expect![[r#"
450 67..100 '{ ...own; }': () 443 67..100 '{ ...own; }': ()
451 77..78 'y': u32 444 77..78 'y': u32
@@ -458,9 +451,8 @@ fn infer_associated_type_bound() {
458fn infer_const_body() { 451fn infer_const_body() {
459 check_infer( 452 check_infer(
460 r#" 453 r#"
461 const A: u32 = 1 + 1; 454const A: u32 = 1 + 1;
462 static B: u64 = { let x = 1; x }; 455static B: u64 = { let x = 1; x };"#,
463 "#,
464 expect![[r#" 456 expect![[r#"
465 15..16 '1': u32 457 15..16 '1': u32
466 15..20 '1 + 1': u32 458 15..20 '1 + 1': u32
@@ -477,13 +469,12 @@ fn infer_const_body() {
477fn tuple_struct_fields() { 469fn tuple_struct_fields() {
478 check_infer( 470 check_infer(
479 r#" 471 r#"
480 struct S(i32, u64); 472struct S(i32, u64);
481 fn test() -> u64 { 473fn test() -> u64 {
482 let a = S(4, 6); 474 let a = S(4, 6);
483 let b = a.0; 475 let b = a.0;
484 a.1 476 a.1
485 } 477}"#,
486 "#,
487 expect![[r#" 478 expect![[r#"
488 37..86 '{ ... a.1 }': u64 479 37..86 '{ ... a.1 }': u64
489 47..48 'a': S 480 47..48 'a': S
@@ -504,13 +495,12 @@ fn tuple_struct_fields() {
504fn tuple_struct_with_fn() { 495fn tuple_struct_with_fn() {
505 check_infer( 496 check_infer(
506 r#" 497 r#"
507 struct S(fn(u32) -> u64); 498struct S(fn(u32) -> u64);
508 fn test() -> u64 { 499fn test() -> u64 {
509 let a = S(|i| 2*i); 500 let a = S(|i| 2*i);
510 let b = a.0(4); 501 let b = a.0(4);
511 a.0(2) 502 a.0(2)
512 } 503}"#,
513 "#,
514 expect![[r#" 504 expect![[r#"
515 43..101 '{ ...0(2) }': u64 505 43..101 '{ ...0(2) }': u64
516 53..54 'a': S 506 53..54 'a': S
@@ -949,27 +939,26 @@ fn test<T: ApplyL>(t: T) {
949fn argument_impl_trait() { 939fn argument_impl_trait() {
950 check_infer_with_mismatches( 940 check_infer_with_mismatches(
951 r#" 941 r#"
952 trait Trait<T> { 942trait Trait<T> {
953 fn foo(&self) -> T; 943 fn foo(&self) -> T;
954 fn foo2(&self) -> i64; 944 fn foo2(&self) -> i64;
955 } 945}
956 fn bar(x: impl Trait<u16>) {} 946fn bar(x: impl Trait<u16>) {}
957 struct S<T>(T); 947struct S<T>(T);
958 impl<T> Trait<T> for S<T> {} 948impl<T> Trait<T> for S<T> {}
959 949
960 fn test(x: impl Trait<u64>, y: &impl Trait<u32>) { 950fn test(x: impl Trait<u64>, y: &impl Trait<u32>) {
961 x; 951 x;
962 y; 952 y;
963 let z = S(1); 953 let z = S(1);
964 bar(z); 954 bar(z);
965 x.foo(); 955 x.foo();
966 y.foo(); 956 y.foo();
967 z.foo(); 957 z.foo();
968 x.foo2(); 958 x.foo2();
969 y.foo2(); 959 y.foo2();
970 z.foo2(); 960 z.foo2();
971 } 961}"#,
972 "#,
973 expect![[r#" 962 expect![[r#"
974 29..33 'self': &Self 963 29..33 'self': &Self
975 54..58 'self': &Self 964 54..58 'self': &Self
@@ -1007,30 +996,29 @@ fn argument_impl_trait() {
1007fn argument_impl_trait_type_args_1() { 996fn argument_impl_trait_type_args_1() {
1008 check_infer_with_mismatches( 997 check_infer_with_mismatches(
1009 r#" 998 r#"
1010 trait Trait {} 999trait Trait {}
1011 trait Foo { 1000trait Foo {
1012 // this function has an implicit Self param, an explicit type param, 1001 // this function has an implicit Self param, an explicit type param,
1013 // and an implicit impl Trait param! 1002 // and an implicit impl Trait param!
1014 fn bar<T>(x: impl Trait) -> T { loop {} } 1003 fn bar<T>(x: impl Trait) -> T { loop {} }
1015 } 1004}
1016 fn foo<T>(x: impl Trait) -> T { loop {} } 1005fn foo<T>(x: impl Trait) -> T { loop {} }
1017 struct S; 1006struct S;
1018 impl Trait for S {} 1007impl Trait for S {}
1019 struct F; 1008struct F;
1020 impl Foo for F {} 1009impl Foo for F {}
1021 1010
1022 fn test() { 1011fn test() {
1023 Foo::bar(S); 1012 Foo::bar(S);
1024 <F as Foo>::bar(S); 1013 <F as Foo>::bar(S);
1025 F::bar(S); 1014 F::bar(S);
1026 Foo::bar::<u32>(S); 1015 Foo::bar::<u32>(S);
1027 <F as Foo>::bar::<u32>(S); 1016 <F as Foo>::bar::<u32>(S);
1028 1017
1029 foo(S); 1018 foo(S);
1030 foo::<u32>(S); 1019 foo::<u32>(S);
1031 foo::<u32, i32>(S); // we should ignore the extraneous i32 1020 foo::<u32, i32>(S); // we should ignore the extraneous i32
1032 } 1021}"#,
1033 "#,
1034 expect![[r#" 1022 expect![[r#"
1035 155..156 'x': impl Trait 1023 155..156 'x': impl Trait
1036 175..186 '{ loop {} }': T 1024 175..186 '{ loop {} }': T
@@ -1073,21 +1061,20 @@ fn argument_impl_trait_type_args_1() {
1073fn argument_impl_trait_type_args_2() { 1061fn argument_impl_trait_type_args_2() {
1074 check_infer_with_mismatches( 1062 check_infer_with_mismatches(
1075 r#" 1063 r#"
1076 trait Trait {} 1064trait Trait {}
1077 struct S; 1065struct S;
1078 impl Trait for S {} 1066impl Trait for S {}
1079 struct F<T>; 1067struct F<T>;
1080 impl<T> F<T> { 1068impl<T> F<T> {
1081 fn foo<U>(self, x: impl Trait) -> (T, U) { loop {} } 1069 fn foo<U>(self, x: impl Trait) -> (T, U) { loop {} }
1082 } 1070}
1083 1071
1084 fn test() { 1072fn test() {
1085 F.foo(S); 1073 F.foo(S);
1086 F::<u32>.foo(S); 1074 F::<u32>.foo(S);
1087 F::<u32>.foo::<i32>(S); 1075 F::<u32>.foo::<i32>(S);
1088 F::<u32>.foo::<i32, u32>(S); // extraneous argument should be ignored 1076 F::<u32>.foo::<i32, u32>(S); // extraneous argument should be ignored
1089 } 1077}"#,
1090 "#,
1091 expect![[r#" 1078 expect![[r#"
1092 87..91 'self': F<T> 1079 87..91 'self': F<T>
1093 93..94 'x': impl Trait 1080 93..94 'x': impl Trait
@@ -1115,15 +1102,14 @@ fn argument_impl_trait_type_args_2() {
1115fn argument_impl_trait_to_fn_pointer() { 1102fn argument_impl_trait_to_fn_pointer() {
1116 check_infer_with_mismatches( 1103 check_infer_with_mismatches(
1117 r#" 1104 r#"
1118 trait Trait {} 1105trait Trait {}
1119 fn foo(x: impl Trait) { loop {} } 1106fn foo(x: impl Trait) { loop {} }
1120 struct S; 1107struct S;
1121 impl Trait for S {} 1108impl Trait for S {}
1122 1109
1123 fn test() { 1110fn test() {
1124 let f: fn(S) -> () = foo; 1111 let f: fn(S) -> () = foo;
1125 } 1112}"#,
1126 "#,
1127 expect![[r#" 1113 expect![[r#"
1128 22..23 'x': impl Trait 1114 22..23 'x': impl Trait
1129 37..48 '{ loop {} }': () 1115 37..48 '{ loop {} }': ()
@@ -1140,24 +1126,23 @@ fn argument_impl_trait_to_fn_pointer() {
1140fn impl_trait() { 1126fn impl_trait() {
1141 check_infer( 1127 check_infer(
1142 r#" 1128 r#"
1143 trait Trait<T> { 1129trait Trait<T> {
1144 fn foo(&self) -> T; 1130 fn foo(&self) -> T;
1145 fn foo2(&self) -> i64; 1131 fn foo2(&self) -> i64;
1146 } 1132}
1147 fn bar() -> impl Trait<u64> {} 1133fn bar() -> impl Trait<u64> {}
1148 1134
1149 fn test(x: impl Trait<u64>, y: &impl Trait<u64>) { 1135fn test(x: impl Trait<u64>, y: &impl Trait<u64>) {
1150 x; 1136 x;
1151 y; 1137 y;
1152 let z = bar(); 1138 let z = bar();
1153 x.foo(); 1139 x.foo();
1154 y.foo(); 1140 y.foo();
1155 z.foo(); 1141 z.foo();
1156 x.foo2(); 1142 x.foo2();
1157 y.foo2(); 1143 y.foo2();
1158 z.foo2(); 1144 z.foo2();
1159 } 1145}"#,
1160 "#,
1161 expect![[r#" 1146 expect![[r#"
1162 29..33 'self': &Self 1147 29..33 'self': &Self
1163 54..58 'self': &Self 1148 54..58 'self': &Self
@@ -1191,16 +1176,15 @@ fn simple_return_pos_impl_trait() {
1191 cov_mark::check!(lower_rpit); 1176 cov_mark::check!(lower_rpit);
1192 check_infer( 1177 check_infer(
1193 r#" 1178 r#"
1194 trait Trait<T> { 1179trait Trait<T> {
1195 fn foo(&self) -> T; 1180 fn foo(&self) -> T;
1196 } 1181}
1197 fn bar() -> impl Trait<u64> { loop {} } 1182fn bar() -> impl Trait<u64> { loop {} }
1198 1183
1199 fn test() { 1184fn test() {
1200 let a = bar(); 1185 let a = bar();
1201 a.foo(); 1186 a.foo();
1202 } 1187}"#,
1203 "#,
1204 expect![[r#" 1188 expect![[r#"
1205 29..33 'self': &Self 1189 29..33 'self': &Self
1206 71..82 '{ loop {} }': ! 1190 71..82 '{ loop {} }': !
@@ -1220,25 +1204,24 @@ fn simple_return_pos_impl_trait() {
1220fn more_return_pos_impl_trait() { 1204fn more_return_pos_impl_trait() {
1221 check_infer( 1205 check_infer(
1222 r#" 1206 r#"
1223 trait Iterator { 1207trait Iterator {
1224 type Item; 1208 type Item;
1225 fn next(&mut self) -> Self::Item; 1209 fn next(&mut self) -> Self::Item;
1226 } 1210}
1227 trait Trait<T> { 1211trait Trait<T> {
1228 fn foo(&self) -> T; 1212 fn foo(&self) -> T;
1229 } 1213}
1230 fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) { loop {} } 1214fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) { loop {} }
1231 fn baz<T>(t: T) -> (impl Iterator<Item = impl Trait<T>>, impl Trait<T>) { loop {} } 1215fn baz<T>(t: T) -> (impl Iterator<Item = impl Trait<T>>, impl Trait<T>) { loop {} }
1232 1216
1233 fn test() { 1217fn test() {
1234 let (a, b) = bar(); 1218 let (a, b) = bar();
1235 a.next().foo(); 1219 a.next().foo();
1236 b.foo(); 1220 b.foo();
1237 let (c, d) = baz(1u128); 1221 let (c, d) = baz(1u128);
1238 c.next().foo(); 1222 c.next().foo();
1239 d.foo(); 1223 d.foo();
1240 } 1224}"#,
1241 "#,
1242 expect![[r#" 1225 expect![[r#"
1243 49..53 'self': &mut Self 1226 49..53 'self': &mut Self
1244 101..105 'self': &Self 1227 101..105 'self': &Self
@@ -1279,24 +1262,23 @@ fn more_return_pos_impl_trait() {
1279fn dyn_trait() { 1262fn dyn_trait() {
1280 check_infer( 1263 check_infer(
1281 r#" 1264 r#"
1282 trait Trait<T> { 1265trait Trait<T> {
1283 fn foo(&self) -> T; 1266 fn foo(&self) -> T;
1284 fn foo2(&self) -> i64; 1267 fn foo2(&self) -> i64;
1285 } 1268}
1286 fn bar() -> dyn Trait<u64> {} 1269fn bar() -> dyn Trait<u64> {}
1287 1270
1288 fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) { 1271fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) {
1289 x; 1272 x;
1290 y; 1273 y;
1291 let z = bar(); 1274 let z = bar();
1292 x.foo(); 1275 x.foo();
1293 y.foo(); 1276 y.foo();
1294 z.foo(); 1277 z.foo();
1295 x.foo2(); 1278 x.foo2();
1296 y.foo2(); 1279 y.foo2();
1297 z.foo2(); 1280 z.foo2();
1298 } 1281}"#,
1299 "#,
1300 expect![[r#" 1282 expect![[r#"
1301 29..33 'self': &Self 1283 29..33 'self': &Self
1302 54..58 'self': &Self 1284 54..58 'self': &Self
@@ -1329,22 +1311,21 @@ fn dyn_trait() {
1329fn dyn_trait_in_impl() { 1311fn dyn_trait_in_impl() {
1330 check_infer( 1312 check_infer(
1331 r#" 1313 r#"
1332 trait Trait<T, U> { 1314trait Trait<T, U> {
1333 fn foo(&self) -> (T, U); 1315 fn foo(&self) -> (T, U);
1334 } 1316}
1335 struct S<T, U> {} 1317struct S<T, U> {}
1336 impl<T, U> S<T, U> { 1318impl<T, U> S<T, U> {
1337 fn bar(&self) -> &dyn Trait<T, U> { loop {} } 1319 fn bar(&self) -> &dyn Trait<T, U> { loop {} }
1338 } 1320}
1339 trait Trait2<T, U> { 1321trait Trait2<T, U> {
1340 fn baz(&self) -> (T, U); 1322 fn baz(&self) -> (T, U);
1341 } 1323}
1342 impl<T, U> Trait2<T, U> for dyn Trait<T, U> { } 1324impl<T, U> Trait2<T, U> for dyn Trait<T, U> { }
1343 1325
1344 fn test(s: S<u32, i32>) { 1326fn test(s: S<u32, i32>) {
1345 s.bar().baz(); 1327 s.bar().baz();
1346 } 1328}"#,
1347 "#,
1348 expect![[r#" 1329 expect![[r#"
1349 32..36 'self': &Self 1330 32..36 'self': &Self
1350 102..106 'self': &S<T, U> 1331 102..106 'self': &S<T, U>
@@ -1365,20 +1346,19 @@ fn dyn_trait_in_impl() {
1365fn dyn_trait_bare() { 1346fn dyn_trait_bare() {
1366 check_infer( 1347 check_infer(
1367 r#" 1348 r#"
1368 trait Trait { 1349trait Trait {
1369 fn foo(&self) -> u64; 1350 fn foo(&self) -> u64;
1370 } 1351}
1371 fn bar() -> Trait {} 1352fn bar() -> Trait {}
1372 1353
1373 fn test(x: Trait, y: &Trait) -> u64 { 1354fn test(x: Trait, y: &Trait) -> u64 {
1374 x; 1355 x;
1375 y; 1356 y;
1376 let z = bar(); 1357 let z = bar();
1377 x.foo(); 1358 x.foo();
1378 y.foo(); 1359 y.foo();
1379 z.foo(); 1360 z.foo();
1380 } 1361}"#,
1381 "#,
1382 expect![[r#" 1362 expect![[r#"
1383 26..30 'self': &Self 1363 26..30 'self': &Self
1384 60..62 '{}': () 1364 60..62 '{}': ()
@@ -1404,17 +1384,24 @@ fn dyn_trait_bare() {
1404fn weird_bounds() { 1384fn weird_bounds() {
1405 check_infer( 1385 check_infer(
1406 r#" 1386 r#"
1407 trait Trait {} 1387trait Trait {}
1408 fn test(a: impl Trait + 'lifetime, b: impl 'lifetime, c: impl (Trait), d: impl ('lifetime), e: impl ?Sized, f: impl Trait + ?Sized) {} 1388fn test(
1409 "#, 1389 a: impl Trait + 'lifetime,
1390 b: impl 'lifetime,
1391 c: impl (Trait),
1392 d: impl ('lifetime),
1393 e: impl ?Sized,
1394 f: impl Trait + ?Sized
1395) {}
1396"#,
1410 expect![[r#" 1397 expect![[r#"
1411 23..24 'a': impl Trait 1398 28..29 'a': impl Trait
1412 50..51 'b': impl 1399 59..60 'b': impl
1413 69..70 'c': impl Trait 1400 82..83 'c': impl Trait
1414 86..87 'd': impl 1401 103..104 'd': impl
1415 107..108 'e': impl 1402 128..129 'e': impl
1416 123..124 'f': impl Trait 1403 148..149 'f': impl Trait
1417 147..149 '{}': () 1404 173..175 '{}': ()
1418 "#]], 1405 "#]],
1419 ); 1406 );
1420} 1407}
@@ -1439,27 +1426,26 @@ fn test(x: (impl Trait + UnknownTrait)) {
1439fn assoc_type_bindings() { 1426fn assoc_type_bindings() {
1440 check_infer( 1427 check_infer(
1441 r#" 1428 r#"
1442 trait Trait { 1429trait Trait {
1443 type Type; 1430 type Type;
1444 } 1431}
1445 1432
1446 fn get<T: Trait>(t: T) -> <T as Trait>::Type {} 1433fn get<T: Trait>(t: T) -> <T as Trait>::Type {}
1447 fn get2<U, T: Trait<Type = U>>(t: T) -> U {} 1434fn get2<U, T: Trait<Type = U>>(t: T) -> U {}
1448 fn set<T: Trait<Type = u64>>(t: T) -> T {t} 1435fn set<T: Trait<Type = u64>>(t: T) -> T {t}
1449 1436
1450 struct S<T>; 1437struct S<T>;
1451 impl<T> Trait for S<T> { type Type = T; } 1438impl<T> Trait for S<T> { type Type = T; }
1452 1439
1453 fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) { 1440fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) {
1454 get(x); 1441 get(x);
1455 get2(x); 1442 get2(x);
1456 get(y); 1443 get(y);
1457 get2(y); 1444 get2(y);
1458 get(set(S)); 1445 get(set(S));
1459 get2(set(S)); 1446 get2(set(S));
1460 get2(S::<str>); 1447 get2(S::<str>);
1461 } 1448}"#,
1462 "#,
1463 expect![[r#" 1449 expect![[r#"
1464 49..50 't': T 1450 49..50 't': T
1465 77..79 '{}': () 1451 77..79 '{}': ()
@@ -1546,18 +1532,17 @@ mod iter {
1546fn projection_eq_within_chalk() { 1532fn projection_eq_within_chalk() {
1547 check_infer( 1533 check_infer(
1548 r#" 1534 r#"
1549 trait Trait1 { 1535trait Trait1 {
1550 type Type; 1536 type Type;
1551 } 1537}
1552 trait Trait2<T> { 1538trait Trait2<T> {
1553 fn foo(self) -> T; 1539 fn foo(self) -> T;
1554 } 1540}
1555 impl<T, U> Trait2<T> for U where U: Trait1<Type = T> {} 1541impl<T, U> Trait2<T> for U where U: Trait1<Type = T> {}
1556 1542
1557 fn test<T: Trait1<Type = u32>>(x: T) { 1543fn test<T: Trait1<Type = u32>>(x: T) {
1558 x.foo(); 1544 x.foo();
1559 } 1545}"#,
1560 "#,
1561 expect![[r#" 1546 expect![[r#"
1562 61..65 'self': Self 1547 61..65 'self': Self
1563 163..164 'x': T 1548 163..164 'x': T
@@ -1589,19 +1574,18 @@ fn test<T: foo::Trait>(x: T) {
1589fn super_trait_method_resolution() { 1574fn super_trait_method_resolution() {
1590 check_infer( 1575 check_infer(
1591 r#" 1576 r#"
1592 mod foo { 1577mod foo {
1593 trait SuperTrait { 1578 trait SuperTrait {
1594 fn foo(&self) -> u32 {} 1579 fn foo(&self) -> u32 {}
1595 } 1580 }
1596 } 1581}
1597 trait Trait1: foo::SuperTrait {} 1582trait Trait1: foo::SuperTrait {}
1598 trait Trait2 where Self: foo::SuperTrait {} 1583trait Trait2 where Self: foo::SuperTrait {}
1599 1584
1600 fn test<T: Trait1, U: Trait2>(x: T, y: U) { 1585fn test<T: Trait1, U: Trait2>(x: T, y: U) {
1601 x.foo(); 1586 x.foo();
1602 y.foo(); 1587 y.foo();
1603 } 1588}"#,
1604 "#,
1605 expect![[r#" 1589 expect![[r#"
1606 49..53 'self': &Self 1590 49..53 'self': &Self
1607 62..64 '{}': () 1591 62..64 '{}': ()
@@ -1620,17 +1604,16 @@ fn super_trait_method_resolution() {
1620fn super_trait_impl_trait_method_resolution() { 1604fn super_trait_impl_trait_method_resolution() {
1621 check_infer( 1605 check_infer(
1622 r#" 1606 r#"
1623 mod foo { 1607mod foo {
1624 trait SuperTrait { 1608 trait SuperTrait {
1625 fn foo(&self) -> u32 {} 1609 fn foo(&self) -> u32 {}
1626 } 1610 }
1627 } 1611}
1628 trait Trait1: foo::SuperTrait {} 1612trait Trait1: foo::SuperTrait {}
1629 1613
1630 fn test(x: &impl Trait1) { 1614fn test(x: &impl Trait1) {
1631 x.foo(); 1615 x.foo();
1632 } 1616}"#,
1633 "#,
1634 expect![[r#" 1617 expect![[r#"
1635 49..53 'self': &Self 1618 49..53 'self': &Self
1636 62..64 '{}': () 1619 62..64 '{}': ()
@@ -1667,20 +1650,19 @@ fn super_trait_cycle() {
1667fn super_trait_assoc_type_bounds() { 1650fn super_trait_assoc_type_bounds() {
1668 check_infer( 1651 check_infer(
1669 r#" 1652 r#"
1670 trait SuperTrait { type Type; } 1653trait SuperTrait { type Type; }
1671 trait Trait where Self: SuperTrait {} 1654trait Trait where Self: SuperTrait {}
1672 1655
1673 fn get2<U, T: Trait<Type = U>>(t: T) -> U {} 1656fn get2<U, T: Trait<Type = U>>(t: T) -> U {}
1674 fn set<T: Trait<Type = u64>>(t: T) -> T {t} 1657fn set<T: Trait<Type = u64>>(t: T) -> T {t}
1675 1658
1676 struct S<T>; 1659struct S<T>;
1677 impl<T> SuperTrait for S<T> { type Type = T; } 1660impl<T> SuperTrait for S<T> { type Type = T; }
1678 impl<T> Trait for S<T> {} 1661impl<T> Trait for S<T> {}
1679 1662
1680 fn test() { 1663fn test() {
1681 get2(set(S)); 1664 get2(set(S));
1682 } 1665}"#,
1683 "#,
1684 expect![[r#" 1666 expect![[r#"
1685 102..103 't': T 1667 102..103 't': T
1686 113..115 '{}': () 1668 113..115 '{}': ()
@@ -1701,16 +1683,15 @@ fn super_trait_assoc_type_bounds() {
1701fn fn_trait() { 1683fn fn_trait() {
1702 check_infer_with_mismatches( 1684 check_infer_with_mismatches(
1703 r#" 1685 r#"
1704 trait FnOnce<Args> { 1686trait FnOnce<Args> {
1705 type Output; 1687 type Output;
1706 1688
1707 fn call_once(self, args: Args) -> <Self as FnOnce<Args>>::Output; 1689 fn call_once(self, args: Args) -> <Self as FnOnce<Args>>::Output;
1708 } 1690}
1709 1691
1710 fn test<F: FnOnce(u32, u64) -> u128>(f: F) { 1692fn test<F: FnOnce(u32, u64) -> u128>(f: F) {
1711 f.call_once((1, 2)); 1693 f.call_once((1, 2));
1712 } 1694}"#,
1713 "#,
1714 expect![[r#" 1695 expect![[r#"
1715 56..60 'self': Self 1696 56..60 'self': Self
1716 62..66 'args': Args 1697 62..66 'args': Args
@@ -1729,37 +1710,36 @@ fn fn_trait() {
1729fn fn_ptr_and_item() { 1710fn fn_ptr_and_item() {
1730 check_infer_with_mismatches( 1711 check_infer_with_mismatches(
1731 r#" 1712 r#"
1732 #[lang="fn_once"] 1713#[lang="fn_once"]
1733 trait FnOnce<Args> { 1714trait FnOnce<Args> {
1734 type Output; 1715 type Output;
1735 1716
1736 fn call_once(self, args: Args) -> Self::Output; 1717 fn call_once(self, args: Args) -> Self::Output;
1737 } 1718}
1738 1719
1739 trait Foo<T> { 1720trait Foo<T> {
1740 fn foo(&self) -> T; 1721 fn foo(&self) -> T;
1741 } 1722}
1742 1723
1743 struct Bar<T>(T); 1724struct Bar<T>(T);
1744 1725
1745 impl<A1, R, F: FnOnce(A1) -> R> Foo<(A1, R)> for Bar<F> { 1726impl<A1, R, F: FnOnce(A1) -> R> Foo<(A1, R)> for Bar<F> {
1746 fn foo(&self) -> (A1, R) { loop {} } 1727 fn foo(&self) -> (A1, R) { loop {} }
1747 } 1728}
1748 1729
1749 enum Opt<T> { None, Some(T) } 1730enum Opt<T> { None, Some(T) }
1750 impl<T> Opt<T> { 1731impl<T> Opt<T> {
1751 fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Opt<U> { loop {} } 1732 fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Opt<U> { loop {} }
1752 } 1733}
1753 1734
1754 fn test() { 1735fn test() {
1755 let bar: Bar<fn(u8) -> u32>; 1736 let bar: Bar<fn(u8) -> u32>;
1756 bar.foo(); 1737 bar.foo();
1757 1738
1758 let opt: Opt<u8>; 1739 let opt: Opt<u8>;
1759 let f: fn(u8) -> u32; 1740 let f: fn(u8) -> u32;
1760 opt.map(f); 1741 opt.map(f);
1761 } 1742}"#,
1762 "#,
1763 expect![[r#" 1743 expect![[r#"
1764 74..78 'self': Self 1744 74..78 'self': Self
1765 80..84 'args': Args 1745 80..84 'args': Args
@@ -1790,46 +1770,45 @@ fn fn_ptr_and_item() {
1790fn fn_trait_deref_with_ty_default() { 1770fn fn_trait_deref_with_ty_default() {
1791 check_infer( 1771 check_infer(
1792 r#" 1772 r#"
1793 #[lang = "deref"] 1773#[lang = "deref"]
1794 trait Deref { 1774trait Deref {
1795 type Target; 1775 type Target;
1796 1776
1797 fn deref(&self) -> &Self::Target; 1777 fn deref(&self) -> &Self::Target;
1798 } 1778}
1799 1779
1800 #[lang="fn_once"] 1780#[lang="fn_once"]
1801 trait FnOnce<Args> { 1781trait FnOnce<Args> {
1802 type Output; 1782 type Output;
1803 1783
1804 fn call_once(self, args: Args) -> Self::Output; 1784 fn call_once(self, args: Args) -> Self::Output;
1805 } 1785}
1806 1786
1807 struct Foo; 1787struct Foo;
1808 1788
1809 impl Foo { 1789impl Foo {
1810 fn foo(&self) -> usize {} 1790 fn foo(&self) -> usize {}
1811 } 1791}
1812 1792
1813 struct Lazy<T, F = fn() -> T>(F); 1793struct Lazy<T, F = fn() -> T>(F);
1814 1794
1815 impl<T, F> Lazy<T, F> { 1795impl<T, F> Lazy<T, F> {
1816 pub fn new(f: F) -> Lazy<T, F> {} 1796 pub fn new(f: F) -> Lazy<T, F> {}
1817 } 1797}
1818 1798
1819 impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> { 1799impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> {
1820 type Target = T; 1800 type Target = T;
1821 } 1801}
1822 1802
1823 fn test() { 1803fn test() {
1824 let lazy1: Lazy<Foo, _> = Lazy::new(|| Foo); 1804 let lazy1: Lazy<Foo, _> = Lazy::new(|| Foo);
1825 let r1 = lazy1.foo(); 1805 let r1 = lazy1.foo();
1826 1806
1827 fn make_foo_fn() -> Foo {} 1807 fn make_foo_fn() -> Foo {}
1828 let make_foo_fn_ptr: fn() -> Foo = make_foo_fn; 1808 let make_foo_fn_ptr: fn() -> Foo = make_foo_fn;
1829 let lazy2: Lazy<Foo, _> = Lazy::new(make_foo_fn_ptr); 1809 let lazy2: Lazy<Foo, _> = Lazy::new(make_foo_fn_ptr);
1830 let r2 = lazy2.foo(); 1810 let r2 = lazy2.foo();
1831 } 1811}"#,
1832 "#,
1833 expect![[r#" 1812 expect![[r#"
1834 64..68 'self': &Self 1813 64..68 'self': &Self
1835 165..169 'self': Self 1814 165..169 'self': Self
@@ -1865,23 +1844,22 @@ fn fn_trait_deref_with_ty_default() {
1865fn closure_1() { 1844fn closure_1() {
1866 check_infer_with_mismatches( 1845 check_infer_with_mismatches(
1867 r#" 1846 r#"
1868 #[lang = "fn_once"] 1847#[lang = "fn_once"]
1869 trait FnOnce<Args> { 1848trait FnOnce<Args> {
1870 type Output; 1849 type Output;
1871 } 1850}
1872 1851
1873 enum Option<T> { Some(T), None } 1852enum Option<T> { Some(T), None }
1874 impl<T> Option<T> { 1853impl<T> Option<T> {
1875 fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> { loop {} } 1854 fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> { loop {} }
1876 } 1855}
1877 1856
1878 fn test() { 1857fn test() {
1879 let x = Option::Some(1u32); 1858 let x = Option::Some(1u32);
1880 x.map(|v| v + 1); 1859 x.map(|v| v + 1);
1881 x.map(|_v| 1u64); 1860 x.map(|_v| 1u64);
1882 let y: Option<i64> = x.map(|_v| 1); 1861 let y: Option<i64> = x.map(|_v| 1);
1883 } 1862}"#,
1884 "#,
1885 expect![[r#" 1863 expect![[r#"
1886 147..151 'self': Option<T> 1864 147..151 'self': Option<T>
1887 153..154 'f': F 1865 153..154 'f': F
@@ -1919,38 +1897,63 @@ fn closure_1() {
1919fn closure_2() { 1897fn closure_2() {
1920 check_infer_with_mismatches( 1898 check_infer_with_mismatches(
1921 r#" 1899 r#"
1922 trait FnOnce<Args> { 1900#[lang = "add"]
1923 type Output; 1901pub trait Add<Rhs = Self> {
1924 } 1902 type Output;
1903 fn add(self, rhs: Rhs) -> Self::Output;
1904}
1925 1905
1926 fn test<F: FnOnce(u32) -> u64>(f: F) { 1906trait FnOnce<Args> {
1927 f(1); 1907 type Output;
1928 let g = |v| v + 1; 1908}
1929 g(1u64); 1909
1930 let h = |v| 1u128 + v; 1910impl Add for u64 {
1931 } 1911 type Output = Self;
1932 "#, 1912 fn add(self, rhs: u64) -> Self::Output {0}
1913}
1914
1915impl Add for u128 {
1916 type Output = Self;
1917 fn add(self, rhs: u128) -> Self::Output {0}
1918}
1919
1920fn test<F: FnOnce(u32) -> u64>(f: F) {
1921 f(1);
1922 let g = |v| v + 1;
1923 g(1u64);
1924 let h = |v| 1u128 + v;
1925}"#,
1933 expect![[r#" 1926 expect![[r#"
1934 72..73 'f': F 1927 72..76 'self': Self
1935 78..154 '{ ...+ v; }': () 1928 78..81 'rhs': Rhs
1936 84..85 'f': F 1929 203..207 'self': u64
1937 84..88 'f(1)': {unknown} 1930 209..212 'rhs': u64
1938 86..87 '1': i32 1931 235..238 '{0}': u64
1939 98..99 'g': |u64| -> i32 1932 236..237 '0': u64
1940 102..111 '|v| v + 1': |u64| -> i32 1933 297..301 'self': u128
1941 103..104 'v': u64 1934 303..306 'rhs': u128
1942 106..107 'v': u64 1935 330..333 '{0}': u128
1943 106..111 'v + 1': i32 1936 331..332 '0': u128
1944 110..111 '1': i32 1937 368..369 'f': F
1945 117..118 'g': |u64| -> i32 1938 374..450 '{ ...+ v; }': ()
1946 117..124 'g(1u64)': i32 1939 380..381 'f': F
1947 119..123 '1u64': u64 1940 380..384 'f(1)': {unknown}
1948 134..135 'h': |u128| -> u128 1941 382..383 '1': i32
1949 138..151 '|v| 1u128 + v': |u128| -> u128 1942 394..395 'g': |u64| -> u64
1950 139..140 'v': u128 1943 398..407 '|v| v + 1': |u64| -> u64
1951 142..147 '1u128': u128 1944 399..400 'v': u64
1952 142..151 '1u128 + v': u128 1945 402..403 'v': u64
1953 150..151 'v': u128 1946 402..407 'v + 1': u64
1947 406..407 '1': u64
1948 413..414 'g': |u64| -> u64
1949 413..420 'g(1u64)': u64
1950 415..419 '1u64': u64
1951 430..431 'h': |u128| -> u128
1952 434..447 '|v| 1u128 + v': |u128| -> u128
1953 435..436 'v': u128
1954 438..443 '1u128': u128
1955 438..447 '1u128 + v': u128
1956 446..447 'v': u128
1954 "#]], 1957 "#]],
1955 ); 1958 );
1956} 1959}
@@ -1959,29 +1962,28 @@ fn closure_2() {
1959fn closure_as_argument_inference_order() { 1962fn closure_as_argument_inference_order() {
1960 check_infer_with_mismatches( 1963 check_infer_with_mismatches(
1961 r#" 1964 r#"
1962 #[lang = "fn_once"] 1965#[lang = "fn_once"]
1963 trait FnOnce<Args> { 1966trait FnOnce<Args> {
1964 type Output; 1967 type Output;
1965 } 1968}
1966 1969
1967 fn foo1<T, U, F: FnOnce(T) -> U>(x: T, f: F) -> U { loop {} } 1970fn foo1<T, U, F: FnOnce(T) -> U>(x: T, f: F) -> U { loop {} }
1968 fn foo2<T, U, F: FnOnce(T) -> U>(f: F, x: T) -> U { loop {} } 1971fn foo2<T, U, F: FnOnce(T) -> U>(f: F, x: T) -> U { loop {} }
1969 1972
1970 struct S; 1973struct S;
1971 impl S { 1974impl S {
1972 fn method(self) -> u64; 1975 fn method(self) -> u64;
1973 1976
1974 fn foo1<T, U, F: FnOnce(T) -> U>(self, x: T, f: F) -> U { loop {} } 1977 fn foo1<T, U, F: FnOnce(T) -> U>(self, x: T, f: F) -> U { loop {} }
1975 fn foo2<T, U, F: FnOnce(T) -> U>(self, f: F, x: T) -> U { loop {} } 1978 fn foo2<T, U, F: FnOnce(T) -> U>(self, f: F, x: T) -> U { loop {} }
1976 } 1979}
1977 1980
1978 fn test() { 1981fn test() {
1979 let x1 = foo1(S, |s| s.method()); 1982 let x1 = foo1(S, |s| s.method());
1980 let x2 = foo2(|s| s.method(), S); 1983 let x2 = foo2(|s| s.method(), S);
1981 let x3 = S.foo1(S, |s| s.method()); 1984 let x3 = S.foo1(S, |s| s.method());
1982 let x4 = S.foo2(|s| s.method(), S); 1985 let x4 = S.foo2(|s| s.method(), S);
1983 } 1986}"#,
1984 "#,
1985 expect![[r#" 1987 expect![[r#"
1986 94..95 'x': T 1988 94..95 'x': T
1987 100..101 'f': F 1989 100..101 'f': F
@@ -2110,27 +2112,26 @@ fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> {
2110fn unselected_projection_on_impl_self() { 2112fn unselected_projection_on_impl_self() {
2111 check_infer( 2113 check_infer(
2112 r#" 2114 r#"
2113 //- /main.rs 2115//- /main.rs
2114 trait Trait { 2116trait Trait {
2115 type Item; 2117 type Item;
2116 2118
2117 fn f(&self, x: Self::Item); 2119 fn f(&self, x: Self::Item);
2118 } 2120}
2119 2121
2120 struct S; 2122struct S;
2121 2123
2122 impl Trait for S { 2124impl Trait for S {
2123 type Item = u32; 2125 type Item = u32;
2124 fn f(&self, x: Self::Item) { let y = x; } 2126 fn f(&self, x: Self::Item) { let y = x; }
2125 } 2127}
2126 2128
2127 struct S2; 2129struct S2;
2128 2130
2129 impl Trait for S2 { 2131impl Trait for S2 {
2130 type Item = i32; 2132 type Item = i32;
2131 fn f(&self, x: <Self>::Item) { let y = x; } 2133 fn f(&self, x: <Self>::Item) { let y = x; }
2132 } 2134}"#,
2133 "#,
2134 expect![[r#" 2135 expect![[r#"
2135 40..44 'self': &Self 2136 40..44 'self': &Self
2136 46..47 'x': Trait::Item<Self> 2137 46..47 'x': Trait::Item<Self>
@@ -2366,58 +2367,57 @@ fn test<I: Iterator<Item: Iterator<Item = u32>>>() {
2366fn proc_macro_server_types() { 2367fn proc_macro_server_types() {
2367 check_infer( 2368 check_infer(
2368 r#" 2369 r#"
2369 macro_rules! with_api { 2370macro_rules! with_api {
2370 ($S:ident, $self:ident, $m:ident) => { 2371 ($S:ident, $self:ident, $m:ident) => {
2371 $m! { 2372 $m! {
2372 TokenStream { 2373 TokenStream {
2373 fn new() -> $S::TokenStream; 2374 fn new() -> $S::TokenStream;
2374 }, 2375 },
2375 Group { 2376 Group {
2376 }, 2377 },
2377 }
2378 };
2379 } 2378 }
2380 macro_rules! associated_item { 2379 };
2381 (type TokenStream) => 2380}
2382 (type TokenStream: 'static;); 2381macro_rules! associated_item {
2383 (type Group) => 2382 (type TokenStream) =>
2384 (type Group: 'static;); 2383 (type TokenStream: 'static;);
2385 ($($item:tt)*) => ($($item)*;) 2384 (type Group) =>
2386 } 2385 (type Group: 'static;);
2387 macro_rules! declare_server_traits { 2386 ($($item:tt)*) => ($($item)*;)
2388 ($($name:ident { 2387}
2389 $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* 2388macro_rules! declare_server_traits {
2390 }),* $(,)?) => { 2389 ($($name:ident {
2391 pub trait Types { 2390 $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
2392 $(associated_item!(type $name);)* 2391 }),* $(,)?) => {
2393 } 2392 pub trait Types {
2394 2393 $(associated_item!(type $name);)*
2395 $(pub trait $name: Types {
2396 $(associated_item!(fn $method($($arg: $arg_ty),*) $(-> $ret_ty)?);)*
2397 })*
2398
2399 pub trait Server: Types $(+ $name)* {}
2400 impl<S: Types $(+ $name)*> Server for S {}
2401 }
2402 } 2394 }
2403 2395
2404 with_api!(Self, self_, declare_server_traits); 2396 $(pub trait $name: Types {
2405 struct G {} 2397 $(associated_item!(fn $method($($arg: $arg_ty),*) $(-> $ret_ty)?);)*
2406 struct T {} 2398 })*
2407 struct Rustc;
2408 impl Types for Rustc {
2409 type TokenStream = T;
2410 type Group = G;
2411 }
2412 2399
2413 fn make<T>() -> T { loop {} } 2400 pub trait Server: Types $(+ $name)* {}
2414 impl TokenStream for Rustc { 2401 impl<S: Types $(+ $name)*> Server for S {}
2415 fn new() -> Self::TokenStream { 2402 }
2416 let group: Self::Group = make(); 2403}
2417 make() 2404
2418 } 2405with_api!(Self, self_, declare_server_traits);
2419 } 2406struct G {}
2420 "#, 2407struct T {}
2408struct Rustc;
2409impl Types for Rustc {
2410 type TokenStream = T;
2411 type Group = G;
2412}
2413
2414fn make<T>() -> T { loop {} }
2415impl TokenStream for Rustc {
2416 fn new() -> Self::TokenStream {
2417 let group: Self::Group = make();
2418 make()
2419 }
2420}"#,
2421 expect![[r#" 2421 expect![[r#"
2422 1061..1072 '{ loop {} }': T 2422 1061..1072 '{ loop {} }': T
2423 1063..1070 'loop {}': ! 2423 1063..1070 'loop {}': !
@@ -2436,23 +2436,22 @@ fn proc_macro_server_types() {
2436fn unify_impl_trait() { 2436fn unify_impl_trait() {
2437 check_infer_with_mismatches( 2437 check_infer_with_mismatches(
2438 r#" 2438 r#"
2439 trait Trait<T> {} 2439trait Trait<T> {}
2440 2440
2441 fn foo(x: impl Trait<u32>) { loop {} } 2441fn foo(x: impl Trait<u32>) { loop {} }
2442 fn bar<T>(x: impl Trait<T>) -> T { loop {} } 2442fn bar<T>(x: impl Trait<T>) -> T { loop {} }
2443 2443
2444 struct S<T>(T); 2444struct S<T>(T);
2445 impl<T> Trait<T> for S<T> {} 2445impl<T> Trait<T> for S<T> {}
2446 2446
2447 fn default<T>() -> T { loop {} } 2447fn default<T>() -> T { loop {} }
2448 2448
2449 fn test() -> impl Trait<i32> { 2449fn test() -> impl Trait<i32> {
2450 let s1 = S(default()); 2450 let s1 = S(default());
2451 foo(s1); 2451 foo(s1);
2452 let x: i32 = bar(S(default())); 2452 let x: i32 = bar(S(default()));
2453 S(default()) 2453 S(default())
2454 } 2454}"#,
2455 "#,
2456 expect![[r#" 2455 expect![[r#"
2457 26..27 'x': impl Trait<u32> 2456 26..27 'x': impl Trait<u32>
2458 46..57 '{ loop {} }': () 2457 46..57 '{ loop {} }': ()
@@ -2493,30 +2492,29 @@ fn unify_impl_trait() {
2493fn assoc_types_from_bounds() { 2492fn assoc_types_from_bounds() {
2494 check_infer( 2493 check_infer(
2495 r#" 2494 r#"
2496 //- /main.rs 2495//- /main.rs
2497 #[lang = "fn_once"] 2496#[lang = "fn_once"]
2498 trait FnOnce<Args> { 2497trait FnOnce<Args> {
2499 type Output; 2498 type Output;
2500 } 2499}
2501 2500
2502 trait T { 2501trait T {
2503 type O; 2502 type O;
2504 } 2503}
2505 2504
2506 impl T for () { 2505impl T for () {
2507 type O = (); 2506 type O = ();
2508 } 2507}
2509 2508
2510 fn f<X, F>(_v: F) 2509fn f<X, F>(_v: F)
2511 where 2510where
2512 X: T, 2511 X: T,
2513 F: FnOnce(&X::O), 2512 F: FnOnce(&X::O),
2514 { } 2513{ }
2515 2514
2516 fn main() { 2515fn main() {
2517 f::<(), _>(|z| { z; }); 2516 f::<(), _>(|z| { z; });
2518 } 2517}"#,
2519 "#,
2520 expect![[r#" 2518 expect![[r#"
2521 133..135 '_v': F 2519 133..135 '_v': F
2522 178..181 '{ }': () 2520 178..181 '{ }': ()
@@ -2602,76 +2600,75 @@ fn test() {
2602fn iterator_chain() { 2600fn iterator_chain() {
2603 check_infer_with_mismatches( 2601 check_infer_with_mismatches(
2604 r#" 2602 r#"
2605 //- /main.rs 2603//- /main.rs
2606 #[lang = "fn_once"] 2604#[lang = "fn_once"]
2607 trait FnOnce<Args> { 2605trait FnOnce<Args> {
2608 type Output; 2606 type Output;
2609 } 2607}
2610 #[lang = "fn_mut"] 2608#[lang = "fn_mut"]
2611 trait FnMut<Args>: FnOnce<Args> { } 2609trait FnMut<Args>: FnOnce<Args> { }
2612 2610
2613 enum Option<T> { Some(T), None } 2611enum Option<T> { Some(T), None }
2614 use Option::*; 2612use Option::*;
2615 2613
2616 pub trait Iterator { 2614pub trait Iterator {
2617 type Item; 2615 type Item;
2618 2616
2619 fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F> 2617 fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
2620 where 2618 where
2621 F: FnMut(Self::Item) -> Option<B>, 2619 F: FnMut(Self::Item) -> Option<B>,
2622 { loop {} } 2620 { loop {} }
2623 2621
2624 fn for_each<F>(self, f: F) 2622 fn for_each<F>(self, f: F)
2625 where 2623 where
2626 F: FnMut(Self::Item), 2624 F: FnMut(Self::Item),
2627 { loop {} } 2625 { loop {} }
2628 } 2626}
2629 2627
2630 pub trait IntoIterator { 2628pub trait IntoIterator {
2631 type Item; 2629 type Item;
2632 type IntoIter: Iterator<Item = Self::Item>; 2630 type IntoIter: Iterator<Item = Self::Item>;
2633 fn into_iter(self) -> Self::IntoIter; 2631 fn into_iter(self) -> Self::IntoIter;
2634 } 2632}
2635 2633
2636 pub struct FilterMap<I, F> { } 2634pub struct FilterMap<I, F> { }
2637 impl<B, I: Iterator, F> Iterator for FilterMap<I, F> 2635impl<B, I: Iterator, F> Iterator for FilterMap<I, F>
2638 where 2636where
2639 F: FnMut(I::Item) -> Option<B>, 2637 F: FnMut(I::Item) -> Option<B>,
2640 { 2638{
2641 type Item = B; 2639 type Item = B;
2642 } 2640}
2643 2641
2644 #[stable(feature = "rust1", since = "1.0.0")] 2642#[stable(feature = "rust1", since = "1.0.0")]
2645 impl<I: Iterator> IntoIterator for I { 2643impl<I: Iterator> IntoIterator for I {
2646 type Item = I::Item; 2644 type Item = I::Item;
2647 type IntoIter = I; 2645 type IntoIter = I;
2648 2646
2649 fn into_iter(self) -> I { 2647 fn into_iter(self) -> I {
2650 self 2648 self
2651 } 2649 }
2652 } 2650}
2653 2651
2654 struct Vec<T> {} 2652struct Vec<T> {}
2655 impl<T> Vec<T> { 2653impl<T> Vec<T> {
2656 fn new() -> Self { loop {} } 2654 fn new() -> Self { loop {} }
2657 } 2655}
2658 2656
2659 impl<T> IntoIterator for Vec<T> { 2657impl<T> IntoIterator for Vec<T> {
2660 type Item = T; 2658 type Item = T;
2661 type IntoIter = IntoIter<T>; 2659 type IntoIter = IntoIter<T>;
2662 } 2660}
2663 2661
2664 pub struct IntoIter<T> { } 2662pub struct IntoIter<T> { }
2665 impl<T> Iterator for IntoIter<T> { 2663impl<T> Iterator for IntoIter<T> {
2666 type Item = T; 2664 type Item = T;
2667 } 2665}
2668 2666
2669 fn main() { 2667fn main() {
2670 Vec::<i32>::new().into_iter() 2668 Vec::<i32>::new().into_iter()
2671 .filter_map(|x| if x > 0 { Some(x as u32) } else { None }) 2669 .filter_map(|x| if x > 0 { Some(x as u32) } else { None })
2672 .for_each(|y| { y; }); 2670 .for_each(|y| { y; });
2673 } 2671}"#,
2674 "#,
2675 expect![[r#" 2672 expect![[r#"
2676 226..230 'self': Self 2673 226..230 'self': Self
2677 232..233 'f': F 2674 232..233 'f': F
@@ -2753,14 +2750,13 @@ fn main() {
2753fn trait_object_no_coercion() { 2750fn trait_object_no_coercion() {
2754 check_infer_with_mismatches( 2751 check_infer_with_mismatches(
2755 r#" 2752 r#"
2756 trait Foo {} 2753trait Foo {}
2757 2754
2758 fn foo(x: &dyn Foo) {} 2755fn foo(x: &dyn Foo) {}
2759 2756
2760 fn test(x: &dyn Foo) { 2757fn test(x: &dyn Foo) {
2761 foo(x); 2758 foo(x);
2762 } 2759}"#,
2763 "#,
2764 expect![[r#" 2760 expect![[r#"
2765 21..22 'x': &dyn Foo 2761 21..22 'x': &dyn Foo
2766 34..36 '{}': () 2762 34..36 '{}': ()
@@ -2777,23 +2773,22 @@ fn trait_object_no_coercion() {
2777fn builtin_copy() { 2773fn builtin_copy() {
2778 check_infer_with_mismatches( 2774 check_infer_with_mismatches(
2779 r#" 2775 r#"
2780 #[lang = "copy"] 2776#[lang = "copy"]
2781 trait Copy {} 2777trait Copy {}
2782 2778
2783 struct IsCopy; 2779struct IsCopy;
2784 impl Copy for IsCopy {} 2780impl Copy for IsCopy {}
2785 struct NotCopy; 2781struct NotCopy;
2786 2782
2787 trait Test { fn test(&self) -> bool; } 2783trait Test { fn test(&self) -> bool; }
2788 impl<T: Copy> Test for T {} 2784impl<T: Copy> Test for T {}
2789 2785
2790 fn test() { 2786fn test() {
2791 IsCopy.test(); 2787 IsCopy.test();
2792 NotCopy.test(); 2788 NotCopy.test();
2793 (IsCopy, IsCopy).test(); 2789 (IsCopy, IsCopy).test();
2794 (IsCopy, NotCopy).test(); 2790 (IsCopy, NotCopy).test();
2795 } 2791}"#,
2796 "#,
2797 expect![[r#" 2792 expect![[r#"
2798 110..114 'self': &Self 2793 110..114 'self': &Self
2799 166..267 '{ ...t(); }': () 2794 166..267 '{ ...t(); }': ()
@@ -2817,24 +2812,23 @@ fn builtin_copy() {
2817fn builtin_fn_def_copy() { 2812fn builtin_fn_def_copy() {
2818 check_infer_with_mismatches( 2813 check_infer_with_mismatches(
2819 r#" 2814 r#"
2820 #[lang = "copy"] 2815#[lang = "copy"]
2821 trait Copy {} 2816trait Copy {}
2822 2817
2823 fn foo() {} 2818fn foo() {}
2824 fn bar<T: Copy>(T) -> T {} 2819fn bar<T: Copy>(T) -> T {}
2825 struct Struct(usize); 2820struct Struct(usize);
2826 enum Enum { Variant(usize) } 2821enum Enum { Variant(usize) }
2827 2822
2828 trait Test { fn test(&self) -> bool; } 2823trait Test { fn test(&self) -> bool; }
2829 impl<T: Copy> Test for T {} 2824impl<T: Copy> Test for T {}
2830 2825
2831 fn test() { 2826fn test() {
2832 foo.test(); 2827 foo.test();
2833 bar.test(); 2828 bar.test();
2834 Struct.test(); 2829 Struct.test();
2835 Enum::Variant.test(); 2830 Enum::Variant.test();
2836 } 2831}"#,
2837 "#,
2838 expect![[r#" 2832 expect![[r#"
2839 41..43 '{}': () 2833 41..43 '{}': ()
2840 60..61 'T': {unknown} 2834 60..61 'T': {unknown}
@@ -2858,18 +2852,17 @@ fn builtin_fn_def_copy() {
2858fn builtin_fn_ptr_copy() { 2852fn builtin_fn_ptr_copy() {
2859 check_infer_with_mismatches( 2853 check_infer_with_mismatches(
2860 r#" 2854 r#"
2861 #[lang = "copy"] 2855#[lang = "copy"]
2862 trait Copy {} 2856trait Copy {}
2863 2857
2864 trait Test { fn test(&self) -> bool; } 2858trait Test { fn test(&self) -> bool; }
2865 impl<T: Copy> Test for T {} 2859impl<T: Copy> Test for T {}
2866 2860
2867 fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) { 2861fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) {
2868 f1.test(); 2862 f1.test();
2869 f2.test(); 2863 f2.test();
2870 f3.test(); 2864 f3.test();
2871 } 2865}"#,
2872 "#,
2873 expect![[r#" 2866 expect![[r#"
2874 54..58 'self': &Self 2867 54..58 'self': &Self
2875 108..110 'f1': fn() 2868 108..110 'f1': fn()
@@ -2890,19 +2883,18 @@ fn builtin_fn_ptr_copy() {
2890fn builtin_sized() { 2883fn builtin_sized() {
2891 check_infer_with_mismatches( 2884 check_infer_with_mismatches(
2892 r#" 2885 r#"
2893 #[lang = "sized"] 2886#[lang = "sized"]
2894 trait Sized {} 2887trait Sized {}
2895 2888
2896 trait Test { fn test(&self) -> bool; } 2889trait Test { fn test(&self) -> bool; }
2897 impl<T: Sized> Test for T {} 2890impl<T: Sized> Test for T {}
2898 2891
2899 fn test() { 2892fn test() {
2900 1u8.test(); 2893 1u8.test();
2901 (*"foo").test(); // not Sized 2894 (*"foo").test(); // not Sized
2902 (1u8, 1u8).test(); 2895 (1u8, 1u8).test();
2903 (1u8, *"foo").test(); // not Sized 2896 (1u8, *"foo").test(); // not Sized
2904 } 2897}"#,
2905 "#,
2906 expect![[r#" 2898 expect![[r#"
2907 56..60 'self': &Self 2899 56..60 'self': &Self
2908 113..228 '{ ...ized }': () 2900 113..228 '{ ...ized }': ()
@@ -2972,19 +2964,18 @@ impl<A: Step> iter::Iterator for ops::Range<A> {
2972fn infer_closure_arg() { 2964fn infer_closure_arg() {
2973 check_infer( 2965 check_infer(
2974 r#" 2966 r#"
2975 //- /lib.rs 2967//- /lib.rs
2976 2968
2977 enum Option<T> { 2969enum Option<T> {
2978 None, 2970 None,
2979 Some(T) 2971 Some(T)
2980 } 2972}
2981 2973
2982 fn foo() { 2974fn foo() {
2983 let s = Option::None; 2975 let s = Option::None;
2984 let f = |x: Option<i32>| {}; 2976 let f = |x: Option<i32>| {};
2985 (&f)(s) 2977 (&f)(s)
2986 } 2978}"#,
2987 "#,
2988 expect![[r#" 2979 expect![[r#"
2989 52..126 '{ ...)(s) }': () 2980 52..126 '{ ...)(s) }': ()
2990 62..63 's': Option<i32> 2981 62..63 's': Option<i32>
@@ -3053,46 +3044,45 @@ fn infer_box_fn_arg() {
3053 // The type mismatch is a bug 3044 // The type mismatch is a bug
3054 check_infer_with_mismatches( 3045 check_infer_with_mismatches(
3055 r#" 3046 r#"
3056 //- /lib.rs deps:std 3047//- /lib.rs deps:std
3057 3048
3058 #[lang = "fn_once"] 3049#[lang = "fn_once"]
3059 pub trait FnOnce<Args> { 3050pub trait FnOnce<Args> {
3060 type Output; 3051 type Output;
3061 3052
3062 extern "rust-call" fn call_once(self, args: Args) -> Self::Output; 3053 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
3063 } 3054}
3064 3055
3065 #[lang = "deref"] 3056#[lang = "deref"]
3066 pub trait Deref { 3057pub trait Deref {
3067 type Target: ?Sized; 3058 type Target: ?Sized;
3068 3059
3069 fn deref(&self) -> &Self::Target; 3060 fn deref(&self) -> &Self::Target;
3070 } 3061}
3071 3062
3072 #[lang = "owned_box"] 3063#[lang = "owned_box"]
3073 pub struct Box<T: ?Sized> { 3064pub struct Box<T: ?Sized> {
3074 inner: *mut T, 3065 inner: *mut T,
3075 } 3066}
3076 3067
3077 impl<T: ?Sized> Deref for Box<T> { 3068impl<T: ?Sized> Deref for Box<T> {
3078 type Target = T; 3069 type Target = T;
3079 3070
3080 fn deref(&self) -> &T { 3071 fn deref(&self) -> &T {
3081 &self.inner 3072 &self.inner
3082 } 3073 }
3083 } 3074}
3084 3075
3085 enum Option<T> { 3076enum Option<T> {
3086 None, 3077 None,
3087 Some(T) 3078 Some(T)
3088 } 3079}
3089 3080
3090 fn foo() { 3081fn foo() {
3091 let s = Option::None; 3082 let s = Option::None;
3092 let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {}); 3083 let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {});
3093 f(&s); 3084 f(&s);
3094 } 3085}"#,
3095 "#,
3096 expect![[r#" 3086 expect![[r#"
3097 100..104 'self': Self 3087 100..104 'self': Self
3098 106..110 'args': Args 3088 106..110 'args': Args
@@ -3258,8 +3248,7 @@ fn f() {
3258 ().method(); 3248 ().method();
3259 //^^^^^^^^^^^ u8 3249 //^^^^^^^^^^^ u8
3260 } 3250 }
3261} 3251}"#,
3262 "#,
3263 expect![[r#" 3252 expect![[r#"
3264 46..50 'self': &Self 3253 46..50 'self': &Self
3265 58..63 '{ 0 }': u8 3254 58..63 '{ 0 }': u8
@@ -3313,8 +3302,7 @@ fn f() {
3313 fn inner() -> S { 3302 fn inner() -> S {
3314 let s = inner(); 3303 let s = inner();
3315 } 3304 }
3316} 3305}"#,
3317 "#,
3318 expect![[r#" 3306 expect![[r#"
3319 17..73 '{ ... } }': () 3307 17..73 '{ ... } }': ()
3320 39..71 '{ ... }': () 3308 39..71 '{ ... }': ()
@@ -3349,8 +3337,7 @@ fn test() {
3349 let x = A; 3337 let x = A;
3350 let y = A; 3338 let y = A;
3351 let r = x.do_op(y); 3339 let r = x.do_op(y);
3352} 3340}"#,
3353 "#,
3354 expect![[r#" 3341 expect![[r#"
3355 63..67 'self': Self 3342 63..67 'self': Self
3356 69..72 'rhs': RHS 3343 69..72 'rhs': RHS
@@ -3399,9 +3386,7 @@ impl foo::Bar for F {
3399fn foo() { 3386fn foo() {
3400 use foo::Bar; 3387 use foo::Bar;
3401 let x = <F as Bar>::boo(); 3388 let x = <F as Bar>::boo();
3402} 3389}"#,
3403
3404 "#,
3405 expect![[r#" 3390 expect![[r#"
3406 132..163 '{ ... }': Bar::Output<Self> 3391 132..163 '{ ... }': Bar::Output<Self>
3407 146..153 'loop {}': ! 3392 146..153 'loop {}': !
@@ -3413,3 +3398,79 @@ fn foo() {
3413 "#]], 3398 "#]],
3414 ); 3399 );
3415} 3400}
3401
3402#[test]
3403fn renamed_extern_crate_in_block() {
3404 check_types(
3405 r#"
3406//- /lib.rs crate:lib deps:serde
3407use serde::Deserialize;
3408
3409struct Foo {}
3410
3411const _ : () = {
3412 extern crate serde as _serde;
3413 impl _serde::Deserialize for Foo {
3414 fn deserialize() -> u8 { 0 }
3415 }
3416};
3417
3418fn foo() {
3419 Foo::deserialize();
3420 //^^^^^^^^^^^^^^^^^^ u8
3421}
3422
3423//- /serde.rs crate:serde
3424
3425pub trait Deserialize {
3426 fn deserialize() -> u8;
3427}"#,
3428 );
3429}
3430
3431#[test]
3432fn bin_op_adt_with_rhs_primitive() {
3433 check_infer_with_mismatches(
3434 r#"
3435#[lang = "add"]
3436pub trait Add<Rhs = Self> {
3437 type Output;
3438 fn add(self, rhs: Rhs) -> Self::Output;
3439}
3440
3441struct Wrapper(u32);
3442impl Add<u32> for Wrapper {
3443 type Output = Self;
3444 fn add(self, rhs: u32) -> Wrapper {
3445 Wrapper(rhs)
3446 }
3447}
3448fn main(){
3449 let wrapped = Wrapper(10);
3450 let num: u32 = 2;
3451 let res = wrapped + num;
3452
3453}"#,
3454 expect![[r#"
3455 72..76 'self': Self
3456 78..81 'rhs': Rhs
3457 192..196 'self': Wrapper
3458 198..201 'rhs': u32
3459 219..247 '{ ... }': Wrapper
3460 229..236 'Wrapper': Wrapper(u32) -> Wrapper
3461 229..241 'Wrapper(rhs)': Wrapper
3462 237..240 'rhs': u32
3463 259..345 '{ ...um; }': ()
3464 269..276 'wrapped': Wrapper
3465 279..286 'Wrapper': Wrapper(u32) -> Wrapper
3466 279..290 'Wrapper(10)': Wrapper
3467 287..289 '10': u32
3468 300..303 'num': u32
3469 311..312 '2': u32
3470 322..325 'res': Wrapper
3471 328..335 'wrapped': Wrapper
3472 328..341 'wrapped + num': Wrapper
3473 338..341 'num': u32
3474 "#]],
3475 )
3476}
diff --git a/crates/hir_ty/src/traits/chalk/tls.rs b/crates/hir_ty/src/tls.rs
index 8892a63a9..87c671a42 100644
--- a/crates/hir_ty/src/traits/chalk/tls.rs
+++ b/crates/hir_ty/src/tls.rs
@@ -4,8 +4,10 @@ use std::fmt;
4use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication}; 4use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplication};
5use itertools::Itertools; 5use itertools::Itertools;
6 6
7use super::{from_chalk, Interner}; 7use crate::{
8use crate::{db::HirDatabase, from_assoc_type_id, CallableDefId}; 8 chalk_db, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, mapping::from_chalk,
9 CallableDefId, Interner,
10};
9use hir_def::{AdtId, AssocContainerId, Lookup, TypeAliasId}; 11use hir_def::{AdtId, AssocContainerId, Lookup, TypeAliasId};
10 12
11pub(crate) use unsafe_tls::{set_current_program, with_current_program}; 13pub(crate) use unsafe_tls::{set_current_program, with_current_program};
@@ -15,7 +17,7 @@ pub(crate) struct DebugContext<'a>(&'a dyn HirDatabase);
15impl DebugContext<'_> { 17impl DebugContext<'_> {
16 pub(crate) fn debug_struct_id( 18 pub(crate) fn debug_struct_id(
17 &self, 19 &self,
18 id: super::AdtId, 20 id: chalk_db::AdtId,
19 f: &mut fmt::Formatter<'_>, 21 f: &mut fmt::Formatter<'_>,
20 ) -> Result<(), fmt::Error> { 22 ) -> Result<(), fmt::Error> {
21 let name = match id.0 { 23 let name = match id.0 {
@@ -28,17 +30,17 @@ impl DebugContext<'_> {
28 30
29 pub(crate) fn debug_trait_id( 31 pub(crate) fn debug_trait_id(
30 &self, 32 &self,
31 id: super::TraitId, 33 id: chalk_db::TraitId,
32 fmt: &mut fmt::Formatter<'_>, 34 fmt: &mut fmt::Formatter<'_>,
33 ) -> Result<(), fmt::Error> { 35 ) -> Result<(), fmt::Error> {
34 let trait_: hir_def::TraitId = from_chalk(self.0, id); 36 let trait_: hir_def::TraitId = from_chalk_trait_id(id);
35 let trait_data = self.0.trait_data(trait_); 37 let trait_data = self.0.trait_data(trait_);
36 write!(fmt, "{}", trait_data.name) 38 write!(fmt, "{}", trait_data.name)
37 } 39 }
38 40
39 pub(crate) fn debug_assoc_type_id( 41 pub(crate) fn debug_assoc_type_id(
40 &self, 42 &self,
41 id: super::AssocTypeId, 43 id: chalk_db::AssocTypeId,
42 fmt: &mut fmt::Formatter<'_>, 44 fmt: &mut fmt::Formatter<'_>,
43 ) -> Result<(), fmt::Error> { 45 ) -> Result<(), fmt::Error> {
44 let type_alias: TypeAliasId = from_assoc_type_id(id); 46 let type_alias: TypeAliasId = from_assoc_type_id(id);
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs
index e5e8cff33..9936d0803 100644
--- a/crates/hir_ty/src/traits.rs
+++ b/crates/hir_ty/src/traits.rs
@@ -1,28 +1,26 @@
1//! Trait solving using Chalk. 1//! Trait solving using Chalk.
2
2use std::env::var; 3use std::env::var;
3 4
4use base_db::CrateId;
5use chalk_ir::cast::Cast; 5use chalk_ir::cast::Cast;
6use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver}; 6use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver};
7
8use base_db::CrateId;
7use hir_def::{lang_item::LangItemTarget, TraitId}; 9use hir_def::{lang_item::LangItemTarget, TraitId};
8use stdx::panic_context; 10use stdx::panic_context;
9 11
10use crate::{ 12use crate::{
11 db::HirDatabase, AliasTy, Canonical, DebruijnIndex, HirDisplay, Substitution, Ty, TyKind, 13 db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Guidance, HirDisplay, InEnvironment,
12 TypeWalk, WhereClause, 14 Interner, Solution, TraitRefExt, Ty, TyKind, WhereClause,
13}; 15};
14 16
15use self::chalk::{from_chalk, Interner, ToChalk};
16
17pub(crate) mod chalk;
18
19/// This controls how much 'time' we give the Chalk solver before giving up. 17/// This controls how much 'time' we give the Chalk solver before giving up.
20const CHALK_SOLVER_FUEL: i32 = 100; 18const CHALK_SOLVER_FUEL: i32 = 100;
21 19
22#[derive(Debug, Copy, Clone)] 20#[derive(Debug, Copy, Clone)]
23struct ChalkContext<'a> { 21pub(crate) struct ChalkContext<'a> {
24 db: &'a dyn HirDatabase, 22 pub(crate) db: &'a dyn HirDatabase,
25 krate: CrateId, 23 pub(crate) krate: CrateId,
26} 24}
27 25
28fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> { 26fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
@@ -70,55 +68,6 @@ impl Default for TraitEnvironment {
70 } 68 }
71} 69}
72 70
73/// Something (usually a goal), along with an environment.
74#[derive(Clone, Debug, PartialEq, Eq, Hash)]
75pub struct InEnvironment<T> {
76 pub environment: chalk_ir::Environment<Interner>,
77 pub goal: T,
78}
79
80impl<T> InEnvironment<T> {
81 pub fn new(environment: chalk_ir::Environment<Interner>, value: T) -> InEnvironment<T> {
82 InEnvironment { environment, goal: value }
83 }
84}
85
86/// Something that needs to be proven (by Chalk) during type checking, e.g. that
87/// a certain type implements a certain trait. Proving the Obligation might
88/// result in additional information about inference variables.
89#[derive(Clone, Debug, PartialEq, Eq, Hash)]
90pub enum DomainGoal {
91 Holds(WhereClause),
92}
93
94#[derive(Clone, Debug, PartialEq, Eq, Hash)]
95pub struct AliasEq {
96 pub alias: AliasTy,
97 pub ty: Ty,
98}
99
100impl TypeWalk for AliasEq {
101 fn walk(&self, f: &mut impl FnMut(&Ty)) {
102 self.ty.walk(f);
103 match &self.alias {
104 AliasTy::Projection(projection_ty) => projection_ty.walk(f),
105 AliasTy::Opaque(opaque) => opaque.walk(f),
106 }
107 }
108
109 fn walk_mut_binders(
110 &mut self,
111 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
112 binders: DebruijnIndex,
113 ) {
114 self.ty.walk_mut_binders(f, binders);
115 match &mut self.alias {
116 AliasTy::Projection(projection_ty) => projection_ty.walk_mut_binders(f, binders),
117 AliasTy::Opaque(opaque) => opaque.walk_mut_binders(f, binders),
118 }
119 }
120}
121
122/// Solve a trait goal using Chalk. 71/// Solve a trait goal using Chalk.
123pub(crate) fn trait_solve_query( 72pub(crate) fn trait_solve_query(
124 db: &dyn HirDatabase, 73 db: &dyn HirDatabase,
@@ -130,6 +79,7 @@ pub(crate) fn trait_solve_query(
130 db.trait_data(it.hir_trait_id()).name.to_string() 79 db.trait_data(it.hir_trait_id()).name.to_string()
131 } 80 }
132 DomainGoal::Holds(WhereClause::AliasEq(_)) => "alias_eq".to_string(), 81 DomainGoal::Holds(WhereClause::AliasEq(_)) => "alias_eq".to_string(),
82 _ => "??".to_string(),
133 }); 83 });
134 log::info!("trait_solve_query({})", goal.value.goal.display(db)); 84 log::info!("trait_solve_query({})", goal.value.goal.display(db));
135 85
@@ -138,19 +88,18 @@ pub(crate) fn trait_solve_query(
138 .. 88 ..
139 })) = &goal.value.goal 89 })) = &goal.value.goal
140 { 90 {
141 if let TyKind::BoundVar(_) = projection_ty.self_type_parameter().kind(&Interner) { 91 if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(&Interner).kind(&Interner) {
142 // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible 92 // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
143 return Some(Solution::Ambig(Guidance::Unknown)); 93 return Some(Solution::Ambig(Guidance::Unknown));
144 } 94 }
145 } 95 }
146 96
147 let canonical = goal.to_chalk(db).cast(&Interner); 97 let canonical = goal.cast(&Interner);
148 98
149 // We currently don't deal with universes (I think / hope they're not yet 99 // We currently don't deal with universes (I think / hope they're not yet
150 // relevant for our use cases?) 100 // relevant for our use cases?)
151 let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 }; 101 let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
152 let solution = solve(db, krate, &u_canonical); 102 solve(db, krate, &u_canonical)
153 solution.map(|solution| solution_from_chalk(db, solution))
154} 103}
155 104
156fn solve( 105fn solve(
@@ -197,7 +146,7 @@ fn solve(
197 // don't set the TLS for Chalk unless Chalk debugging is active, to make 146 // don't set the TLS for Chalk unless Chalk debugging is active, to make
198 // extra sure we only use it for debugging 147 // extra sure we only use it for debugging
199 let solution = 148 let solution =
200 if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() }; 149 if is_chalk_debug() { crate::tls::set_current_program(db, solve) } else { solve() };
201 150
202 solution 151 solution
203} 152}
@@ -218,69 +167,6 @@ fn is_chalk_print() -> bool {
218 std::env::var("CHALK_PRINT").is_ok() 167 std::env::var("CHALK_PRINT").is_ok()
219} 168}
220 169
221fn solution_from_chalk(
222 db: &dyn HirDatabase,
223 solution: chalk_solve::Solution<Interner>,
224) -> Solution {
225 let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<Interner>>| {
226 let result = from_chalk(db, subst);
227 SolutionVariables(result)
228 };
229 match solution {
230 chalk_solve::Solution::Unique(constr_subst) => {
231 let subst = chalk_ir::Canonical {
232 value: constr_subst.value.subst,
233 binders: constr_subst.binders,
234 };
235 Solution::Unique(convert_subst(subst))
236 }
237 chalk_solve::Solution::Ambig(chalk_solve::Guidance::Definite(subst)) => {
238 Solution::Ambig(Guidance::Definite(convert_subst(subst)))
239 }
240 chalk_solve::Solution::Ambig(chalk_solve::Guidance::Suggested(subst)) => {
241 Solution::Ambig(Guidance::Suggested(convert_subst(subst)))
242 }
243 chalk_solve::Solution::Ambig(chalk_solve::Guidance::Unknown) => {
244 Solution::Ambig(Guidance::Unknown)
245 }
246 }
247}
248
249#[derive(Clone, Debug, PartialEq, Eq)]
250pub struct SolutionVariables(pub Canonical<Substitution>);
251
252#[derive(Clone, Debug, PartialEq, Eq)]
253/// A (possible) solution for a proposed goal.
254pub enum Solution {
255 /// The goal indeed holds, and there is a unique value for all existential
256 /// variables.
257 Unique(SolutionVariables),
258
259 /// The goal may be provable in multiple ways, but regardless we may have some guidance
260 /// for type inference. In this case, we don't return any lifetime
261 /// constraints, since we have not "committed" to any particular solution
262 /// yet.
263 Ambig(Guidance),
264}
265
266#[derive(Clone, Debug, PartialEq, Eq)]
267/// When a goal holds ambiguously (e.g., because there are multiple possible
268/// solutions), we issue a set of *guidance* back to type inference.
269pub enum Guidance {
270 /// The existential variables *must* have the given values if the goal is
271 /// ever to hold, but that alone isn't enough to guarantee the goal will
272 /// actually hold.
273 Definite(SolutionVariables),
274
275 /// There are multiple plausible values for the existentials, but the ones
276 /// here are suggested as the preferred choice heuristically. These should
277 /// be used for inference fallback only.
278 Suggested(SolutionVariables),
279
280 /// There's no useful information to feed back to type inference
281 Unknown,
282}
283
284#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 170#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
285pub enum FnTrait { 171pub enum FnTrait {
286 FnOnce, 172 FnOnce,
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs
deleted file mode 100644
index 452b357e8..000000000
--- a/crates/hir_ty/src/traits/chalk/mapping.rs
+++ /dev/null
@@ -1,575 +0,0 @@
1//! This module contains the implementations of the `ToChalk` trait, which
2//! handles conversion between our data types and their corresponding types in
3//! Chalk (in both directions); plus some helper functions for more specialized
4//! conversions.
5
6use chalk_ir::{cast::Cast, fold::shift::Shift, interner::HasInterner, LifetimeData};
7use chalk_solve::rust_ir;
8
9use base_db::salsa::InternKey;
10use hir_def::{GenericDefId, TypeAliasId};
11
12use crate::{
13 db::HirDatabase,
14 primitive::UintTy,
15 traits::{Canonical, DomainGoal},
16 AliasTy, CallableDefId, FnPointer, GenericArg, InEnvironment, OpaqueTy, ProjectionTy,
17 QuantifiedWhereClause, Scalar, Substitution, TraitRef, Ty, TypeWalk, WhereClause,
18};
19
20use super::interner::*;
21use super::*;
22
23impl ToChalk for Ty {
24 type Chalk = chalk_ir::Ty<Interner>;
25 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty<Interner> {
26 match self.into_inner() {
27 TyKind::Ref(m, ty) => ref_to_chalk(db, m, ty),
28 TyKind::Array(ty) => array_to_chalk(db, ty),
29 TyKind::Function(FnPointer { sig, substs, .. }) => {
30 let substitution = chalk_ir::FnSubst(substs.to_chalk(db).shifted_in(&Interner));
31 chalk_ir::TyKind::Function(chalk_ir::FnPointer {
32 num_binders: 0,
33 sig,
34 substitution,
35 })
36 .intern(&Interner)
37 }
38 TyKind::AssociatedType(assoc_type_id, substs) => {
39 let substitution = substs.to_chalk(db);
40 chalk_ir::TyKind::AssociatedType(assoc_type_id, substitution).intern(&Interner)
41 }
42
43 TyKind::OpaqueType(id, substs) => {
44 let substitution = substs.to_chalk(db);
45 chalk_ir::TyKind::OpaqueType(id, substitution).intern(&Interner)
46 }
47
48 TyKind::ForeignType(id) => chalk_ir::TyKind::Foreign(id).intern(&Interner),
49
50 TyKind::Scalar(scalar) => chalk_ir::TyKind::Scalar(scalar).intern(&Interner),
51
52 TyKind::Tuple(cardinality, substs) => {
53 let substitution = substs.to_chalk(db);
54 chalk_ir::TyKind::Tuple(cardinality, substitution).intern(&Interner)
55 }
56 TyKind::Raw(mutability, ty) => {
57 let ty = ty.to_chalk(db);
58 chalk_ir::TyKind::Raw(mutability, ty).intern(&Interner)
59 }
60 TyKind::Slice(ty) => chalk_ir::TyKind::Slice(ty.to_chalk(db)).intern(&Interner),
61 TyKind::Str => chalk_ir::TyKind::Str.intern(&Interner),
62 TyKind::FnDef(id, substs) => {
63 let substitution = substs.to_chalk(db);
64 chalk_ir::TyKind::FnDef(id, substitution).intern(&Interner)
65 }
66 TyKind::Never => chalk_ir::TyKind::Never.intern(&Interner),
67
68 TyKind::Closure(closure_id, substs) => {
69 let substitution = substs.to_chalk(db);
70 chalk_ir::TyKind::Closure(closure_id, substitution).intern(&Interner)
71 }
72
73 TyKind::Adt(adt_id, substs) => {
74 let substitution = substs.to_chalk(db);
75 chalk_ir::TyKind::Adt(adt_id, substitution).intern(&Interner)
76 }
77 TyKind::Alias(AliasTy::Projection(proj_ty)) => {
78 let associated_ty_id = proj_ty.associated_ty_id;
79 let substitution = proj_ty.substitution.to_chalk(db);
80 chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
81 associated_ty_id,
82 substitution,
83 })
84 .cast(&Interner)
85 .intern(&Interner)
86 }
87 TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
88 let opaque_ty_id = opaque_ty.opaque_ty_id;
89 let substitution = opaque_ty.substitution.to_chalk(db);
90 chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { opaque_ty_id, substitution })
91 .cast(&Interner)
92 .intern(&Interner)
93 }
94 TyKind::Placeholder(idx) => idx.to_ty::<Interner>(&Interner),
95 TyKind::BoundVar(idx) => chalk_ir::TyKind::BoundVar(idx).intern(&Interner),
96 TyKind::InferenceVar(..) => panic!("uncanonicalized infer ty"),
97 TyKind::Dyn(dyn_ty) => {
98 let where_clauses = chalk_ir::QuantifiedWhereClauses::from_iter(
99 &Interner,
100 dyn_ty.bounds.value.interned().iter().cloned().map(|p| p.to_chalk(db)),
101 );
102 let bounded_ty = chalk_ir::DynTy {
103 bounds: make_binders(where_clauses, 1),
104 lifetime: LifetimeData::Static.intern(&Interner),
105 };
106 chalk_ir::TyKind::Dyn(bounded_ty).intern(&Interner)
107 }
108 TyKind::Unknown => chalk_ir::TyKind::Error.intern(&Interner),
109 }
110 }
111 fn from_chalk(db: &dyn HirDatabase, chalk: chalk_ir::Ty<Interner>) -> Self {
112 match chalk.data(&Interner).kind.clone() {
113 chalk_ir::TyKind::Error => TyKind::Unknown,
114 chalk_ir::TyKind::Array(ty, _size) => TyKind::Array(from_chalk(db, ty)),
115 chalk_ir::TyKind::Placeholder(idx) => TyKind::Placeholder(idx),
116 chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Projection(proj)) => {
117 let associated_ty = proj.associated_ty_id;
118 let parameters = from_chalk(db, proj.substitution);
119 TyKind::Alias(AliasTy::Projection(ProjectionTy {
120 associated_ty_id: associated_ty,
121 substitution: parameters,
122 }))
123 }
124 chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Opaque(opaque_ty)) => {
125 let opaque_ty_id = opaque_ty.opaque_ty_id;
126 let parameters = from_chalk(db, opaque_ty.substitution);
127 TyKind::Alias(AliasTy::Opaque(OpaqueTy { opaque_ty_id, substitution: parameters }))
128 }
129 chalk_ir::TyKind::Function(chalk_ir::FnPointer {
130 num_binders,
131 sig,
132 substitution,
133 ..
134 }) => {
135 assert_eq!(num_binders, 0);
136 let substs: Substitution = from_chalk(
137 db,
138 substitution.0.shifted_out(&Interner).expect("fn ptr should have no binders"),
139 );
140 TyKind::Function(FnPointer { num_args: (substs.len(&Interner) - 1), sig, substs })
141 }
142 chalk_ir::TyKind::BoundVar(idx) => TyKind::BoundVar(idx),
143 chalk_ir::TyKind::InferenceVar(_iv, _kind) => TyKind::Unknown,
144 chalk_ir::TyKind::Dyn(where_clauses) => {
145 assert_eq!(where_clauses.bounds.binders.len(&Interner), 1);
146 let bounds = where_clauses
147 .bounds
148 .skip_binders()
149 .iter(&Interner)
150 .map(|c| from_chalk(db, c.clone()));
151 TyKind::Dyn(crate::DynTy {
152 bounds: crate::Binders::new(
153 1,
154 crate::QuantifiedWhereClauses::from_iter(&Interner, bounds),
155 ),
156 })
157 }
158
159 chalk_ir::TyKind::Adt(adt_id, subst) => TyKind::Adt(adt_id, from_chalk(db, subst)),
160 chalk_ir::TyKind::AssociatedType(type_id, subst) => {
161 TyKind::AssociatedType(type_id, from_chalk(db, subst))
162 }
163
164 chalk_ir::TyKind::OpaqueType(opaque_type_id, subst) => {
165 TyKind::OpaqueType(opaque_type_id, from_chalk(db, subst))
166 }
167
168 chalk_ir::TyKind::Scalar(scalar) => TyKind::Scalar(scalar),
169 chalk_ir::TyKind::Tuple(cardinality, subst) => {
170 TyKind::Tuple(cardinality, from_chalk(db, subst))
171 }
172 chalk_ir::TyKind::Raw(mutability, ty) => TyKind::Raw(mutability, from_chalk(db, ty)),
173 chalk_ir::TyKind::Slice(ty) => TyKind::Slice(from_chalk(db, ty)),
174 chalk_ir::TyKind::Ref(mutability, _lifetime, ty) => {
175 TyKind::Ref(mutability, from_chalk(db, ty))
176 }
177 chalk_ir::TyKind::Str => TyKind::Str,
178 chalk_ir::TyKind::Never => TyKind::Never,
179
180 chalk_ir::TyKind::FnDef(fn_def_id, subst) => {
181 TyKind::FnDef(fn_def_id, from_chalk(db, subst))
182 }
183
184 chalk_ir::TyKind::Closure(id, subst) => TyKind::Closure(id, from_chalk(db, subst)),
185
186 chalk_ir::TyKind::Foreign(foreign_def_id) => TyKind::ForeignType(foreign_def_id),
187 chalk_ir::TyKind::Generator(_, _) => unimplemented!(), // FIXME
188 chalk_ir::TyKind::GeneratorWitness(_, _) => unimplemented!(), // FIXME
189 }
190 .intern(&Interner)
191 }
192}
193
194/// We currently don't model lifetimes, but Chalk does. So, we have to insert a
195/// fake lifetime here, because Chalks built-in logic may expect it to be there.
196fn ref_to_chalk(
197 db: &dyn HirDatabase,
198 mutability: chalk_ir::Mutability,
199 ty: Ty,
200) -> chalk_ir::Ty<Interner> {
201 let arg = ty.to_chalk(db);
202 let lifetime = LifetimeData::Static.intern(&Interner);
203 chalk_ir::TyKind::Ref(mutability, lifetime, arg).intern(&Interner)
204}
205
206/// We currently don't model constants, but Chalk does. So, we have to insert a
207/// fake constant here, because Chalks built-in logic may expect it to be there.
208fn array_to_chalk(db: &dyn HirDatabase, ty: Ty) -> chalk_ir::Ty<Interner> {
209 let arg = ty.to_chalk(db);
210 let usize_ty = chalk_ir::TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner);
211 let const_ = chalk_ir::ConstData {
212 ty: usize_ty,
213 value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: () }),
214 }
215 .intern(&Interner);
216 chalk_ir::TyKind::Array(arg, const_).intern(&Interner)
217}
218
219impl ToChalk for GenericArg {
220 type Chalk = chalk_ir::GenericArg<Interner>;
221
222 fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk {
223 match self.interned {
224 crate::GenericArgData::Ty(ty) => ty.to_chalk(db).cast(&Interner),
225 }
226 }
227
228 fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self {
229 match chalk.interned() {
230 chalk_ir::GenericArgData::Ty(ty) => Ty::from_chalk(db, ty.clone()).cast(&Interner),
231 chalk_ir::GenericArgData::Lifetime(_) => unimplemented!(),
232 chalk_ir::GenericArgData::Const(_) => unimplemented!(),
233 }
234 }
235}
236
237impl ToChalk for Substitution {
238 type Chalk = chalk_ir::Substitution<Interner>;
239
240 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Substitution<Interner> {
241 chalk_ir::Substitution::from_iter(
242 &Interner,
243 self.iter(&Interner).map(|ty| ty.clone().to_chalk(db)),
244 )
245 }
246
247 fn from_chalk(
248 db: &dyn HirDatabase,
249 parameters: chalk_ir::Substitution<Interner>,
250 ) -> Substitution {
251 let tys = parameters.iter(&Interner).map(|p| from_chalk(db, p.clone())).collect();
252 Substitution(tys)
253 }
254}
255
256impl ToChalk for TraitRef {
257 type Chalk = chalk_ir::TraitRef<Interner>;
258
259 fn to_chalk(self: TraitRef, db: &dyn HirDatabase) -> chalk_ir::TraitRef<Interner> {
260 let trait_id = self.trait_id;
261 let substitution = self.substitution.to_chalk(db);
262 chalk_ir::TraitRef { trait_id, substitution }
263 }
264
265 fn from_chalk(db: &dyn HirDatabase, trait_ref: chalk_ir::TraitRef<Interner>) -> Self {
266 let trait_id = trait_ref.trait_id;
267 let substs = from_chalk(db, trait_ref.substitution);
268 TraitRef { trait_id, substitution: substs }
269 }
270}
271
272impl ToChalk for hir_def::TraitId {
273 type Chalk = TraitId;
274
275 fn to_chalk(self, _db: &dyn HirDatabase) -> TraitId {
276 chalk_ir::TraitId(self.as_intern_id())
277 }
278
279 fn from_chalk(_db: &dyn HirDatabase, trait_id: TraitId) -> hir_def::TraitId {
280 InternKey::from_intern_id(trait_id.0)
281 }
282}
283
284impl ToChalk for hir_def::ImplId {
285 type Chalk = ImplId;
286
287 fn to_chalk(self, _db: &dyn HirDatabase) -> ImplId {
288 chalk_ir::ImplId(self.as_intern_id())
289 }
290
291 fn from_chalk(_db: &dyn HirDatabase, impl_id: ImplId) -> hir_def::ImplId {
292 InternKey::from_intern_id(impl_id.0)
293 }
294}
295
296impl ToChalk for CallableDefId {
297 type Chalk = FnDefId;
298
299 fn to_chalk(self, db: &dyn HirDatabase) -> FnDefId {
300 db.intern_callable_def(self).into()
301 }
302
303 fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDefId {
304 db.lookup_intern_callable_def(fn_def_id.into())
305 }
306}
307
308pub(crate) struct TypeAliasAsValue(pub(crate) TypeAliasId);
309
310impl ToChalk for TypeAliasAsValue {
311 type Chalk = AssociatedTyValueId;
312
313 fn to_chalk(self, _db: &dyn HirDatabase) -> AssociatedTyValueId {
314 rust_ir::AssociatedTyValueId(self.0.as_intern_id())
315 }
316
317 fn from_chalk(
318 _db: &dyn HirDatabase,
319 assoc_ty_value_id: AssociatedTyValueId,
320 ) -> TypeAliasAsValue {
321 TypeAliasAsValue(TypeAliasId::from_intern_id(assoc_ty_value_id.0))
322 }
323}
324
325impl ToChalk for WhereClause {
326 type Chalk = chalk_ir::WhereClause<Interner>;
327
328 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::WhereClause<Interner> {
329 match self {
330 WhereClause::Implemented(trait_ref) => {
331 chalk_ir::WhereClause::Implemented(trait_ref.to_chalk(db))
332 }
333 WhereClause::AliasEq(alias_eq) => chalk_ir::WhereClause::AliasEq(alias_eq.to_chalk(db)),
334 }
335 }
336
337 fn from_chalk(
338 db: &dyn HirDatabase,
339 where_clause: chalk_ir::WhereClause<Interner>,
340 ) -> WhereClause {
341 match where_clause {
342 chalk_ir::WhereClause::Implemented(tr) => WhereClause::Implemented(from_chalk(db, tr)),
343 chalk_ir::WhereClause::AliasEq(alias_eq) => {
344 WhereClause::AliasEq(from_chalk(db, alias_eq))
345 }
346
347 chalk_ir::WhereClause::LifetimeOutlives(_) => {
348 // we shouldn't get these from Chalk
349 panic!("encountered LifetimeOutlives from Chalk")
350 }
351
352 chalk_ir::WhereClause::TypeOutlives(_) => {
353 // we shouldn't get these from Chalk
354 panic!("encountered TypeOutlives from Chalk")
355 }
356 }
357 }
358}
359
360impl ToChalk for ProjectionTy {
361 type Chalk = chalk_ir::ProjectionTy<Interner>;
362
363 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy<Interner> {
364 chalk_ir::ProjectionTy {
365 associated_ty_id: self.associated_ty_id,
366 substitution: self.substitution.to_chalk(db),
367 }
368 }
369
370 fn from_chalk(
371 db: &dyn HirDatabase,
372 projection_ty: chalk_ir::ProjectionTy<Interner>,
373 ) -> ProjectionTy {
374 ProjectionTy {
375 associated_ty_id: projection_ty.associated_ty_id,
376 substitution: from_chalk(db, projection_ty.substitution),
377 }
378 }
379}
380impl ToChalk for OpaqueTy {
381 type Chalk = chalk_ir::OpaqueTy<Interner>;
382
383 fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk {
384 chalk_ir::OpaqueTy {
385 opaque_ty_id: self.opaque_ty_id,
386 substitution: self.substitution.to_chalk(db),
387 }
388 }
389
390 fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self {
391 OpaqueTy {
392 opaque_ty_id: chalk.opaque_ty_id,
393 substitution: from_chalk(db, chalk.substitution),
394 }
395 }
396}
397
398impl ToChalk for AliasTy {
399 type Chalk = chalk_ir::AliasTy<Interner>;
400
401 fn to_chalk(self, db: &dyn HirDatabase) -> Self::Chalk {
402 match self {
403 AliasTy::Projection(projection_ty) => {
404 chalk_ir::AliasTy::Projection(projection_ty.to_chalk(db))
405 }
406 AliasTy::Opaque(opaque_ty) => chalk_ir::AliasTy::Opaque(opaque_ty.to_chalk(db)),
407 }
408 }
409
410 fn from_chalk(db: &dyn HirDatabase, chalk: Self::Chalk) -> Self {
411 match chalk {
412 chalk_ir::AliasTy::Projection(projection_ty) => {
413 AliasTy::Projection(from_chalk(db, projection_ty))
414 }
415 chalk_ir::AliasTy::Opaque(opaque_ty) => AliasTy::Opaque(from_chalk(db, opaque_ty)),
416 }
417 }
418}
419
420impl ToChalk for AliasEq {
421 type Chalk = chalk_ir::AliasEq<Interner>;
422
423 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq<Interner> {
424 chalk_ir::AliasEq { alias: self.alias.to_chalk(db), ty: self.ty.to_chalk(db) }
425 }
426
427 fn from_chalk(db: &dyn HirDatabase, alias_eq: chalk_ir::AliasEq<Interner>) -> Self {
428 AliasEq { alias: from_chalk(db, alias_eq.alias), ty: from_chalk(db, alias_eq.ty) }
429 }
430}
431
432impl ToChalk for DomainGoal {
433 type Chalk = chalk_ir::DomainGoal<Interner>;
434
435 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::DomainGoal<Interner> {
436 match self {
437 DomainGoal::Holds(WhereClause::Implemented(tr)) => tr.to_chalk(db).cast(&Interner),
438 DomainGoal::Holds(WhereClause::AliasEq(alias_eq)) => {
439 alias_eq.to_chalk(db).cast(&Interner)
440 }
441 }
442 }
443
444 fn from_chalk(_db: &dyn HirDatabase, _goal: chalk_ir::DomainGoal<Interner>) -> Self {
445 unimplemented!()
446 }
447}
448
449impl<T> ToChalk for Canonical<T>
450where
451 T: ToChalk,
452 T::Chalk: HasInterner<Interner = Interner>,
453{
454 type Chalk = chalk_ir::Canonical<T::Chalk>;
455
456 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> {
457 let value = self.value.to_chalk(db);
458 chalk_ir::Canonical { value, binders: self.binders }
459 }
460
461 fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> {
462 Canonical { binders: canonical.binders, value: from_chalk(db, canonical.value) }
463 }
464}
465
466impl<T: ToChalk> ToChalk for InEnvironment<T>
467where
468 T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>,
469{
470 type Chalk = chalk_ir::InEnvironment<T::Chalk>;
471
472 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::InEnvironment<T::Chalk> {
473 chalk_ir::InEnvironment { environment: self.environment, goal: self.goal.to_chalk(db) }
474 }
475
476 fn from_chalk(
477 _db: &dyn HirDatabase,
478 _in_env: chalk_ir::InEnvironment<T::Chalk>,
479 ) -> InEnvironment<T> {
480 unimplemented!()
481 }
482}
483
484impl<T: ToChalk> ToChalk for crate::Binders<T>
485where
486 T::Chalk: chalk_ir::interner::HasInterner<Interner = Interner>,
487{
488 type Chalk = chalk_ir::Binders<T::Chalk>;
489
490 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Binders<T::Chalk> {
491 chalk_ir::Binders::new(
492 chalk_ir::VariableKinds::from_iter(
493 &Interner,
494 std::iter::repeat(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General))
495 .take(self.num_binders),
496 ),
497 self.value.to_chalk(db),
498 )
499 }
500
501 fn from_chalk(db: &dyn HirDatabase, binders: chalk_ir::Binders<T::Chalk>) -> crate::Binders<T> {
502 let (v, b) = binders.into_value_and_skipped_binders();
503 crate::Binders::new(b.len(&Interner), from_chalk(db, v))
504 }
505}
506
507pub(super) fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T>
508where
509 T: HasInterner<Interner = Interner>,
510{
511 chalk_ir::Binders::new(
512 chalk_ir::VariableKinds::from_iter(
513 &Interner,
514 std::iter::repeat(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General))
515 .take(num_vars),
516 ),
517 value,
518 )
519}
520
521pub(super) fn convert_where_clauses(
522 db: &dyn HirDatabase,
523 def: GenericDefId,
524 substs: &Substitution,
525) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
526 let generic_predicates = db.generic_predicates(def);
527 let mut result = Vec::with_capacity(generic_predicates.len());
528 for pred in generic_predicates.iter() {
529 result.push(pred.clone().subst(substs).to_chalk(db));
530 }
531 result
532}
533
534pub(super) fn generic_predicate_to_inline_bound(
535 db: &dyn HirDatabase,
536 pred: &QuantifiedWhereClause,
537 self_ty: &Ty,
538) -> Option<chalk_ir::Binders<rust_ir::InlineBound<Interner>>> {
539 // An InlineBound is like a GenericPredicate, except the self type is left out.
540 // We don't have a special type for this, but Chalk does.
541 let self_ty_shifted_in = self_ty.clone().shift_bound_vars(DebruijnIndex::ONE);
542 match &pred.value {
543 WhereClause::Implemented(trait_ref) => {
544 if trait_ref.self_type_parameter() != &self_ty_shifted_in {
545 // we can only convert predicates back to type bounds if they
546 // have the expected self type
547 return None;
548 }
549 let args_no_self = trait_ref.substitution.interned(&Interner)[1..]
550 .iter()
551 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
552 .collect();
553 let trait_bound = rust_ir::TraitBound { trait_id: trait_ref.trait_id, args_no_self };
554 Some(make_binders(rust_ir::InlineBound::TraitBound(trait_bound), pred.num_binders))
555 }
556 WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
557 if projection_ty.self_type_parameter() != &self_ty_shifted_in {
558 return None;
559 }
560 let trait_ = projection_ty.trait_(db);
561 let args_no_self = projection_ty.substitution.interned(&Interner)[1..]
562 .iter()
563 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
564 .collect();
565 let alias_eq_bound = rust_ir::AliasEqBound {
566 value: ty.clone().to_chalk(db),
567 trait_bound: rust_ir::TraitBound { trait_id: trait_.to_chalk(db), args_no_self },
568 associated_ty_id: projection_ty.associated_ty_id,
569 parameters: Vec::new(), // FIXME we don't support generic associated types yet
570 };
571 Some(make_binders(rust_ir::InlineBound::AliasEqBound(alias_eq_bound), pred.num_binders))
572 }
573 _ => None,
574 }
575}
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index b23e91b1b..2f04ee57a 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -1,22 +1,21 @@
1//! Helper functions for working with def, which don't need to be a separate 1//! Helper functions for working with def, which don't need to be a separate
2//! query, but can't be computed directly from `*Data` (ie, which need a `db`). 2//! query, but can't be computed directly from `*Data` (ie, which need a `db`).
3use std::sync::Arc;
4 3
5use chalk_ir::{BoundVar, DebruijnIndex}; 4use chalk_ir::{fold::Shift, BoundVar, DebruijnIndex};
6use hir_def::{ 5use hir_def::{
7 adt::VariantData,
8 db::DefDatabase, 6 db::DefDatabase,
9 generics::{ 7 generics::{
10 GenericParams, TypeParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, 8 GenericParams, TypeParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
11 }, 9 },
10 intern::Interned,
12 path::Path, 11 path::Path,
13 resolver::{HasResolver, TypeNs}, 12 resolver::{HasResolver, TypeNs},
14 type_ref::TypeRef, 13 type_ref::TypeRef,
15 AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId, VariantId, 14 AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId,
16}; 15};
17use hir_expand::name::{name, Name}; 16use hir_expand::name::{name, Name};
18 17
19use crate::{db::HirDatabase, Interner, Substitution, TraitRef, TyKind, TypeWalk, WhereClause}; 18use crate::{db::HirDatabase, Interner, Substitution, TraitRef, TraitRefExt, TyKind, WhereClause};
20 19
21fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { 20fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
22 let resolver = trait_.resolver(db); 21 let resolver = trait_.resolver(db);
@@ -32,11 +31,10 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
32 .filter_map(|pred| match pred { 31 .filter_map(|pred| match pred {
33 WherePredicate::ForLifetime { target, bound, .. } 32 WherePredicate::ForLifetime { target, bound, .. }
34 | WherePredicate::TypeBound { target, bound } => match target { 33 | WherePredicate::TypeBound { target, bound } => match target {
35 WherePredicateTypeTarget::TypeRef(TypeRef::Path(p)) 34 WherePredicateTypeTarget::TypeRef(type_ref) => match &**type_ref {
36 if p == &Path::from(name![Self]) => 35 TypeRef::Path(p) if p == &Path::from(name![Self]) => bound.as_path(),
37 { 36 _ => None,
38 bound.as_path() 37 },
39 }
40 WherePredicateTypeTarget::TypeParam(local_id) if Some(*local_id) == trait_self => { 38 WherePredicateTypeTarget::TypeParam(local_id) if Some(*local_id) == trait_self => {
41 bound.as_path() 39 bound.as_path()
42 } 40 }
@@ -66,19 +64,21 @@ fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<Tr
66 .filter_map(|pred| { 64 .filter_map(|pred| {
67 pred.as_ref().filter_map(|pred| match pred.skip_binders() { 65 pred.as_ref().filter_map(|pred| match pred.skip_binders() {
68 // FIXME: how to correctly handle higher-ranked bounds here? 66 // FIXME: how to correctly handle higher-ranked bounds here?
69 WhereClause::Implemented(tr) => { 67 WhereClause::Implemented(tr) => Some(
70 Some(tr.clone().shift_bound_vars_out(DebruijnIndex::ONE)) 68 tr.clone()
71 } 69 .shifted_out_to(&Interner, DebruijnIndex::ONE)
70 .expect("FIXME unexpected higher-ranked trait bound"),
71 ),
72 _ => None, 72 _ => None,
73 }) 73 })
74 }) 74 })
75 .map(|pred| pred.subst(&trait_ref.substitution)) 75 .map(|pred| pred.substitute(&Interner, &trait_ref.substitution))
76 .collect() 76 .collect()
77} 77}
78 78
79/// Returns an iterator over the whole super trait hierarchy (including the 79/// Returns an iterator over the whole super trait hierarchy (including the
80/// trait itself). 80/// trait itself).
81pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { 81pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
82 // we need to take care a bit here to avoid infinite loops in case of cycles 82 // we need to take care a bit here to avoid infinite loops in case of cycles
83 // (i.e. if we have `trait A: B; trait B: A;`) 83 // (i.e. if we have `trait A: B; trait B: A;`)
84 let mut result = vec![trait_]; 84 let mut result = vec![trait_];
@@ -103,6 +103,8 @@ pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<Tra
103/// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get 103/// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get
104/// `Self: OtherTrait<i32>`. 104/// `Self: OtherTrait<i32>`.
105pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> Vec<TraitRef> { 105pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> Vec<TraitRef> {
106 // FIXME: replace by Chalk's `super_traits`, maybe make this a query
107
106 // we need to take care a bit here to avoid infinite loops in case of cycles 108 // we need to take care a bit here to avoid infinite loops in case of cycles
107 // (i.e. if we have `trait A: B; trait B: A;`) 109 // (i.e. if we have `trait A: B; trait B: A;`)
108 let mut result = vec![trait_ref]; 110 let mut result = vec![trait_ref];
@@ -132,25 +134,6 @@ pub(super) fn associated_type_by_name_including_super_traits(
132 }) 134 })
133} 135}
134 136
135pub(super) fn variant_data(db: &dyn DefDatabase, var: VariantId) -> Arc<VariantData> {
136 match var {
137 VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
138 VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
139 VariantId::EnumVariantId(it) => {
140 db.enum_data(it.parent).variants[it.local_id].variant_data.clone()
141 }
142 }
143}
144
145/// Helper for mutating `Arc<[T]>` (i.e. `Arc::make_mut` for Arc slices).
146/// The underlying values are cloned if there are other strong references.
147pub(crate) fn make_mut_slice<T: Clone>(a: &mut Arc<[T]>) -> &mut [T] {
148 if Arc::get_mut(a).is_none() {
149 *a = a.iter().cloned().collect();
150 }
151 Arc::get_mut(a).unwrap()
152}
153
154pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics { 137pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
155 let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def))); 138 let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def)));
156 Generics { def, params: db.generic_params(def), parent_generics } 139 Generics { def, params: db.generic_params(def), parent_generics }
@@ -159,7 +142,7 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
159#[derive(Debug)] 142#[derive(Debug)]
160pub(crate) struct Generics { 143pub(crate) struct Generics {
161 def: GenericDefId, 144 def: GenericDefId,
162 pub(crate) params: Arc<GenericParams>, 145 pub(crate) params: Interned<GenericParams>,
163 parent_generics: Option<Box<Generics>>, 146 parent_generics: Option<Box<Generics>>,
164} 147}
165 148
diff --git a/crates/hir_ty/src/walk.rs b/crates/hir_ty/src/walk.rs
new file mode 100644
index 000000000..6ef1d5336
--- /dev/null
+++ b/crates/hir_ty/src/walk.rs
@@ -0,0 +1,150 @@
1//! The `TypeWalk` trait (probably to be replaced by Chalk's `Fold` and
2//! `Visit`).
3
4use chalk_ir::interner::HasInterner;
5
6use crate::{
7 AliasEq, AliasTy, Binders, CallableSig, FnSubst, GenericArg, GenericArgData, Interner,
8 OpaqueTy, ProjectionTy, Substitution, TraitRef, Ty, TyKind, WhereClause,
9};
10
11/// This allows walking structures that contain types to do something with those
12/// types, similar to Chalk's `Fold` trait.
13pub trait TypeWalk {
14 fn walk(&self, f: &mut impl FnMut(&Ty));
15}
16
17impl TypeWalk for Ty {
18 fn walk(&self, f: &mut impl FnMut(&Ty)) {
19 match self.kind(&Interner) {
20 TyKind::Alias(AliasTy::Projection(p_ty)) => {
21 for t in p_ty.substitution.iter(&Interner) {
22 t.walk(f);
23 }
24 }
25 TyKind::Alias(AliasTy::Opaque(o_ty)) => {
26 for t in o_ty.substitution.iter(&Interner) {
27 t.walk(f);
28 }
29 }
30 TyKind::Dyn(dyn_ty) => {
31 for p in dyn_ty.bounds.skip_binders().interned().iter() {
32 p.walk(f);
33 }
34 }
35 TyKind::Slice(ty)
36 | TyKind::Array(ty, _)
37 | TyKind::Ref(_, _, ty)
38 | TyKind::Raw(_, ty) => {
39 ty.walk(f);
40 }
41 TyKind::Function(fn_pointer) => {
42 fn_pointer.substitution.0.walk(f);
43 }
44 TyKind::Adt(_, substs)
45 | TyKind::FnDef(_, substs)
46 | TyKind::Tuple(_, substs)
47 | TyKind::OpaqueType(_, substs)
48 | TyKind::AssociatedType(_, substs)
49 | TyKind::Closure(.., substs) => {
50 substs.walk(f);
51 }
52 _ => {}
53 }
54 f(self);
55 }
56}
57
58impl<T: TypeWalk> TypeWalk for Vec<T> {
59 fn walk(&self, f: &mut impl FnMut(&Ty)) {
60 for t in self {
61 t.walk(f);
62 }
63 }
64}
65
66impl TypeWalk for OpaqueTy {
67 fn walk(&self, f: &mut impl FnMut(&Ty)) {
68 self.substitution.walk(f);
69 }
70}
71
72impl TypeWalk for ProjectionTy {
73 fn walk(&self, f: &mut impl FnMut(&Ty)) {
74 self.substitution.walk(f);
75 }
76}
77
78impl TypeWalk for AliasTy {
79 fn walk(&self, f: &mut impl FnMut(&Ty)) {
80 match self {
81 AliasTy::Projection(it) => it.walk(f),
82 AliasTy::Opaque(it) => it.walk(f),
83 }
84 }
85}
86
87impl TypeWalk for GenericArg {
88 fn walk(&self, f: &mut impl FnMut(&Ty)) {
89 match &self.interned() {
90 GenericArgData::Ty(ty) => {
91 ty.walk(f);
92 }
93 _ => {}
94 }
95 }
96}
97
98impl TypeWalk for Substitution {
99 fn walk(&self, f: &mut impl FnMut(&Ty)) {
100 for t in self.iter(&Interner) {
101 t.walk(f);
102 }
103 }
104}
105
106impl<T: TypeWalk + HasInterner<Interner = Interner>> TypeWalk for Binders<T> {
107 fn walk(&self, f: &mut impl FnMut(&Ty)) {
108 self.skip_binders().walk(f);
109 }
110}
111
112impl TypeWalk for TraitRef {
113 fn walk(&self, f: &mut impl FnMut(&Ty)) {
114 self.substitution.walk(f);
115 }
116}
117
118impl TypeWalk for WhereClause {
119 fn walk(&self, f: &mut impl FnMut(&Ty)) {
120 match self {
121 WhereClause::Implemented(trait_ref) => trait_ref.walk(f),
122 WhereClause::AliasEq(alias_eq) => alias_eq.walk(f),
123 _ => {}
124 }
125 }
126}
127
128impl TypeWalk for CallableSig {
129 fn walk(&self, f: &mut impl FnMut(&Ty)) {
130 for t in self.params_and_return.iter() {
131 t.walk(f);
132 }
133 }
134}
135
136impl TypeWalk for AliasEq {
137 fn walk(&self, f: &mut impl FnMut(&Ty)) {
138 self.ty.walk(f);
139 match &self.alias {
140 AliasTy::Projection(projection_ty) => projection_ty.walk(f),
141 AliasTy::Opaque(opaque) => opaque.walk(f),
142 }
143 }
144}
145
146impl TypeWalk for FnSubst<Interner> {
147 fn walk(&self, f: &mut impl FnMut(&Ty)) {
148 self.0.walk(f)
149 }
150}
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index dd42116a7..1c911a8b2 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -20,12 +20,12 @@ use itertools::Itertools;
20use rustc_hash::FxHashSet; 20use rustc_hash::FxHashSet;
21use syntax::{ 21use syntax::{
22 ast::{self, AstNode}, 22 ast::{self, AstNode},
23 SyntaxNode, SyntaxNodePtr, TextRange, 23 SyntaxNode, SyntaxNodePtr, TextRange, TextSize,
24}; 24};
25use text_edit::TextEdit; 25use text_edit::TextEdit;
26use unlinked_file::UnlinkedFile; 26use unlinked_file::UnlinkedFile;
27 27
28use crate::{FileId, Label, SourceChange}; 28use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange};
29 29
30use self::fixes::DiagnosticWithFix; 30use self::fixes::DiagnosticWithFix;
31 31
@@ -35,7 +35,7 @@ pub struct Diagnostic {
35 pub message: String, 35 pub message: String,
36 pub range: TextRange, 36 pub range: TextRange,
37 pub severity: Severity, 37 pub severity: Severity,
38 pub fix: Option<Fix>, 38 pub fix: Option<Assist>,
39 pub unused: bool, 39 pub unused: bool,
40 pub code: Option<DiagnosticCode>, 40 pub code: Option<DiagnosticCode>,
41} 41}
@@ -56,7 +56,7 @@ impl Diagnostic {
56 } 56 }
57 } 57 }
58 58
59 fn with_fix(self, fix: Option<Fix>) -> Self { 59 fn with_fix(self, fix: Option<Assist>) -> Self {
60 Self { fix, ..self } 60 Self { fix, ..self }
61 } 61 }
62 62
@@ -69,21 +69,6 @@ impl Diagnostic {
69 } 69 }
70} 70}
71 71
72#[derive(Debug)]
73pub struct Fix {
74 pub label: Label,
75 pub source_change: SourceChange,
76 /// Allows to trigger the fix only when the caret is in the range given
77 pub fix_trigger_range: TextRange,
78}
79
80impl Fix {
81 fn new(label: &str, source_change: SourceChange, fix_trigger_range: TextRange) -> Self {
82 let label = Label::new(label);
83 Self { label, source_change, fix_trigger_range }
84 }
85}
86
87#[derive(Debug, Copy, Clone)] 72#[derive(Debug, Copy, Clone)]
88pub enum Severity { 73pub enum Severity {
89 Error, 74 Error,
@@ -99,6 +84,7 @@ pub struct DiagnosticsConfig {
99pub(crate) fn diagnostics( 84pub(crate) fn diagnostics(
100 db: &RootDatabase, 85 db: &RootDatabase,
101 config: &DiagnosticsConfig, 86 config: &DiagnosticsConfig,
87 resolve: bool,
102 file_id: FileId, 88 file_id: FileId,
103) -> Vec<Diagnostic> { 89) -> Vec<Diagnostic> {
104 let _p = profile::span("diagnostics"); 90 let _p = profile::span("diagnostics");
@@ -122,25 +108,25 @@ pub(crate) fn diagnostics(
122 let res = RefCell::new(res); 108 let res = RefCell::new(res);
123 let sink_builder = DiagnosticSinkBuilder::new() 109 let sink_builder = DiagnosticSinkBuilder::new()
124 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 110 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
125 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 111 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
126 }) 112 })
127 .on::<hir::diagnostics::MissingFields, _>(|d| { 113 .on::<hir::diagnostics::MissingFields, _>(|d| {
128 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 114 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
129 }) 115 })
130 .on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| { 116 .on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| {
131 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 117 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
132 }) 118 })
133 .on::<hir::diagnostics::NoSuchField, _>(|d| { 119 .on::<hir::diagnostics::NoSuchField, _>(|d| {
134 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 120 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
135 }) 121 })
136 .on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| { 122 .on::<hir::diagnostics::RemoveThisSemicolon, _>(|d| {
137 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 123 res.borrow_mut().push(diagnostic_with_fix(d, &sema, resolve));
138 }) 124 })
139 .on::<hir::diagnostics::IncorrectCase, _>(|d| { 125 .on::<hir::diagnostics::IncorrectCase, _>(|d| {
140 res.borrow_mut().push(warning_with_fix(d, &sema)); 126 res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
141 }) 127 })
142 .on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| { 128 .on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| {
143 res.borrow_mut().push(warning_with_fix(d, &sema)); 129 res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
144 }) 130 })
145 .on::<hir::diagnostics::InactiveCode, _>(|d| { 131 .on::<hir::diagnostics::InactiveCode, _>(|d| {
146 // If there's inactive code somewhere in a macro, don't propagate to the call-site. 132 // If there's inactive code somewhere in a macro, don't propagate to the call-site.
@@ -159,14 +145,16 @@ pub(crate) fn diagnostics(
159 ); 145 );
160 }) 146 })
161 .on::<UnlinkedFile, _>(|d| { 147 .on::<UnlinkedFile, _>(|d| {
148 // Limit diagnostic to the first few characters in the file. This matches how VS Code
149 // renders it with the full span, but on other editors, and is less invasive.
150 let range = sema.diagnostics_display_range(d.display_source()).range;
151 let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range);
152
162 // Override severity and mark as unused. 153 // Override severity and mark as unused.
163 res.borrow_mut().push( 154 res.borrow_mut().push(
164 Diagnostic::hint( 155 Diagnostic::hint(range, d.message())
165 sema.diagnostics_display_range(d.display_source()).range, 156 .with_fix(d.fix(&sema, resolve))
166 d.message(), 157 .with_code(Some(d.code())),
167 )
168 .with_fix(d.fix(&sema))
169 .with_code(Some(d.code())),
170 ); 158 );
171 }) 159 })
172 .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| { 160 .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| {
@@ -221,15 +209,23 @@ pub(crate) fn diagnostics(
221 res.into_inner() 209 res.into_inner()
222} 210}
223 211
224fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { 212fn diagnostic_with_fix<D: DiagnosticWithFix>(
213 d: &D,
214 sema: &Semantics<RootDatabase>,
215 resolve: bool,
216) -> Diagnostic {
225 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message()) 217 Diagnostic::error(sema.diagnostics_display_range(d.display_source()).range, d.message())
226 .with_fix(d.fix(&sema)) 218 .with_fix(d.fix(&sema, resolve))
227 .with_code(Some(d.code())) 219 .with_code(Some(d.code()))
228} 220}
229 221
230fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic { 222fn warning_with_fix<D: DiagnosticWithFix>(
223 d: &D,
224 sema: &Semantics<RootDatabase>,
225 resolve: bool,
226) -> Diagnostic {
231 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message()) 227 Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message())
232 .with_fix(d.fix(&sema)) 228 .with_fix(d.fix(&sema, resolve))
233 .with_code(Some(d.code())) 229 .with_code(Some(d.code()))
234} 230}
235 231
@@ -259,7 +255,8 @@ fn check_unnecessary_braces_in_use_statement(
259 255
260 acc.push( 256 acc.push(
261 Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string()) 257 Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string())
262 .with_fix(Some(Fix::new( 258 .with_fix(Some(fix(
259 "remove_braces",
263 "Remove unnecessary braces", 260 "Remove unnecessary braces",
264 SourceChange::from_text_edit(file_id, edit), 261 SourceChange::from_text_edit(file_id, edit),
265 use_range, 262 use_range,
@@ -282,6 +279,23 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
282 None 279 None
283} 280}
284 281
282fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist {
283 let mut res = unresolved_fix(id, label, target);
284 res.source_change = Some(source_change);
285 res
286}
287
288fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
289 assert!(!id.contains(' '));
290 Assist {
291 id: AssistId(id, AssistKind::QuickFix),
292 label: Label::new(label),
293 group: None,
294 target,
295 source_change: None,
296 }
297}
298
285#[cfg(test)] 299#[cfg(test)]
286mod tests { 300mod tests {
287 use expect_test::{expect, Expect}; 301 use expect_test::{expect, Expect};
@@ -300,16 +314,17 @@ mod tests {
300 314
301 let (analysis, file_position) = fixture::position(ra_fixture_before); 315 let (analysis, file_position) = fixture::position(ra_fixture_before);
302 let diagnostic = analysis 316 let diagnostic = analysis
303 .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) 317 .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id)
304 .unwrap() 318 .unwrap()
305 .pop() 319 .pop()
306 .unwrap(); 320 .unwrap();
307 let fix = diagnostic.fix.unwrap(); 321 let fix = diagnostic.fix.unwrap();
308 let actual = { 322 let actual = {
309 let file_id = *fix.source_change.source_file_edits.keys().next().unwrap(); 323 let source_change = fix.source_change.unwrap();
324 let file_id = *source_change.source_file_edits.keys().next().unwrap();
310 let mut actual = analysis.file_text(file_id).unwrap().to_string(); 325 let mut actual = analysis.file_text(file_id).unwrap().to_string();
311 326
312 for edit in fix.source_change.source_file_edits.values() { 327 for edit in source_change.source_file_edits.values() {
313 edit.apply(&mut actual); 328 edit.apply(&mut actual);
314 } 329 }
315 actual 330 actual
@@ -317,9 +332,9 @@ mod tests {
317 332
318 assert_eq_text!(&after, &actual); 333 assert_eq_text!(&after, &actual);
319 assert!( 334 assert!(
320 fix.fix_trigger_range.contains_inclusive(file_position.offset), 335 fix.target.contains_inclusive(file_position.offset),
321 "diagnostic fix range {:?} does not touch cursor position {:?}", 336 "diagnostic fix range {:?} does not touch cursor position {:?}",
322 fix.fix_trigger_range, 337 fix.target,
323 file_position.offset 338 file_position.offset
324 ); 339 );
325 } 340 }
@@ -328,7 +343,7 @@ mod tests {
328 fn check_no_fix(ra_fixture: &str) { 343 fn check_no_fix(ra_fixture: &str) {
329 let (analysis, file_position) = fixture::position(ra_fixture); 344 let (analysis, file_position) = fixture::position(ra_fixture);
330 let diagnostic = analysis 345 let diagnostic = analysis
331 .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) 346 .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id)
332 .unwrap() 347 .unwrap()
333 .pop() 348 .pop()
334 .unwrap(); 349 .unwrap();
@@ -342,7 +357,7 @@ mod tests {
342 let diagnostics = files 357 let diagnostics = files
343 .into_iter() 358 .into_iter()
344 .flat_map(|file_id| { 359 .flat_map(|file_id| {
345 analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap() 360 analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap()
346 }) 361 })
347 .collect::<Vec<_>>(); 362 .collect::<Vec<_>>();
348 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics); 363 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
@@ -350,7 +365,8 @@ mod tests {
350 365
351 fn check_expect(ra_fixture: &str, expect: Expect) { 366 fn check_expect(ra_fixture: &str, expect: Expect) {
352 let (analysis, file_id) = fixture::file(ra_fixture); 367 let (analysis, file_id) = fixture::file(ra_fixture);
353 let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); 368 let diagnostics =
369 analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap();
354 expect.assert_debug_eq(&diagnostics) 370 expect.assert_debug_eq(&diagnostics)
355 } 371 }
356 372
@@ -663,24 +679,31 @@ fn test_fn() {
663 range: 0..8, 679 range: 0..8,
664 severity: Error, 680 severity: Error,
665 fix: Some( 681 fix: Some(
666 Fix { 682 Assist {
683 id: AssistId(
684 "create_module",
685 QuickFix,
686 ),
667 label: "Create module", 687 label: "Create module",
668 source_change: SourceChange { 688 group: None,
669 source_file_edits: {}, 689 target: 0..8,
670 file_system_edits: [ 690 source_change: Some(
671 CreateFile { 691 SourceChange {
672 dst: AnchoredPathBuf { 692 source_file_edits: {},
673 anchor: FileId( 693 file_system_edits: [
674 0, 694 CreateFile {
675 ), 695 dst: AnchoredPathBuf {
676 path: "foo.rs", 696 anchor: FileId(
697 0,
698 ),
699 path: "foo.rs",
700 },
701 initial_contents: "",
677 }, 702 },
678 initial_contents: "", 703 ],
679 }, 704 is_snippet: false,
680 ], 705 },
681 is_snippet: false, 706 ),
682 },
683 fix_trigger_range: 0..8,
684 }, 707 },
685 ), 708 ),
686 unused: false, 709 unused: false,
@@ -702,7 +725,7 @@ fn test_fn() {
702 expect![[r#" 725 expect![[r#"
703 [ 726 [
704 Diagnostic { 727 Diagnostic {
705 message: "unresolved macro call", 728 message: "unresolved macro `foo::bar!`",
706 range: 5..8, 729 range: 5..8,
707 severity: Error, 730 severity: Error,
708 fix: None, 731 fix: None,
@@ -888,10 +911,11 @@ struct Foo {
888 911
889 let (analysis, file_id) = fixture::file(r#"mod foo;"#); 912 let (analysis, file_id) = fixture::file(r#"mod foo;"#);
890 913
891 let diagnostics = analysis.diagnostics(&config, file_id).unwrap(); 914 let diagnostics = analysis.diagnostics(&config, true, file_id).unwrap();
892 assert!(diagnostics.is_empty()); 915 assert!(diagnostics.is_empty());
893 916
894 let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); 917 let diagnostics =
918 analysis.diagnostics(&DiagnosticsConfig::default(), true, file_id).unwrap();
895 assert!(!diagnostics.is_empty()); 919 assert!(!diagnostics.is_empty());
896 } 920 }
897 921
@@ -997,8 +1021,9 @@ impl TestStruct {
997 let expected = r#"fn foo() {}"#; 1021 let expected = r#"fn foo() {}"#;
998 1022
999 let (analysis, file_position) = fixture::position(input); 1023 let (analysis, file_position) = fixture::position(input);
1000 let diagnostics = 1024 let diagnostics = analysis
1001 analysis.diagnostics(&DiagnosticsConfig::default(), file_position.file_id).unwrap(); 1025 .diagnostics(&DiagnosticsConfig::default(), true, file_position.file_id)
1026 .unwrap();
1002 assert_eq!(diagnostics.len(), 1); 1027 assert_eq!(diagnostics.len(), 1);
1003 1028
1004 check_fix(input, expected); 1029 check_fix(input, expected);
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs
index 5c89e2170..2b1787f9b 100644
--- a/crates/ide/src/diagnostics/field_shorthand.rs
+++ b/crates/ide/src/diagnostics/field_shorthand.rs
@@ -5,7 +5,7 @@ use ide_db::{base_db::FileId, source_change::SourceChange};
5use syntax::{ast, match_ast, AstNode, SyntaxNode}; 5use syntax::{ast, match_ast, AstNode, SyntaxNode};
6use text_edit::TextEdit; 6use text_edit::TextEdit;
7 7
8use crate::{Diagnostic, Fix}; 8use crate::{diagnostics::fix, Diagnostic};
9 9
10pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) { 10pub(super) fn check(acc: &mut Vec<Diagnostic>, file_id: FileId, node: &SyntaxNode) {
11 match_ast! { 11 match_ast! {
@@ -47,7 +47,8 @@ fn check_expr_field_shorthand(
47 let field_range = record_field.syntax().text_range(); 47 let field_range = record_field.syntax().text_range();
48 acc.push( 48 acc.push(
49 Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix( 49 Diagnostic::hint(field_range, "Shorthand struct initialization".to_string()).with_fix(
50 Some(Fix::new( 50 Some(fix(
51 "use_expr_field_shorthand",
51 "Use struct shorthand initialization", 52 "Use struct shorthand initialization",
52 SourceChange::from_text_edit(file_id, edit), 53 SourceChange::from_text_edit(file_id, edit),
53 field_range, 54 field_range,
@@ -86,7 +87,8 @@ fn check_pat_field_shorthand(
86 87
87 let field_range = record_pat_field.syntax().text_range(); 88 let field_range = record_pat_field.syntax().text_range();
88 acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix( 89 acc.push(Diagnostic::hint(field_range, "Shorthand struct pattern".to_string()).with_fix(
89 Some(Fix::new( 90 Some(fix(
91 "use_pat_field_shorthand",
90 "Use struct field shorthand", 92 "Use struct field shorthand",
91 SourceChange::from_text_edit(file_id, edit), 93 SourceChange::from_text_edit(file_id, edit),
92 field_range, 94 field_range,
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index 5fb3e2d91..7be8b3459 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -20,20 +20,30 @@ use syntax::{
20}; 20};
21use text_edit::TextEdit; 21use text_edit::TextEdit;
22 22
23use crate::{diagnostics::Fix, references::rename::rename_with_semantics, FilePosition}; 23use crate::{
24 diagnostics::{fix, unresolved_fix},
25 references::rename::rename_with_semantics,
26 Assist, FilePosition,
27};
24 28
25/// A [Diagnostic] that potentially has a fix available. 29/// A [Diagnostic] that potentially has a fix available.
26/// 30///
27/// [Diagnostic]: hir::diagnostics::Diagnostic 31/// [Diagnostic]: hir::diagnostics::Diagnostic
28pub(crate) trait DiagnosticWithFix: Diagnostic { 32pub(crate) trait DiagnosticWithFix: Diagnostic {
29 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix>; 33 /// `resolve` determines if the diagnostic should fill in the `edit` field
34 /// of the assist.
35 ///
36 /// If `resolve` is false, the edit will be computed later, on demand, and
37 /// can be omitted.
38 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist>;
30} 39}
31 40
32impl DiagnosticWithFix for UnresolvedModule { 41impl DiagnosticWithFix for UnresolvedModule {
33 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { 42 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
34 let root = sema.db.parse_or_expand(self.file)?; 43 let root = sema.db.parse_or_expand(self.file)?;
35 let unresolved_module = self.decl.to_node(&root); 44 let unresolved_module = self.decl.to_node(&root);
36 Some(Fix::new( 45 Some(fix(
46 "create_module",
37 "Create module", 47 "Create module",
38 FileSystemEdit::CreateFile { 48 FileSystemEdit::CreateFile {
39 dst: AnchoredPathBuf { 49 dst: AnchoredPathBuf {
@@ -49,7 +59,7 @@ impl DiagnosticWithFix for UnresolvedModule {
49} 59}
50 60
51impl DiagnosticWithFix for NoSuchField { 61impl DiagnosticWithFix for NoSuchField {
52 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { 62 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
53 let root = sema.db.parse_or_expand(self.file)?; 63 let root = sema.db.parse_or_expand(self.file)?;
54 missing_record_expr_field_fix( 64 missing_record_expr_field_fix(
55 &sema, 65 &sema,
@@ -60,7 +70,7 @@ impl DiagnosticWithFix for NoSuchField {
60} 70}
61 71
62impl DiagnosticWithFix for MissingFields { 72impl DiagnosticWithFix for MissingFields {
63 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { 73 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
64 // Note that although we could add a diagnostics to 74 // Note that although we could add a diagnostics to
65 // fill the missing tuple field, e.g : 75 // fill the missing tuple field, e.g :
66 // `struct A(usize);` 76 // `struct A(usize);`
@@ -86,7 +96,8 @@ impl DiagnosticWithFix for MissingFields {
86 .into_text_edit(&mut builder); 96 .into_text_edit(&mut builder);
87 builder.finish() 97 builder.finish()
88 }; 98 };
89 Some(Fix::new( 99 Some(fix(
100 "fill_missing_fields",
90 "Fill struct fields", 101 "Fill struct fields",
91 SourceChange::from_text_edit(self.file.original_file(sema.db), edit), 102 SourceChange::from_text_edit(self.file.original_file(sema.db), edit),
92 sema.original_range(&field_list_parent.syntax()).range, 103 sema.original_range(&field_list_parent.syntax()).range,
@@ -95,7 +106,7 @@ impl DiagnosticWithFix for MissingFields {
95} 106}
96 107
97impl DiagnosticWithFix for MissingOkOrSomeInTailExpr { 108impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
98 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { 109 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
99 let root = sema.db.parse_or_expand(self.file)?; 110 let root = sema.db.parse_or_expand(self.file)?;
100 let tail_expr = self.expr.to_node(&root); 111 let tail_expr = self.expr.to_node(&root);
101 let tail_expr_range = tail_expr.syntax().text_range(); 112 let tail_expr_range = tail_expr.syntax().text_range();
@@ -103,12 +114,12 @@ impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
103 let edit = TextEdit::replace(tail_expr_range, replacement); 114 let edit = TextEdit::replace(tail_expr_range, replacement);
104 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); 115 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
105 let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" }; 116 let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" };
106 Some(Fix::new(name, source_change, tail_expr_range)) 117 Some(fix("wrap_tail_expr", name, source_change, tail_expr_range))
107 } 118 }
108} 119}
109 120
110impl DiagnosticWithFix for RemoveThisSemicolon { 121impl DiagnosticWithFix for RemoveThisSemicolon {
111 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { 122 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
112 let root = sema.db.parse_or_expand(self.file)?; 123 let root = sema.db.parse_or_expand(self.file)?;
113 124
114 let semicolon = self 125 let semicolon = self
@@ -123,12 +134,12 @@ impl DiagnosticWithFix for RemoveThisSemicolon {
123 let edit = TextEdit::delete(semicolon); 134 let edit = TextEdit::delete(semicolon);
124 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); 135 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
125 136
126 Some(Fix::new("Remove this semicolon", source_change, semicolon)) 137 Some(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon))
127 } 138 }
128} 139}
129 140
130impl DiagnosticWithFix for IncorrectCase { 141impl DiagnosticWithFix for IncorrectCase {
131 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { 142 fn fix(&self, sema: &Semantics<RootDatabase>, resolve: bool) -> Option<Assist> {
132 let root = sema.db.parse_or_expand(self.file)?; 143 let root = sema.db.parse_or_expand(self.file)?;
133 let name_node = self.ident.to_node(&root); 144 let name_node = self.ident.to_node(&root);
134 145
@@ -136,16 +147,19 @@ impl DiagnosticWithFix for IncorrectCase {
136 let frange = name_node.original_file_range(sema.db); 147 let frange = name_node.original_file_range(sema.db);
137 let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; 148 let file_position = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
138 149
139 let rename_changes =
140 rename_with_semantics(sema, file_position, &self.suggested_text).ok()?;
141
142 let label = format!("Rename to {}", self.suggested_text); 150 let label = format!("Rename to {}", self.suggested_text);
143 Some(Fix::new(&label, rename_changes, frange.range)) 151 let mut res = unresolved_fix("change_case", &label, frange.range);
152 if resolve {
153 let source_change = rename_with_semantics(sema, file_position, &self.suggested_text);
154 res.source_change = Some(source_change.ok().unwrap_or_default());
155 }
156
157 Some(res)
144 } 158 }
145} 159}
146 160
147impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap { 161impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap {
148 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { 162 fn fix(&self, sema: &Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
149 let root = sema.db.parse_or_expand(self.file)?; 163 let root = sema.db.parse_or_expand(self.file)?;
150 let next_expr = self.next_expr.to_node(&root); 164 let next_expr = self.next_expr.to_node(&root);
151 let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?; 165 let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;
@@ -163,7 +177,8 @@ impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap {
163 177
164 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit); 178 let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);
165 179
166 Some(Fix::new( 180 Some(fix(
181 "replace_with_find_map",
167 "Replace filter_map(..).next() with find_map()", 182 "Replace filter_map(..).next() with find_map()",
168 source_change, 183 source_change,
169 trigger_range, 184 trigger_range,
@@ -175,7 +190,7 @@ fn missing_record_expr_field_fix(
175 sema: &Semantics<RootDatabase>, 190 sema: &Semantics<RootDatabase>,
176 usage_file_id: FileId, 191 usage_file_id: FileId,
177 record_expr_field: &ast::RecordExprField, 192 record_expr_field: &ast::RecordExprField,
178) -> Option<Fix> { 193) -> Option<Assist> {
179 let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?; 194 let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?;
180 let def_id = sema.resolve_variant(record_lit)?; 195 let def_id = sema.resolve_variant(record_lit)?;
181 let module; 196 let module;
@@ -233,7 +248,12 @@ fn missing_record_expr_field_fix(
233 def_file_id, 248 def_file_id,
234 TextEdit::insert(last_field_syntax.text_range().end(), new_field), 249 TextEdit::insert(last_field_syntax.text_range().end(), new_field),
235 ); 250 );
236 return Some(Fix::new("Create field", source_change, record_expr_field.syntax().text_range())); 251 return Some(fix(
252 "create_field",
253 "Create field",
254 source_change,
255 record_expr_field.syntax().text_range(),
256 ));
237 257
238 fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> { 258 fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> {
239 match field_def_list { 259 match field_def_list {
diff --git a/crates/ide/src/diagnostics/unlinked_file.rs b/crates/ide/src/diagnostics/unlinked_file.rs
index e174fb767..7d39f4fbe 100644
--- a/crates/ide/src/diagnostics/unlinked_file.rs
+++ b/crates/ide/src/diagnostics/unlinked_file.rs
@@ -16,9 +16,10 @@ use syntax::{
16}; 16};
17use text_edit::TextEdit; 17use text_edit::TextEdit;
18 18
19use crate::Fix; 19use crate::{
20 20 diagnostics::{fix, fixes::DiagnosticWithFix},
21use super::fixes::DiagnosticWithFix; 21 Assist,
22};
22 23
23// Diagnostic: unlinked-file 24// Diagnostic: unlinked-file
24// 25//
@@ -49,7 +50,7 @@ impl Diagnostic for UnlinkedFile {
49} 50}
50 51
51impl DiagnosticWithFix for UnlinkedFile { 52impl DiagnosticWithFix for UnlinkedFile {
52 fn fix(&self, sema: &hir::Semantics<RootDatabase>) -> Option<Fix> { 53 fn fix(&self, sema: &hir::Semantics<RootDatabase>, _resolve: bool) -> Option<Assist> {
53 // If there's an existing module that could add a `mod` item to include the unlinked file, 54 // If there's an existing module that could add a `mod` item to include the unlinked file,
54 // suggest that as a fix. 55 // suggest that as a fix.
55 56
@@ -100,7 +101,7 @@ fn make_fix(
100 parent_file_id: FileId, 101 parent_file_id: FileId,
101 new_mod_name: &str, 102 new_mod_name: &str,
102 added_file_id: FileId, 103 added_file_id: FileId,
103) -> Option<Fix> { 104) -> Option<Assist> {
104 fn is_outline_mod(item: &ast::Item) -> bool { 105 fn is_outline_mod(item: &ast::Item) -> bool {
105 matches!(item, ast::Item::Module(m) if m.item_list().is_none()) 106 matches!(item, ast::Item::Module(m) if m.item_list().is_none())
106 } 107 }
@@ -152,7 +153,8 @@ fn make_fix(
152 153
153 let edit = builder.finish(); 154 let edit = builder.finish();
154 let trigger_range = db.parse(added_file_id).tree().syntax().text_range(); 155 let trigger_range = db.parse(added_file_id).tree().syntax().text_range();
155 Some(Fix::new( 156 Some(fix(
157 "add_mod_declaration",
156 &format!("Insert `{}`", mod_decl), 158 &format!("Insert `{}`", mod_decl),
157 SourceChange::from_text_edit(parent_file_id, edit), 159 SourceChange::from_text_edit(parent_file_id, edit),
158 trigger_range, 160 trigger_range,
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 67e2e5a1c..cb5a8e19a 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -1,6 +1,9 @@
1//! Extracts, resolves and rewrites links and intra-doc links in markdown documentation. 1//! Extracts, resolves and rewrites links and intra-doc links in markdown documentation.
2 2
3use std::{convert::TryFrom, iter::once, ops::Range}; 3use std::{
4 convert::{TryFrom, TryInto},
5 iter::once,
6};
4 7
5use itertools::Itertools; 8use itertools::Itertools;
6use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; 9use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
@@ -16,8 +19,7 @@ use ide_db::{
16 RootDatabase, 19 RootDatabase,
17}; 20};
18use syntax::{ 21use syntax::{
19 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TextSize, 22 ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TokenAtOffset, T,
20 TokenAtOffset, T,
21}; 23};
22 24
23use crate::{FilePosition, Semantics}; 25use crate::{FilePosition, Semantics};
@@ -26,12 +28,7 @@ pub(crate) type DocumentationLink = String;
26 28
27/// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs) 29/// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs)
28pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) -> String { 30pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) -> String {
29 let mut cb = |link: BrokenLink| { 31 let mut cb = broken_link_clone_cb;
30 Some((
31 /*url*/ link.reference.to_owned().into(),
32 /*title*/ link.reference.to_owned().into(),
33 ))
34 };
35 let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb)); 32 let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb));
36 33
37 let doc = map_links(doc, |target, title: &str| { 34 let doc = map_links(doc, |target, title: &str| {
@@ -111,86 +108,39 @@ pub(crate) fn external_docs(
111 let node = token.parent()?; 108 let node = token.parent()?;
112 let definition = match_ast! { 109 let definition = match_ast! {
113 match node { 110 match node {
114 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), 111 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db))?,
115 ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)), 112 ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db))?,
116 _ => None, 113 _ => return None,
117 } 114 }
118 }; 115 };
119 116
120 get_doc_link(db, definition?) 117 get_doc_link(db, definition)
121} 118}
122 119
123/// Extracts all links from a given markdown text. 120/// Extracts all links from a given markdown text.
124pub(crate) fn extract_definitions_from_markdown( 121pub(crate) fn extract_definitions_from_markdown(
125 markdown: &str, 122 markdown: &str,
126) -> Vec<(Range<usize>, String, Option<hir::Namespace>)> { 123) -> Vec<(TextRange, String, Option<hir::Namespace>)> {
127 let mut res = vec![]; 124 Parser::new_with_broken_link_callback(
128 let mut cb = |link: BrokenLink| { 125 markdown,
129 // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong 126 Options::empty(),
130 // this is fixed in the repo but not on the crates.io release yet 127 Some(&mut broken_link_clone_cb),
131 Some(( 128 )
132 /*url*/ link.reference.to_owned().into(), 129 .into_offset_iter()
133 /*title*/ link.reference.to_owned().into(), 130 .filter_map(|(event, range)| {
134 ))
135 };
136 let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb));
137 for (event, range) in doc.into_offset_iter() {
138 if let Event::Start(Tag::Link(_, target, title)) = event { 131 if let Event::Start(Tag::Link(_, target, title)) = event {
139 let link = if target.is_empty() { title } else { target }; 132 let link = if target.is_empty() { title } else { target };
140 let (link, ns) = parse_intra_doc_link(&link); 133 let (link, ns) = parse_intra_doc_link(&link);
141 res.push((range, link.to_string(), ns)); 134 Some((
142 } 135 TextRange::new(range.start.try_into().ok()?, range.end.try_into().ok()?),
143 } 136 link.to_string(),
144 res 137 ns,
145} 138 ))
146 139 } else {
147/// Extracts a link from a comment at the given position returning the spanning range, link and 140 None
148/// optionally it's namespace.
149pub(crate) fn extract_positioned_link_from_comment(
150 position: TextSize,
151 comment: &ast::Comment,
152) -> Option<(TextRange, String, Option<hir::Namespace>)> {
153 let doc_comment = comment.doc_comment()?;
154 let comment_start =
155 comment.syntax().text_range().start() + TextSize::from(comment.prefix().len() as u32);
156 let def_links = extract_definitions_from_markdown(doc_comment);
157 let (range, def_link, ns) =
158 def_links.into_iter().find_map(|(Range { start, end }, def_link, ns)| {
159 let range = TextRange::at(
160 comment_start + TextSize::from(start as u32),
161 TextSize::from((end - start) as u32),
162 );
163 range.contains(position).then(|| (range, def_link, ns))
164 })?;
165 Some((range, def_link, ns))
166}
167
168/// Turns a syntax node into it's [`Definition`] if it can hold docs.
169pub(crate) fn doc_owner_to_def(
170 sema: &Semantics<RootDatabase>,
171 item: &SyntaxNode,
172) -> Option<Definition> {
173 let res: hir::ModuleDef = match_ast! {
174 match item {
175 ast::SourceFile(_it) => sema.scope(item).module()?.into(),
176 ast::Fn(it) => sema.to_def(&it)?.into(),
177 ast::Struct(it) => sema.to_def(&it)?.into(),
178 ast::Enum(it) => sema.to_def(&it)?.into(),
179 ast::Union(it) => sema.to_def(&it)?.into(),
180 ast::Trait(it) => sema.to_def(&it)?.into(),
181 ast::Const(it) => sema.to_def(&it)?.into(),
182 ast::Static(it) => sema.to_def(&it)?.into(),
183 ast::TypeAlias(it) => sema.to_def(&it)?.into(),
184 ast::Variant(it) => sema.to_def(&it)?.into(),
185 ast::Trait(it) => sema.to_def(&it)?.into(),
186 ast::Impl(it) => return sema.to_def(&it).map(Definition::SelfType),
187 ast::Macro(it) => return sema.to_def(&it).map(Definition::Macro),
188 ast::TupleField(it) => return sema.to_def(&it).map(Definition::Field),
189 ast::RecordField(it) => return sema.to_def(&it).map(Definition::Field),
190 _ => return None,
191 } 141 }
192 }; 142 })
193 Some(Definition::ModuleDef(res)) 143 .collect()
194} 144}
195 145
196pub(crate) fn resolve_doc_path_for_def( 146pub(crate) fn resolve_doc_path_for_def(
@@ -220,6 +170,42 @@ pub(crate) fn resolve_doc_path_for_def(
220 } 170 }
221} 171}
222 172
173pub(crate) fn doc_attributes(
174 sema: &Semantics<RootDatabase>,
175 node: &SyntaxNode,
176) -> Option<(hir::AttrsWithOwner, Definition)> {
177 match_ast! {
178 match node {
179 ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))),
180 ast::Module(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))),
181 ast::Fn(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))),
182 ast::Struct(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))),
183 ast::Union(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))),
184 ast::Enum(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))),
185 ast::Variant(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))),
186 ast::Trait(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))),
187 ast::Static(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))),
188 ast::Const(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))),
189 ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))),
190 ast::Impl(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))),
191 ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))),
192 ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))),
193 ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))),
194 // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
195 _ => return None
196 }
197 }
198}
199
200fn broken_link_clone_cb<'a, 'b>(link: BrokenLink<'a>) -> Option<(CowStr<'b>, CowStr<'b>)> {
201 // These allocations are actually unnecessary but the lifetimes on BrokenLinkCallback are wrong
202 // this is fixed in the repo but not on the crates.io release yet
203 Some((
204 /*url*/ link.reference.to_owned().into(),
205 /*title*/ link.reference.to_owned().into(),
206 ))
207}
208
223// FIXME: 209// FIXME:
224// BUG: For Option::Some 210// BUG: For Option::Some
225// Returns https://doc.rust-lang.org/nightly/core/prelude/v1/enum.Option.html#variant.Some 211// Returns https://doc.rust-lang.org/nightly/core/prelude/v1/enum.Option.html#variant.Some
@@ -228,20 +214,20 @@ pub(crate) fn resolve_doc_path_for_def(
228// This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented 214// This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented
229// https://github.com/rust-lang/rfcs/pull/2988 215// https://github.com/rust-lang/rfcs/pull/2988
230fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> { 216fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> {
231 // Get the outermost definition for the moduledef. This is used to resolve the public path to the type, 217 // Get the outermost definition for the module def. This is used to resolve the public path to the type,
232 // then we can join the method, field, etc onto it if required. 218 // then we can join the method, field, etc onto it if required.
233 let target_def: ModuleDef = match definition { 219 let target_def: ModuleDef = match definition {
234 Definition::ModuleDef(moddef) => match moddef { 220 Definition::ModuleDef(def) => match def {
235 ModuleDef::Function(f) => f 221 ModuleDef::Function(f) => f
236 .as_assoc_item(db) 222 .as_assoc_item(db)
237 .and_then(|assoc| match assoc.container(db) { 223 .and_then(|assoc| match assoc.container(db) {
238 AssocItemContainer::Trait(t) => Some(t.into()), 224 AssocItemContainer::Trait(t) => Some(t.into()),
239 AssocItemContainer::Impl(impld) => { 225 AssocItemContainer::Impl(impl_) => {
240 impld.self_ty(db).as_adt().map(|adt| adt.into()) 226 impl_.self_ty(db).as_adt().map(|adt| adt.into())
241 } 227 }
242 }) 228 })
243 .unwrap_or_else(|| f.clone().into()), 229 .unwrap_or_else(|| def),
244 moddef => moddef, 230 def => def,
245 }, 231 },
246 Definition::Field(f) => f.parent_def(db).into(), 232 Definition::Field(f) => f.parent_def(db).into(),
247 // FIXME: Handle macros 233 // FIXME: Handle macros
@@ -250,17 +236,28 @@ fn get_doc_link(db: &RootDatabase, definition: Definition) -> Option<String> {
250 236
251 let ns = ItemInNs::from(target_def); 237 let ns = ItemInNs::from(target_def);
252 238
253 let module = definition.module(db)?; 239 let krate = match definition {
254 let krate = module.krate(); 240 // Definition::module gives back the parent module, we don't want that as it fails for root modules
241 Definition::ModuleDef(ModuleDef::Module(module)) => module.krate(),
242 _ => definition.module(db)?.krate(),
243 };
255 let import_map = db.import_map(krate.into()); 244 let import_map = db.import_map(krate.into());
256 let base = once(krate.display_name(db)?.to_string()) 245
257 .chain(import_map.path_of(ns)?.segments.iter().map(|name| name.to_string())) 246 let mut base = krate.display_name(db)?.to_string();
258 .join("/") 247 let is_root_module = matches!(
259 + "/"; 248 definition,
249 Definition::ModuleDef(ModuleDef::Module(module)) if krate.root_module(db) == module
250 );
251 if !is_root_module {
252 base = once(base)
253 .chain(import_map.path_of(ns)?.segments.iter().map(|name| name.to_string()))
254 .join("/");
255 }
256 base += "/";
260 257
261 let filename = get_symbol_filename(db, &target_def); 258 let filename = get_symbol_filename(db, &target_def);
262 let fragment = match definition { 259 let fragment = match definition {
263 Definition::ModuleDef(moddef) => match moddef { 260 Definition::ModuleDef(def) => match def {
264 ModuleDef::Function(f) => { 261 ModuleDef::Function(f) => {
265 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(f))) 262 get_symbol_fragment(db, &FieldOrAssocItem::AssocItem(AssocItem::Function(f)))
266 } 263 }
@@ -547,6 +544,19 @@ mod tests {
547 } 544 }
548 545
549 #[test] 546 #[test]
547 fn test_doc_url_crate() {
548 check(
549 r#"
550//- /main.rs crate:main deps:test
551use test$0::Foo;
552//- /lib.rs crate:test
553pub struct Foo;
554"#,
555 expect![[r#"https://docs.rs/test/*/test/index.html"#]],
556 );
557 }
558
559 #[test]
550 fn test_doc_url_struct() { 560 fn test_doc_url_struct() {
551 check( 561 check(
552 r#" 562 r#"
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 9eeabbeda..eebae5ebe 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -1,9 +1,9 @@
1use std::iter;
2
1use hir::Semantics; 3use hir::Semantics;
2use ide_db::RootDatabase; 4use ide_db::RootDatabase;
3use syntax::{ 5use syntax::{
4 algo::{find_node_at_offset, SyntaxRewriter}, 6 algo::find_node_at_offset, ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*,
5 ast, AstNode, NodeOrToken, SyntaxKind,
6 SyntaxKind::*,
7 SyntaxNode, WalkEvent, T, 7 SyntaxNode, WalkEvent, T,
8}; 8};
9 9
@@ -44,26 +44,23 @@ fn expand_macro_recur(
44 sema: &Semantics<RootDatabase>, 44 sema: &Semantics<RootDatabase>,
45 macro_call: &ast::MacroCall, 45 macro_call: &ast::MacroCall,
46) -> Option<SyntaxNode> { 46) -> Option<SyntaxNode> {
47 let mut expanded = sema.expand(macro_call)?; 47 let expanded = sema.expand(macro_call)?.clone_for_update();
48 48
49 let children = expanded.descendants().filter_map(ast::MacroCall::cast); 49 let children = expanded.descendants().filter_map(ast::MacroCall::cast);
50 let mut rewriter = SyntaxRewriter::default(); 50 let mut replacements = Vec::new();
51 51
52 for child in children.into_iter() { 52 for child in children {
53 if let Some(new_node) = expand_macro_recur(sema, &child) { 53 if let Some(new_node) = expand_macro_recur(sema, &child) {
54 // Replace the whole node if it is root 54 // check if the whole original syntax is replaced
55 // `replace_descendants` will not replace the parent node
56 // but `SyntaxNode::descendants include itself
57 if expanded == *child.syntax() { 55 if expanded == *child.syntax() {
58 expanded = new_node; 56 return Some(new_node);
59 } else {
60 rewriter.replace(child.syntax(), &new_node)
61 } 57 }
58 replacements.push((child, new_node));
62 } 59 }
63 } 60 }
64 61
65 let res = rewriter.rewrite(&expanded); 62 replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
66 Some(res) 63 Some(expanded)
67} 64}
68 65
69// FIXME: It would also be cool to share logic here and in the mbe tests, 66// FIXME: It would also be cool to share logic here and in the mbe tests,
@@ -91,24 +88,42 @@ fn insert_whitespaces(syn: SyntaxNode) -> String {
91 let is_last = 88 let is_last =
92 |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) }; 89 |f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) };
93 90
94 res += &match token.kind() { 91 match token.kind() {
95 k if is_text(k) && is_next(|it| !it.is_punct(), true) => token.text().to_string() + " ", 92 k if is_text(k) && is_next(|it| !it.is_punct(), true) => {
93 res.push_str(token.text());
94 res.push(' ');
95 }
96 L_CURLY if is_next(|it| it != R_CURLY, true) => { 96 L_CURLY if is_next(|it| it != R_CURLY, true) => {
97 indent += 1; 97 indent += 1;
98 let leading_space = if is_last(is_text, false) { " " } else { "" }; 98 if is_last(is_text, false) {
99 format!("{}{{\n{}", leading_space, " ".repeat(indent)) 99 res.push(' ');
100 }
101 res.push_str("{\n");
102 res.extend(iter::repeat(" ").take(2 * indent));
100 } 103 }
101 R_CURLY if is_last(|it| it != L_CURLY, true) => { 104 R_CURLY if is_last(|it| it != L_CURLY, true) => {
102 indent = indent.saturating_sub(1); 105 indent = indent.saturating_sub(1);
103 format!("\n{}}}", " ".repeat(indent)) 106 res.push('\n');
107 res.extend(iter::repeat(" ").take(2 * indent));
108 res.push_str("}");
104 } 109 }
105 R_CURLY => format!("}}\n{}", " ".repeat(indent)), 110 R_CURLY => {
106 T![;] => format!(";\n{}", " ".repeat(indent)), 111 res.push_str("}\n");
107 T![->] => " -> ".to_string(), 112 res.extend(iter::repeat(" ").take(2 * indent));
108 T![=] => " = ".to_string(), 113 }
109 T![=>] => " => ".to_string(), 114 LIFETIME_IDENT if is_next(|it| it == IDENT, true) => {
110 _ => token.text().to_string(), 115 res.push_str(token.text());
111 }; 116 res.push(' ');
117 }
118 T![;] => {
119 res.push_str(";\n");
120 res.extend(iter::repeat(" ").take(2 * indent));
121 }
122 T![->] => res.push_str(" -> "),
123 T![=] => res.push_str(" = "),
124 T![=>] => res.push_str(" => "),
125 _ => res.push_str(token.text()),
126 }
112 127
113 last = Some(token.kind()); 128 last = Some(token.kind());
114 } 129 }
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index 153726ce8..2b9ed123c 100644
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -19,6 +19,7 @@ pub enum FoldKind {
19 Region, 19 Region,
20 Consts, 20 Consts,
21 Statics, 21 Statics,
22 Array,
22} 23}
23 24
24#[derive(Debug)] 25#[derive(Debug)]
@@ -119,6 +120,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
119 match kind { 120 match kind {
120 COMMENT => Some(FoldKind::Comment), 121 COMMENT => Some(FoldKind::Comment),
121 ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), 122 ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList),
123 ARRAY_EXPR => Some(FoldKind::Array),
122 ASSOC_ITEM_LIST 124 ASSOC_ITEM_LIST
123 | RECORD_FIELD_LIST 125 | RECORD_FIELD_LIST
124 | RECORD_PAT_FIELD_LIST 126 | RECORD_PAT_FIELD_LIST
@@ -269,6 +271,7 @@ mod tests {
269 FoldKind::Region => "region", 271 FoldKind::Region => "region",
270 FoldKind::Consts => "consts", 272 FoldKind::Consts => "consts",
271 FoldKind::Statics => "statics", 273 FoldKind::Statics => "statics",
274 FoldKind::Array => "array",
272 }; 275 };
273 assert_eq!(kind, &attr.unwrap()); 276 assert_eq!(kind, &attr.unwrap());
274 } 277 }
@@ -465,6 +468,20 @@ fn foo<fold arglist>(
465 } 468 }
466 469
467 #[test] 470 #[test]
471 fn fold_multiline_array() {
472 check(
473 r#"
474const FOO: [usize; 4] = <fold array>[
475 1,
476 2,
477 3,
478 4,
479]</fold>;
480"#,
481 )
482 }
483
484 #[test]
468 fn fold_region() { 485 fn fold_region() {
469 check( 486 check(
470 r#" 487 r#"
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 8574d1e3f..a04333e63 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,5 +1,5 @@
1use either::Either; 1use either::Either;
2use hir::Semantics; 2use hir::{InFile, Semantics};
3use ide_db::{ 3use ide_db::{
4 defs::{NameClass, NameRefClass}, 4 defs::{NameClass, NameRefClass},
5 RootDatabase, 5 RootDatabase,
@@ -8,7 +8,7 @@ use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, Toke
8 8
9use crate::{ 9use crate::{
10 display::TryToNav, 10 display::TryToNav,
11 doc_links::{doc_owner_to_def, extract_positioned_link_from_comment, resolve_doc_path_for_def}, 11 doc_links::{doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def},
12 FilePosition, NavigationTarget, RangeInfo, 12 FilePosition, NavigationTarget, RangeInfo,
13}; 13};
14 14
@@ -32,9 +32,16 @@ pub(crate) fn goto_definition(
32 let original_token = pick_best(file.token_at_offset(position.offset))?; 32 let original_token = pick_best(file.token_at_offset(position.offset))?;
33 let token = sema.descend_into_macros(original_token.clone()); 33 let token = sema.descend_into_macros(original_token.clone());
34 let parent = token.parent()?; 34 let parent = token.parent()?;
35 if let Some(comment) = ast::Comment::cast(token) { 35 if let Some(_) = ast::Comment::cast(token) {
36 let (_, link, ns) = extract_positioned_link_from_comment(position.offset, &comment)?; 36 let (attributes, def) = doc_attributes(&sema, &parent)?;
37 let def = doc_owner_to_def(&sema, &parent)?; 37
38 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
39 let (_, link, ns) =
40 extract_definitions_from_markdown(docs.as_str()).into_iter().find(|(range, ..)| {
41 doc_mapping.map(range.clone()).map_or(false, |InFile { file_id, value: range }| {
42 file_id == position.file_id.into() && range.contains(position.offset)
43 })
44 })?;
38 let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?; 45 let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?;
39 return Some(RangeInfo::new(original_token.text_range(), vec![nav])); 46 return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
40 } 47 }
@@ -103,6 +110,13 @@ mod tests {
103 assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }); 110 assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
104 } 111 }
105 112
113 fn check_unresolved(ra_fixture: &str) {
114 let (analysis, position) = fixture::position(ra_fixture);
115 let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
116
117 assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {:?}", navs)
118 }
119
106 #[test] 120 #[test]
107 fn goto_def_for_extern_crate() { 121 fn goto_def_for_extern_crate() {
108 check( 122 check(
@@ -920,17 +934,12 @@ fn f() -> impl Iterator<Item$0 = u8> {}
920 } 934 }
921 935
922 #[test] 936 #[test]
923 #[should_panic = "unresolved reference"]
924 fn unknown_assoc_ty() { 937 fn unknown_assoc_ty() {
925 check( 938 check_unresolved(
926 r#" 939 r#"
927trait Iterator { 940trait Iterator { type Item; }
928 type Item;
929 //^^^^
930}
931
932fn f() -> impl Iterator<Invalid$0 = u8> {} 941fn f() -> impl Iterator<Invalid$0 = u8> {}
933 "#, 942"#,
934 ) 943 )
935 } 944 }
936 945
@@ -1160,4 +1169,51 @@ fn fn_macro() {}
1160 "#, 1169 "#,
1161 ) 1170 )
1162 } 1171 }
1172
1173 #[test]
1174 fn goto_intra_doc_links() {
1175 check(
1176 r#"
1177
1178pub mod theitem {
1179 /// This is the item. Cool!
1180 pub struct TheItem;
1181 //^^^^^^^
1182}
1183
1184/// Gives you a [`TheItem$0`].
1185///
1186/// [`TheItem`]: theitem::TheItem
1187pub fn gimme() -> theitem::TheItem {
1188 theitem::TheItem
1189}
1190"#,
1191 );
1192 }
1193
1194 #[test]
1195 fn goto_ident_from_pat_macro() {
1196 check(
1197 r#"
1198macro_rules! pat {
1199 ($name:ident) => { Enum::Variant1($name) }
1200}
1201
1202enum Enum {
1203 Variant1(u8),
1204 Variant2,
1205}
1206
1207fn f(e: Enum) {
1208 match e {
1209 pat!(bind) => {
1210 //^^^^
1211 bind$0
1212 }
1213 Enum::Variant2 => {}
1214 }
1215}
1216"#,
1217 );
1218 }
1163} 1219}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 614433417..9de653739 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1,6 +1,6 @@
1use either::Either; 1use either::Either;
2use hir::{ 2use hir::{
3 AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, Module, 3 AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, InFile, Module,
4 ModuleDef, Semantics, 4 ModuleDef, Semantics,
5}; 5};
6use ide_db::{ 6use ide_db::{
@@ -16,8 +16,8 @@ use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, Toke
16use crate::{ 16use crate::{
17 display::{macro_label, TryToNav}, 17 display::{macro_label, TryToNav},
18 doc_links::{ 18 doc_links::{
19 doc_owner_to_def, extract_positioned_link_from_comment, remove_links, 19 doc_attributes, extract_definitions_from_markdown, remove_links, resolve_doc_path_for_def,
20 resolve_doc_path_for_def, rewrite_links, 20 rewrite_links,
21 }, 21 },
22 markdown_remove::remove_markdown, 22 markdown_remove::remove_markdown,
23 markup::Markup, 23 markup::Markup,
@@ -116,11 +116,19 @@ pub(crate) fn hover(
116 ), 116 ),
117 117
118 _ => ast::Comment::cast(token.clone()) 118 _ => ast::Comment::cast(token.clone())
119 .and_then(|comment| { 119 .and_then(|_| {
120 let (attributes, def) = doc_attributes(&sema, &node)?;
121 let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?;
120 let (idl_range, link, ns) = 122 let (idl_range, link, ns) =
121 extract_positioned_link_from_comment(position.offset, &comment)?; 123 extract_definitions_from_markdown(docs.as_str()).into_iter().find_map(|(range, link, ns)| {
124 let InFile { file_id, value: range } = doc_mapping.map(range.clone())?;
125 if file_id == position.file_id.into() && range.contains(position.offset) {
126 Some((range, link, ns))
127 } else {
128 None
129 }
130 })?;
122 range = Some(idl_range); 131 range = Some(idl_range);
123 let def = doc_owner_to_def(&sema, &node)?;
124 resolve_doc_path_for_def(db, def, &link, ns) 132 resolve_doc_path_for_def(db, def, &link, ns)
125 }) 133 })
126 .map(Definition::ModuleDef), 134 .map(Definition::ModuleDef),
@@ -3814,23 +3822,33 @@ fn main() {
3814 fn hover_intra_doc_links() { 3822 fn hover_intra_doc_links() {
3815 check( 3823 check(
3816 r#" 3824 r#"
3817/// This is the [`foo`](foo$0) function. 3825
3818fn foo() {} 3826pub mod theitem {
3827 /// This is the item. Cool!
3828 pub struct TheItem;
3829}
3830
3831/// Gives you a [`TheItem$0`].
3832///
3833/// [`TheItem`]: theitem::TheItem
3834pub fn gimme() -> theitem::TheItem {
3835 theitem::TheItem
3836}
3819"#, 3837"#,
3820 expect![[r#" 3838 expect![[r#"
3821 *[`foo`](foo)* 3839 *[`TheItem`]*
3822 3840
3823 ```rust 3841 ```rust
3824 test 3842 test::theitem
3825 ``` 3843 ```
3826 3844
3827 ```rust 3845 ```rust
3828 fn foo() 3846 pub struct TheItem
3829 ``` 3847 ```
3830 3848
3831 --- 3849 ---
3832 3850
3833 This is the [`foo`](https://docs.rs/test/*/test/fn.foo.html) function. 3851 This is the item. Cool!
3834 "#]], 3852 "#]],
3835 ); 3853 );
3836 } 3854 }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 3f73c0632..99e45633e 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -69,7 +69,7 @@ use crate::display::ToNav;
69pub use crate::{ 69pub use crate::{
70 annotations::{Annotation, AnnotationConfig, AnnotationKind}, 70 annotations::{Annotation, AnnotationConfig, AnnotationKind},
71 call_hierarchy::CallItem, 71 call_hierarchy::CallItem,
72 diagnostics::{Diagnostic, DiagnosticsConfig, Fix, Severity}, 72 diagnostics::{Diagnostic, DiagnosticsConfig, Severity},
73 display::navigation_target::NavigationTarget, 73 display::navigation_target::NavigationTarget,
74 expand_macro::ExpandedMacro, 74 expand_macro::ExpandedMacro,
75 file_structure::{StructureNode, StructureNodeKind}, 75 file_structure::{StructureNode, StructureNodeKind},
@@ -82,7 +82,7 @@ pub use crate::{
82 references::{rename::RenameError, ReferenceSearchResult}, 82 references::{rename::RenameError, ReferenceSearchResult},
83 runnables::{Runnable, RunnableKind, TestId}, 83 runnables::{Runnable, RunnableKind, TestId},
84 syntax_highlighting::{ 84 syntax_highlighting::{
85 tags::{Highlight, HlMod, HlMods, HlPunct, HlTag}, 85 tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
86 HlRange, 86 HlRange,
87 }, 87 },
88}; 88};
@@ -244,6 +244,12 @@ impl Analysis {
244 self.with_db(|db| db.parse(file_id).tree()) 244 self.with_db(|db| db.parse(file_id).tree())
245 } 245 }
246 246
247 /// Returns true if this file belongs to an immutable library.
248 pub fn is_library_file(&self, file_id: FileId) -> Cancelable<bool> {
249 use ide_db::base_db::SourceDatabaseExt;
250 self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library)
251 }
252
247 /// Gets the file's `LineIndex`: data structure to convert between absolute 253 /// Gets the file's `LineIndex`: data structure to convert between absolute
248 /// offsets and line/column representation. 254 /// offsets and line/column representation.
249 pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { 255 pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> {
@@ -526,9 +532,39 @@ impl Analysis {
526 pub fn diagnostics( 532 pub fn diagnostics(
527 &self, 533 &self,
528 config: &DiagnosticsConfig, 534 config: &DiagnosticsConfig,
535 resolve: bool,
529 file_id: FileId, 536 file_id: FileId,
530 ) -> Cancelable<Vec<Diagnostic>> { 537 ) -> Cancelable<Vec<Diagnostic>> {
531 self.with_db(|db| diagnostics::diagnostics(db, config, file_id)) 538 self.with_db(|db| diagnostics::diagnostics(db, config, resolve, file_id))
539 }
540
541 /// Convenience function to return assists + quick fixes for diagnostics
542 pub fn assists_with_fixes(
543 &self,
544 assist_config: &AssistConfig,
545 diagnostics_config: &DiagnosticsConfig,
546 resolve: bool,
547 frange: FileRange,
548 ) -> Cancelable<Vec<Assist>> {
549 let include_fixes = match &assist_config.allowed {
550 Some(it) => it.iter().any(|&it| it == AssistKind::None || it == AssistKind::QuickFix),
551 None => true,
552 };
553
554 self.with_db(|db| {
555 let mut res = Assist::get(db, assist_config, resolve, frange);
556 ssr::add_ssr_assist(db, &mut res, resolve, frange);
557
558 if include_fixes {
559 res.extend(
560 diagnostics::diagnostics(db, diagnostics_config, resolve, frange.file_id)
561 .into_iter()
562 .filter_map(|it| it.fix)
563 .filter(|it| it.target.intersect(frange.range).is_some()),
564 );
565 }
566 res
567 })
532 } 568 }
533 569
534 /// Returns the edit required to rename reference at the position to the new 570 /// Returns the edit required to rename reference at the position to the new
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index 8d37f4f92..246f10a0a 100644
--- a/crates/ide/src/move_item.rs
+++ b/crates/ide/src/move_item.rs
@@ -1,4 +1,4 @@
1use std::iter::once; 1use std::{iter::once, mem};
2 2
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::{base_db::FileRange, RootDatabase}; 4use ide_db::{base_db::FileRange, RootDatabase};
@@ -102,7 +102,7 @@ fn move_in_direction(
102 ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction), 102 ast::GenericArgList(it) => swap_sibling_in_list(node, it.generic_args(), range, direction),
103 ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction), 103 ast::VariantList(it) => swap_sibling_in_list(node, it.variants(), range, direction),
104 ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction), 104 ast::TypeBoundList(it) => swap_sibling_in_list(node, it.bounds(), range, direction),
105 _ => Some(replace_nodes(node, &match direction { 105 _ => Some(replace_nodes(range, node, &match direction {
106 Direction::Up => node.prev_sibling(), 106 Direction::Up => node.prev_sibling(),
107 Direction::Down => node.next_sibling(), 107 Direction::Down => node.next_sibling(),
108 }?)) 108 }?))
@@ -125,7 +125,7 @@ fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
125 .next(); 125 .next();
126 126
127 if let Some((l, r)) = list_lookup { 127 if let Some((l, r)) = list_lookup {
128 Some(replace_nodes(l.syntax(), r.syntax())) 128 Some(replace_nodes(range, l.syntax(), r.syntax()))
129 } else { 129 } else {
130 // Cursor is beyond any movable list item (for example, on curly brace in enum). 130 // Cursor is beyond any movable list item (for example, on curly brace in enum).
131 // It's not necessary, that parent of list is movable (arg list's parent is not, for example), 131 // It's not necessary, that parent of list is movable (arg list's parent is not, for example),
@@ -134,11 +134,38 @@ fn swap_sibling_in_list<A: AstNode + Clone, I: Iterator<Item = A>>(
134 } 134 }
135} 135}
136 136
137fn replace_nodes(first: &SyntaxNode, second: &SyntaxNode) -> TextEdit { 137fn replace_nodes<'a>(
138 range: TextRange,
139 mut first: &'a SyntaxNode,
140 mut second: &'a SyntaxNode,
141) -> TextEdit {
142 let cursor_offset = if range.is_empty() {
143 // FIXME: `applySnippetTextEdits` does not support non-empty selection ranges
144 if first.text_range().contains_range(range) {
145 Some(range.start() - first.text_range().start())
146 } else if second.text_range().contains_range(range) {
147 mem::swap(&mut first, &mut second);
148 Some(range.start() - first.text_range().start())
149 } else {
150 None
151 }
152 } else {
153 None
154 };
155
156 let first_with_cursor = match cursor_offset {
157 Some(offset) => {
158 let mut item_text = first.text().to_string();
159 item_text.insert_str(offset.into(), "$0");
160 item_text
161 }
162 None => first.text().to_string(),
163 };
164
138 let mut edit = TextEditBuilder::default(); 165 let mut edit = TextEditBuilder::default();
139 166
140 algo::diff(first, second).into_text_edit(&mut edit); 167 algo::diff(first, second).into_text_edit(&mut edit);
141 algo::diff(second, first).into_text_edit(&mut edit); 168 edit.replace(second.text_range(), first_with_cursor);
142 169
143 edit.finish() 170 edit.finish()
144} 171}
@@ -188,7 +215,7 @@ fn main() {
188 expect![[r#" 215 expect![[r#"
189fn main() { 216fn main() {
190 match true { 217 match true {
191 false => { 218 false =>$0 {
192 println!("Test"); 219 println!("Test");
193 }, 220 },
194 true => { 221 true => {
@@ -222,7 +249,7 @@ fn main() {
222 false => { 249 false => {
223 println!("Test"); 250 println!("Test");
224 }, 251 },
225 true => { 252 true =>$0 {
226 println!("Hello, world"); 253 println!("Hello, world");
227 } 254 }
228 }; 255 };
@@ -274,7 +301,7 @@ fn main() {
274 "#, 301 "#,
275 expect![[r#" 302 expect![[r#"
276fn main() { 303fn main() {
277 let test2 = 456; 304 let test2$0 = 456;
278 let test = 123; 305 let test = 123;
279} 306}
280 "#]], 307 "#]],
@@ -293,7 +320,7 @@ fn main() {
293 "#, 320 "#,
294 expect![[r#" 321 expect![[r#"
295fn main() { 322fn main() {
296 println!("All I want to say is..."); 323 println!("All I want to say is...");$0
297 println!("Hello, world"); 324 println!("Hello, world");
298} 325}
299 "#]], 326 "#]],
@@ -313,7 +340,7 @@ fn main() {
313fn main() { 340fn main() {
314 if true { 341 if true {
315 println!("Test"); 342 println!("Test");
316 } 343 }$0
317 344
318 println!("Hello, world"); 345 println!("Hello, world");
319} 346}
@@ -334,7 +361,7 @@ fn main() {
334fn main() { 361fn main() {
335 for i in 0..10 { 362 for i in 0..10 {
336 println!("Test"); 363 println!("Test");
337 } 364 }$0
338 365
339 println!("Hello, world"); 366 println!("Hello, world");
340} 367}
@@ -355,7 +382,7 @@ fn main() {
355fn main() { 382fn main() {
356 loop { 383 loop {
357 println!("Test"); 384 println!("Test");
358 } 385 }$0
359 386
360 println!("Hello, world"); 387 println!("Hello, world");
361} 388}
@@ -376,7 +403,7 @@ fn main() {
376fn main() { 403fn main() {
377 while true { 404 while true {
378 println!("Test"); 405 println!("Test");
379 } 406 }$0
380 407
381 println!("Hello, world"); 408 println!("Hello, world");
382} 409}
@@ -393,7 +420,7 @@ fn main() {
393 "#, 420 "#,
394 expect![[r#" 421 expect![[r#"
395fn main() { 422fn main() {
396 return 123; 423 return 123;$0
397 424
398 println!("Hello, world"); 425 println!("Hello, world");
399} 426}
@@ -430,7 +457,7 @@ fn main() {}
430fn foo() {}$0$0 457fn foo() {}$0$0
431 "#, 458 "#,
432 expect![[r#" 459 expect![[r#"
433fn foo() {} 460fn foo() {}$0
434 461
435fn main() {} 462fn main() {}
436 "#]], 463 "#]],
@@ -451,7 +478,7 @@ impl Wow for Yay $0$0{}
451 expect![[r#" 478 expect![[r#"
452struct Yay; 479struct Yay;
453 480
454impl Wow for Yay {} 481impl Wow for Yay $0{}
455 482
456trait Wow {} 483trait Wow {}
457 "#]], 484 "#]],
@@ -467,7 +494,7 @@ use std::vec::Vec;
467use std::collections::HashMap$0$0; 494use std::collections::HashMap$0$0;
468 "#, 495 "#,
469 expect![[r#" 496 expect![[r#"
470use std::collections::HashMap; 497use std::collections::HashMap$0;
471use std::vec::Vec; 498use std::vec::Vec;
472 "#]], 499 "#]],
473 Direction::Up, 500 Direction::Up,
@@ -502,7 +529,7 @@ fn main() {
502 } 529 }
503 530
504 #[test] 531 #[test]
505 fn test_moves_param_up() { 532 fn test_moves_param() {
506 check( 533 check(
507 r#" 534 r#"
508fn test(one: i32, two$0$0: u32) {} 535fn test(one: i32, two$0$0: u32) {}
@@ -512,7 +539,7 @@ fn main() {
512} 539}
513 "#, 540 "#,
514 expect![[r#" 541 expect![[r#"
515fn test(two: u32, one: i32) {} 542fn test(two$0: u32, one: i32) {}
516 543
517fn main() { 544fn main() {
518 test(123, 456); 545 test(123, 456);
@@ -520,6 +547,15 @@ fn main() {
520 "#]], 547 "#]],
521 Direction::Up, 548 Direction::Up,
522 ); 549 );
550 check(
551 r#"
552fn f($0$0arg: u8, arg2: u16) {}
553 "#,
554 expect![[r#"
555fn f(arg2: u16, $0arg: u8) {}
556 "#]],
557 Direction::Down,
558 );
523 } 559 }
524 560
525 #[test] 561 #[test]
@@ -536,7 +572,7 @@ fn main() {
536fn test(one: i32, two: u32) {} 572fn test(one: i32, two: u32) {}
537 573
538fn main() { 574fn main() {
539 test(456, 123); 575 test(456$0, 123);
540} 576}
541 "#]], 577 "#]],
542 Direction::Up, 578 Direction::Up,
@@ -557,7 +593,7 @@ fn main() {
557fn test(one: i32, two: u32) {} 593fn test(one: i32, two: u32) {}
558 594
559fn main() { 595fn main() {
560 test(456, 123); 596 test(456, 123$0);
561} 597}
562 "#]], 598 "#]],
563 Direction::Down, 599 Direction::Down,
@@ -594,7 +630,7 @@ struct Test<A, B$0$0>(A, B);
594fn main() {} 630fn main() {}
595 "#, 631 "#,
596 expect![[r#" 632 expect![[r#"
597struct Test<B, A>(A, B); 633struct Test<B$0, A>(A, B);
598 634
599fn main() {} 635fn main() {}
600 "#]], 636 "#]],
@@ -616,7 +652,7 @@ fn main() {
616struct Test<A, B>(A, B); 652struct Test<A, B>(A, B);
617 653
618fn main() { 654fn main() {
619 let t = Test::<&str, i32>(123, "yay"); 655 let t = Test::<&str$0, i32>(123, "yay");
620} 656}
621 "#]], 657 "#]],
622 Direction::Up, 658 Direction::Up,
@@ -636,7 +672,7 @@ fn main() {}
636 "#, 672 "#,
637 expect![[r#" 673 expect![[r#"
638enum Hello { 674enum Hello {
639 Two, 675 Two$0,
640 One 676 One
641} 677}
642 678
@@ -663,7 +699,7 @@ trait One {}
663 699
664trait Two {} 700trait Two {}
665 701
666fn test<T: Two + One>(t: T) {} 702fn test<T: Two$0 + One>(t: T) {}
667 703
668fn main() {} 704fn main() {}
669 "#]], 705 "#]],
@@ -709,7 +745,7 @@ trait Yay {
709impl Yay for Test { 745impl Yay for Test {
710 type One = i32; 746 type One = i32;
711 747
712 fn inner() { 748 fn inner() {$0
713 println!("Mmmm"); 749 println!("Mmmm");
714 } 750 }
715 751
@@ -736,7 +772,7 @@ fn test() {
736 "#, 772 "#,
737 expect![[r#" 773 expect![[r#"
738fn test() { 774fn test() {
739 mod hi { 775 mod hi {$0
740 fn inner() {} 776 fn inner() {}
741 } 777 }
742 778
@@ -764,7 +800,7 @@ fn main() {}
764 expect![[r#" 800 expect![[r#"
765fn main() {} 801fn main() {}
766 802
767#[derive(Debug)] 803$0#[derive(Debug)]
768enum FooBar { 804enum FooBar {
769 Foo, 805 Foo,
770 Bar, 806 Bar,
@@ -784,7 +820,7 @@ fn main() {}
784 expect![[r#" 820 expect![[r#"
785fn main() {} 821fn main() {}
786 822
787enum FooBar { 823$0enum FooBar {
788 Foo, 824 Foo,
789 Bar, 825 Bar,
790} 826}
@@ -804,7 +840,7 @@ fn main() {}
804 expect![[r#" 840 expect![[r#"
805struct Test; 841struct Test;
806 842
807impl SomeTrait for Test {} 843$0impl SomeTrait for Test {}
808 844
809trait SomeTrait {} 845trait SomeTrait {}
810 846
@@ -831,7 +867,7 @@ fn main() {}
831enum FooBar { 867enum FooBar {
832 Foo, 868 Foo,
833 Bar, 869 Bar,
834} 870}$0
835 "#]], 871 "#]],
836 Direction::Down, 872 Direction::Down,
837 ); 873 );
@@ -848,7 +884,7 @@ fn main() {}
848 expect![[r#" 884 expect![[r#"
849struct Test; 885struct Test;
850 886
851impl SomeTrait for Test {} 887impl SomeTrait for Test {}$0
852 888
853trait SomeTrait {} 889trait SomeTrait {}
854 890
diff --git a/crates/ide/src/prime_caches.rs b/crates/ide/src/prime_caches.rs
index ea0acfaa0..03597f507 100644
--- a/crates/ide/src/prime_caches.rs
+++ b/crates/ide/src/prime_caches.rs
@@ -27,6 +27,7 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress)
27 let topo = &graph.crates_in_topological_order(); 27 let topo = &graph.crates_in_topological_order();
28 28
29 cb(PrimeCachesProgress::Started); 29 cb(PrimeCachesProgress::Started);
30 let _d = stdx::defer(|| cb(PrimeCachesProgress::Finished));
30 31
31 // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that. 32 // FIXME: This would be easy to parallelize, since it's in the ideal ordering for that.
32 // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks 33 // Unfortunately rayon prevents panics from propagation out of a `scope`, which breaks
@@ -41,6 +42,4 @@ pub(crate) fn prime_caches(db: &RootDatabase, cb: &(dyn Fn(PrimeCachesProgress)
41 }); 42 });
42 db.crate_def_map(*krate); 43 db.crate_def_map(*krate);
43 } 44 }
44
45 cb(PrimeCachesProgress::Finished);
46} 45}
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 5ccb84714..18552459b 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -12,7 +12,10 @@ use syntax::{
12 SyntaxNode, SyntaxToken, T, 12 SyntaxNode, SyntaxToken, T,
13}; 13};
14 14
15use crate::{syntax_highlighting::tags::HlPunct, Highlight, HlMod, HlTag}; 15use crate::{
16 syntax_highlighting::tags::{HlOperator, HlPunct},
17 Highlight, HlMod, HlTag,
18};
16 19
17pub(super) fn element( 20pub(super) fn element(
18 sema: &Semantics<RootDatabase>, 21 sema: &Semantics<RootDatabase>,
@@ -132,7 +135,7 @@ pub(super) fn element(
132 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), 135 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
133 BYTE => HlTag::ByteLiteral.into(), 136 BYTE => HlTag::ByteLiteral.into(),
134 CHAR => HlTag::CharLiteral.into(), 137 CHAR => HlTag::CharLiteral.into(),
135 QUESTION => Highlight::new(HlTag::Operator) | HlMod::ControlFlow, 138 QUESTION => Highlight::new(HlTag::Operator(HlOperator::Other)) | HlMod::ControlFlow,
136 LIFETIME => { 139 LIFETIME => {
137 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); 140 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap();
138 141
@@ -146,8 +149,11 @@ pub(super) fn element(
146 } 149 }
147 } 150 }
148 p if p.is_punct() => match p { 151 p if p.is_punct() => match p {
152 T![&] if element.parent().and_then(ast::BinExpr::cast).is_some() => {
153 HlTag::Operator(HlOperator::Bitwise).into()
154 }
149 T![&] => { 155 T![&] => {
150 let h = HlTag::Operator.into(); 156 let h = HlTag::Operator(HlOperator::Other).into();
151 let is_unsafe = element 157 let is_unsafe = element
152 .parent() 158 .parent()
153 .and_then(ast::RefExpr::cast) 159 .and_then(ast::RefExpr::cast)
@@ -159,13 +165,18 @@ pub(super) fn element(
159 h 165 h
160 } 166 }
161 } 167 }
162 T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => HlTag::Operator.into(), 168 T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => {
169 HlTag::Operator(HlOperator::Other).into()
170 }
163 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { 171 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
164 HlTag::Symbol(SymbolKind::Macro).into() 172 HlTag::Symbol(SymbolKind::Macro).into()
165 } 173 }
166 T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => { 174 T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => {
167 HlTag::BuiltinType.into() 175 HlTag::BuiltinType.into()
168 } 176 }
177 T![!] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
178 HlTag::Operator(HlOperator::Logical).into()
179 }
169 T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => { 180 T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => {
170 HlTag::Keyword.into() 181 HlTag::Keyword.into()
171 } 182 }
@@ -175,9 +186,9 @@ pub(super) fn element(
175 let expr = prefix_expr.expr()?; 186 let expr = prefix_expr.expr()?;
176 let ty = sema.type_of_expr(&expr)?; 187 let ty = sema.type_of_expr(&expr)?;
177 if ty.is_raw_ptr() { 188 if ty.is_raw_ptr() {
178 HlTag::Operator | HlMod::Unsafe 189 HlTag::Operator(HlOperator::Other) | HlMod::Unsafe
179 } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() { 190 } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() {
180 HlTag::Operator.into() 191 HlTag::Operator(HlOperator::Other).into()
181 } else { 192 } else {
182 HlTag::Punctuation(HlPunct::Other).into() 193 HlTag::Punctuation(HlPunct::Other).into()
183 } 194 }
@@ -188,19 +199,43 @@ pub(super) fn element(
188 let expr = prefix_expr.expr()?; 199 let expr = prefix_expr.expr()?;
189 match expr { 200 match expr {
190 ast::Expr::Literal(_) => HlTag::NumericLiteral, 201 ast::Expr::Literal(_) => HlTag::NumericLiteral,
191 _ => HlTag::Operator, 202 _ => HlTag::Operator(HlOperator::Other),
192 } 203 }
193 .into() 204 .into()
194 } 205 }
195 _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => { 206 _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
196 HlTag::Operator.into() 207 HlTag::Operator(HlOperator::Other).into()
208 }
209 T![+] | T![-] | T![*] | T![/] | T![+=] | T![-=] | T![*=] | T![/=]
210 if element.parent().and_then(ast::BinExpr::cast).is_some() =>
211 {
212 HlTag::Operator(HlOperator::Arithmetic).into()
213 }
214 T![|] | T![&] | T![!] | T![^] | T![|=] | T![&=] | T![^=]
215 if element.parent().and_then(ast::BinExpr::cast).is_some() =>
216 {
217 HlTag::Operator(HlOperator::Bitwise).into()
218 }
219 T![&&] | T![||] if element.parent().and_then(ast::BinExpr::cast).is_some() => {
220 HlTag::Operator(HlOperator::Logical).into()
221 }
222 T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=]
223 if element.parent().and_then(ast::BinExpr::cast).is_some() =>
224 {
225 HlTag::Operator(HlOperator::Comparison).into()
226 }
227 _ if element.parent().and_then(ast::BinExpr::cast).is_some() => {
228 HlTag::Operator(HlOperator::Other).into()
197 } 229 }
198 _ if element.parent().and_then(ast::BinExpr::cast).is_some() => HlTag::Operator.into(),
199 _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => { 230 _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => {
200 HlTag::Operator.into() 231 HlTag::Operator(HlOperator::Other).into()
232 }
233 _ if element.parent().and_then(ast::RangePat::cast).is_some() => {
234 HlTag::Operator(HlOperator::Other).into()
235 }
236 _ if element.parent().and_then(ast::RestPat::cast).is_some() => {
237 HlTag::Operator(HlOperator::Other).into()
201 } 238 }
202 _ if element.parent().and_then(ast::RangePat::cast).is_some() => HlTag::Operator.into(),
203 _ if element.parent().and_then(ast::RestPat::cast).is_some() => HlTag::Operator.into(),
204 _ if element.parent().and_then(ast::Attr::cast).is_some() => HlTag::Attribute.into(), 239 _ if element.parent().and_then(ast::Attr::cast).is_some() => HlTag::Attribute.into(),
205 kind => HlTag::Punctuation(match kind { 240 kind => HlTag::Punctuation(match kind {
206 T!['['] | T![']'] => HlPunct::Bracket, 241 T!['['] | T![']'] => HlPunct::Bracket,
@@ -323,8 +358,18 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
323 hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait), 358 hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait),
324 hir::ModuleDef::TypeAlias(type_) => { 359 hir::ModuleDef::TypeAlias(type_) => {
325 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); 360 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias));
326 if type_.as_assoc_item(db).is_some() { 361 if let Some(item) = type_.as_assoc_item(db) {
327 h |= HlMod::Associated 362 h |= HlMod::Associated;
363 match item.container(db) {
364 AssocItemContainer::Impl(i) => {
365 if i.trait_(db).is_some() {
366 h |= HlMod::Trait;
367 }
368 }
369 AssocItemContainer::Trait(_t) => {
370 h |= HlMod::Trait;
371 }
372 }
328 } 373 }
329 return h; 374 return h;
330 } 375 }
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index b62d43256..bc221d599 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -1,17 +1,17 @@
1//! "Recursive" Syntax highlighting for code in doctests and fixtures. 1//! "Recursive" Syntax highlighting for code in doctests and fixtures.
2 2
3use std::{mem, ops::Range}; 3use std::mem;
4 4
5use either::Either; 5use either::Either;
6use hir::{HasAttrs, InFile, Semantics}; 6use hir::{InFile, Semantics};
7use ide_db::{call_info::ActiveParameter, defs::Definition, SymbolKind}; 7use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind};
8use syntax::{ 8use syntax::{
9 ast::{self, AstNode}, 9 ast::{self, AstNode},
10 match_ast, AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, 10 AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize,
11}; 11};
12 12
13use crate::{ 13use crate::{
14 doc_links::{extract_definitions_from_markdown, resolve_doc_path_for_def}, 14 doc_links::{doc_attributes, extract_definitions_from_markdown, resolve_doc_path_for_def},
15 Analysis, HlMod, HlRange, HlTag, RootDatabase, 15 Analysis, HlMod, HlRange, HlTag, RootDatabase,
16}; 16};
17 17
@@ -78,44 +78,6 @@ pub(super) fn ra_fixture(
78} 78}
79 79
80const RUSTDOC_FENCE: &'static str = "```"; 80const RUSTDOC_FENCE: &'static str = "```";
81const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[
82 "",
83 "rust",
84 "should_panic",
85 "ignore",
86 "no_run",
87 "compile_fail",
88 "edition2015",
89 "edition2018",
90 "edition2021",
91];
92
93fn doc_attributes<'node>(
94 sema: &Semantics<RootDatabase>,
95 node: &'node SyntaxNode,
96) -> Option<(hir::AttrsWithOwner, Definition)> {
97 match_ast! {
98 match node {
99 ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))),
100 ast::Module(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Module(def)))),
101 ast::Fn(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Function(def)))),
102 ast::Struct(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(def))))),
103 ast::Union(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Union(def))))),
104 ast::Enum(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(def))))),
105 ast::Variant(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Variant(def)))),
106 ast::Trait(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Trait(def)))),
107 ast::Static(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Static(def)))),
108 ast::Const(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::Const(def)))),
109 ast::TypeAlias(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::ModuleDef(hir::ModuleDef::TypeAlias(def)))),
110 ast::Impl(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))),
111 ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))),
112 ast::TupleField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))),
113 ast::Macro(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))),
114 // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
115 _ => return None
116 }
117 }
118}
119 81
120/// Injection of syntax highlighting of doctests. 82/// Injection of syntax highlighting of doctests.
121pub(super) fn doc_comment( 83pub(super) fn doc_comment(
@@ -139,8 +101,28 @@ pub(super) fn doc_comment(
139 // Replace the original, line-spanning comment ranges by new, only comment-prefix 101 // Replace the original, line-spanning comment ranges by new, only comment-prefix
140 // spanning comment ranges. 102 // spanning comment ranges.
141 let mut new_comments = Vec::new(); 103 let mut new_comments = Vec::new();
142 let mut intra_doc_links = Vec::new();
143 let mut string; 104 let mut string;
105
106 if let Some((docs, doc_mapping)) = attributes.docs_with_rangemap(sema.db) {
107 extract_definitions_from_markdown(docs.as_str())
108 .into_iter()
109 .filter_map(|(range, link, ns)| {
110 let def = resolve_doc_path_for_def(sema.db, def, &link, ns)?;
111 let InFile { file_id, value: range } = doc_mapping.map(range)?;
112 (file_id == node.file_id).then(|| (range, def))
113 })
114 .for_each(|(range, def)| {
115 hl.add(HlRange {
116 range,
117 highlight: module_def_to_hl_tag(def)
118 | HlMod::Documentation
119 | HlMod::Injected
120 | HlMod::IntraDocLink,
121 binding_hash: None,
122 })
123 });
124 }
125
144 for attr in attributes.by_key("doc").attrs() { 126 for attr in attributes.by_key("doc").attrs() {
145 let InFile { file_id, value: src } = attrs_source_map.source_of(&attr); 127 let InFile { file_id, value: src } = attrs_source_map.source_of(&attr);
146 if file_id != node.file_id { 128 if file_id != node.file_id {
@@ -181,30 +163,11 @@ pub(super) fn doc_comment(
181 is_codeblock = !is_codeblock; 163 is_codeblock = !is_codeblock;
182 // Check whether code is rust by inspecting fence guards 164 // Check whether code is rust by inspecting fence guards
183 let guards = &line[idx + RUSTDOC_FENCE.len()..]; 165 let guards = &line[idx + RUSTDOC_FENCE.len()..];
184 let is_rust = 166 let is_rust = is_rust_fence(guards);
185 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
186 is_doctest = is_codeblock && is_rust; 167 is_doctest = is_codeblock && is_rust;
187 continue; 168 continue;
188 } 169 }
189 None if !is_doctest => { 170 None if !is_doctest => continue,
190 intra_doc_links.extend(
191 extract_definitions_from_markdown(line)
192 .into_iter()
193 .filter_map(|(range, link, ns)| {
194 Some(range).zip(resolve_doc_path_for_def(sema.db, def, &link, ns))
195 })
196 .map(|(Range { start, end }, def)| {
197 (
198 def,
199 TextRange::at(
200 prev_range_start + TextSize::from(start as u32),
201 TextSize::from((end - start) as u32),
202 ),
203 )
204 }),
205 );
206 continue;
207 }
208 None => (), 171 None => (),
209 } 172 }
210 173
@@ -223,17 +186,6 @@ pub(super) fn doc_comment(
223 } 186 }
224 } 187 }
225 188
226 for (def, range) in intra_doc_links {
227 hl.add(HlRange {
228 range,
229 highlight: module_def_to_hl_tag(def)
230 | HlMod::Documentation
231 | HlMod::Injected
232 | HlMod::IntraDocLink,
233 binding_hash: None,
234 });
235 }
236
237 if new_comments.is_empty() { 189 if new_comments.is_empty() {
238 return; // no need to run an analysis on an empty file 190 return; // no need to run an analysis on an empty file
239 } 191 }
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 1cec991aa..e58392d67 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -28,7 +28,7 @@ pub enum HlTag {
28 FormatSpecifier, 28 FormatSpecifier,
29 Keyword, 29 Keyword,
30 NumericLiteral, 30 NumericLiteral,
31 Operator, 31 Operator(HlOperator),
32 Punctuation(HlPunct), 32 Punctuation(HlPunct),
33 StringLiteral, 33 StringLiteral,
34 UnresolvedReference, 34 UnresolvedReference,
@@ -87,6 +87,20 @@ pub enum HlPunct {
87 Other, 87 Other,
88} 88}
89 89
90#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
91pub enum HlOperator {
92 /// |, &, !, ^, |=, &=, ^=
93 Bitwise,
94 /// +, -, *, /, +=, -=, *=, /=
95 Arithmetic,
96 /// &&, ||, !
97 Logical,
98 /// >, <, ==, >=, <=, !=
99 Comparison,
100 ///
101 Other,
102}
103
90impl HlTag { 104impl HlTag {
91 fn as_str(self) -> &'static str { 105 fn as_str(self) -> &'static str {
92 match self { 106 match self {
@@ -133,7 +147,13 @@ impl HlTag {
133 HlPunct::Other => "punctuation", 147 HlPunct::Other => "punctuation",
134 }, 148 },
135 HlTag::NumericLiteral => "numeric_literal", 149 HlTag::NumericLiteral => "numeric_literal",
136 HlTag::Operator => "operator", 150 HlTag::Operator(op) => match op {
151 HlOperator::Bitwise => "bitwise",
152 HlOperator::Arithmetic => "arithmetic",
153 HlOperator::Logical => "logical",
154 HlOperator::Comparison => "comparison",
155 HlOperator::Other => "operator",
156 },
137 HlTag::StringLiteral => "string_literal", 157 HlTag::StringLiteral => "string_literal",
138 HlTag::UnresolvedReference => "unresolved_reference", 158 HlTag::UnresolvedReference => "unresolved_reference",
139 HlTag::None => "none", 159 HlTag::None => "none",
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 045162eb8..6ee6d85fb 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -76,7 +76,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
76 <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span> 76 <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span>
77 <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> 77 <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
78 <span class="comment documentation">///</span> 78 <span class="comment documentation">///</span>
79 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="operator injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span> 79 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="logical injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span>
80 <span class="comment documentation">///</span> 80 <span class="comment documentation">///</span>
81 <span class="comment documentation">/// </span><span class="comment injected">/* multi-line</span> 81 <span class="comment documentation">/// </span><span class="comment injected">/* multi-line</span>
82 <span class="comment documentation">/// </span><span class="comment injected"> comment */</span> 82 <span class="comment documentation">/// </span><span class="comment injected"> comment */</span>
@@ -100,10 +100,18 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
100<span class="brace">}</span> 100<span class="brace">}</span>
101 101
102<span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span> 102<span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Foo`](Foo)</span><span class="comment documentation"> is a struct</span>
103<span class="comment documentation">/// </span><span class="function documentation intra_doc_link injected">[`all_the_links`](all_the_links)</span><span class="comment documentation"> is this function</span> 103<span class="comment documentation">/// This function is &gt; </span><span class="function documentation intra_doc_link injected">[`all_the_links`](all_the_links)</span><span class="comment documentation"> &lt;</span>
104<span class="comment documentation">/// [`noop`](noop) is a macro below</span> 104<span class="comment documentation">/// [`noop`](noop) is a macro below</span>
105<span class="comment documentation">/// </span><span class="struct documentation intra_doc_link injected">[`Item`]</span><span class="comment documentation"> is a struct in the module </span><span class="module documentation intra_doc_link injected">[`module`]</span>
106<span class="comment documentation">///</span>
107<span class="comment documentation">/// [`Item`]: module::Item</span>
108<span class="comment documentation">/// [mix_and_match]: ThisShouldntResolve</span>
105<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">all_the_links</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> 109<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">all_the_links</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
106 110
111<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">module</span> <span class="brace">{</span>
112 <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="struct declaration">Item</span><span class="semicolon">;</span>
113<span class="brace">}</span>
114
107<span class="comment documentation">/// ```</span> 115<span class="comment documentation">/// ```</span>
108<span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> 116<span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
109<span class="comment documentation">/// ```</span> 117<span class="comment documentation">/// ```</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 973173254..c43bcb691 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -213,7 +213,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
213 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span> 213 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span>
214 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span> 214 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span>
215 215
216 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="semicolon">;</span> 216 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="logical">!</span><span class="bool_literal">true</span><span class="semicolon">;</span>
217 217
218 <span class="label declaration">'foo</span><span class="colon">:</span> <span class="keyword control">loop</span> <span class="brace">{</span> 218 <span class="label declaration">'foo</span><span class="colon">:</span> <span class="keyword control">loop</span> <span class="brace">{</span>
219 <span class="keyword control">break</span> <span class="label">'foo</span><span class="semicolon">;</span> 219 <span class="keyword control">break</span> <span class="label">'foo</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 369ae0972..17cc6334b 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -1,6 +1,8 @@
1use std::time::Instant;
2
1use expect_test::{expect_file, ExpectFile}; 3use expect_test::{expect_file, ExpectFile};
2use ide_db::SymbolKind; 4use ide_db::SymbolKind;
3use test_utils::{bench, bench_fixture, skip_slow_tests}; 5use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear};
4 6
5use crate::{fixture, FileRange, HlTag, TextRange}; 7use crate::{fixture, FileRange, HlTag, TextRange};
6 8
@@ -258,6 +260,36 @@ fn benchmark_syntax_highlighting_long_struct() {
258} 260}
259 261
260#[test] 262#[test]
263fn syntax_highlighting_not_quadratic() {
264 if skip_slow_tests() {
265 return;
266 }
267
268 let mut al = AssertLinear::default();
269 while al.next_round() {
270 for i in 6..=10 {
271 let n = 1 << i;
272
273 let fixture = bench_fixture::big_struct_n(n);
274 let (analysis, file_id) = fixture::file(&fixture);
275
276 let time = Instant::now();
277
278 let hash = analysis
279 .highlight(file_id)
280 .unwrap()
281 .iter()
282 .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
283 .count();
284 assert!(hash > n as usize);
285
286 let elapsed = time.elapsed();
287 al.sample(n as f64, elapsed.as_millis() as f64);
288 }
289 }
290}
291
292#[test]
261fn benchmark_syntax_highlighting_parser() { 293fn benchmark_syntax_highlighting_parser() {
262 if skip_slow_tests() { 294 if skip_slow_tests() {
263 return; 295 return;
@@ -275,7 +307,7 @@ fn benchmark_syntax_highlighting_parser() {
275 .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function)) 307 .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
276 .count() 308 .count()
277 }; 309 };
278 assert_eq!(hash, 1629); 310 assert_eq!(hash, 1632);
279} 311}
280 312
281#[test] 313#[test]
@@ -544,10 +576,18 @@ impl Foo {
544} 576}
545 577
546/// [`Foo`](Foo) is a struct 578/// [`Foo`](Foo) is a struct
547/// [`all_the_links`](all_the_links) is this function 579/// This function is > [`all_the_links`](all_the_links) <
548/// [`noop`](noop) is a macro below 580/// [`noop`](noop) is a macro below
581/// [`Item`] is a struct in the module [`module`]
582///
583/// [`Item`]: module::Item
584/// [mix_and_match]: ThisShouldntResolve
549pub fn all_the_links() {} 585pub fn all_the_links() {}
550 586
587pub mod module {
588 pub struct Item;
589}
590
551/// ``` 591/// ```
552/// noop!(1); 592/// noop!(1);
553/// ``` 593/// ```
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index 11408d445..82c732390 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -22,18 +22,19 @@ use ide_db::{
22use syntax::{ 22use syntax::{
23 algo::find_node_at_offset, 23 algo::find_node_at_offset,
24 ast::{self, edit::IndentLevel, AstToken}, 24 ast::{self, edit::IndentLevel, AstToken},
25 AstNode, SourceFile, 25 AstNode, Parse, SourceFile,
26 SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR}, 26 SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR},
27 TextRange, TextSize, 27 TextRange, TextSize,
28}; 28};
29 29
30use text_edit::TextEdit; 30use text_edit::{Indel, TextEdit};
31 31
32use crate::SourceChange; 32use crate::SourceChange;
33 33
34pub(crate) use on_enter::on_enter; 34pub(crate) use on_enter::on_enter;
35 35
36pub(crate) const TRIGGER_CHARS: &str = ".=>"; 36// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
37pub(crate) const TRIGGER_CHARS: &str = ".=>{";
37 38
38// Feature: On Typing Assists 39// Feature: On Typing Assists
39// 40//
@@ -41,6 +42,7 @@ pub(crate) const TRIGGER_CHARS: &str = ".=>";
41// 42//
42// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression 43// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
43// - typing `.` in a chain method call auto-indents 44// - typing `.` in a chain method call auto-indents
45// - typing `{` in front of an expression inserts a closing `}` after the expression
44// 46//
45// VS Code:: 47// VS Code::
46// 48//
@@ -57,28 +59,79 @@ pub(crate) fn on_char_typed(
57 position: FilePosition, 59 position: FilePosition,
58 char_typed: char, 60 char_typed: char,
59) -> Option<SourceChange> { 61) -> Option<SourceChange> {
60 assert!(TRIGGER_CHARS.contains(char_typed)); 62 if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
61 let file = &db.parse(position.file_id).tree(); 63 return None;
62 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); 64 }
65 let file = &db.parse(position.file_id);
66 if !stdx::always!(file.tree().syntax().text().char_at(position.offset) == Some(char_typed)) {
67 return None;
68 }
63 let edit = on_char_typed_inner(file, position.offset, char_typed)?; 69 let edit = on_char_typed_inner(file, position.offset, char_typed)?;
64 Some(SourceChange::from_text_edit(position.file_id, edit)) 70 Some(SourceChange::from_text_edit(position.file_id, edit))
65} 71}
66 72
67fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { 73fn on_char_typed_inner(
68 assert!(TRIGGER_CHARS.contains(char_typed)); 74 file: &Parse<SourceFile>,
75 offset: TextSize,
76 char_typed: char,
77) -> Option<TextEdit> {
78 if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
79 return None;
80 }
69 match char_typed { 81 match char_typed {
70 '.' => on_dot_typed(file, offset), 82 '.' => on_dot_typed(&file.tree(), offset),
71 '=' => on_eq_typed(file, offset), 83 '=' => on_eq_typed(&file.tree(), offset),
72 '>' => on_arrow_typed(file, offset), 84 '>' => on_arrow_typed(&file.tree(), offset),
85 '{' => on_opening_brace_typed(file, offset),
73 _ => unreachable!(), 86 _ => unreachable!(),
74 } 87 }
75} 88}
76 89
90/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a
91/// block.
92fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> {
93 if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) {
94 return None;
95 }
96
97 let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?;
98
99 // Remove the `{` to get a better parse tree, and reparse
100 let file = file.reparse(&Indel::delete(brace_token.text_range()));
101
102 let mut expr: ast::Expr = find_node_at_offset(file.tree().syntax(), offset)?;
103 if expr.syntax().text_range().start() != offset {
104 return None;
105 }
106
107 // Enclose the outermost expression starting at `offset`
108 while let Some(parent) = expr.syntax().parent() {
109 if parent.text_range().start() != expr.syntax().text_range().start() {
110 break;
111 }
112
113 match ast::Expr::cast(parent) {
114 Some(parent) => expr = parent,
115 None => break,
116 }
117 }
118
119 // If it's a statement in a block, we don't know how many statements should be included
120 if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) {
121 return None;
122 }
123
124 // Insert `}` right after the expression.
125 Some(TextEdit::insert(expr.syntax().text_range().end() + TextSize::of("{"), "}".to_string()))
126}
127
77/// Returns an edit which should be applied after `=` was typed. Primarily, 128/// Returns an edit which should be applied after `=` was typed. Primarily,
78/// this works when adding `let =`. 129/// this works when adding `let =`.
79// FIXME: use a snippet completion instead of this hack here. 130// FIXME: use a snippet completion instead of this hack here.
80fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { 131fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
81 assert_eq!(file.syntax().text().char_at(offset), Some('=')); 132 if !stdx::always!(file.syntax().text().char_at(offset) == Some('=')) {
133 return None;
134 }
82 let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; 135 let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
83 if let_stmt.semicolon_token().is_some() { 136 if let_stmt.semicolon_token().is_some() {
84 return None; 137 return None;
@@ -100,7 +153,9 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
100 153
101/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. 154/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
102fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { 155fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
103 assert_eq!(file.syntax().text().char_at(offset), Some('.')); 156 if !stdx::always!(file.syntax().text().char_at(offset) == Some('.')) {
157 return None;
158 }
104 let whitespace = 159 let whitespace =
105 file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; 160 file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;
106 161
@@ -129,7 +184,9 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
129/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` 184/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
130fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { 185fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
131 let file_text = file.syntax().text(); 186 let file_text = file.syntax().text();
132 assert_eq!(file_text.char_at(offset), Some('>')); 187 if !stdx::always!(file_text.char_at(offset) == Some('>')) {
188 return None;
189 }
133 let after_arrow = offset + TextSize::of('>'); 190 let after_arrow = offset + TextSize::of('>');
134 if file_text.char_at(after_arrow) != Some('{') { 191 if file_text.char_at(after_arrow) != Some('{') {
135 return None; 192 return None;
@@ -152,7 +209,7 @@ mod tests {
152 let edit = TextEdit::insert(offset, char_typed.to_string()); 209 let edit = TextEdit::insert(offset, char_typed.to_string());
153 edit.apply(&mut before); 210 edit.apply(&mut before);
154 let parse = SourceFile::parse(&before); 211 let parse = SourceFile::parse(&before);
155 on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { 212 on_char_typed_inner(&parse, offset, char_typed).map(|it| {
156 it.apply(&mut before); 213 it.apply(&mut before);
157 before.to_string() 214 before.to_string()
158 }) 215 })
@@ -165,8 +222,8 @@ mod tests {
165 assert_eq_text!(ra_fixture_after, &actual); 222 assert_eq_text!(ra_fixture_after, &actual);
166 } 223 }
167 224
168 fn type_char_noop(char_typed: char, before: &str) { 225 fn type_char_noop(char_typed: char, ra_fixture_before: &str) {
169 let file_change = do_type_char(char_typed, before); 226 let file_change = do_type_char(char_typed, ra_fixture_before);
170 assert!(file_change.is_none()) 227 assert!(file_change.is_none())
171 } 228 }
172 229
@@ -183,16 +240,16 @@ mod tests {
183 // "); 240 // ");
184 type_char( 241 type_char(
185 '=', 242 '=',
186 r" 243 r#"
187fn foo() { 244fn foo() {
188 let foo $0 1 + 1 245 let foo $0 1 + 1
189} 246}
190", 247"#,
191 r" 248 r#"
192fn foo() { 249fn foo() {
193 let foo = 1 + 1; 250 let foo = 1 + 1;
194} 251}
195", 252"#,
196 ); 253 );
197 // do_check(r" 254 // do_check(r"
198 // fn foo() { 255 // fn foo() {
@@ -211,27 +268,27 @@ fn foo() {
211 fn indents_new_chain_call() { 268 fn indents_new_chain_call() {
212 type_char( 269 type_char(
213 '.', 270 '.',
214 r" 271 r#"
215 fn main() { 272fn main() {
216 xs.foo() 273 xs.foo()
217 $0 274 $0
218 } 275}
219 ", 276 "#,
220 r" 277 r#"
221 fn main() { 278fn main() {
222 xs.foo() 279 xs.foo()
223 . 280 .
224 } 281}
225 ", 282 "#,
226 ); 283 );
227 type_char_noop( 284 type_char_noop(
228 '.', 285 '.',
229 r" 286 r#"
230 fn main() { 287fn main() {
231 xs.foo() 288 xs.foo()
232 $0 289 $0
233 } 290}
234 ", 291 "#,
235 ) 292 )
236 } 293 }
237 294
@@ -240,26 +297,26 @@ fn foo() {
240 type_char( 297 type_char(
241 '.', 298 '.',
242 r" 299 r"
243 fn main() { 300fn main() {
244 xs.foo() 301 xs.foo()
245 $0; 302 $0;
246 } 303}
247 ",
248 r"
249 fn main() {
250 xs.foo()
251 .;
252 }
253 ", 304 ",
305 r#"
306fn main() {
307 xs.foo()
308 .;
309}
310 "#,
254 ); 311 );
255 type_char_noop( 312 type_char_noop(
256 '.', 313 '.',
257 r" 314 r#"
258 fn main() { 315fn main() {
259 xs.foo() 316 xs.foo()
260 $0; 317 $0;
261 } 318}
262 ", 319 "#,
263 ) 320 )
264 } 321 }
265 322
@@ -288,30 +345,30 @@ fn main() {
288 fn indents_continued_chain_call() { 345 fn indents_continued_chain_call() {
289 type_char( 346 type_char(
290 '.', 347 '.',
291 r" 348 r#"
292 fn main() { 349fn main() {
293 xs.foo() 350 xs.foo()
294 .first() 351 .first()
295 $0 352 $0
296 } 353}
297 ", 354 "#,
298 r" 355 r#"
299 fn main() { 356fn main() {
300 xs.foo() 357 xs.foo()
301 .first() 358 .first()
302 . 359 .
303 } 360}
304 ", 361 "#,
305 ); 362 );
306 type_char_noop( 363 type_char_noop(
307 '.', 364 '.',
308 r" 365 r#"
309 fn main() { 366fn main() {
310 xs.foo() 367 xs.foo()
311 .first() 368 .first()
312 $0 369 $0
313 } 370}
314 ", 371 "#,
315 ); 372 );
316 } 373 }
317 374
@@ -319,33 +376,33 @@ fn main() {
319 fn indents_middle_of_chain_call() { 376 fn indents_middle_of_chain_call() {
320 type_char( 377 type_char(
321 '.', 378 '.',
322 r" 379 r#"
323 fn source_impl() { 380fn source_impl() {
324 let var = enum_defvariant_list().unwrap() 381 let var = enum_defvariant_list().unwrap()
325 $0 382 $0
326 .nth(92) 383 .nth(92)
327 .unwrap(); 384 .unwrap();
328 } 385}
329 ", 386 "#,
330 r" 387 r#"
331 fn source_impl() { 388fn source_impl() {
332 let var = enum_defvariant_list().unwrap() 389 let var = enum_defvariant_list().unwrap()
333 . 390 .
334 .nth(92) 391 .nth(92)
335 .unwrap(); 392 .unwrap();
336 } 393}
337 ", 394 "#,
338 ); 395 );
339 type_char_noop( 396 type_char_noop(
340 '.', 397 '.',
341 r" 398 r#"
342 fn source_impl() { 399fn source_impl() {
343 let var = enum_defvariant_list().unwrap() 400 let var = enum_defvariant_list().unwrap()
344 $0 401 $0
345 .nth(92) 402 .nth(92)
346 .unwrap(); 403 .unwrap();
347 } 404}
348 ", 405 "#,
349 ); 406 );
350 } 407 }
351 408
@@ -353,24 +410,113 @@ fn main() {
353 fn dont_indent_freestanding_dot() { 410 fn dont_indent_freestanding_dot() {
354 type_char_noop( 411 type_char_noop(
355 '.', 412 '.',
356 r" 413 r#"
357 fn main() { 414fn main() {
358 $0 415 $0
359 } 416}
360 ", 417 "#,
361 ); 418 );
362 type_char_noop( 419 type_char_noop(
363 '.', 420 '.',
364 r" 421 r#"
365 fn main() { 422fn main() {
366 $0 423$0
367 } 424}
368 ", 425 "#,
369 ); 426 );
370 } 427 }
371 428
372 #[test] 429 #[test]
373 fn adds_space_after_return_type() { 430 fn adds_space_after_return_type() {
374 type_char('>', "fn foo() -$0{ 92 }", "fn foo() -> { 92 }") 431 type_char(
432 '>',
433 r#"
434fn foo() -$0{ 92 }
435"#,
436 r#"
437fn foo() -> { 92 }
438"#,
439 );
440 }
441
442 #[test]
443 fn adds_closing_brace() {
444 type_char(
445 '{',
446 r#"
447fn f() { match () { _ => $0() } }
448 "#,
449 r#"
450fn f() { match () { _ => {()} } }
451 "#,
452 );
453 type_char(
454 '{',
455 r#"
456fn f() { $0() }
457 "#,
458 r#"
459fn f() { {()} }
460 "#,
461 );
462 type_char(
463 '{',
464 r#"
465fn f() { let x = $0(); }
466 "#,
467 r#"
468fn f() { let x = {()}; }
469 "#,
470 );
471 type_char(
472 '{',
473 r#"
474fn f() { let x = $0a.b(); }
475 "#,
476 r#"
477fn f() { let x = {a.b()}; }
478 "#,
479 );
480 type_char(
481 '{',
482 r#"
483const S: () = $0();
484fn f() {}
485 "#,
486 r#"
487const S: () = {()};
488fn f() {}
489 "#,
490 );
491 type_char(
492 '{',
493 r#"
494const S: () = $0a.b();
495fn f() {}
496 "#,
497 r#"
498const S: () = {a.b()};
499fn f() {}
500 "#,
501 );
502 type_char(
503 '{',
504 r#"
505fn f() {
506 match x {
507 0 => $0(),
508 1 => (),
509 }
510}
511 "#,
512 r#"
513fn f() {
514 match x {
515 0 => {()},
516 1 => (),
517 }
518}
519 "#,
520 );
375 } 521 }
376} 522}
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 9144681bf..7d2db201a 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -4,10 +4,11 @@
4use ide_db::base_db::{FilePosition, SourceDatabase}; 4use ide_db::base_db::{FilePosition, SourceDatabase};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use syntax::{ 6use syntax::{
7 ast::{self, AstToken}, 7 algo::find_node_at_offset,
8 ast::{self, edit::IndentLevel, AstToken},
8 AstNode, SmolStr, SourceFile, 9 AstNode, SmolStr, SourceFile,
9 SyntaxKind::*, 10 SyntaxKind::*,
10 SyntaxToken, TextRange, TextSize, TokenAtOffset, 11 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset,
11}; 12};
12 13
13use text_edit::TextEdit; 14use text_edit::TextEdit;
@@ -18,6 +19,8 @@ use text_edit::TextEdit;
18// 19//
19// - kbd:[Enter] inside triple-slash comments automatically inserts `///` 20// - kbd:[Enter] inside triple-slash comments automatically inserts `///`
20// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//` 21// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//`
22// - kbd:[Enter] inside `//!` doc comments automatically inserts `//!`
23// - kbd:[Enter] after `{` indents contents and closing `}` of single-line block
21// 24//
22// This action needs to be assigned to shortcut explicitly. 25// This action needs to be assigned to shortcut explicitly.
23// 26//
@@ -37,25 +40,43 @@ use text_edit::TextEdit;
37pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { 40pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
38 let parse = db.parse(position.file_id); 41 let parse = db.parse(position.file_id);
39 let file = parse.tree(); 42 let file = parse.tree();
40 let comment = file 43 let token = file.syntax().token_at_offset(position.offset).left_biased()?;
41 .syntax()
42 .token_at_offset(position.offset)
43 .left_biased()
44 .and_then(ast::Comment::cast)?;
45 44
45 if let Some(comment) = ast::Comment::cast(token.clone()) {
46 return on_enter_in_comment(&comment, &file, position.offset);
47 }
48
49 if token.kind() == L_CURLY {
50 // Typing enter after the `{` of a block expression, where the `}` is on the same line
51 if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{'))
52 .and_then(|block| on_enter_in_block(block, position))
53 {
54 cov_mark::hit!(indent_block_contents);
55 return Some(edit);
56 }
57 }
58
59 None
60}
61
62fn on_enter_in_comment(
63 comment: &ast::Comment,
64 file: &ast::SourceFile,
65 offset: TextSize,
66) -> Option<TextEdit> {
46 if comment.kind().shape.is_block() { 67 if comment.kind().shape.is_block() {
47 return None; 68 return None;
48 } 69 }
49 70
50 let prefix = comment.prefix(); 71 let prefix = comment.prefix();
51 let comment_range = comment.syntax().text_range(); 72 let comment_range = comment.syntax().text_range();
52 if position.offset < comment_range.start() + TextSize::of(prefix) { 73 if offset < comment_range.start() + TextSize::of(prefix) {
53 return None; 74 return None;
54 } 75 }
55 76
56 let mut remove_trailing_whitespace = false; 77 let mut remove_trailing_whitespace = false;
57 // Continuing single-line non-doc comments (like this one :) ) is annoying 78 // Continuing single-line non-doc comments (like this one :) ) is annoying
58 if prefix == "//" && comment_range.end() == position.offset { 79 if prefix == "//" && comment_range.end() == offset {
59 if comment.text().ends_with(' ') { 80 if comment.text().ends_with(' ') {
60 cov_mark::hit!(continues_end_of_line_comment_with_space); 81 cov_mark::hit!(continues_end_of_line_comment_with_space);
61 remove_trailing_whitespace = true; 82 remove_trailing_whitespace = true;
@@ -69,14 +90,42 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text
69 let delete = if remove_trailing_whitespace { 90 let delete = if remove_trailing_whitespace {
70 let trimmed_len = comment.text().trim_end().len() as u32; 91 let trimmed_len = comment.text().trim_end().len() as u32;
71 let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len; 92 let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len;
72 TextRange::new(position.offset - TextSize::from(trailing_whitespace_len), position.offset) 93 TextRange::new(offset - TextSize::from(trailing_whitespace_len), offset)
73 } else { 94 } else {
74 TextRange::empty(position.offset) 95 TextRange::empty(offset)
75 }; 96 };
76 let edit = TextEdit::replace(delete, inserted); 97 let edit = TextEdit::replace(delete, inserted);
77 Some(edit) 98 Some(edit)
78} 99}
79 100
101fn on_enter_in_block(block: ast::BlockExpr, position: FilePosition) -> Option<TextEdit> {
102 let contents = block_contents(&block)?;
103
104 if block.syntax().text().contains_char('\n') {
105 return None;
106 }
107
108 let indent = IndentLevel::from_node(block.syntax());
109 let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1));
110 edit.union(TextEdit::insert(contents.text_range().end(), format!("\n{}", indent))).ok()?;
111 Some(edit)
112}
113
114fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> {
115 let mut node = block.tail_expr().map(|e| e.syntax().clone());
116
117 for stmt in block.statements() {
118 if node.is_some() {
119 // More than 1 node in the block
120 return None;
121 }
122
123 node = Some(stmt.syntax().clone());
124 }
125
126 node
127}
128
80fn followed_by_comment(comment: &ast::Comment) -> bool { 129fn followed_by_comment(comment: &ast::Comment) -> bool {
81 let ws = match comment.syntax().next_token().and_then(ast::Whitespace::cast) { 130 let ws = match comment.syntax().next_token().and_then(ast::Whitespace::cast) {
82 Some(it) => it, 131 Some(it) => it,
@@ -187,6 +236,25 @@ fn foo() {
187 } 236 }
188 237
189 #[test] 238 #[test]
239 fn continues_another_doc_comment() {
240 do_check(
241 r#"
242fn main() {
243 //! Documentation for$0 on enter
244 let x = 1 + 1;
245}
246"#,
247 r#"
248fn main() {
249 //! Documentation for
250 //! $0 on enter
251 let x = 1 + 1;
252}
253"#,
254 );
255 }
256
257 #[test]
190 fn continues_code_comment_in_the_middle_of_line() { 258 fn continues_code_comment_in_the_middle_of_line() {
191 do_check( 259 do_check(
192 r" 260 r"
@@ -276,4 +344,144 @@ fn main() {
276", 344",
277 ); 345 );
278 } 346 }
347
348 #[test]
349 fn indents_fn_body_block() {
350 cov_mark::check!(indent_block_contents);
351 do_check(
352 r#"
353fn f() {$0()}
354 "#,
355 r#"
356fn f() {
357 $0()
358}
359 "#,
360 );
361 }
362
363 #[test]
364 fn indents_block_expr() {
365 do_check(
366 r#"
367fn f() {
368 let x = {$0()};
369}
370 "#,
371 r#"
372fn f() {
373 let x = {
374 $0()
375 };
376}
377 "#,
378 );
379 }
380
381 #[test]
382 fn indents_match_arm() {
383 do_check(
384 r#"
385fn f() {
386 match 6 {
387 1 => {$0f()},
388 _ => (),
389 }
390}
391 "#,
392 r#"
393fn f() {
394 match 6 {
395 1 => {
396 $0f()
397 },
398 _ => (),
399 }
400}
401 "#,
402 );
403 }
404
405 #[test]
406 fn indents_block_with_statement() {
407 do_check(
408 r#"
409fn f() {$0a = b}
410 "#,
411 r#"
412fn f() {
413 $0a = b
414}
415 "#,
416 );
417 do_check(
418 r#"
419fn f() {$0fn f() {}}
420 "#,
421 r#"
422fn f() {
423 $0fn f() {}
424}
425 "#,
426 );
427 }
428
429 #[test]
430 fn indents_nested_blocks() {
431 do_check(
432 r#"
433fn f() {$0{}}
434 "#,
435 r#"
436fn f() {
437 $0{}
438}
439 "#,
440 );
441 }
442
443 #[test]
444 fn does_not_indent_empty_block() {
445 do_check_noop(
446 r#"
447fn f() {$0}
448 "#,
449 );
450 do_check_noop(
451 r#"
452fn f() {{$0}}
453 "#,
454 );
455 }
456
457 #[test]
458 fn does_not_indent_block_with_too_much_content() {
459 do_check_noop(
460 r#"
461fn f() {$0 a = b; ()}
462 "#,
463 );
464 do_check_noop(
465 r#"
466fn f() {$0 a = b; a = b; }
467 "#,
468 );
469 }
470
471 #[test]
472 fn does_not_indent_multiline_block() {
473 do_check_noop(
474 r#"
475fn f() {$0
476}
477 "#,
478 );
479 do_check_noop(
480 r#"
481fn f() {$0
482
483}
484 "#,
485 );
486 }
279} 487}
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index 5ccd7f7a2..49aa70f74 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -934,4 +934,37 @@ fn main() {
934", 934",
935 ); 935 );
936 } 936 }
937
938 #[test]
939 fn inner_items() {
940 check_assist(
941 auto_import,
942 r#"
943mod baz {
944 pub struct Foo {}
945}
946
947mod bar {
948 fn bar() {
949 Foo$0;
950 println!("Hallo");
951 }
952}
953"#,
954 r#"
955mod baz {
956 pub struct Foo {}
957}
958
959mod bar {
960 use crate::baz::Foo;
961
962 fn bar() {
963 Foo;
964 println!("Hallo");
965 }
966}
967"#,
968 );
969 }
937} 970}
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 5fdc8bf38..5f80a40c8 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -75,7 +75,8 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
75 let insert_after = scope_for_fn_insertion(&body, anchor)?; 75 let insert_after = scope_for_fn_insertion(&body, anchor)?;
76 let module = ctx.sema.scope(&insert_after).module()?; 76 let module = ctx.sema.scope(&insert_after).module()?;
77 77
78 let vars_defined_in_body_and_outlive = vars_defined_in_body_and_outlive(ctx, &body); 78 let vars_defined_in_body_and_outlive =
79 vars_defined_in_body_and_outlive(ctx, &body, &node.parent().as_ref().unwrap_or(&node));
79 let ret_ty = body_return_ty(ctx, &body)?; 80 let ret_ty = body_return_ty(ctx, &body)?;
80 81
81 // FIXME: we compute variables that outlive here just to check `never!` condition 82 // FIXME: we compute variables that outlive here just to check `never!` condition
@@ -257,7 +258,7 @@ struct Function {
257 control_flow: ControlFlow, 258 control_flow: ControlFlow,
258 ret_ty: RetType, 259 ret_ty: RetType,
259 body: FunctionBody, 260 body: FunctionBody,
260 vars_defined_in_body_and_outlive: Vec<Local>, 261 vars_defined_in_body_and_outlive: Vec<OutlivedLocal>,
261} 262}
262 263
263#[derive(Debug)] 264#[derive(Debug)]
@@ -296,9 +297,9 @@ impl Function {
296 RetType::Expr(ty) => FunType::Single(ty.clone()), 297 RetType::Expr(ty) => FunType::Single(ty.clone()),
297 RetType::Stmt => match self.vars_defined_in_body_and_outlive.as_slice() { 298 RetType::Stmt => match self.vars_defined_in_body_and_outlive.as_slice() {
298 [] => FunType::Unit, 299 [] => FunType::Unit,
299 [var] => FunType::Single(var.ty(ctx.db())), 300 [var] => FunType::Single(var.local.ty(ctx.db())),
300 vars => { 301 vars => {
301 let types = vars.iter().map(|v| v.ty(ctx.db())).collect(); 302 let types = vars.iter().map(|v| v.local.ty(ctx.db())).collect();
302 FunType::Tuple(types) 303 FunType::Tuple(types)
303 } 304 }
304 }, 305 },
@@ -562,6 +563,12 @@ impl HasTokenAtOffset for FunctionBody {
562 } 563 }
563} 564}
564 565
566#[derive(Debug)]
567struct OutlivedLocal {
568 local: Local,
569 mut_usage_outside_body: bool,
570}
571
565/// Try to guess what user wants to extract 572/// Try to guess what user wants to extract
566/// 573///
567/// We have basically have two cases: 574/// We have basically have two cases:
@@ -592,7 +599,12 @@ fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Fu
592 // we have selected a few statements in a block 599 // we have selected a few statements in a block
593 // so covering_element returns the whole block 600 // so covering_element returns the whole block
594 if node.kind() == BLOCK_EXPR { 601 if node.kind() == BLOCK_EXPR {
595 let body = FunctionBody::from_range(node.clone(), selection_range); 602 // Extract the full statements.
603 let statements_range = node
604 .children()
605 .filter(|c| selection_range.intersect(c.text_range()).is_some())
606 .fold(selection_range, |acc, c| acc.cover(c.text_range()));
607 let body = FunctionBody::from_range(node.clone(), statements_range);
596 if body.is_some() { 608 if body.is_some() {
597 return body; 609 return body;
598 } 610 }
@@ -603,7 +615,8 @@ fn extraction_target(node: &SyntaxNode, selection_range: TextRange) -> Option<Fu
603 // so we try to expand covering_element to parent and repeat the previous 615 // so we try to expand covering_element to parent and repeat the previous
604 if let Some(parent) = node.parent() { 616 if let Some(parent) = node.parent() {
605 if parent.kind() == BLOCK_EXPR { 617 if parent.kind() == BLOCK_EXPR {
606 let body = FunctionBody::from_range(parent, selection_range); 618 // Extract the full statement.
619 let body = FunctionBody::from_range(parent, node.text_range());
607 if body.is_some() { 620 if body.is_some() {
608 return body; 621 return body;
609 } 622 }
@@ -707,10 +720,10 @@ fn has_exclusive_usages(ctx: &AssistContext, usages: &LocalUsages, body: &Functi
707 .any(|reference| reference_is_exclusive(reference, body, ctx)) 720 .any(|reference| reference_is_exclusive(reference, body, ctx))
708} 721}
709 722
710/// checks if this reference requires `&mut` access inside body 723/// checks if this reference requires `&mut` access inside node
711fn reference_is_exclusive( 724fn reference_is_exclusive(
712 reference: &FileReference, 725 reference: &FileReference,
713 body: &FunctionBody, 726 node: &dyn HasTokenAtOffset,
714 ctx: &AssistContext, 727 ctx: &AssistContext,
715) -> bool { 728) -> bool {
716 // we directly modify variable with set: `n = 0`, `n += 1` 729 // we directly modify variable with set: `n = 0`, `n += 1`
@@ -719,7 +732,7 @@ fn reference_is_exclusive(
719 } 732 }
720 733
721 // we take `&mut` reference to variable: `&mut v` 734 // we take `&mut` reference to variable: `&mut v`
722 let path = match path_element_of_reference(body, reference) { 735 let path = match path_element_of_reference(node, reference) {
723 Some(path) => path, 736 Some(path) => path,
724 None => return false, 737 None => return false,
725 }; 738 };
@@ -729,6 +742,14 @@ fn reference_is_exclusive(
729 742
730/// checks if this expr requires `&mut` access, recurses on field access 743/// checks if this expr requires `&mut` access, recurses on field access
731fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Option<bool> { 744fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Option<bool> {
745 match expr {
746 ast::Expr::MacroCall(_) => {
747 // FIXME: expand macro and check output for mutable usages of the variable?
748 return None;
749 }
750 _ => (),
751 }
752
732 let parent = expr.syntax().parent()?; 753 let parent = expr.syntax().parent()?;
733 754
734 if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) { 755 if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) {
@@ -787,7 +808,7 @@ impl HasTokenAtOffset for SyntaxNode {
787 } 808 }
788} 809}
789 810
790/// find relevant `ast::PathExpr` for reference 811/// find relevant `ast::Expr` for reference
791/// 812///
792/// # Preconditions 813/// # Preconditions
793/// 814///
@@ -804,7 +825,11 @@ fn path_element_of_reference(
804 stdx::never!(false, "cannot find path parent of variable usage: {:?}", token); 825 stdx::never!(false, "cannot find path parent of variable usage: {:?}", token);
805 None 826 None
806 })?; 827 })?;
807 stdx::always!(matches!(path, ast::Expr::PathExpr(_))); 828 stdx::always!(
829 matches!(path, ast::Expr::PathExpr(_) | ast::Expr::MacroCall(_)),
830 "unexpected expression type for variable usage: {:?}",
831 path
832 );
808 Some(path) 833 Some(path)
809} 834}
810 835
@@ -820,10 +845,16 @@ fn vars_defined_in_body(body: &FunctionBody, ctx: &AssistContext) -> Vec<Local>
820} 845}
821 846
822/// list local variables defined inside `body` that should be returned from extracted function 847/// list local variables defined inside `body` that should be returned from extracted function
823fn vars_defined_in_body_and_outlive(ctx: &AssistContext, body: &FunctionBody) -> Vec<Local> { 848fn vars_defined_in_body_and_outlive(
824 let mut vars_defined_in_body = vars_defined_in_body(&body, ctx); 849 ctx: &AssistContext,
825 vars_defined_in_body.retain(|var| var_outlives_body(ctx, body, var)); 850 body: &FunctionBody,
851 parent: &SyntaxNode,
852) -> Vec<OutlivedLocal> {
853 let vars_defined_in_body = vars_defined_in_body(&body, ctx);
826 vars_defined_in_body 854 vars_defined_in_body
855 .into_iter()
856 .filter_map(|var| var_outlives_body(ctx, body, var, parent))
857 .collect()
827} 858}
828 859
829/// checks if the relevant local was defined before(outside of) body 860/// checks if the relevant local was defined before(outside of) body
@@ -843,11 +874,23 @@ fn either_syntax(value: &Either<ast::IdentPat, ast::SelfParam>) -> &SyntaxNode {
843 } 874 }
844} 875}
845 876
846/// checks if local variable is used after(outside of) body 877/// returns usage details if local variable is used after(outside of) body
847fn var_outlives_body(ctx: &AssistContext, body: &FunctionBody, var: &Local) -> bool { 878fn var_outlives_body(
848 let usages = LocalUsages::find(ctx, *var); 879 ctx: &AssistContext,
880 body: &FunctionBody,
881 var: Local,
882 parent: &SyntaxNode,
883) -> Option<OutlivedLocal> {
884 let usages = LocalUsages::find(ctx, var);
849 let has_usages = usages.iter().any(|reference| body.preceedes_range(reference.range)); 885 let has_usages = usages.iter().any(|reference| body.preceedes_range(reference.range));
850 has_usages 886 if !has_usages {
887 return None;
888 }
889 let has_mut_usages = usages
890 .iter()
891 .filter(|reference| body.preceedes_range(reference.range))
892 .any(|reference| reference_is_exclusive(reference, parent, ctx));
893 Some(OutlivedLocal { local: var, mut_usage_outside_body: has_mut_usages })
851} 894}
852 895
853fn body_return_ty(ctx: &AssistContext, body: &FunctionBody) -> Option<RetType> { 896fn body_return_ty(ctx: &AssistContext, body: &FunctionBody) -> Option<RetType> {
@@ -927,16 +970,25 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel)
927 let mut buf = String::new(); 970 let mut buf = String::new();
928 match fun.vars_defined_in_body_and_outlive.as_slice() { 971 match fun.vars_defined_in_body_and_outlive.as_slice() {
929 [] => {} 972 [] => {}
930 [var] => format_to!(buf, "let {} = ", var.name(ctx.db()).unwrap()), 973 [var] => {
974 format_to!(buf, "let {}{} = ", mut_modifier(var), var.local.name(ctx.db()).unwrap())
975 }
931 [v0, vs @ ..] => { 976 [v0, vs @ ..] => {
932 buf.push_str("let ("); 977 buf.push_str("let (");
933 format_to!(buf, "{}", v0.name(ctx.db()).unwrap()); 978 format_to!(buf, "{}{}", mut_modifier(v0), v0.local.name(ctx.db()).unwrap());
934 for var in vs { 979 for var in vs {
935 format_to!(buf, ", {}", var.name(ctx.db()).unwrap()); 980 format_to!(buf, ", {}{}", mut_modifier(var), var.local.name(ctx.db()).unwrap());
936 } 981 }
937 buf.push_str(") = "); 982 buf.push_str(") = ");
938 } 983 }
939 } 984 }
985 fn mut_modifier(var: &OutlivedLocal) -> &'static str {
986 if var.mut_usage_outside_body {
987 "mut "
988 } else {
989 ""
990 }
991 }
940 format_to!(buf, "{}", expr); 992 format_to!(buf, "{}", expr);
941 if fun.ret_ty.is_unit() 993 if fun.ret_ty.is_unit()
942 && (!fun.vars_defined_in_body_and_outlive.is_empty() || !expr.is_block_like()) 994 && (!fun.vars_defined_in_body_and_outlive.is_empty() || !expr.is_block_like())
@@ -1175,9 +1227,19 @@ fn make_body(
1175 FunctionBody::Expr(expr) => { 1227 FunctionBody::Expr(expr) => {
1176 let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax()); 1228 let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax());
1177 let expr = ast::Expr::cast(expr).unwrap(); 1229 let expr = ast::Expr::cast(expr).unwrap();
1178 let expr = expr.dedent(old_indent).indent(IndentLevel(1)); 1230 match expr {
1231 ast::Expr::BlockExpr(block) => {
1232 // If the extracted expression is itself a block, there is no need to wrap it inside another block.
1233 let block = block.dedent(old_indent);
1234 // Recreate the block for formatting consistency with other extracted functions.
1235 make::block_expr(block.statements(), block.tail_expr())
1236 }
1237 _ => {
1238 let expr = expr.dedent(old_indent).indent(IndentLevel(1));
1179 1239
1180 make::block_expr(Vec::new(), Some(expr)) 1240 make::block_expr(Vec::new(), Some(expr))
1241 }
1242 }
1181 } 1243 }
1182 FunctionBody::Span { parent, text_range } => { 1244 FunctionBody::Span { parent, text_range } => {
1183 let mut elements: Vec<_> = parent 1245 let mut elements: Vec<_> = parent
@@ -1199,10 +1261,10 @@ fn make_body(
1199 match fun.vars_defined_in_body_and_outlive.as_slice() { 1261 match fun.vars_defined_in_body_and_outlive.as_slice() {
1200 [] => {} 1262 [] => {}
1201 [var] => { 1263 [var] => {
1202 tail_expr = Some(path_expr_from_local(ctx, *var)); 1264 tail_expr = Some(path_expr_from_local(ctx, var.local));
1203 } 1265 }
1204 vars => { 1266 vars => {
1205 let exprs = vars.iter().map(|var| path_expr_from_local(ctx, *var)); 1267 let exprs = vars.iter().map(|var| path_expr_from_local(ctx, var.local));
1206 let expr = make::expr_tuple(exprs); 1268 let expr = make::expr_tuple(exprs);
1207 tail_expr = Some(expr); 1269 tail_expr = Some(expr);
1208 } 1270 }
@@ -1492,7 +1554,7 @@ fn foo() {
1492} 1554}
1493 1555
1494fn $0fun_name() -> i32 { 1556fn $0fun_name() -> i32 {
1495 { 1 + 1 } 1557 1 + 1
1496}"#, 1558}"#,
1497 ); 1559 );
1498 } 1560 }
@@ -1739,6 +1801,60 @@ fn $0fun_name() -> i32 {
1739 } 1801 }
1740 1802
1741 #[test] 1803 #[test]
1804 fn extract_partial_block_single_line() {
1805 check_assist(
1806 extract_function,
1807 r#"
1808fn foo() {
1809 let n = 1;
1810 let mut v = $0n * n;$0
1811 v += 1;
1812}"#,
1813 r#"
1814fn foo() {
1815 let n = 1;
1816 let mut v = fun_name(n);
1817 v += 1;
1818}
1819
1820fn $0fun_name(n: i32) -> i32 {
1821 let mut v = n * n;
1822 v
1823}"#,
1824 );
1825 }
1826
1827 #[test]
1828 fn extract_partial_block() {
1829 check_assist(
1830 extract_function,
1831 r#"
1832fn foo() {
1833 let m = 2;
1834 let n = 1;
1835 let mut v = m $0* n;
1836 let mut w = 3;$0
1837 v += 1;
1838 w += 1;
1839}"#,
1840 r#"
1841fn foo() {
1842 let m = 2;
1843 let n = 1;
1844 let (mut v, mut w) = fun_name(m, n);
1845 v += 1;
1846 w += 1;
1847}
1848
1849fn $0fun_name(m: i32, n: i32) -> (i32, i32) {
1850 let mut v = m * n;
1851 let mut w = 3;
1852 (v, w)
1853}"#,
1854 );
1855 }
1856
1857 #[test]
1742 fn argument_form_expr() { 1858 fn argument_form_expr() {
1743 check_assist( 1859 check_assist(
1744 extract_function, 1860 extract_function,
@@ -2111,6 +2227,30 @@ fn $0fun_name(n: i32) -> i32 {
2111 } 2227 }
2112 2228
2113 #[test] 2229 #[test]
2230 fn variable_defined_inside_and_used_after_mutably_no_ret() {
2231 check_assist(
2232 extract_function,
2233 r"
2234fn foo() {
2235 let n = 1;
2236 $0let mut k = n * n;$0
2237 k += 1;
2238}",
2239 r"
2240fn foo() {
2241 let n = 1;
2242 let mut k = fun_name(n);
2243 k += 1;
2244}
2245
2246fn $0fun_name(n: i32) -> i32 {
2247 let mut k = n * n;
2248 k
2249}",
2250 );
2251 }
2252
2253 #[test]
2114 fn two_variables_defined_inside_and_used_after_no_ret() { 2254 fn two_variables_defined_inside_and_used_after_no_ret() {
2115 check_assist( 2255 check_assist(
2116 extract_function, 2256 extract_function,
@@ -2137,6 +2277,38 @@ fn $0fun_name(n: i32) -> (i32, i32) {
2137 } 2277 }
2138 2278
2139 #[test] 2279 #[test]
2280 fn multi_variables_defined_inside_and_used_after_mutably_no_ret() {
2281 check_assist(
2282 extract_function,
2283 r"
2284fn foo() {
2285 let n = 1;
2286 $0let mut k = n * n;
2287 let mut m = k + 2;
2288 let mut o = m + 3;
2289 o += 1;$0
2290 k += o;
2291 m = 1;
2292}",
2293 r"
2294fn foo() {
2295 let n = 1;
2296 let (mut k, mut m, o) = fun_name(n);
2297 k += o;
2298 m = 1;
2299}
2300
2301fn $0fun_name(n: i32) -> (i32, i32, i32) {
2302 let mut k = n * n;
2303 let mut m = k + 2;
2304 let mut o = m + 3;
2305 o += 1;
2306 (k, m, o)
2307}",
2308 );
2309 }
2310
2311 #[test]
2140 fn nontrivial_patterns_define_variables() { 2312 fn nontrivial_patterns_define_variables() {
2141 check_assist( 2313 check_assist(
2142 extract_function, 2314 extract_function,
@@ -2364,17 +2536,15 @@ fn foo() {
2364} 2536}
2365 2537
2366fn $0fun_name(n: &mut i32) { 2538fn $0fun_name(n: &mut i32) {
2367 { 2539 *n += *n;
2368 *n += *n; 2540 bar(*n);
2369 bar(*n); 2541 bar(*n+1);
2370 bar(*n+1); 2542 bar(*n**n);
2371 bar(*n**n); 2543 bar(&*n);
2372 bar(&*n); 2544 n.inc();
2373 n.inc(); 2545 let v = n;
2374 let v = n; 2546 *v = v.succ();
2375 *v = v.succ(); 2547 n.succ();
2376 n.succ();
2377 }
2378}", 2548}",
2379 ); 2549 );
2380 } 2550 }
@@ -3372,4 +3542,36 @@ fn foo() -> Result<(), i64> {
3372}"##, 3542}"##,
3373 ); 3543 );
3374 } 3544 }
3545
3546 #[test]
3547 fn param_usage_in_macro() {
3548 check_assist(
3549 extract_function,
3550 r"
3551macro_rules! m {
3552 ($val:expr) => { $val };
3553}
3554
3555fn foo() {
3556 let n = 1;
3557 $0let k = n * m!(n);$0
3558 let m = k + 1;
3559}",
3560 r"
3561macro_rules! m {
3562 ($val:expr) => { $val };
3563}
3564
3565fn foo() {
3566 let n = 1;
3567 let k = fun_name(n);
3568 let m = k + 1;
3569}
3570
3571fn $0fun_name(n: i32) -> i32 {
3572 let k = n * m!(n);
3573 k
3574}",
3575 );
3576 }
3375} 3577}
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs
index 7a32483dc..136b9a55b 100644
--- a/crates/ide_assists/src/handlers/extract_variable.rs
+++ b/crates/ide_assists/src/handlers/extract_variable.rs
@@ -2,7 +2,8 @@ use stdx::format_to;
2use syntax::{ 2use syntax::{
3 ast::{self, AstNode}, 3 ast::{self, AstNode},
4 SyntaxKind::{ 4 SyntaxKind::{
5 BLOCK_EXPR, BREAK_EXPR, CLOSURE_EXPR, COMMENT, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR, 5 BLOCK_EXPR, BREAK_EXPR, CLOSURE_EXPR, COMMENT, LOOP_EXPR, MATCH_ARM, MATCH_GUARD,
6 PATH_EXPR, RETURN_EXPR,
6 }, 7 },
7 SyntaxNode, 8 SyntaxNode,
8}; 9};
@@ -147,9 +148,18 @@ impl Anchor {
147 } 148 }
148 149
149 if let Some(parent) = node.parent() { 150 if let Some(parent) = node.parent() {
150 if parent.kind() == MATCH_ARM || parent.kind() == CLOSURE_EXPR { 151 if parent.kind() == CLOSURE_EXPR {
152 cov_mark::hit!(test_extract_var_in_closure_no_block);
151 return Some(Anchor::WrapInBlock(node)); 153 return Some(Anchor::WrapInBlock(node));
152 } 154 }
155 if parent.kind() == MATCH_ARM {
156 if node.kind() == MATCH_GUARD {
157 cov_mark::hit!(test_extract_var_in_match_guard);
158 } else {
159 cov_mark::hit!(test_extract_var_in_match_arm_no_block);
160 return Some(Anchor::WrapInBlock(node));
161 }
162 }
153 } 163 }
154 164
155 if let Some(stmt) = ast::Stmt::cast(node.clone()) { 165 if let Some(stmt) = ast::Stmt::cast(node.clone()) {
@@ -280,9 +290,10 @@ fn foo() {
280 290
281 #[test] 291 #[test]
282 fn test_extract_var_in_match_arm_no_block() { 292 fn test_extract_var_in_match_arm_no_block() {
293 cov_mark::check!(test_extract_var_in_match_arm_no_block);
283 check_assist( 294 check_assist(
284 extract_variable, 295 extract_variable,
285 " 296 r#"
286fn main() { 297fn main() {
287 let x = true; 298 let x = true;
288 let tuple = match x { 299 let tuple = match x {
@@ -290,8 +301,8 @@ fn main() {
290 _ => (0, false) 301 _ => (0, false)
291 }; 302 };
292} 303}
293", 304"#,
294 " 305 r#"
295fn main() { 306fn main() {
296 let x = true; 307 let x = true;
297 let tuple = match x { 308 let tuple = match x {
@@ -299,7 +310,7 @@ fn main() {
299 _ => (0, false) 310 _ => (0, false)
300 }; 311 };
301} 312}
302", 313"#,
303 ); 314 );
304 } 315 }
305 316
@@ -307,7 +318,7 @@ fn main() {
307 fn test_extract_var_in_match_arm_with_block() { 318 fn test_extract_var_in_match_arm_with_block() {
308 check_assist( 319 check_assist(
309 extract_variable, 320 extract_variable,
310 " 321 r#"
311fn main() { 322fn main() {
312 let x = true; 323 let x = true;
313 let tuple = match x { 324 let tuple = match x {
@@ -318,8 +329,8 @@ fn main() {
318 _ => (0, false) 329 _ => (0, false)
319 }; 330 };
320} 331}
321", 332"#,
322 " 333 r#"
323fn main() { 334fn main() {
324 let x = true; 335 let x = true;
325 let tuple = match x { 336 let tuple = match x {
@@ -331,24 +342,50 @@ fn main() {
331 _ => (0, false) 342 _ => (0, false)
332 }; 343 };
333} 344}
334", 345"#,
346 );
347 }
348
349 #[test]
350 fn test_extract_var_in_match_guard() {
351 cov_mark::check!(test_extract_var_in_match_guard);
352 check_assist(
353 extract_variable,
354 r#"
355fn main() {
356 match () {
357 () if $010 > 0$0 => 1
358 _ => 2
359 };
360}
361"#,
362 r#"
363fn main() {
364 let $0var_name = 10 > 0;
365 match () {
366 () if var_name => 1
367 _ => 2
368 };
369}
370"#,
335 ); 371 );
336 } 372 }
337 373
338 #[test] 374 #[test]
339 fn test_extract_var_in_closure_no_block() { 375 fn test_extract_var_in_closure_no_block() {
376 cov_mark::check!(test_extract_var_in_closure_no_block);
340 check_assist( 377 check_assist(
341 extract_variable, 378 extract_variable,
342 " 379 r#"
343fn main() { 380fn main() {
344 let lambda = |x: u32| $0x * 2$0; 381 let lambda = |x: u32| $0x * 2$0;
345} 382}
346", 383"#,
347 " 384 r#"
348fn main() { 385fn main() {
349 let lambda = |x: u32| { let $0var_name = x * 2; var_name }; 386 let lambda = |x: u32| { let $0var_name = x * 2; var_name };
350} 387}
351", 388"#,
352 ); 389 );
353 } 390 }
354 391
@@ -356,16 +393,16 @@ fn main() {
356 fn test_extract_var_in_closure_with_block() { 393 fn test_extract_var_in_closure_with_block() {
357 check_assist( 394 check_assist(
358 extract_variable, 395 extract_variable,
359 " 396 r#"
360fn main() { 397fn main() {
361 let lambda = |x: u32| { $0x * 2$0 }; 398 let lambda = |x: u32| { $0x * 2$0 };
362} 399}
363", 400"#,
364 " 401 r#"
365fn main() { 402fn main() {
366 let lambda = |x: u32| { let $0var_name = x * 2; var_name }; 403 let lambda = |x: u32| { let $0var_name = x * 2; var_name };
367} 404}
368", 405"#,
369 ); 406 );
370 } 407 }
371 408
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index 878b3a3fa..be927cc1c 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -1,5 +1,6 @@
1use std::iter; 1use std::iter;
2 2
3use either::Either;
3use hir::{Adt, HasSource, ModuleDef, Semantics}; 4use hir::{Adt, HasSource, ModuleDef, Semantics};
4use ide_db::helpers::{mod_path_to_ast, FamousDefs}; 5use ide_db::helpers::{mod_path_to_ast, FamousDefs};
5use ide_db::RootDatabase; 6use ide_db::RootDatabase;
@@ -7,7 +8,7 @@ use itertools::Itertools;
7use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; 8use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
8 9
9use crate::{ 10use crate::{
10 utils::{does_pat_match_variant, render_snippet, Cursor}, 11 utils::{self, render_snippet, Cursor},
11 AssistContext, AssistId, AssistKind, Assists, 12 AssistContext, AssistId, AssistKind, Assists,
12}; 13};
13 14
@@ -48,6 +49,18 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
48 } 49 }
49 } 50 }
50 51
52 let top_lvl_pats: Vec<_> = arms
53 .iter()
54 .filter_map(ast::MatchArm::pat)
55 .flat_map(|pat| match pat {
56 // Special case OrPat as separate top-level pats
57 Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
58 _ => Either::Right(iter::once(pat)),
59 })
60 // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129.
61 .filter(|pat| !matches!(pat, Pat::WildcardPat(_)))
62 .collect();
63
51 let module = ctx.sema.scope(expr.syntax()).module()?; 64 let module = ctx.sema.scope(expr.syntax()).module()?;
52 65
53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { 66 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
@@ -56,27 +69,20 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
56 let mut variants = variants 69 let mut variants = variants
57 .into_iter() 70 .into_iter()
58 .filter_map(|variant| build_pat(ctx.db(), module, variant)) 71 .filter_map(|variant| build_pat(ctx.db(), module, variant))
59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 72 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 73 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
61 .collect::<Vec<_>>(); 74 .collect::<Vec<_>>();
62 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { 75 if Some(enum_def)
76 == FamousDefs(&ctx.sema, Some(module.krate()))
77 .core_option_Option()
78 .map(|x| lift_enum(x))
79 {
63 // Match `Some` variant first. 80 // Match `Some` variant first.
64 cov_mark::hit!(option_order); 81 cov_mark::hit!(option_order);
65 variants.reverse() 82 variants.reverse()
66 } 83 }
67 variants 84 variants
68 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { 85 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
69 // Partial fill not currently supported for tuple of enums.
70 if !arms.is_empty() {
71 return None;
72 }
73
74 // We do not currently support filling match arms for a tuple
75 // containing a single enum.
76 if enum_defs.len() < 2 {
77 return None;
78 }
79
80 // When calculating the match arms for a tuple of enums, we want 86 // When calculating the match arms for a tuple of enums, we want
81 // to create a match arm for each possible combination of enum 87 // to create a match arm for each possible combination of enum
82 // values. The `multi_cartesian_product` method transforms 88 // values. The `multi_cartesian_product` method transforms
@@ -91,7 +97,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
91 variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant)); 97 variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
92 ast::Pat::from(make::tuple_pat(patterns)) 98 ast::Pat::from(make::tuple_pat(patterns))
93 }) 99 })
94 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 100 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
95 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 101 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
96 .collect() 102 .collect()
97 } else { 103 } else {
@@ -134,61 +140,114 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
134 ) 140 )
135} 141}
136 142
137fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { 143fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
138 existing_arms.iter().filter_map(|arm| arm.pat()).all(|pat| { 144 !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var))
139 // Special casee OrPat as separate top-level pats 145}
140 let top_level_pats: Vec<Pat> = match pat {
141 Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(),
142 _ => vec![pat],
143 };
144 146
145 !top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var)) 147// Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check?
146 }) 148fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
149 match (pat, var) {
150 (Pat::WildcardPat(_), _) => true,
151 (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => {
152 tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v))
153 }
154 _ => utils::does_pat_match_variant(pat, var),
155 }
156}
157
158#[derive(Eq, PartialEq, Clone)]
159enum ExtendedEnum {
160 Bool,
161 Enum(hir::Enum),
162}
163
164#[derive(Eq, PartialEq, Clone)]
165enum ExtendedVariant {
166 True,
167 False,
168 Variant(hir::Variant),
169}
170
171fn lift_enum(e: hir::Enum) -> ExtendedEnum {
172 ExtendedEnum::Enum(e)
173}
174
175impl ExtendedEnum {
176 fn variants(&self, db: &RootDatabase) -> Vec<ExtendedVariant> {
177 match self {
178 ExtendedEnum::Enum(e) => {
179 e.variants(db).into_iter().map(|x| ExtendedVariant::Variant(x)).collect::<Vec<_>>()
180 }
181 ExtendedEnum::Bool => {
182 Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
183 }
184 }
185 }
147} 186}
148 187
149fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { 188fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
150 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { 189 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
151 Some(Adt::Enum(e)) => Some(e), 190 Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
152 _ => None, 191 _ => {
192 if ty.is_bool() {
193 Some(ExtendedEnum::Bool)
194 } else {
195 None
196 }
197 }
153 }) 198 })
154} 199}
155 200
156fn resolve_tuple_of_enum_def( 201fn resolve_tuple_of_enum_def(
157 sema: &Semantics<RootDatabase>, 202 sema: &Semantics<RootDatabase>,
158 expr: &ast::Expr, 203 expr: &ast::Expr,
159) -> Option<Vec<hir::Enum>> { 204) -> Option<Vec<ExtendedEnum>> {
160 sema.type_of_expr(&expr)? 205 sema.type_of_expr(&expr)?
161 .tuple_fields(sema.db) 206 .tuple_fields(sema.db)
162 .iter() 207 .iter()
163 .map(|ty| { 208 .map(|ty| {
164 ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { 209 ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
165 Some(Adt::Enum(e)) => Some(e), 210 Some(Adt::Enum(e)) => Some(lift_enum(e)),
166 // For now we only handle expansion for a tuple of enums. Here 211 // For now we only handle expansion for a tuple of enums. Here
167 // we map non-enum items to None and rely on `collect` to 212 // we map non-enum items to None and rely on `collect` to
168 // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. 213 // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
169 _ => None, 214 _ => {
215 if ty.is_bool() {
216 Some(ExtendedEnum::Bool)
217 } else {
218 None
219 }
220 }
170 }) 221 })
171 }) 222 })
172 .collect() 223 .collect()
173} 224}
174 225
175fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Option<ast::Pat> { 226fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> {
176 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); 227 match var {
228 ExtendedVariant::Variant(var) => {
229 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
230
231 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
232 let pat: ast::Pat = match var.source(db)?.value.kind() {
233 ast::StructKind::Tuple(field_list) => {
234 let pats =
235 iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
236 make::tuple_struct_pat(path, pats).into()
237 }
238 ast::StructKind::Record(field_list) => {
239 let pats =
240 field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into());
241 make::record_pat(path, pats).into()
242 }
243 ast::StructKind::Unit => make::path_pat(path),
244 };
177 245
178 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 246 Some(pat)
179 let pat: ast::Pat = match var.source(db)?.value.kind() {
180 ast::StructKind::Tuple(field_list) => {
181 let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
182 make::tuple_struct_pat(path, pats).into()
183 }
184 ast::StructKind::Record(field_list) => {
185 let pats = field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into());
186 make::record_pat(path, pats).into()
187 } 247 }
188 ast::StructKind::Unit => make::path_pat(path), 248 ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))),
189 }; 249 ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))),
190 250 }
191 Some(pat)
192} 251}
193 252
194#[cfg(test)] 253#[cfg(test)]
@@ -221,6 +280,21 @@ mod tests {
221 } 280 }
222 281
223 #[test] 282 #[test]
283 fn all_boolean_match_arms_provided() {
284 check_assist_not_applicable(
285 fill_match_arms,
286 r#"
287 fn foo(a: bool) {
288 match a$0 {
289 true => {}
290 false => {}
291 }
292 }
293 "#,
294 )
295 }
296
297 #[test]
224 fn tuple_of_non_enum() { 298 fn tuple_of_non_enum() {
225 // for now this case is not handled, although it potentially could be 299 // for now this case is not handled, although it potentially could be
226 // in the future 300 // in the future
@@ -236,6 +310,113 @@ mod tests {
236 } 310 }
237 311
238 #[test] 312 #[test]
313 fn fill_match_arms_boolean() {
314 check_assist(
315 fill_match_arms,
316 r#"
317 fn foo(a: bool) {
318 match a$0 {
319 }
320 }
321 "#,
322 r#"
323 fn foo(a: bool) {
324 match a {
325 $0true => {}
326 false => {}
327 }
328 }
329 "#,
330 )
331 }
332
333 #[test]
334 fn partial_fill_boolean() {
335 check_assist(
336 fill_match_arms,
337 r#"
338 fn foo(a: bool) {
339 match a$0 {
340 true => {}
341 }
342 }
343 "#,
344 r#"
345 fn foo(a: bool) {
346 match a {
347 true => {}
348 $0false => {}
349 }
350 }
351 "#,
352 )
353 }
354
355 #[test]
356 fn all_boolean_tuple_arms_provided() {
357 check_assist_not_applicable(
358 fill_match_arms,
359 r#"
360 fn foo(a: bool) {
361 match (a, a)$0 {
362 (true, true) => {}
363 (true, false) => {}
364 (false, true) => {}
365 (false, false) => {}
366 }
367 }
368 "#,
369 )
370 }
371
372 #[test]
373 fn fill_boolean_tuple() {
374 check_assist(
375 fill_match_arms,
376 r#"
377 fn foo(a: bool) {
378 match (a, a)$0 {
379 }
380 }
381 "#,
382 r#"
383 fn foo(a: bool) {
384 match (a, a) {
385 $0(true, true) => {}
386 (true, false) => {}
387 (false, true) => {}
388 (false, false) => {}
389 }
390 }
391 "#,
392 )
393 }
394
395 #[test]
396 fn partial_fill_boolean_tuple() {
397 check_assist(
398 fill_match_arms,
399 r#"
400 fn foo(a: bool) {
401 match (a, a)$0 {
402 (false, true) => {}
403 }
404 }
405 "#,
406 r#"
407 fn foo(a: bool) {
408 match (a, a) {
409 (false, true) => {}
410 $0(true, true) => {}
411 (true, false) => {}
412 (false, false) => {}
413 }
414 }
415 "#,
416 )
417 }
418
419 #[test]
239 fn partial_fill_record_tuple() { 420 fn partial_fill_record_tuple() {
240 check_assist( 421 check_assist(
241 fill_match_arms, 422 fill_match_arms,
@@ -473,20 +654,81 @@ fn main() {
473 654
474 #[test] 655 #[test]
475 fn fill_match_arms_tuple_of_enum_partial() { 656 fn fill_match_arms_tuple_of_enum_partial() {
476 check_assist_not_applicable( 657 check_assist(
477 fill_match_arms, 658 fill_match_arms,
478 r#" 659 r#"
479 enum A { One, Two } 660enum A { One, Two }
480 enum B { One, Two } 661enum B { One, Two }
481 662
482 fn main() { 663fn main() {
483 let a = A::One; 664 let a = A::One;
484 let b = B::One; 665 let b = B::One;
485 match (a$0, b) { 666 match (a$0, b) {
486 (A::Two, B::One) => {} 667 (A::Two, B::One) => {}
487 } 668 }
488 } 669}
489 "#, 670"#,
671 r#"
672enum A { One, Two }
673enum B { One, Two }
674
675fn main() {
676 let a = A::One;
677 let b = B::One;
678 match (a, b) {
679 (A::Two, B::One) => {}
680 $0(A::One, B::One) => {}
681 (A::One, B::Two) => {}
682 (A::Two, B::Two) => {}
683 }
684}
685"#,
686 );
687 }
688
689 #[test]
690 fn fill_match_arms_tuple_of_enum_partial_with_wildcards() {
691 let ra_fixture = r#"
692fn main() {
693 let a = Some(1);
694 let b = Some(());
695 match (a$0, b) {
696 (Some(_), _) => {}
697 (None, Some(_)) => {}
698 }
699}
700"#;
701 check_assist(
702 fill_match_arms,
703 &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE),
704 r#"
705fn main() {
706 let a = Some(1);
707 let b = Some(());
708 match (a, b) {
709 (Some(_), _) => {}
710 (None, Some(_)) => {}
711 $0(None, None) => {}
712 }
713}
714"#,
715 );
716 }
717
718 #[test]
719 fn fill_match_arms_partial_with_deep_pattern() {
720 // Fixme: cannot handle deep patterns
721 let ra_fixture = r#"
722fn main() {
723 match $0Some(true) {
724 Some(true) => {}
725 None => {}
726 }
727}
728"#;
729 check_assist_not_applicable(
730 fill_match_arms,
731 &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE),
490 ); 732 );
491 } 733 }
492 734
@@ -514,10 +756,7 @@ fn main() {
514 756
515 #[test] 757 #[test]
516 fn fill_match_arms_single_element_tuple_of_enum() { 758 fn fill_match_arms_single_element_tuple_of_enum() {
517 // For now we don't hande the case of a single element tuple, but 759 check_assist(
518 // we could handle this in the future if `make::tuple_pat` allowed
519 // creating a tuple with a single pattern.
520 check_assist_not_applicable(
521 fill_match_arms, 760 fill_match_arms,
522 r#" 761 r#"
523 enum A { One, Two } 762 enum A { One, Two }
@@ -528,6 +767,17 @@ fn main() {
528 } 767 }
529 } 768 }
530 "#, 769 "#,
770 r#"
771 enum A { One, Two }
772
773 fn main() {
774 let a = A::One;
775 match (a, ) {
776 $0(A::One,) => {}
777 (A::Two,) => {}
778 }
779 }
780 "#,
531 ); 781 );
532 } 782 }
533 783
diff --git a/crates/ide_assists/src/handlers/flip_comma.rs b/crates/ide_assists/src/handlers/flip_comma.rs
index 7f5e75d34..99be5e090 100644
--- a/crates/ide_assists/src/handlers/flip_comma.rs
+++ b/crates/ide_assists/src/handlers/flip_comma.rs
@@ -66,26 +66,12 @@ mod tests {
66 } 66 }
67 67
68 #[test] 68 #[test]
69 #[should_panic]
70 fn flip_comma_before_punct() { 69 fn flip_comma_before_punct() {
71 // See https://github.com/rust-analyzer/rust-analyzer/issues/1619 70 // See https://github.com/rust-analyzer/rust-analyzer/issues/1619
72 // "Flip comma" assist shouldn't be applicable to the last comma in enum or struct 71 // "Flip comma" assist shouldn't be applicable to the last comma in enum or struct
73 // declaration body. 72 // declaration body.
74 check_assist_target( 73 check_assist_not_applicable(flip_comma, "pub enum Test { A,$0 }");
75 flip_comma, 74 check_assist_not_applicable(flip_comma, "pub struct Test { foo: usize,$0 }");
76 "pub enum Test { \
77 A,$0 \
78 }",
79 ",",
80 );
81
82 check_assist_target(
83 flip_comma,
84 "pub struct Test { \
85 foo: usize,$0 \
86 }",
87 ",",
88 );
89 } 75 }
90 76
91 #[test] 77 #[test]
diff --git a/crates/ide_assists/src/handlers/generate_deref.rs b/crates/ide_assists/src/handlers/generate_deref.rs
new file mode 100644
index 000000000..4998ff7a4
--- /dev/null
+++ b/crates/ide_assists/src/handlers/generate_deref.rs
@@ -0,0 +1,227 @@
1use std::fmt::Display;
2
3use ide_db::{helpers::FamousDefs, RootDatabase};
4use syntax::{
5 ast::{self, NameOwner},
6 AstNode, SyntaxNode,
7};
8
9use crate::{
10 assist_context::{AssistBuilder, AssistContext, Assists},
11 utils::generate_trait_impl_text,
12 AssistId, AssistKind,
13};
14
15// Assist: generate_deref
16//
17// Generate `Deref` impl using the given struct field.
18//
19// ```
20// struct A;
21// struct B {
22// $0a: A
23// }
24// ```
25// ->
26// ```
27// struct A;
28// struct B {
29// a: A
30// }
31//
32// impl std::ops::Deref for B {
33// type Target = A;
34//
35// fn deref(&self) -> &Self::Target {
36// &self.a
37// }
38// }
39// ```
40pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
41 generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx))
42}
43
44fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
45 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
46 let field = ctx.find_node_at_offset::<ast::RecordField>()?;
47
48 if existing_deref_impl(&ctx.sema, &strukt).is_some() {
49 cov_mark::hit!(test_add_record_deref_impl_already_exists);
50 return None;
51 }
52
53 let field_type = field.ty()?;
54 let field_name = field.name()?;
55 let target = field.syntax().text_range();
56 acc.add(
57 AssistId("generate_deref", AssistKind::Generate),
58 format!("Generate `Deref` impl using `{}`", field_name),
59 target,
60 |edit| generate_edit(edit, strukt, field_type.syntax(), field_name.syntax()),
61 )
62}
63
64fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
65 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
66 let field = ctx.find_node_at_offset::<ast::TupleField>()?;
67 let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
68 let field_list_index =
69 field_list.syntax().children().into_iter().position(|s| &s == field.syntax())?;
70
71 if existing_deref_impl(&ctx.sema, &strukt).is_some() {
72 cov_mark::hit!(test_add_field_deref_impl_already_exists);
73 return None;
74 }
75
76 let field_type = field.ty()?;
77 let target = field.syntax().text_range();
78 acc.add(
79 AssistId("generate_deref", AssistKind::Generate),
80 format!("Generate `Deref` impl using `{}`", field.syntax()),
81 target,
82 |edit| generate_edit(edit, strukt, field_type.syntax(), field_list_index),
83 )
84}
85
86fn generate_edit(
87 edit: &mut AssistBuilder,
88 strukt: ast::Struct,
89 field_type_syntax: &SyntaxNode,
90 field_name: impl Display,
91) {
92 let start_offset = strukt.syntax().text_range().end();
93 let impl_code = format!(
94 r#" type Target = {0};
95
96 fn deref(&self) -> &Self::Target {{
97 &self.{1}
98 }}"#,
99 field_type_syntax, field_name
100 );
101 let strukt_adt = ast::Adt::Struct(strukt);
102 let deref_impl = generate_trait_impl_text(&strukt_adt, "std::ops::Deref", &impl_code);
103 edit.insert(start_offset, deref_impl);
104}
105
106fn existing_deref_impl(
107 sema: &'_ hir::Semantics<'_, RootDatabase>,
108 strukt: &ast::Struct,
109) -> Option<()> {
110 let strukt = sema.to_def(strukt)?;
111 let krate = strukt.module(sema.db).krate();
112
113 let deref_trait = FamousDefs(sema, Some(krate)).core_ops_Deref()?;
114 let strukt_type = strukt.ty(sema.db);
115
116 if strukt_type.impls_trait(sema.db, deref_trait, &[]) {
117 Some(())
118 } else {
119 None
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use crate::tests::{check_assist, check_assist_not_applicable};
126
127 use super::*;
128
129 #[test]
130 fn test_generate_record_deref() {
131 check_assist(
132 generate_deref,
133 r#"struct A { }
134struct B { $0a: A }"#,
135 r#"struct A { }
136struct B { a: A }
137
138impl std::ops::Deref for B {
139 type Target = A;
140
141 fn deref(&self) -> &Self::Target {
142 &self.a
143 }
144}"#,
145 );
146 }
147
148 #[test]
149 fn test_generate_field_deref_idx_0() {
150 check_assist(
151 generate_deref,
152 r#"struct A { }
153struct B($0A);"#,
154 r#"struct A { }
155struct B(A);
156
157impl std::ops::Deref for B {
158 type Target = A;
159
160 fn deref(&self) -> &Self::Target {
161 &self.0
162 }
163}"#,
164 );
165 }
166 #[test]
167 fn test_generate_field_deref_idx_1() {
168 check_assist(
169 generate_deref,
170 r#"struct A { }
171struct B(u8, $0A);"#,
172 r#"struct A { }
173struct B(u8, A);
174
175impl std::ops::Deref for B {
176 type Target = A;
177
178 fn deref(&self) -> &Self::Target {
179 &self.1
180 }
181}"#,
182 );
183 }
184
185 fn check_not_applicable(ra_fixture: &str) {
186 let fixture = format!(
187 "//- /main.rs crate:main deps:core,std\n{}\n{}",
188 ra_fixture,
189 FamousDefs::FIXTURE
190 );
191 check_assist_not_applicable(generate_deref, &fixture)
192 }
193
194 #[test]
195 fn test_generate_record_deref_not_applicable_if_already_impl() {
196 cov_mark::check!(test_add_record_deref_impl_already_exists);
197 check_not_applicable(
198 r#"struct A { }
199struct B { $0a: A }
200
201impl std::ops::Deref for B {
202 type Target = A;
203
204 fn deref(&self) -> &Self::Target {
205 &self.a
206 }
207}"#,
208 )
209 }
210
211 #[test]
212 fn test_generate_field_deref_not_applicable_if_already_impl() {
213 cov_mark::check!(test_add_field_deref_impl_already_exists);
214 check_not_applicable(
215 r#"struct A { }
216struct B($0A)
217
218impl std::ops::Deref for B {
219 type Target = A;
220
221 fn deref(&self) -> &Self::Target {
222 &self.0
223 }
224}"#,
225 )
226 }
227}
diff --git a/crates/ide_assists/src/handlers/inline_local_variable.rs b/crates/ide_assists/src/handlers/inline_local_variable.rs
index ea1466dc8..f5dafc8cb 100644
--- a/crates/ide_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ide_assists/src/handlers/inline_local_variable.rs
@@ -1,7 +1,9 @@
1use ide_db::{defs::Definition, search::FileReference}; 1use either::Either;
2use hir::PathResolution;
3use ide_db::{base_db::FileId, defs::Definition, search::FileReference};
2use rustc_hash::FxHashMap; 4use rustc_hash::FxHashMap;
3use syntax::{ 5use syntax::{
4 ast::{self, AstNode, AstToken}, 6 ast::{self, AstNode, AstToken, NameOwner},
5 TextRange, 7 TextRange,
6}; 8};
7 9
@@ -27,44 +29,28 @@ use crate::{
27// } 29// }
28// ``` 30// ```
29pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 31pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
30 let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?; 32 let InlineData { let_stmt, delete_let, replace_usages, target } =
31 let bind_pat = match let_stmt.pat()? { 33 inline_let(ctx).or_else(|| inline_usage(ctx))?;
32 ast::Pat::IdentPat(pat) => pat,
33 _ => return None,
34 };
35 if bind_pat.mut_token().is_some() {
36 cov_mark::hit!(test_not_inline_mut_variable);
37 return None;
38 }
39 if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) {
40 cov_mark::hit!(not_applicable_outside_of_bind_pat);
41 return None;
42 }
43 let initializer_expr = let_stmt.initializer()?; 34 let initializer_expr = let_stmt.initializer()?;
44 35
45 let def = ctx.sema.to_def(&bind_pat)?; 36 let delete_range = if delete_let {
46 let def = Definition::Local(def); 37 if let Some(whitespace) = let_stmt
47 let usages = def.usages(&ctx.sema).all(); 38 .syntax()
48 if usages.is_empty() { 39 .next_sibling_or_token()
49 cov_mark::hit!(test_not_applicable_if_variable_unused); 40 .and_then(|it| ast::Whitespace::cast(it.as_token()?.clone()))
50 return None; 41 {
51 }; 42 Some(TextRange::new(
52 43 let_stmt.syntax().text_range().start(),
53 let delete_range = if let Some(whitespace) = let_stmt 44 whitespace.syntax().text_range().end(),
54 .syntax() 45 ))
55 .next_sibling_or_token() 46 } else {
56 .and_then(|it| ast::Whitespace::cast(it.as_token()?.clone())) 47 Some(let_stmt.syntax().text_range())
57 { 48 }
58 TextRange::new(
59 let_stmt.syntax().text_range().start(),
60 whitespace.syntax().text_range().end(),
61 )
62 } else { 49 } else {
63 let_stmt.syntax().text_range() 50 None
64 }; 51 };
65 52
66 let wrap_in_parens = usages 53 let wrap_in_parens = replace_usages
67 .references
68 .iter() 54 .iter()
69 .map(|(&file_id, refs)| { 55 .map(|(&file_id, refs)| {
70 refs.iter() 56 refs.iter()
@@ -114,14 +100,20 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
114 let init_str = initializer_expr.syntax().text().to_string(); 100 let init_str = initializer_expr.syntax().text().to_string();
115 let init_in_paren = format!("({})", &init_str); 101 let init_in_paren = format!("({})", &init_str);
116 102
117 let target = bind_pat.syntax().text_range(); 103 let target = match target {
104 ast::NameOrNameRef::Name(it) => it.syntax().text_range(),
105 ast::NameOrNameRef::NameRef(it) => it.syntax().text_range(),
106 };
107
118 acc.add( 108 acc.add(
119 AssistId("inline_local_variable", AssistKind::RefactorInline), 109 AssistId("inline_local_variable", AssistKind::RefactorInline),
120 "Inline variable", 110 "Inline variable",
121 target, 111 target,
122 move |builder| { 112 move |builder| {
123 builder.delete(delete_range); 113 if let Some(range) = delete_range {
124 for (file_id, references) in usages.references { 114 builder.delete(range);
115 }
116 for (file_id, references) in replace_usages {
125 for (&should_wrap, reference) in wrap_in_parens[&file_id].iter().zip(references) { 117 for (&should_wrap, reference) in wrap_in_parens[&file_id].iter().zip(references) {
126 let replacement = 118 let replacement =
127 if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 119 if should_wrap { init_in_paren.clone() } else { init_str.clone() };
@@ -140,6 +132,81 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
140 ) 132 )
141} 133}
142 134
135struct InlineData {
136 let_stmt: ast::LetStmt,
137 delete_let: bool,
138 target: ast::NameOrNameRef,
139 replace_usages: FxHashMap<FileId, Vec<FileReference>>,
140}
141
142fn inline_let(ctx: &AssistContext) -> Option<InlineData> {
143 let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?;
144 let bind_pat = match let_stmt.pat()? {
145 ast::Pat::IdentPat(pat) => pat,
146 _ => return None,
147 };
148 if bind_pat.mut_token().is_some() {
149 cov_mark::hit!(test_not_inline_mut_variable);
150 return None;
151 }
152 if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) {
153 cov_mark::hit!(not_applicable_outside_of_bind_pat);
154 return None;
155 }
156
157 let def = ctx.sema.to_def(&bind_pat)?;
158 let def = Definition::Local(def);
159 let usages = def.usages(&ctx.sema).all();
160 if usages.is_empty() {
161 cov_mark::hit!(test_not_applicable_if_variable_unused);
162 return None;
163 };
164
165 Some(InlineData {
166 let_stmt,
167 delete_let: true,
168 target: ast::NameOrNameRef::Name(bind_pat.name()?),
169 replace_usages: usages.references,
170 })
171}
172
173fn inline_usage(ctx: &AssistContext) -> Option<InlineData> {
174 let path_expr = ctx.find_node_at_offset::<ast::PathExpr>()?;
175 let path = path_expr.path()?;
176 let name = match path.as_single_segment()?.kind()? {
177 ast::PathSegmentKind::Name(name) => name,
178 _ => return None,
179 };
180
181 let local = match ctx.sema.resolve_path(&path)? {
182 PathResolution::Local(local) => local,
183 _ => return None,
184 };
185
186 let bind_pat = match local.source(ctx.db()).value {
187 Either::Left(ident) => ident,
188 _ => return None,
189 };
190
191 let let_stmt = ast::LetStmt::cast(bind_pat.syntax().parent()?)?;
192
193 let def = Definition::Local(local);
194 let mut usages = def.usages(&ctx.sema).all();
195
196 let delete_let = usages.references.values().map(|v| v.len()).sum::<usize>() == 1;
197
198 for references in usages.references.values_mut() {
199 references.retain(|reference| reference.name.as_name_ref() == Some(&name));
200 }
201
202 Some(InlineData {
203 let_stmt,
204 delete_let,
205 target: ast::NameOrNameRef::NameRef(name),
206 replace_usages: usages.references,
207 })
208}
209
143#[cfg(test)] 210#[cfg(test)]
144mod tests { 211mod tests {
145 use crate::tests::{check_assist, check_assist_not_applicable}; 212 use crate::tests::{check_assist, check_assist_not_applicable};
@@ -726,4 +793,84 @@ fn main() {
726", 793",
727 ) 794 )
728 } 795 }
796
797 #[test]
798 fn works_on_local_usage() {
799 check_assist(
800 inline_local_variable,
801 r#"
802fn f() {
803 let xyz = 0;
804 xyz$0;
805}
806"#,
807 r#"
808fn f() {
809 0;
810}
811"#,
812 );
813 }
814
815 #[test]
816 fn does_not_remove_let_when_multiple_usages() {
817 check_assist(
818 inline_local_variable,
819 r#"
820fn f() {
821 let xyz = 0;
822 xyz$0;
823 xyz;
824}
825"#,
826 r#"
827fn f() {
828 let xyz = 0;
829 0;
830 xyz;
831}
832"#,
833 );
834 }
835
836 #[test]
837 fn not_applicable_with_non_ident_pattern() {
838 check_assist_not_applicable(
839 inline_local_variable,
840 r#"
841fn main() {
842 let (x, y) = (0, 1);
843 x$0;
844}
845"#,
846 );
847 }
848
849 #[test]
850 fn not_applicable_on_local_usage_in_macro() {
851 check_assist_not_applicable(
852 inline_local_variable,
853 r#"
854macro_rules! m {
855 ($i:ident) => { $i }
856}
857fn f() {
858 let xyz = 0;
859 m!(xyz$0); // replacing it would break the macro
860}
861"#,
862 );
863 check_assist_not_applicable(
864 inline_local_variable,
865 r#"
866macro_rules! m {
867 ($i:ident) => { $i }
868}
869fn f() {
870 let xyz$0 = 0;
871 m!(xyz); // replacing it would break the macro
872}
873"#,
874 );
875 }
729} 876}
diff --git a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
index 02782eb6d..9f4f71d6c 100644
--- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
@@ -1,7 +1,8 @@
1use rustc_hash::FxHashSet; 1use rustc_hash::FxHashSet;
2use syntax::{ 2use syntax::{
3 ast::{self, GenericParamsOwner, NameOwner}, 3 ast::{self, edit_in_place::GenericParamsOwnerEdit, make, GenericParamsOwner},
4 AstNode, TextRange, TextSize, 4 ted::{self, Position},
5 AstNode, TextRange,
5}; 6};
6 7
7use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists}; 8use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists};
@@ -37,10 +38,12 @@ static ASSIST_LABEL: &str = "Introduce named lifetime";
37pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 38pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
38 let lifetime = 39 let lifetime =
39 ctx.find_node_at_offset::<ast::Lifetime>().filter(|lifetime| lifetime.text() == "'_")?; 40 ctx.find_node_at_offset::<ast::Lifetime>().filter(|lifetime| lifetime.text() == "'_")?;
41 let lifetime_loc = lifetime.lifetime_ident_token()?.text_range();
42
40 if let Some(fn_def) = lifetime.syntax().ancestors().find_map(ast::Fn::cast) { 43 if let Some(fn_def) = lifetime.syntax().ancestors().find_map(ast::Fn::cast) {
41 generate_fn_def_assist(acc, &fn_def, lifetime.lifetime_ident_token()?.text_range()) 44 generate_fn_def_assist(acc, fn_def, lifetime_loc, lifetime)
42 } else if let Some(impl_def) = lifetime.syntax().ancestors().find_map(ast::Impl::cast) { 45 } else if let Some(impl_def) = lifetime.syntax().ancestors().find_map(ast::Impl::cast) {
43 generate_impl_def_assist(acc, &impl_def, lifetime.lifetime_ident_token()?.text_range()) 46 generate_impl_def_assist(acc, impl_def, lifetime_loc, lifetime)
44 } else { 47 } else {
45 None 48 None
46 } 49 }
@@ -49,26 +52,26 @@ pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -
49/// Generate the assist for the fn def case 52/// Generate the assist for the fn def case
50fn generate_fn_def_assist( 53fn generate_fn_def_assist(
51 acc: &mut Assists, 54 acc: &mut Assists,
52 fn_def: &ast::Fn, 55 fn_def: ast::Fn,
53 lifetime_loc: TextRange, 56 lifetime_loc: TextRange,
57 lifetime: ast::Lifetime,
54) -> Option<()> { 58) -> Option<()> {
55 let param_list: ast::ParamList = fn_def.param_list()?; 59 let param_list: ast::ParamList = fn_def.param_list()?;
56 let new_lifetime_param = generate_unique_lifetime_param_name(&fn_def.generic_param_list())?; 60 let new_lifetime_param = generate_unique_lifetime_param_name(fn_def.generic_param_list())?;
57 let end_of_fn_ident = fn_def.name()?.ident_token()?.text_range().end();
58 let self_param = 61 let self_param =
59 // use the self if it's a reference and has no explicit lifetime 62 // use the self if it's a reference and has no explicit lifetime
60 param_list.self_param().filter(|p| p.lifetime().is_none() && p.amp_token().is_some()); 63 param_list.self_param().filter(|p| p.lifetime().is_none() && p.amp_token().is_some());
61 // compute the location which implicitly has the same lifetime as the anonymous lifetime 64 // compute the location which implicitly has the same lifetime as the anonymous lifetime
62 let loc_needing_lifetime = if let Some(self_param) = self_param { 65 let loc_needing_lifetime = if let Some(self_param) = self_param {
63 // if we have a self reference, use that 66 // if we have a self reference, use that
64 Some(self_param.name()?.syntax().text_range().start()) 67 Some(NeedsLifetime::SelfParam(self_param))
65 } else { 68 } else {
66 // otherwise, if there's a single reference parameter without a named liftime, use that 69 // otherwise, if there's a single reference parameter without a named liftime, use that
67 let fn_params_without_lifetime: Vec<_> = param_list 70 let fn_params_without_lifetime: Vec<_> = param_list
68 .params() 71 .params()
69 .filter_map(|param| match param.ty() { 72 .filter_map(|param| match param.ty() {
70 Some(ast::Type::RefType(ascribed_type)) if ascribed_type.lifetime().is_none() => { 73 Some(ast::Type::RefType(ascribed_type)) if ascribed_type.lifetime().is_none() => {
71 Some(ascribed_type.amp_token()?.text_range().end()) 74 Some(NeedsLifetime::RefType(ascribed_type))
72 } 75 }
73 _ => None, 76 _ => None,
74 }) 77 })
@@ -81,30 +84,46 @@ fn generate_fn_def_assist(
81 } 84 }
82 }; 85 };
83 acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { 86 acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| {
84 add_lifetime_param(fn_def, builder, end_of_fn_ident, new_lifetime_param); 87 let fn_def = builder.make_ast_mut(fn_def);
85 builder.replace(lifetime_loc, format!("'{}", new_lifetime_param)); 88 let lifetime = builder.make_ast_mut(lifetime);
86 loc_needing_lifetime.map(|loc| builder.insert(loc, format!("'{} ", new_lifetime_param))); 89 let loc_needing_lifetime =
90 loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position());
91
92 add_lifetime_param(fn_def.get_or_create_generic_param_list(), new_lifetime_param);
93 ted::replace(
94 lifetime.syntax(),
95 make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(),
96 );
97 loc_needing_lifetime.map(|position| {
98 ted::insert(position, make_ast_lifetime(new_lifetime_param).clone_for_update().syntax())
99 });
87 }) 100 })
88} 101}
89 102
90/// Generate the assist for the impl def case 103/// Generate the assist for the impl def case
91fn generate_impl_def_assist( 104fn generate_impl_def_assist(
92 acc: &mut Assists, 105 acc: &mut Assists,
93 impl_def: &ast::Impl, 106 impl_def: ast::Impl,
94 lifetime_loc: TextRange, 107 lifetime_loc: TextRange,
108 lifetime: ast::Lifetime,
95) -> Option<()> { 109) -> Option<()> {
96 let new_lifetime_param = generate_unique_lifetime_param_name(&impl_def.generic_param_list())?; 110 let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?;
97 let end_of_impl_kw = impl_def.impl_token()?.text_range().end();
98 acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { 111 acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| {
99 add_lifetime_param(impl_def, builder, end_of_impl_kw, new_lifetime_param); 112 let impl_def = builder.make_ast_mut(impl_def);
100 builder.replace(lifetime_loc, format!("'{}", new_lifetime_param)); 113 let lifetime = builder.make_ast_mut(lifetime);
114
115 add_lifetime_param(impl_def.get_or_create_generic_param_list(), new_lifetime_param);
116 ted::replace(
117 lifetime.syntax(),
118 make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(),
119 );
101 }) 120 })
102} 121}
103 122
104/// Given a type parameter list, generate a unique lifetime parameter name 123/// Given a type parameter list, generate a unique lifetime parameter name
105/// which is not in the list 124/// which is not in the list
106fn generate_unique_lifetime_param_name( 125fn generate_unique_lifetime_param_name(
107 existing_type_param_list: &Option<ast::GenericParamList>, 126 existing_type_param_list: Option<ast::GenericParamList>,
108) -> Option<char> { 127) -> Option<char> {
109 match existing_type_param_list { 128 match existing_type_param_list {
110 Some(type_params) => { 129 Some(type_params) => {
@@ -118,25 +137,37 @@ fn generate_unique_lifetime_param_name(
118 } 137 }
119} 138}
120 139
121/// Add the lifetime param to `builder`. If there are type parameters in `type_params_owner`, add it to the end. Otherwise 140fn add_lifetime_param(type_params: ast::GenericParamList, new_lifetime_param: char) {
122/// add new type params brackets with the lifetime parameter at `new_type_params_loc`. 141 let generic_param =
123fn add_lifetime_param<TypeParamsOwner: ast::GenericParamsOwner>( 142 make::generic_param(format!("'{}", new_lifetime_param), None).clone_for_update();
124 type_params_owner: &TypeParamsOwner, 143 type_params.add_generic_param(generic_param);
125 builder: &mut AssistBuilder, 144}
126 new_type_params_loc: TextSize, 145
127 new_lifetime_param: char, 146fn make_ast_lifetime(new_lifetime_param: char) -> ast::Lifetime {
128) { 147 make::generic_param(format!("'{}", new_lifetime_param), None)
129 match type_params_owner.generic_param_list() { 148 .syntax()
130 // add the new lifetime parameter to an existing type param list 149 .descendants()
131 Some(type_params) => { 150 .find_map(ast::Lifetime::cast)
132 builder.insert( 151 .unwrap()
133 (u32::from(type_params.syntax().text_range().end()) - 1).into(), 152}
134 format!(", '{}", new_lifetime_param), 153
135 ); 154enum NeedsLifetime {
155 SelfParam(ast::SelfParam),
156 RefType(ast::RefType),
157}
158
159impl NeedsLifetime {
160 fn make_mut(self, builder: &mut AssistBuilder) -> Self {
161 match self {
162 Self::SelfParam(it) => Self::SelfParam(builder.make_ast_mut(it)),
163 Self::RefType(it) => Self::RefType(builder.make_ast_mut(it)),
136 } 164 }
137 // create a new type param list containing only the new lifetime parameter 165 }
138 None => { 166
139 builder.insert(new_type_params_loc, format!("<'{}>", new_lifetime_param)); 167 fn to_position(self) -> Option<Position> {
168 match self {
169 Self::SelfParam(it) => Some(Position::after(it.amp_token()?)),
170 Self::RefType(it) => Some(Position::after(it.amp_token()?)),
140 } 171 }
141 } 172 }
142} 173}
@@ -312,4 +343,13 @@ mod tests {
312 r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#, 343 r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#,
313 ); 344 );
314 } 345 }
346
347 #[test]
348 fn test_function_add_lifetime_to_self_ref_mut() {
349 check_assist(
350 introduce_named_lifetime,
351 r#"fn foo(&mut self) -> &'_$0 ()"#,
352 r#"fn foo<'a>(&'a mut self) -> &'a ()"#,
353 );
354 }
315} 355}
diff --git a/crates/ide_assists/src/handlers/remove_dbg.rs b/crates/ide_assists/src/handlers/remove_dbg.rs
index 6114091f2..c8226550f 100644
--- a/crates/ide_assists/src/handlers/remove_dbg.rs
+++ b/crates/ide_assists/src/handlers/remove_dbg.rs
@@ -1,5 +1,5 @@
1use syntax::{ 1use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode, AstToken},
3 match_ast, SyntaxElement, TextRange, TextSize, T, 3 match_ast, SyntaxElement, TextRange, TextSize, T,
4}; 4};
5 5
@@ -24,7 +24,39 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
24 let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?; 24 let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
25 let new_contents = adjusted_macro_contents(&macro_call)?; 25 let new_contents = adjusted_macro_contents(&macro_call)?;
26 26
27 let macro_text_range = macro_call.syntax().text_range(); 27 let parent = macro_call.syntax().parent();
28
29 let macro_text_range = if let Some(it) = parent.as_ref() {
30 if new_contents.is_empty() {
31 match_ast! {
32 match it {
33 ast::BlockExpr(_it) => {
34 macro_call.syntax()
35 .prev_sibling_or_token()
36 .and_then(whitespace_start)
37 .map(|start| TextRange::new(start, macro_call.syntax().text_range().end()))
38 .unwrap_or(macro_call.syntax().text_range())
39 },
40 ast::ExprStmt(it) => {
41 let start = it
42 .syntax()
43 .prev_sibling_or_token()
44 .and_then(whitespace_start)
45 .unwrap_or(it.syntax().text_range().start());
46 let end = it.syntax().text_range().end();
47
48 TextRange::new(start, end)
49 },
50 _ => macro_call.syntax().text_range()
51 }
52 }
53 } else {
54 macro_call.syntax().text_range()
55 }
56 } else {
57 macro_call.syntax().text_range()
58 };
59
28 let macro_end = if macro_call.semicolon_token().is_some() { 60 let macro_end = if macro_call.semicolon_token().is_some() {
29 macro_text_range.end() - TextSize::of(';') 61 macro_text_range.end() - TextSize::of(';')
30 } else { 62 } else {
@@ -36,11 +68,22 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
36 "Remove dbg!()", 68 "Remove dbg!()",
37 macro_text_range, 69 macro_text_range,
38 |builder| { 70 |builder| {
39 builder.replace(TextRange::new(macro_text_range.start(), macro_end), new_contents); 71 builder.replace(
72 TextRange::new(macro_text_range.start(), macro_end),
73 if new_contents.is_empty() && parent.and_then(ast::LetStmt::cast).is_some() {
74 ast::make::expr_unit().to_string()
75 } else {
76 new_contents
77 },
78 );
40 }, 79 },
41 ) 80 )
42} 81}
43 82
83fn whitespace_start(it: SyntaxElement) -> Option<TextSize> {
84 Some(it.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start())
85}
86
44fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> { 87fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
45 let contents = get_valid_macrocall_contents(&macro_call, "dbg")?; 88 let contents = get_valid_macrocall_contents(&macro_call, "dbg")?;
46 let macro_text_with_brackets = macro_call.token_tree()?.syntax().text(); 89 let macro_text_with_brackets = macro_call.token_tree()?.syntax().text();
@@ -94,15 +137,11 @@ fn get_valid_macrocall_contents(
94 let mut contents_between_brackets = children_with_tokens.collect::<Vec<_>>(); 137 let mut contents_between_brackets = children_with_tokens.collect::<Vec<_>>();
95 let last_child = contents_between_brackets.pop()?; 138 let last_child = contents_between_brackets.pop()?;
96 139
97 if contents_between_brackets.is_empty() { 140 match (first_child.kind(), last_child.kind()) {
98 None 141 (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => {
99 } else { 142 Some(contents_between_brackets)
100 match (first_child.kind(), last_child.kind()) {
101 (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => {
102 Some(contents_between_brackets)
103 }
104 _ => None,
105 } 143 }
144 _ => None,
106 } 145 }
107} 146}
108 147
@@ -418,4 +457,48 @@ fn main() {
418}"#, 457}"#,
419 ); 458 );
420 } 459 }
460
461 #[test]
462 fn test_remove_empty_dbg() {
463 check_assist(remove_dbg, r#"fn foo() { $0dbg!(); }"#, r#"fn foo() { }"#);
464 check_assist(
465 remove_dbg,
466 r#"
467fn foo() {
468 $0dbg!();
469}
470"#,
471 r#"
472fn foo() {
473}
474"#,
475 );
476 check_assist(
477 remove_dbg,
478 r#"
479fn foo() {
480 let test = $0dbg!();
481}"#,
482 r#"
483fn foo() {
484 let test = ();
485}"#,
486 );
487 check_assist(
488 remove_dbg,
489 r#"
490fn foo() {
491 let t = {
492 println!("Hello, world");
493 $0dbg!()
494 };
495}"#,
496 r#"
497fn foo() {
498 let t = {
499 println!("Hello, world");
500 };
501}"#,
502 );
503 }
421} 504}
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs
index 383ca6c47..1a95135ca 100644
--- a/crates/ide_assists/src/handlers/reorder_fields.rs
+++ b/crates/ide_assists/src/handlers/reorder_fields.rs
@@ -1,6 +1,8 @@
1use either::Either;
2use itertools::Itertools;
1use rustc_hash::FxHashMap; 3use rustc_hash::FxHashMap;
2 4
3use syntax::{algo, ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode}; 5use syntax::{ast, ted, AstNode};
4 6
5use crate::{AssistContext, AssistId, AssistKind, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
6 8
@@ -22,60 +24,70 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
22pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 24pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
23 let record = ctx 25 let record = ctx
24 .find_node_at_offset::<ast::RecordExpr>() 26 .find_node_at_offset::<ast::RecordExpr>()
25 .map(|it| it.syntax().clone()) 27 .map(Either::Left)
26 .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(|it| it.syntax().clone()))?; 28 .or_else(|| ctx.find_node_at_offset::<ast::RecordPat>().map(Either::Right))?;
27
28 let path = record.children().find_map(ast::Path::cast)?;
29 29
30 let path = record.as_ref().either(|it| it.path(), |it| it.path())?;
30 let ranks = compute_fields_ranks(&path, &ctx)?; 31 let ranks = compute_fields_ranks(&path, &ctx)?;
32 let get_rank_of_field =
33 |of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX);
31 34
32 let fields: Vec<SyntaxNode> = { 35 let field_list = match &record {
33 let field_kind = match record.kind() { 36 Either::Left(it) => Either::Left(it.record_expr_field_list()?),
34 RECORD_EXPR => RECORD_EXPR_FIELD, 37 Either::Right(it) => Either::Right(it.record_pat_field_list()?),
35 RECORD_PAT => RECORD_PAT_FIELD,
36 _ => {
37 stdx::never!();
38 return None;
39 }
40 };
41 record.children().flat_map(|n| n.children()).filter(|n| n.kind() == field_kind).collect()
42 }; 38 };
43 39 let fields = match field_list {
44 let sorted_fields = { 40 Either::Left(it) => Either::Left((
45 let mut fields = fields.clone(); 41 it.fields()
46 fields.sort_by_key(|node| *ranks.get(&get_field_name(node)).unwrap_or(&usize::max_value())); 42 .sorted_unstable_by_key(|field| {
47 fields 43 get_rank_of_field(field.field_name().map(|it| it.to_string()))
44 })
45 .collect::<Vec<_>>(),
46 it,
47 )),
48 Either::Right(it) => Either::Right((
49 it.fields()
50 .sorted_unstable_by_key(|field| {
51 get_rank_of_field(field.field_name().map(|it| it.to_string()))
52 })
53 .collect::<Vec<_>>(),
54 it,
55 )),
48 }; 56 };
49 57
50 if sorted_fields == fields { 58 let is_sorted = fields.as_ref().either(
59 |(sorted, field_list)| field_list.fields().zip(sorted).all(|(a, b)| a == *b),
60 |(sorted, field_list)| field_list.fields().zip(sorted).all(|(a, b)| a == *b),
61 );
62 if is_sorted {
51 cov_mark::hit!(reorder_sorted_fields); 63 cov_mark::hit!(reorder_sorted_fields);
52 return None; 64 return None;
53 } 65 }
54 66 let target = record.as_ref().either(AstNode::syntax, AstNode::syntax).text_range();
55 let target = record.text_range();
56 acc.add( 67 acc.add(
57 AssistId("reorder_fields", AssistKind::RefactorRewrite), 68 AssistId("reorder_fields", AssistKind::RefactorRewrite),
58 "Reorder record fields", 69 "Reorder record fields",
59 target, 70 target,
60 |edit| { 71 |builder| match fields {
61 let mut rewriter = algo::SyntaxRewriter::default(); 72 Either::Left((sorted, field_list)) => {
62 for (old, new) in fields.iter().zip(&sorted_fields) { 73 replace(builder.make_ast_mut(field_list).fields(), sorted)
63 rewriter.replace(old, new); 74 }
75 Either::Right((sorted, field_list)) => {
76 replace(builder.make_ast_mut(field_list).fields(), sorted)
64 } 77 }
65 edit.rewrite(rewriter);
66 }, 78 },
67 ) 79 )
68} 80}
69 81
70fn get_field_name(node: &SyntaxNode) -> String { 82fn replace<T: AstNode + PartialEq>(
71 let res = match_ast! { 83 fields: impl Iterator<Item = T>,
72 match node { 84 sorted_fields: impl IntoIterator<Item = T>,
73 ast::RecordExprField(field) => field.field_name().map(|it| it.to_string()), 85) {
74 ast::RecordPatField(field) => field.field_name().map(|it| it.to_string()), 86 fields.zip(sorted_fields).filter(|(field, sorted)| field != sorted).for_each(
75 _ => None, 87 |(field, sorted_field)| {
76 } 88 ted::replace(field.syntax(), sorted_field.syntax().clone_for_update());
77 }; 89 },
78 res.unwrap_or_default() 90 );
79} 91}
80 92
81fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> { 93fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
@@ -86,7 +98,7 @@ fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashM
86 98
87 let res = strukt 99 let res = strukt
88 .fields(ctx.db()) 100 .fields(ctx.db())
89 .iter() 101 .into_iter()
90 .enumerate() 102 .enumerate()
91 .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx)) 103 .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx))
92 .collect(); 104 .collect();
@@ -137,7 +149,6 @@ const test: Foo = Foo { foo: 1, bar: 0 };
137"#, 149"#,
138 ) 150 )
139 } 151 }
140
141 #[test] 152 #[test]
142 fn reorder_struct_pattern() { 153 fn reorder_struct_pattern() {
143 check_assist( 154 check_assist(
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index f872d20c8..694d897d1 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -5,7 +5,6 @@ use itertools::Itertools;
5use syntax::{ 5use syntax::{
6 ast::{self, make, AstNode, NameOwner}, 6 ast::{self, make, AstNode, NameOwner},
7 SyntaxKind::{IDENT, WHITESPACE}, 7 SyntaxKind::{IDENT, WHITESPACE},
8 TextSize,
9}; 8};
10 9
11use crate::{ 10use crate::{
@@ -43,32 +42,28 @@ pub(crate) fn replace_derive_with_manual_impl(
43 ctx: &AssistContext, 42 ctx: &AssistContext,
44) -> Option<()> { 43) -> Option<()> {
45 let attr = ctx.find_node_at_offset::<ast::Attr>()?; 44 let attr = ctx.find_node_at_offset::<ast::Attr>()?;
45 let (name, args) = attr.as_simple_call()?;
46 if name != "derive" {
47 return None;
48 }
46 49
47 let has_derive = attr 50 if !args.syntax().text_range().contains(ctx.offset()) {
48 .syntax() 51 cov_mark::hit!(outside_of_attr_args);
49 .descendants_with_tokens()
50 .filter(|t| t.kind() == IDENT)
51 .find_map(syntax::NodeOrToken::into_token)
52 .filter(|t| t.text() == "derive")
53 .is_some();
54 if !has_derive {
55 return None; 52 return None;
56 } 53 }
57 54
58 let trait_token = ctx.token_at_offset().find(|t| t.kind() == IDENT && t.text() != "derive")?; 55 let trait_token = args.syntax().token_at_offset(ctx.offset()).find(|t| t.kind() == IDENT)?;
59 let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); 56 let trait_name = trait_token.text();
60 57
61 let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; 58 let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
62 let annotated_name = adt.name()?;
63 let insert_pos = adt.syntax().text_range().end();
64 59
65 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; 60 let current_module = ctx.sema.scope(adt.syntax()).module()?;
66 let current_crate = current_module.krate(); 61 let current_crate = current_module.krate();
67 62
68 let found_traits = items_locator::items_with_name( 63 let found_traits = items_locator::items_with_name(
69 &ctx.sema, 64 &ctx.sema,
70 current_crate, 65 current_crate,
71 NameToImport::Exact(trait_token.text().to_string()), 66 NameToImport::Exact(trait_name.to_string()),
72 items_locator::AssocItemSearch::Exclude, 67 items_locator::AssocItemSearch::Exclude,
73 Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT), 68 Some(items_locator::DEFAULT_QUERY_SEARCH_LIMIT),
74 ) 69 )
@@ -86,10 +81,11 @@ pub(crate) fn replace_derive_with_manual_impl(
86 81
87 let mut no_traits_found = true; 82 let mut no_traits_found = true;
88 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { 83 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
89 add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &adt, &annotated_name, insert_pos)?; 84 add_assist(acc, ctx, &attr, &args, &trait_path, Some(trait_), &adt)?;
90 } 85 }
91 if no_traits_found { 86 if no_traits_found {
92 add_assist(acc, ctx, &attr, &trait_path, None, &adt, &annotated_name, insert_pos)?; 87 let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_name)));
88 add_assist(acc, ctx, &attr, &args, &trait_path, None, &adt)?;
93 } 89 }
94 Some(()) 90 Some(())
95} 91}
@@ -98,15 +94,14 @@ fn add_assist(
98 acc: &mut Assists, 94 acc: &mut Assists,
99 ctx: &AssistContext, 95 ctx: &AssistContext,
100 attr: &ast::Attr, 96 attr: &ast::Attr,
97 input: &ast::TokenTree,
101 trait_path: &ast::Path, 98 trait_path: &ast::Path,
102 trait_: Option<hir::Trait>, 99 trait_: Option<hir::Trait>,
103 adt: &ast::Adt, 100 adt: &ast::Adt,
104 annotated_name: &ast::Name,
105 insert_pos: TextSize,
106) -> Option<()> { 101) -> Option<()> {
107 let target = attr.syntax().text_range(); 102 let target = attr.syntax().text_range();
108 let input = attr.token_tree()?; 103 let annotated_name = adt.name()?;
109 let label = format!("Convert to manual `impl {} for {}`", trait_path, annotated_name); 104 let label = format!("Convert to manual `impl {} for {}`", trait_path, annotated_name);
110 let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?; 105 let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?;
111 106
112 acc.add( 107 acc.add(
@@ -114,8 +109,9 @@ fn add_assist(
114 label, 109 label,
115 target, 110 target,
116 |builder| { 111 |builder| {
112 let insert_pos = adt.syntax().text_range().end();
117 let impl_def_with_items = 113 let impl_def_with_items =
118 impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path); 114 impl_def_from_trait(&ctx.sema, &annotated_name, trait_, trait_path);
119 update_attribute(builder, &input, &trait_name, &attr); 115 update_attribute(builder, &input, &trait_name, &attr);
120 let trait_path = format!("{}", trait_path); 116 let trait_path = format!("{}", trait_path);
121 match (ctx.config.snippet_cap, impl_def_with_items) { 117 match (ctx.config.snippet_cap, impl_def_with_items) {
@@ -216,7 +212,7 @@ mod tests {
216 fn add_custom_impl_debug() { 212 fn add_custom_impl_debug() {
217 check_assist( 213 check_assist(
218 replace_derive_with_manual_impl, 214 replace_derive_with_manual_impl,
219 " 215 r#"
220mod fmt { 216mod fmt {
221 pub struct Error; 217 pub struct Error;
222 pub type Result = Result<(), Error>; 218 pub type Result = Result<(), Error>;
@@ -230,8 +226,8 @@ mod fmt {
230struct Foo { 226struct Foo {
231 bar: String, 227 bar: String,
232} 228}
233", 229"#,
234 " 230 r#"
235mod fmt { 231mod fmt {
236 pub struct Error; 232 pub struct Error;
237 pub type Result = Result<(), Error>; 233 pub type Result = Result<(), Error>;
@@ -250,14 +246,14 @@ impl fmt::Debug for Foo {
250 ${0:todo!()} 246 ${0:todo!()}
251 } 247 }
252} 248}
253", 249"#,
254 ) 250 )
255 } 251 }
256 #[test] 252 #[test]
257 fn add_custom_impl_all() { 253 fn add_custom_impl_all() {
258 check_assist( 254 check_assist(
259 replace_derive_with_manual_impl, 255 replace_derive_with_manual_impl,
260 " 256 r#"
261mod foo { 257mod foo {
262 pub trait Bar { 258 pub trait Bar {
263 type Qux; 259 type Qux;
@@ -272,8 +268,8 @@ mod foo {
272struct Foo { 268struct Foo {
273 bar: String, 269 bar: String,
274} 270}
275", 271"#,
276 " 272 r#"
277mod foo { 273mod foo {
278 pub trait Bar { 274 pub trait Bar {
279 type Qux; 275 type Qux;
@@ -299,20 +295,20 @@ impl foo::Bar for Foo {
299 todo!() 295 todo!()
300 } 296 }
301} 297}
302", 298"#,
303 ) 299 )
304 } 300 }
305 #[test] 301 #[test]
306 fn add_custom_impl_for_unique_input() { 302 fn add_custom_impl_for_unique_input() {
307 check_assist( 303 check_assist(
308 replace_derive_with_manual_impl, 304 replace_derive_with_manual_impl,
309 " 305 r#"
310#[derive(Debu$0g)] 306#[derive(Debu$0g)]
311struct Foo { 307struct Foo {
312 bar: String, 308 bar: String,
313} 309}
314 ", 310 "#,
315 " 311 r#"
316struct Foo { 312struct Foo {
317 bar: String, 313 bar: String,
318} 314}
@@ -320,7 +316,7 @@ struct Foo {
320impl Debug for Foo { 316impl Debug for Foo {
321 $0 317 $0
322} 318}
323 ", 319 "#,
324 ) 320 )
325 } 321 }
326 322
@@ -328,13 +324,13 @@ impl Debug for Foo {
328 fn add_custom_impl_for_with_visibility_modifier() { 324 fn add_custom_impl_for_with_visibility_modifier() {
329 check_assist( 325 check_assist(
330 replace_derive_with_manual_impl, 326 replace_derive_with_manual_impl,
331 " 327 r#"
332#[derive(Debug$0)] 328#[derive(Debug$0)]
333pub struct Foo { 329pub struct Foo {
334 bar: String, 330 bar: String,
335} 331}
336 ", 332 "#,
337 " 333 r#"
338pub struct Foo { 334pub struct Foo {
339 bar: String, 335 bar: String,
340} 336}
@@ -342,7 +338,7 @@ pub struct Foo {
342impl Debug for Foo { 338impl Debug for Foo {
343 $0 339 $0
344} 340}
345 ", 341 "#,
346 ) 342 )
347 } 343 }
348 344
@@ -350,18 +346,18 @@ impl Debug for Foo {
350 fn add_custom_impl_when_multiple_inputs() { 346 fn add_custom_impl_when_multiple_inputs() {
351 check_assist( 347 check_assist(
352 replace_derive_with_manual_impl, 348 replace_derive_with_manual_impl,
353 " 349 r#"
354#[derive(Display, Debug$0, Serialize)] 350#[derive(Display, Debug$0, Serialize)]
355struct Foo {} 351struct Foo {}
356 ", 352 "#,
357 " 353 r#"
358#[derive(Display, Serialize)] 354#[derive(Display, Serialize)]
359struct Foo {} 355struct Foo {}
360 356
361impl Debug for Foo { 357impl Debug for Foo {
362 $0 358 $0
363} 359}
364 ", 360 "#,
365 ) 361 )
366 } 362 }
367 363
@@ -369,10 +365,10 @@ impl Debug for Foo {
369 fn test_ignore_derive_macro_without_input() { 365 fn test_ignore_derive_macro_without_input() {
370 check_assist_not_applicable( 366 check_assist_not_applicable(
371 replace_derive_with_manual_impl, 367 replace_derive_with_manual_impl,
372 " 368 r#"
373#[derive($0)] 369#[derive($0)]
374struct Foo {} 370struct Foo {}
375 ", 371 "#,
376 ) 372 )
377 } 373 }
378 374
@@ -380,18 +376,18 @@ struct Foo {}
380 fn test_ignore_if_cursor_on_param() { 376 fn test_ignore_if_cursor_on_param() {
381 check_assist_not_applicable( 377 check_assist_not_applicable(
382 replace_derive_with_manual_impl, 378 replace_derive_with_manual_impl,
383 " 379 r#"
384#[derive$0(Debug)] 380#[derive$0(Debug)]
385struct Foo {} 381struct Foo {}
386 ", 382 "#,
387 ); 383 );
388 384
389 check_assist_not_applicable( 385 check_assist_not_applicable(
390 replace_derive_with_manual_impl, 386 replace_derive_with_manual_impl,
391 " 387 r#"
392#[derive(Debug)$0] 388#[derive(Debug)$0]
393struct Foo {} 389struct Foo {}
394 ", 390 "#,
395 ) 391 )
396 } 392 }
397 393
@@ -399,10 +395,22 @@ struct Foo {}
399 fn test_ignore_if_not_derive() { 395 fn test_ignore_if_not_derive() {
400 check_assist_not_applicable( 396 check_assist_not_applicable(
401 replace_derive_with_manual_impl, 397 replace_derive_with_manual_impl,
402 " 398 r#"
403#[allow(non_camel_$0case_types)] 399#[allow(non_camel_$0case_types)]
404struct Foo {} 400struct Foo {}
405 ", 401 "#,
406 ) 402 )
407 } 403 }
404
405 #[test]
406 fn works_at_start_of_file() {
407 cov_mark::check!(outside_of_attr_args);
408 check_assist_not_applicable(
409 replace_derive_with_manual_impl,
410 r#"
411$0#[derive(Debug)]
412struct S;
413 "#,
414 );
415 }
408} 416}
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 1c55b9fbf..88ae5c9a9 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -28,7 +28,9 @@ pub use assist_config::AssistConfig;
28 28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)] 29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum AssistKind { 30pub enum AssistKind {
31 // FIXME: does the None variant make sense? Probably not.
31 None, 32 None,
33
32 QuickFix, 34 QuickFix,
33 Generate, 35 Generate,
34 Refactor, 36 Refactor,
@@ -133,6 +135,7 @@ mod handlers {
133 mod generate_default_from_enum_variant; 135 mod generate_default_from_enum_variant;
134 mod generate_default_from_new; 136 mod generate_default_from_new;
135 mod generate_is_empty_from_len; 137 mod generate_is_empty_from_len;
138 mod generate_deref;
136 mod generate_derive; 139 mod generate_derive;
137 mod generate_enum_is_method; 140 mod generate_enum_is_method;
138 mod generate_enum_projection_method; 141 mod generate_enum_projection_method;
@@ -201,6 +204,7 @@ mod handlers {
201 generate_default_from_enum_variant::generate_default_from_enum_variant, 204 generate_default_from_enum_variant::generate_default_from_enum_variant,
202 generate_default_from_new::generate_default_from_new, 205 generate_default_from_new::generate_default_from_new,
203 generate_is_empty_from_len::generate_is_empty_from_len, 206 generate_is_empty_from_len::generate_is_empty_from_len,
207 generate_deref::generate_deref,
204 generate_derive::generate_derive, 208 generate_derive::generate_derive,
205 generate_enum_is_method::generate_enum_is_method, 209 generate_enum_is_method::generate_enum_is_method,
206 generate_enum_projection_method::generate_enum_as_method, 210 generate_enum_projection_method::generate_enum_as_method,
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index a7a923beb..49533e7d2 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -84,7 +84,8 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
84 }); 84 });
85 85
86 let actual = { 86 let actual = {
87 let source_change = assist.source_change.unwrap(); 87 let source_change =
88 assist.source_change.expect("Assist did not contain any source changes");
88 let mut actual = before; 89 let mut actual = before;
89 if let Some(source_file_edit) = source_change.get_source_edit(file_id) { 90 if let Some(source_file_edit) = source_change.get_source_edit(file_id) {
90 source_file_edit.apply(&mut actual); 91 source_file_edit.apply(&mut actual);
@@ -121,7 +122,8 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
121 122
122 match (assist, expected) { 123 match (assist, expected) {
123 (Some(assist), ExpectedResult::After(after)) => { 124 (Some(assist), ExpectedResult::After(after)) => {
124 let source_change = assist.source_change.unwrap(); 125 let source_change =
126 assist.source_change.expect("Assist did not contain any source changes");
125 assert!(!source_change.source_file_edits.is_empty()); 127 assert!(!source_change.source_file_edits.is_empty());
126 let skip_header = source_change.source_file_edits.len() == 1 128 let skip_header = source_change.source_file_edits.len() == 1
127 && source_change.file_system_edits.len() == 0; 129 && source_change.file_system_edits.len() == 0;
@@ -191,6 +193,7 @@ fn assist_order_field_struct() {
191 let mut assists = assists.iter(); 193 let mut assists = assists.iter();
192 194
193 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); 195 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
196 assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`");
194 assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); 197 assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
195 assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); 198 assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
196 assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); 199 assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index f4a4749c8..59bcef8fb 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -593,6 +593,33 @@ impl Default for Example {
593} 593}
594 594
595#[test] 595#[test]
596fn doctest_generate_deref() {
597 check_doc_test(
598 "generate_deref",
599 r#####"
600struct A;
601struct B {
602 $0a: A
603}
604"#####,
605 r#####"
606struct A;
607struct B {
608 a: A
609}
610
611impl std::ops::Deref for B {
612 type Target = A;
613
614 fn deref(&self) -> &Self::Target {
615 &self.a
616 }
617}
618"#####,
619 )
620}
621
622#[test]
596fn doctest_generate_derive() { 623fn doctest_generate_derive() {
597 check_doc_test( 624 check_doc_test(
598 "generate_derive", 625 "generate_derive",
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index 1ad017198..8e211ae1e 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -113,6 +113,9 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
113 if ctx.use_item_syntax.is_some() 113 if ctx.use_item_syntax.is_some()
114 || ctx.attribute_under_caret.is_some() 114 || ctx.attribute_under_caret.is_some()
115 || ctx.mod_declaration_under_caret.is_some() 115 || ctx.mod_declaration_under_caret.is_some()
116 || ctx.record_lit_syntax.is_some()
117 || ctx.has_trait_parent
118 || ctx.has_impl_parent
116 { 119 {
117 return None; 120 return None;
118 } 121 }
@@ -1034,4 +1037,117 @@ fn main() {
1034 expect![[]], 1037 expect![[]],
1035 ); 1038 );
1036 } 1039 }
1040
1041 #[test]
1042 fn no_fuzzy_during_fields_of_record_lit_syntax() {
1043 check(
1044 r#"
1045mod m {
1046 pub fn some_fn() -> i32 {
1047 42
1048 }
1049}
1050struct Foo {
1051 some_field: i32,
1052}
1053fn main() {
1054 let _ = Foo { so$0 };
1055}
1056"#,
1057 expect![[]],
1058 );
1059 }
1060
1061 #[test]
1062 fn fuzzy_after_fields_of_record_lit_syntax() {
1063 check(
1064 r#"
1065mod m {
1066 pub fn some_fn() -> i32 {
1067 42
1068 }
1069}
1070struct Foo {
1071 some_field: i32,
1072}
1073fn main() {
1074 let _ = Foo { some_field: so$0 };
1075}
1076"#,
1077 expect![[r#"
1078 fn some_fn() (m::some_fn) fn() -> i32
1079 "#]],
1080 );
1081 }
1082
1083 #[test]
1084 fn no_flyimports_in_traits_and_impl_declarations() {
1085 check(
1086 r#"
1087mod m {
1088 pub fn some_fn() -> i32 {
1089 42
1090 }
1091}
1092trait Foo {
1093 som$0
1094}
1095"#,
1096 expect![[r#""#]],
1097 );
1098
1099 check(
1100 r#"
1101mod m {
1102 pub fn some_fn() -> i32 {
1103 42
1104 }
1105}
1106struct Foo;
1107impl Foo {
1108 som$0
1109}
1110"#,
1111 expect![[r#""#]],
1112 );
1113
1114 check(
1115 r#"
1116mod m {
1117 pub fn some_fn() -> i32 {
1118 42
1119 }
1120}
1121struct Foo;
1122trait Bar {}
1123impl Bar for Foo {
1124 som$0
1125}
1126"#,
1127 expect![[r#""#]],
1128 );
1129 }
1130
1131 #[test]
1132 fn no_inherent_candidates_proposed() {
1133 check(
1134 r#"
1135mod baz {
1136 pub trait DefDatabase {
1137 fn method1(&self);
1138 }
1139 pub trait HirDatabase: DefDatabase {
1140 fn method2(&self);
1141 }
1142}
1143
1144mod bar {
1145 fn test(db: &dyn crate::baz::HirDatabase) {
1146 db.metho$0
1147 }
1148}
1149 "#,
1150 expect![[r#""#]],
1151 );
1152 }
1037} 1153}
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index cc4ac9ea2..16991b688 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -29,7 +29,7 @@ pub struct CompletionItem {
29 /// Range of identifier that is being completed. 29 /// Range of identifier that is being completed.
30 /// 30 ///
31 /// It should be used primarily for UI, but we also use this to convert 31 /// It should be used primarily for UI, but we also use this to convert
32 /// genetic TextEdit into LSP's completion edit (see conv.rs). 32 /// generic TextEdit into LSP's completion edit (see conv.rs).
33 /// 33 ///
34 /// `source_range` must contain the completion offset. `insert_text` should 34 /// `source_range` must contain the completion offset. `insert_text` should
35 /// start with what `source_range` points to, or VSCode will filter out the 35 /// start with what `source_range` points to, or VSCode will filter out the
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 831d543bb..6f3d5c5c5 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -106,6 +106,34 @@ pub use crate::{
106/// `foo` *should* be present among the completion variants. Filtering by 106/// `foo` *should* be present among the completion variants. Filtering by
107/// identifier prefix/fuzzy match should be done higher in the stack, together 107/// identifier prefix/fuzzy match should be done higher in the stack, together
108/// with ordering of completions (currently this is done by the client). 108/// with ordering of completions (currently this is done by the client).
109///
110/// # Hypothetical Completion Problem
111///
112/// There's a curious unsolved problem in the current implementation. Often, you
113/// want to compute completions on a *slightly different* text document.
114///
115/// In the simplest case, when the code looks like `let x = `, you want to
116/// insert a fake identifier to get a better syntax tree: `let x = complete_me`.
117///
118/// We do this in `CompletionContext`, and it works OK-enough for *syntax*
119/// analysis. However, we might want to, eg, ask for the type of `complete_me`
120/// variable, and that's where our current infrastructure breaks down. salsa
121/// doesn't allow such "phantom" inputs.
122///
123/// Another case where this would be instrumental is macro expansion. We want to
124/// insert a fake ident and re-expand code. There's `expand_hypothetical` as a
125/// work-around for this.
126///
127/// A different use-case is completion of injection (examples and links in doc
128/// comments). When computing completion for a path in a doc-comment, you want
129/// to inject a fake path expression into the item being documented and complete
130/// that.
131///
132/// IntelliJ has CodeFragment/Context infrastructure for that. You can create a
133/// temporary PSI node, and say that the context ("parent") of this node is some
134/// existing node. Asking for, eg, type of this `CodeFragment` node works
135/// correctly, as the underlying infrastructure makes use of contexts to do
136/// analysis.
109pub fn completions( 137pub fn completions(
110 db: &RootDatabase, 138 db: &RootDatabase,
111 config: &CompletionConfig, 139 config: &CompletionConfig,
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index 111e9325a..eac5ef6b9 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -152,6 +152,10 @@ impl RootDatabase {
152 hir::db::FileItemTreeQuery 152 hir::db::FileItemTreeQuery
153 hir::db::BlockDefMapQuery 153 hir::db::BlockDefMapQuery
154 hir::db::CrateDefMapQueryQuery 154 hir::db::CrateDefMapQueryQuery
155 hir::db::FieldsAttrsQuery
156 hir::db::VariantsAttrsQuery
157 hir::db::FieldsAttrsSourceMapQuery
158 hir::db::VariantsAttrsSourceMapQuery
155 hir::db::StructDataQuery 159 hir::db::StructDataQuery
156 hir::db::UnionDataQuery 160 hir::db::UnionDataQuery
157 hir::db::EnumDataQuery 161 hir::db::EnumDataQuery
@@ -197,7 +201,7 @@ impl RootDatabase {
197 hir::db::InternImplTraitIdQuery 201 hir::db::InternImplTraitIdQuery
198 hir::db::InternClosureQuery 202 hir::db::InternClosureQuery
199 hir::db::AssociatedTyValueQuery 203 hir::db::AssociatedTyValueQuery
200 hir::db::TraitSolveQuery 204 hir::db::TraitSolveQueryQuery
201 205
202 // SymbolsDatabase 206 // SymbolsDatabase
203 crate::symbol_index::FileSymbolsQuery 207 crate::symbol_index::FileSymbolsQuery
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index e583a52f4..bad277a95 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -4,8 +4,9 @@ use either::Either;
4use hir::{HasAttrs, HirDisplay, Semantics, Type}; 4use hir::{HasAttrs, HirDisplay, Semantics, Type};
5use stdx::format_to; 5use stdx::format_to;
6use syntax::{ 6use syntax::{
7 algo,
7 ast::{self, ArgListOwner, NameOwner}, 8 ast::{self, ArgListOwner, NameOwner},
8 match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, 9 match_ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextSize,
9}; 10};
10 11
11use crate::RootDatabase; 12use crate::RootDatabase;
@@ -43,7 +44,12 @@ pub fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo>
43 let sema = Semantics::new(db); 44 let sema = Semantics::new(db);
44 let file = sema.parse(position.file_id); 45 let file = sema.parse(position.file_id);
45 let file = file.syntax(); 46 let file = file.syntax();
46 let token = file.token_at_offset(position.offset).next()?; 47 let token = file
48 .token_at_offset(position.offset)
49 .left_biased()
50 // if the cursor is sandwiched between two space tokens and the call is unclosed
51 // this prevents us from leaving the CallExpression
52 .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
47 let token = sema.descend_into_macros(token); 53 let token = sema.descend_into_macros(token);
48 54
49 let (callable, active_parameter) = call_info_impl(&sema, token)?; 55 let (callable, active_parameter) = call_info_impl(&sema, token)?;
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
index 281a081a3..be1cc12de 100644
--- a/crates/ide_db/src/call_info/tests.rs
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -522,3 +522,30 @@ fn main(f: fn(i32, f64) -> char) {
522 "#]], 522 "#]],
523 ) 523 )
524} 524}
525
526#[test]
527fn call_info_for_unclosed_call() {
528 check(
529 r#"
530fn foo(foo: u32, bar: u32) {}
531fn main() {
532 foo($0
533}"#,
534 expect![[r#"
535 fn foo(foo: u32, bar: u32)
536 (<foo: u32>, bar: u32)
537 "#]],
538 );
539 // check with surrounding space
540 check(
541 r#"
542fn foo(foo: u32, bar: u32) {}
543fn main() {
544 foo( $0
545}"#,
546 expect![[r#"
547 fn foo(foo: u32, bar: u32)
548 (<foo: u32>, bar: u32)
549 "#]],
550 )
551}
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 66798ea3a..720de0d1f 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -1,6 +1,7 @@
1//! A module with ide helpers for high-level ide features. 1//! A module with ide helpers for high-level ide features.
2pub mod insert_use; 2pub mod insert_use;
3pub mod import_assets; 3pub mod import_assets;
4pub mod rust_doc;
4 5
5use std::collections::VecDeque; 6use std::collections::VecDeque;
6 7
@@ -113,6 +114,10 @@ impl FamousDefs<'_, '_> {
113 self.find_module("core:iter") 114 self.find_module("core:iter")
114 } 115 }
115 116
117 pub fn core_ops_Deref(&self) -> Option<Trait> {
118 self.find_trait("core:ops:Deref")
119 }
120
116 fn find_trait(&self, path: &str) -> Option<Trait> { 121 fn find_trait(&self, path: &str) -> Option<Trait> {
117 match self.find_def(path)? { 122 match self.find_def(path)? {
118 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), 123 hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it),
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs
index 4d79e064e..29ae12dcf 100644
--- a/crates/ide_db/src/helpers/famous_defs_fixture.rs
+++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs
@@ -112,6 +112,12 @@ pub mod ops {
112 type Output; 112 type Output;
113 extern "rust-call" fn call_once(self, args: Args) -> Self::Output; 113 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
114 } 114 }
115
116 #[lang = "deref"]
117 pub trait Deref {
118 type Target: ?Sized;
119 fn deref(&self) -> &Self::Target;
120 }
115} 121}
116 122
117pub mod option { 123pub mod option {
@@ -141,3 +147,5 @@ mod return_keyword {}
141 147
142/// Docs for prim_str 148/// Docs for prim_str
143mod prim_str {} 149mod prim_str {}
150
151pub use core::ops; \ No newline at end of file
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs
index 8ce648367..91d6a4665 100644
--- a/crates/ide_db/src/helpers/import_assets.rs
+++ b/crates/ide_db/src/helpers/import_assets.rs
@@ -436,6 +436,8 @@ fn trait_applicable_items(
436 }) 436 })
437 .collect(); 437 .collect();
438 438
439 let related_dyn_traits =
440 trait_candidate.receiver_ty.applicable_inherent_traits(db).collect::<FxHashSet<_>>();
439 let mut located_imports = FxHashSet::default(); 441 let mut located_imports = FxHashSet::default();
440 442
441 if trait_assoc_item { 443 if trait_assoc_item {
@@ -451,12 +453,16 @@ fn trait_applicable_items(
451 return None; 453 return None;
452 } 454 }
453 } 455 }
456 let located_trait = assoc.containing_trait(db)?;
457 if related_dyn_traits.contains(&located_trait) {
458 return None;
459 }
454 460
455 let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); 461 let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
456 let original_item = assoc_to_item(assoc); 462 let original_item = assoc_to_item(assoc);
457 located_imports.insert(LocatedImport::new( 463 located_imports.insert(LocatedImport::new(
458 mod_path(item)?, 464 mod_path(trait_item)?,
459 item, 465 trait_item,
460 original_item, 466 original_item,
461 mod_path(original_item), 467 mod_path(original_item),
462 )); 468 ));
@@ -473,11 +479,15 @@ fn trait_applicable_items(
473 |_, function| { 479 |_, function| {
474 let assoc = function.as_assoc_item(db)?; 480 let assoc = function.as_assoc_item(db)?;
475 if required_assoc_items.contains(&assoc) { 481 if required_assoc_items.contains(&assoc) {
476 let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); 482 let located_trait = assoc.containing_trait(db)?;
483 if related_dyn_traits.contains(&located_trait) {
484 return None;
485 }
486 let trait_item = ItemInNs::from(ModuleDef::from(located_trait));
477 let original_item = assoc_to_item(assoc); 487 let original_item = assoc_to_item(assoc);
478 located_imports.insert(LocatedImport::new( 488 located_imports.insert(LocatedImport::new(
479 mod_path(item)?, 489 mod_path(trait_item)?,
480 item, 490 trait_item,
481 original_item, 491 original_item,
482 mod_path(original_item), 492 mod_path(original_item),
483 )); 493 ));
diff --git a/crates/ide_db/src/helpers/rust_doc.rs b/crates/ide_db/src/helpers/rust_doc.rs
new file mode 100644
index 000000000..e27e23867
--- /dev/null
+++ b/crates/ide_db/src/helpers/rust_doc.rs
@@ -0,0 +1,34 @@
1//! Rustdoc specific doc comment handling
2
3// stripped down version of https://github.com/rust-lang/rust/blob/392ba2ba1a7d6c542d2459fb8133bebf62a4a423/src/librustdoc/html/markdown.rs#L810-L933
4pub fn is_rust_fence(s: &str) -> bool {
5 let mut seen_rust_tags = false;
6 let mut seen_other_tags = false;
7
8 let tokens = s
9 .trim()
10 .split(|c| c == ',' || c == ' ' || c == '\t')
11 .map(str::trim)
12 .filter(|t| !t.is_empty());
13
14 for token in tokens {
15 match token {
16 "should_panic" | "no_run" | "ignore" | "allow_fail" => {
17 seen_rust_tags = !seen_other_tags
18 }
19 "rust" => seen_rust_tags = true,
20 "test_harness" | "compile_fail" => seen_rust_tags = !seen_other_tags || seen_rust_tags,
21 x if x.starts_with("edition") => {}
22 x if x.starts_with('E') && x.len() == 5 => {
23 if x[1..].parse::<u32>().is_ok() {
24 seen_rust_tags = !seen_other_tags || seen_rust_tags;
25 } else {
26 seen_other_tags = true;
27 }
28 }
29 _ => seen_other_tags = true,
30 }
31 }
32
33 !seen_other_tags || seen_rust_tags
34}
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 9ba98f7fb..a7c8c13c6 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -213,7 +213,7 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr {
213 213
214 // Quote the string 214 // Quote the string
215 // Note that `tt::Literal` expect an escaped string 215 // Note that `tt::Literal` expect an escaped string
216 let text = format!("{:?}", text.escape_debug().to_string()); 216 let text = format!("\"{}\"", text.escape_debug());
217 text.into() 217 text.into()
218} 218}
219 219
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs
index e02d038b6..3a1d840ea 100644
--- a/crates/mbe/src/tests/expand.rs
+++ b/crates/mbe/src/tests/expand.rs
@@ -936,7 +936,25 @@ fn test_meta_doc_comments() {
936 MultiLines Doc 936 MultiLines Doc
937 */ 937 */
938 }"#, 938 }"#,
939 "# [doc = \" Single Line Doc 1\"] # [doc = \"\\\\n MultiLines Doc\\\\n \"] fn bar () {}", 939 "# [doc = \" Single Line Doc 1\"] # [doc = \"\\n MultiLines Doc\\n \"] fn bar () {}",
940 );
941}
942
943#[test]
944fn test_meta_extended_key_value_attributes() {
945 parse_macro(
946 r#"
947macro_rules! foo {
948 (#[$i:meta]) => (
949 #[$ i]
950 fn bar() {}
951 )
952}
953"#,
954 )
955 .assert_expand_items(
956 r#"foo! { #[doc = concat!("The `", "bla", "` lang item.")] }"#,
957 r#"# [doc = concat ! ("The `" , "bla" , "` lang item.")] fn bar () {}"#,
940 ); 958 );
941} 959}
942 960
@@ -959,7 +977,27 @@ fn test_meta_doc_comments_non_latin() {
959 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 977 莊生曉夢迷蝴蝶,望帝春心託杜鵑。
960 */ 978 */
961 }"#, 979 }"#,
962 "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\\\n \"] fn bar () {}", 980 "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\n \"] fn bar () {}",
981 );
982}
983
984#[test]
985fn test_meta_doc_comments_escaped_characters() {
986 parse_macro(
987 r#"
988 macro_rules! foo {
989 ($(#[$ i:meta])+) => (
990 $(#[$ i])+
991 fn bar() {}
992 )
993 }
994"#,
995 )
996 .assert_expand_items(
997 r#"foo! {
998 /// \ " '
999 }"#,
1000 r#"# [doc = " \\ \" \'"] fn bar () {}"#,
963 ); 1001 );
964} 1002}
965 1003
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index cebb8f400..9bdf0b5fa 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -76,42 +76,7 @@ pub(crate) mod fragments {
76 76
77 // Parse a meta item , which excluded [], e.g : #[ MetaItem ] 77 // Parse a meta item , which excluded [], e.g : #[ MetaItem ]
78 pub(crate) fn meta_item(p: &mut Parser) { 78 pub(crate) fn meta_item(p: &mut Parser) {
79 fn is_delimiter(p: &mut Parser) -> bool { 79 attributes::meta(p);
80 matches!(p.current(), T!['{'] | T!['('] | T!['['])
81 }
82
83 if is_delimiter(p) {
84 items::token_tree(p);
85 return;
86 }
87
88 let m = p.start();
89 while !p.at(EOF) {
90 if is_delimiter(p) {
91 items::token_tree(p);
92 break;
93 } else {
94 // https://doc.rust-lang.org/reference/attributes.html
95 // https://doc.rust-lang.org/reference/paths.html#simple-paths
96 // The start of an meta must be a simple path
97 match p.current() {
98 IDENT | T![super] | T![self] | T![crate] => p.bump_any(),
99 T![=] => {
100 p.bump_any();
101 match p.current() {
102 c if c.is_literal() => p.bump_any(),
103 T![true] | T![false] => p.bump_any(),
104 _ => {}
105 }
106 break;
107 }
108 _ if p.at(T![::]) => p.bump(T![::]),
109 _ => break,
110 }
111 }
112 }
113
114 m.complete(p, TOKEN_TREE);
115 } 80 }
116 81
117 pub(crate) fn item(p: &mut Parser) { 82 pub(crate) fn item(p: &mut Parser) {
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs
index 96791ffc2..124a10eb2 100644
--- a/crates/parser/src/grammar/attributes.rs
+++ b/crates/parser/src/grammar/attributes.rs
@@ -14,6 +14,21 @@ pub(super) fn outer_attrs(p: &mut Parser) {
14 } 14 }
15} 15}
16 16
17pub(super) fn meta(p: &mut Parser) {
18 paths::use_path(p);
19
20 match p.current() {
21 T![=] => {
22 p.bump(T![=]);
23 if expressions::expr(p).0.is_none() {
24 p.error("expected expression");
25 }
26 }
27 T!['('] | T!['['] | T!['{'] => items::token_tree(p),
28 _ => {}
29 }
30}
31
17fn attr(p: &mut Parser, inner: bool) { 32fn attr(p: &mut Parser, inner: bool) {
18 let attr = p.start(); 33 let attr = p.start();
19 assert!(p.at(T![#])); 34 assert!(p.at(T![#]));
@@ -25,18 +40,7 @@ fn attr(p: &mut Parser, inner: bool) {
25 } 40 }
26 41
27 if p.eat(T!['[']) { 42 if p.eat(T!['[']) {
28 paths::use_path(p); 43 meta(p);
29
30 match p.current() {
31 T![=] => {
32 p.bump(T![=]);
33 if expressions::expr(p).0.is_none() {
34 p.error("expected expression");
35 }
36 }
37 T!['('] | T!['['] | T!['{'] => items::token_tree(p),
38 _ => {}
39 }
40 44
41 if !p.eat(T![']']) { 45 if !p.eat(T![']']) {
42 p.error("expected `]`"); 46 p.error("expected `]`");
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index da71498a8..3ab347834 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -206,13 +206,15 @@ fn record_pat_field_list(p: &mut Parser) {
206 T![.] if p.at(T![..]) => p.bump(T![..]), 206 T![.] if p.at(T![..]) => p.bump(T![..]),
207 T!['{'] => error_block(p, "expected ident"), 207 T!['{'] => error_block(p, "expected ident"),
208 208
209 c => { 209 _ => {
210 let m = p.start(); 210 let m = p.start();
211 match c { 211 attributes::outer_attrs(p);
212 match p.current() {
212 // test record_pat_field 213 // test record_pat_field
213 // fn foo() { 214 // fn foo() {
214 // let S { 0: 1 } = (); 215 // let S { 0: 1 } = ();
215 // let S { x: 1 } = (); 216 // let S { x: 1 } = ();
217 // let S { #[cfg(any())] x: 1 } = ();
216 // } 218 // }
217 IDENT | INT_NUMBER if p.nth(1) == T![:] => { 219 IDENT | INT_NUMBER if p.nth(1) == T![:] => {
218 name_ref_or_index(p); 220 name_ref_or_index(p);
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs
index 94cbf7d85..6ae3e734f 100644
--- a/crates/parser/src/grammar/types.rs
+++ b/crates/parser/src/grammar/types.rs
@@ -283,17 +283,21 @@ pub(super) fn path_type(p: &mut Parser) {
283// type B = crate::foo!(); 283// type B = crate::foo!();
284fn path_or_macro_type_(p: &mut Parser, allow_bounds: bool) { 284fn path_or_macro_type_(p: &mut Parser, allow_bounds: bool) {
285 assert!(paths::is_path_start(p)); 285 assert!(paths::is_path_start(p));
286 let r = p.start();
286 let m = p.start(); 287 let m = p.start();
288
287 paths::type_path(p); 289 paths::type_path(p);
288 290
289 let kind = if p.at(T![!]) && !p.at(T![!=]) { 291 let kind = if p.at(T![!]) && !p.at(T![!=]) {
290 items::macro_call_after_excl(p); 292 items::macro_call_after_excl(p);
291 MACRO_CALL 293 m.complete(p, MACRO_CALL);
294 MACRO_TYPE
292 } else { 295 } else {
296 m.abandon(p);
293 PATH_TYPE 297 PATH_TYPE
294 }; 298 };
295 299
296 let path = m.complete(p, kind); 300 let path = r.complete(p, kind);
297 301
298 if allow_bounds { 302 if allow_bounds {
299 opt_type_bounds_as_dyn_trait_type(p, path); 303 opt_type_bounds_as_dyn_trait_type(p, path);
@@ -319,7 +323,7 @@ pub(super) fn path_type_(p: &mut Parser, allow_bounds: bool) {
319fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser, type_marker: CompletedMarker) { 323fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser, type_marker: CompletedMarker) {
320 assert!(matches!( 324 assert!(matches!(
321 type_marker.kind(), 325 type_marker.kind(),
322 SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_CALL 326 SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_TYPE
323 )); 327 ));
324 if !p.at(T![+]) { 328 if !p.at(T![+]) {
325 return; 329 return;
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
index 22011cb33..f09ad37e3 100644
--- a/crates/paths/src/lib.rs
+++ b/crates/paths/src/lib.rs
@@ -1,6 +1,7 @@
1//! Thin wrappers around `std::path`, distinguishing between absolute and 1//! Thin wrappers around `std::path`, distinguishing between absolute and
2//! relative paths. 2//! relative paths.
3use std::{ 3use std::{
4 borrow::Borrow,
4 convert::{TryFrom, TryInto}, 5 convert::{TryFrom, TryInto},
5 ops, 6 ops,
6 path::{Component, Path, PathBuf}, 7 path::{Component, Path, PathBuf},
@@ -35,6 +36,12 @@ impl AsRef<AbsPath> for AbsPathBuf {
35 } 36 }
36} 37}
37 38
39impl Borrow<AbsPath> for AbsPathBuf {
40 fn borrow(&self) -> &AbsPath {
41 self.as_path()
42 }
43}
44
38impl TryFrom<PathBuf> for AbsPathBuf { 45impl TryFrom<PathBuf> for AbsPathBuf {
39 type Error = PathBuf; 46 type Error = PathBuf;
40 fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> { 47 fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
diff --git a/crates/proc_macro_api/Cargo.toml b/crates/proc_macro_api/Cargo.toml
index 16fd56c7e..1ba1e4abd 100644
--- a/crates/proc_macro_api/Cargo.toml
+++ b/crates/proc_macro_api/Cargo.toml
@@ -15,10 +15,11 @@ serde_json = { version = "1.0", features = ["unbounded_depth"] }
15log = "0.4.8" 15log = "0.4.8"
16crossbeam-channel = "0.5.0" 16crossbeam-channel = "0.5.0"
17jod-thread = "0.1.1" 17jod-thread = "0.1.1"
18memmap = "0.7.0"
19object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] }
20snap = "1.0"
18 21
19tt = { path = "../tt", version = "0.0.0" } 22tt = { path = "../tt", version = "0.0.0" }
20base_db = { path = "../base_db", version = "0.0.0" } 23base_db = { path = "../base_db", version = "0.0.0" }
21stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
22snap = "1" 25profile = { path = "../profile", version = "0.0.0" }
23object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] }
24memmap = "0.7.0"
diff --git a/crates/proc_macro_api/src/lib.rs b/crates/proc_macro_api/src/lib.rs
index 2dd2a8541..654bd9943 100644
--- a/crates/proc_macro_api/src/lib.rs
+++ b/crates/proc_macro_api/src/lib.rs
@@ -77,6 +77,7 @@ impl ProcMacroClient {
77 } 77 }
78 78
79 pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> { 79 pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> {
80 let _p = profile::span("ProcMacroClient::by_dylib_path");
80 match version::read_dylib_info(dylib_path) { 81 match version::read_dylib_info(dylib_path) {
81 Ok(info) => { 82 Ok(info) => {
82 if info.version.0 < 1 || info.version.1 < 47 { 83 if info.version.0 < 1 || info.version.1 < 47 {
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs
index f7050be4e..faca336de 100644
--- a/crates/project_model/src/build_data.rs
+++ b/crates/project_model/src/build_data.rs
@@ -1,7 +1,6 @@
1//! Handles build script specific information 1//! Handles build script specific information
2 2
3use std::{ 3use std::{
4 io::BufReader,
5 path::PathBuf, 4 path::PathBuf,
6 process::{Command, Stdio}, 5 process::{Command, Stdio},
7 sync::Arc, 6 sync::Arc,
@@ -13,12 +12,13 @@ use cargo_metadata::{BuildScript, Message};
13use itertools::Itertools; 12use itertools::Itertools;
14use paths::{AbsPath, AbsPathBuf}; 13use paths::{AbsPath, AbsPathBuf};
15use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
16use stdx::JodChild; 15use serde::Deserialize;
16use stdx::format_to;
17 17
18use crate::{cfg_flag::CfgFlag, CargoConfig}; 18use crate::{cfg_flag::CfgFlag, CargoConfig};
19 19
20#[derive(Debug, Clone, Default, PartialEq, Eq)] 20#[derive(Debug, Clone, Default, PartialEq, Eq)]
21pub(crate) struct BuildData { 21pub(crate) struct PackageBuildData {
22 /// List of config flags defined by this package's build script 22 /// List of config flags defined by this package's build script
23 pub(crate) cfgs: Vec<CfgFlag>, 23 pub(crate) cfgs: Vec<CfgFlag>,
24 /// List of cargo-related environment variables with their value 24 /// List of cargo-related environment variables with their value
@@ -32,6 +32,17 @@ pub(crate) struct BuildData {
32 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>, 32 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
33} 33}
34 34
35#[derive(Debug, Default, PartialEq, Eq, Clone)]
36pub(crate) struct WorkspaceBuildData {
37 per_package: FxHashMap<String, PackageBuildData>,
38 error: Option<String>,
39}
40
41#[derive(Debug, Default, PartialEq, Eq, Clone)]
42pub struct BuildDataResult {
43 per_workspace: FxHashMap<AbsPathBuf, WorkspaceBuildData>,
44}
45
35#[derive(Clone, Debug)] 46#[derive(Clone, Debug)]
36pub(crate) struct BuildDataConfig { 47pub(crate) struct BuildDataConfig {
37 cargo_toml: AbsPathBuf, 48 cargo_toml: AbsPathBuf,
@@ -47,19 +58,17 @@ impl PartialEq for BuildDataConfig {
47 58
48impl Eq for BuildDataConfig {} 59impl Eq for BuildDataConfig {}
49 60
50#[derive(Debug, Default)] 61#[derive(Debug)]
51pub struct BuildDataCollector { 62pub struct BuildDataCollector {
63 wrap_rustc: bool,
52 configs: FxHashMap<AbsPathBuf, BuildDataConfig>, 64 configs: FxHashMap<AbsPathBuf, BuildDataConfig>,
53} 65}
54 66
55#[derive(Debug, Default, PartialEq, Eq)]
56pub struct BuildDataResult {
57 data: FxHashMap<AbsPathBuf, BuildDataMap>,
58}
59
60pub(crate) type BuildDataMap = FxHashMap<String, BuildData>;
61
62impl BuildDataCollector { 67impl BuildDataCollector {
68 pub fn new(wrap_rustc: bool) -> Self {
69 Self { wrap_rustc, configs: FxHashMap::default() }
70 }
71
63 pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) { 72 pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) {
64 self.configs.insert(workspace_root.to_path_buf(), config); 73 self.configs.insert(workspace_root.to_path_buf(), config);
65 } 74 }
@@ -67,23 +76,41 @@ impl BuildDataCollector {
67 pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> { 76 pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> {
68 let mut res = BuildDataResult::default(); 77 let mut res = BuildDataResult::default();
69 for (path, config) in self.configs.iter() { 78 for (path, config) in self.configs.iter() {
70 res.data.insert( 79 let workspace_build_data = WorkspaceBuildData::collect(
71 path.clone(), 80 &config.cargo_toml,
72 collect_from_workspace( 81 &config.cargo_features,
73 &config.cargo_toml, 82 &config.packages,
74 &config.cargo_features, 83 self.wrap_rustc,
75 &config.packages, 84 progress,
76 progress, 85 )?;
77 )?, 86 res.per_workspace.insert(path.clone(), workspace_build_data);
78 );
79 } 87 }
80 Ok(res) 88 Ok(res)
81 } 89 }
82} 90}
83 91
92impl WorkspaceBuildData {
93 pub(crate) fn get(&self, package_id: &str) -> Option<&PackageBuildData> {
94 self.per_package.get(package_id)
95 }
96}
97
84impl BuildDataResult { 98impl BuildDataResult {
85 pub(crate) fn get(&self, root: &AbsPath) -> Option<&BuildDataMap> { 99 pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> {
86 self.data.get(&root.to_path_buf()) 100 self.per_workspace.get(workspace_root)
101 }
102 pub fn error(&self) -> Option<String> {
103 let mut buf = String::new();
104 for (_workspace_root, build_data) in &self.per_workspace {
105 if let Some(err) = &build_data.error {
106 format_to!(buf, "cargo check failed:\n{}", err);
107 }
108 }
109 if buf.is_empty() {
110 return None;
111 }
112
113 Some(buf)
87 } 114 }
88} 115}
89 116
@@ -97,108 +124,155 @@ impl BuildDataConfig {
97 } 124 }
98} 125}
99 126
100fn collect_from_workspace( 127impl WorkspaceBuildData {
101 cargo_toml: &AbsPath, 128 fn collect(
102 cargo_features: &CargoConfig, 129 cargo_toml: &AbsPath,
103 packages: &Vec<cargo_metadata::Package>, 130 cargo_features: &CargoConfig,
104 progress: &dyn Fn(String), 131 packages: &Vec<cargo_metadata::Package>,
105) -> Result<BuildDataMap> { 132 wrap_rustc: bool,
106 let mut cmd = Command::new(toolchain::cargo()); 133 progress: &dyn Fn(String),
107 cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"]) 134 ) -> Result<WorkspaceBuildData> {
108 .arg(cargo_toml.as_ref()); 135 let mut cmd = Command::new(toolchain::cargo());
109 136
110 // --all-targets includes tests, benches and examples in addition to the 137 if wrap_rustc {
111 // default lib and bins. This is an independent concept from the --targets 138 // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
112 // flag below. 139 // that to compile only proc macros and build scripts during the initial
113 cmd.arg("--all-targets"); 140 // `cargo check`.
114 141 let myself = std::env::current_exe()?;
115 if let Some(target) = &cargo_features.target { 142 cmd.env("RUSTC_WRAPPER", myself);
116 cmd.args(&["--target", target]); 143 cmd.env("RA_RUSTC_WRAPPER", "1");
117 } 144 }
118 145
119 if cargo_features.all_features { 146 cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
120 cmd.arg("--all-features"); 147 .arg(cargo_toml.as_ref());
121 } else { 148
122 if cargo_features.no_default_features { 149 // --all-targets includes tests, benches and examples in addition to the
123 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` 150 // default lib and bins. This is an independent concept from the --targets
124 // https://github.com/oli-obk/cargo_metadata/issues/79 151 // flag below.
125 cmd.arg("--no-default-features"); 152 cmd.arg("--all-targets");
153
154 if let Some(target) = &cargo_features.target {
155 cmd.args(&["--target", target]);
126 } 156 }
127 if !cargo_features.features.is_empty() { 157
128 cmd.arg("--features"); 158 if cargo_features.all_features {
129 cmd.arg(cargo_features.features.join(" ")); 159 cmd.arg("--all-features");
160 } else {
161 if cargo_features.no_default_features {
162 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
163 // https://github.com/oli-obk/cargo_metadata/issues/79
164 cmd.arg("--no-default-features");
165 }
166 if !cargo_features.features.is_empty() {
167 cmd.arg("--features");
168 cmd.arg(cargo_features.features.join(" "));
169 }
130 } 170 }
131 }
132 171
133 cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); 172 cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
134 173
135 let mut child = cmd.spawn().map(JodChild)?; 174 let mut res = WorkspaceBuildData::default();
136 let child_stdout = child.stdout.take().unwrap(); 175
137 let stdout = BufReader::new(child_stdout); 176 let mut callback_err = None;
138 177 let output = stdx::process::streaming_output(
139 let mut res = BuildDataMap::default(); 178 cmd,
140 for message in cargo_metadata::Message::parse_stream(stdout).flatten() { 179 &mut |line| {
141 match message { 180 if callback_err.is_some() {
142 Message::BuildScriptExecuted(BuildScript { 181 return;
143 package_id, out_dir, cfgs, env, .. 182 }
144 }) => { 183
145 let cfgs = { 184 // Copy-pasted from existing cargo_metadata. It seems like we
146 let mut acc = Vec::new(); 185 // should be using sered_stacker here?
147 for cfg in cfgs { 186 let mut deserializer = serde_json::Deserializer::from_str(&line);
148 match cfg.parse::<CfgFlag>() { 187 deserializer.disable_recursion_limit();
149 Ok(it) => acc.push(it), 188 let message = Message::deserialize(&mut deserializer)
150 Err(err) => { 189 .unwrap_or(Message::TextLine(line.to_string()));
151 anyhow::bail!("invalid cfg from cargo-metadata: {}", err) 190
191 match message {
192 Message::BuildScriptExecuted(BuildScript {
193 package_id,
194 out_dir,
195 cfgs,
196 env,
197 ..
198 }) => {
199 let cfgs = {
200 let mut acc = Vec::new();
201 for cfg in cfgs {
202 match cfg.parse::<CfgFlag>() {
203 Ok(it) => acc.push(it),
204 Err(err) => {
205 callback_err = Some(anyhow::format_err!(
206 "invalid cfg from cargo-metadata: {}",
207 err
208 ));
209 return;
210 }
211 };
152 } 212 }
213 acc
153 }; 214 };
215 let package_build_data =
216 res.per_package.entry(package_id.repr.clone()).or_default();
217 // cargo_metadata crate returns default (empty) path for
218 // older cargos, which is not absolute, so work around that.
219 if !out_dir.as_str().is_empty() {
220 let out_dir =
221 AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string()));
222 package_build_data.out_dir = Some(out_dir);
223 package_build_data.cfgs = cfgs;
224 }
225
226 package_build_data.envs = env;
154 } 227 }
155 acc 228 Message::CompilerArtifact(message) => {
156 }; 229 progress(format!("metadata {}", message.target.name));
157 let res = res.entry(package_id.repr.clone()).or_default(); 230
158 // cargo_metadata crate returns default (empty) path for 231 if message.target.kind.contains(&"proc-macro".to_string()) {
159 // older cargos, which is not absolute, so work around that. 232 let package_id = message.package_id;
160 if !out_dir.as_str().is_empty() { 233 // Skip rmeta file
161 let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string())); 234 if let Some(filename) =
162 res.out_dir = Some(out_dir); 235 message.filenames.iter().find(|name| is_dylib(name))
163 res.cfgs = cfgs; 236 {
164 } 237 let filename = AbsPathBuf::assert(PathBuf::from(&filename));
165 238 let package_build_data =
166 res.envs = env; 239 res.per_package.entry(package_id.repr.clone()).or_default();
167 } 240 package_build_data.proc_macro_dylib_path = Some(filename);
168 Message::CompilerArtifact(message) => { 241 }
169 progress(format!("metadata {}", message.target.name)); 242 }
170
171 if message.target.kind.contains(&"proc-macro".to_string()) {
172 let package_id = message.package_id;
173 // Skip rmeta file
174 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) {
175 let filename = AbsPathBuf::assert(PathBuf::from(&filename));
176 let res = res.entry(package_id.repr.clone()).or_default();
177 res.proc_macro_dylib_path = Some(filename);
178 } 243 }
244 Message::CompilerMessage(message) => {
245 progress(message.target.name.clone());
246 }
247 Message::BuildFinished(_) => {}
248 Message::TextLine(_) => {}
249 _ => {}
250 }
251 },
252 &mut |_| (),
253 )?;
254
255 for package in packages {
256 let package_build_data = res.per_package.entry(package.id.repr.clone()).or_default();
257 inject_cargo_env(package, package_build_data);
258 if let Some(out_dir) = &package_build_data.out_dir {
259 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
260 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
261 package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
179 } 262 }
180 } 263 }
181 Message::CompilerMessage(message) => {
182 progress(message.target.name.clone());
183 }
184 Message::BuildFinished(_) => {}
185 Message::TextLine(_) => {}
186 _ => {}
187 } 264 }
188 }
189 265
190 for package in packages { 266 if !output.status.success() {
191 let build_data = res.entry(package.id.repr.clone()).or_default(); 267 let mut stderr = String::from_utf8(output.stderr).unwrap_or_default();
192 inject_cargo_env(package, build_data); 268 if stderr.is_empty() {
193 if let Some(out_dir) = &build_data.out_dir { 269 stderr = "cargo check failed".to_string();
194 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
195 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
196 build_data.envs.push(("OUT_DIR".to_string(), out_dir));
197 } 270 }
271 res.error = Some(stderr)
198 } 272 }
199 }
200 273
201 Ok(res) 274 Ok(res)
275 }
202} 276}
203 277
204// FIXME: File a better way to know if it is a dylib 278// FIXME: File a better way to know if it is a dylib
@@ -212,7 +286,7 @@ fn is_dylib(path: &Utf8Path) -> bool {
212/// Recreates the compile-time environment variables that Cargo sets. 286/// Recreates the compile-time environment variables that Cargo sets.
213/// 287///
214/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> 288/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
215fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut BuildData) { 289fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut PackageBuildData) {
216 let env = &mut build_data.envs; 290 let env = &mut build_data.envs;
217 291
218 // FIXME: Missing variables: 292 // FIXME: Missing variables:
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 1b53fcc30..2fcd0f8fa 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -12,7 +12,7 @@ use proc_macro_api::ProcMacroClient;
12use rustc_hash::{FxHashMap, FxHashSet}; 12use rustc_hash::{FxHashMap, FxHashSet};
13 13
14use crate::{ 14use crate::{
15 build_data::{BuildData, BuildDataMap, BuildDataResult}, 15 build_data::{BuildDataResult, PackageBuildData, WorkspaceBuildData},
16 cargo_workspace, 16 cargo_workspace,
17 cfg_flag::CfgFlag, 17 cfg_flag::CfgFlag,
18 rustc_cfg, 18 rustc_cfg,
@@ -354,10 +354,10 @@ fn cargo_to_crate_graph(
354 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 354 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
355 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 355 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
356 cargo: &CargoWorkspace, 356 cargo: &CargoWorkspace,
357 build_data_map: Option<&BuildDataMap>, 357 build_data_map: Option<&WorkspaceBuildData>,
358 sysroot: &Sysroot, 358 sysroot: &Sysroot,
359 rustc: &Option<CargoWorkspace>, 359 rustc: &Option<CargoWorkspace>,
360 rustc_build_data_map: Option<&BuildDataMap>, 360 rustc_build_data_map: Option<&WorkspaceBuildData>,
361) -> CrateGraph { 361) -> CrateGraph {
362 let _p = profile::span("cargo_to_crate_graph"); 362 let _p = profile::span("cargo_to_crate_graph");
363 let mut crate_graph = CrateGraph::default(); 363 let mut crate_graph = CrateGraph::default();
@@ -464,7 +464,7 @@ fn handle_rustc_crates(
464 rustc_workspace: &CargoWorkspace, 464 rustc_workspace: &CargoWorkspace,
465 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, 465 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
466 crate_graph: &mut CrateGraph, 466 crate_graph: &mut CrateGraph,
467 rustc_build_data_map: Option<&FxHashMap<String, BuildData>>, 467 rustc_build_data_map: Option<&WorkspaceBuildData>,
468 cfg_options: &CfgOptions, 468 cfg_options: &CfgOptions,
469 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 469 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
470 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>, 470 pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
@@ -555,7 +555,7 @@ fn handle_rustc_crates(
555fn add_target_crate_root( 555fn add_target_crate_root(
556 crate_graph: &mut CrateGraph, 556 crate_graph: &mut CrateGraph,
557 pkg: &cargo_workspace::PackageData, 557 pkg: &cargo_workspace::PackageData,
558 build_data: Option<&BuildData>, 558 build_data: Option<&PackageBuildData>,
559 cfg_options: &CfgOptions, 559 cfg_options: &CfgOptions,
560 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, 560 proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
561 file_id: FileId, 561 file_id: FileId,
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 3130785cc..0571a912c 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -22,7 +22,7 @@ env_logger = { version = "0.8.1", default-features = false }
22itertools = "0.10.0" 22itertools = "0.10.0"
23jod-thread = "0.1.0" 23jod-thread = "0.1.0"
24log = "0.4.8" 24log = "0.4.8"
25lsp-types = { version = "0.88.0", features = ["proposed"] } 25lsp-types = { version = "0.89.0", features = ["proposed"] }
26parking_lot = "0.11.0" 26parking_lot = "0.11.0"
27xflags = "0.2.1" 27xflags = "0.2.1"
28oorandom = "11.1.2" 28oorandom = "11.1.2"
diff --git a/crates/rust-analyzer/build.rs b/crates/rust-analyzer/build.rs
index 999dc5928..13b903891 100644
--- a/crates/rust-analyzer/build.rs
+++ b/crates/rust-analyzer/build.rs
@@ -39,7 +39,8 @@ fn set_rerun() {
39} 39}
40 40
41fn rev() -> Option<String> { 41fn rev() -> Option<String> {
42 let output = Command::new("git").args(&["describe", "--tags"]).output().ok()?; 42 let output =
43 Command::new("git").args(&["describe", "--tags", "--exclude", "nightly"]).output().ok()?;
43 let stdout = String::from_utf8(output.stdout).ok()?; 44 let stdout = String::from_utf8(output.stdout).ok()?;
44 Some(stdout) 45 Some(stdout)
45} 46}
diff --git a/crates/rust-analyzer/src/benchmarks.rs b/crates/rust-analyzer/src/benchmarks.rs
index bf569b40b..bdd94b1c4 100644
--- a/crates/rust-analyzer/src/benchmarks.rs
+++ b/crates/rust-analyzer/src/benchmarks.rs
@@ -30,8 +30,11 @@ fn benchmark_integrated_highlighting() {
30 let file = "./crates/ide_db/src/apply_change.rs"; 30 let file = "./crates/ide_db/src/apply_change.rs";
31 31
32 let cargo_config = Default::default(); 32 let cargo_config = Default::default();
33 let load_cargo_config = 33 let load_cargo_config = LoadCargoConfig {
34 LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: false }; 34 load_out_dirs_from_check: true,
35 wrap_rustc: false,
36 with_proc_macro: false,
37 };
35 38
36 let (mut host, vfs, _proc_macro) = { 39 let (mut host, vfs, _proc_macro) = {
37 let _it = stdx::timeit("workspace loading"); 40 let _it = stdx::timeit("workspace loading");
diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs
index b05fc00b9..63953098a 100644
--- a/crates/rust-analyzer/src/bin/flags.rs
+++ b/crates/rust-analyzer/src/bin/flags.rs
@@ -71,6 +71,8 @@ xflags::xflags! {
71 optional --load-output-dirs 71 optional --load-output-dirs
72 /// Use proc-macro-srv for proc-macro expanding. 72 /// Use proc-macro-srv for proc-macro expanding.
73 optional --with-proc-macro 73 optional --with-proc-macro
74 /// Only resolve names, don't run type inference.
75 optional --skip-inference
74 } 76 }
75 77
76 cmd diagnostics 78 cmd diagnostics
@@ -158,6 +160,7 @@ pub struct AnalysisStats {
158 pub no_sysroot: bool, 160 pub no_sysroot: bool,
159 pub load_output_dirs: bool, 161 pub load_output_dirs: bool,
160 pub with_proc_macro: bool, 162 pub with_proc_macro: bool,
163 pub skip_inference: bool,
161} 164}
162 165
163#[derive(Debug)] 166#[derive(Debug)]
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 3b9b9e8b4..f0abb5b15 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -3,6 +3,7 @@
3//! Based on cli flags, either spawns an LSP server, or runs a batch analysis 3//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
4mod flags; 4mod flags;
5mod logger; 5mod logger;
6mod rustc_wrapper;
6 7
7use std::{convert::TryFrom, env, fs, path::Path, process}; 8use std::{convert::TryFrom, env, fs, path::Path, process};
8 9
@@ -26,6 +27,20 @@ static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
26static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; 27static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
27 28
28fn main() { 29fn main() {
30 if std::env::var("RA_RUSTC_WRAPPER").is_ok() {
31 let mut args = std::env::args_os();
32 let _me = args.next().unwrap();
33 let rustc = args.next().unwrap();
34 let code = match rustc_wrapper::run_rustc_skipping_cargo_checking(rustc, args.collect()) {
35 Ok(rustc_wrapper::ExitCode(code)) => code.unwrap_or(102),
36 Err(err) => {
37 eprintln!("{}", err);
38 101
39 }
40 };
41 process::exit(code);
42 }
43
29 if let Err(err) = try_main() { 44 if let Err(err) = try_main() {
30 log::error!("Unexpected error: {}", err); 45 log::error!("Unexpected error: {}", err);
31 eprintln!("{}", err); 46 eprintln!("{}", err);
@@ -78,6 +93,7 @@ fn try_main() -> Result<()> {
78 path: cmd.path, 93 path: cmd.path,
79 load_output_dirs: cmd.load_output_dirs, 94 load_output_dirs: cmd.load_output_dirs,
80 with_proc_macro: cmd.with_proc_macro, 95 with_proc_macro: cmd.with_proc_macro,
96 skip_inference: cmd.skip_inference,
81 } 97 }
82 .run(verbosity)?, 98 .run(verbosity)?,
83 99
diff --git a/crates/rust-analyzer/src/bin/rustc_wrapper.rs b/crates/rust-analyzer/src/bin/rustc_wrapper.rs
new file mode 100644
index 000000000..2f6d4706d
--- /dev/null
+++ b/crates/rust-analyzer/src/bin/rustc_wrapper.rs
@@ -0,0 +1,46 @@
1//! We setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself during the
2//! initial `cargo check`. That way, we avoid checking the actual project, and
3//! only build proc macros and build.rs.
4//!
5//! Code taken from IntelliJ :0)
6//! https://github.com/intellij-rust/intellij-rust/blob/master/native-helper/src/main.rs
7use std::{
8 ffi::OsString,
9 io,
10 process::{Command, Stdio},
11};
12
13/// ExitCode/ExitStatus are impossible to create :(.
14pub(crate) struct ExitCode(pub(crate) Option<i32>);
15
16pub(crate) fn run_rustc_skipping_cargo_checking(
17 rustc_executable: OsString,
18 args: Vec<OsString>,
19) -> io::Result<ExitCode> {
20 let is_cargo_check = args.iter().any(|arg| {
21 let arg = arg.to_string_lossy();
22 // `cargo check` invokes `rustc` with `--emit=metadata` argument.
23 //
24 // https://doc.rust-lang.org/rustc/command-line-arguments.html#--emit-specifies-the-types-of-output-files-to-generate
25 // link — Generates the crates specified by --crate-type. The default
26 // output filenames depend on the crate type and platform. This
27 // is the default if --emit is not specified.
28 // metadata — Generates a file containing metadata about the crate.
29 // The default output filename is CRATE_NAME.rmeta.
30 arg.starts_with("--emit=") && arg.contains("metadata") && !arg.contains("link")
31 });
32 if is_cargo_check {
33 return Ok(ExitCode(Some(0)));
34 }
35 run_rustc(rustc_executable, args)
36}
37
38fn run_rustc(rustc_executable: OsString, args: Vec<OsString>) -> io::Result<ExitCode> {
39 let mut child = Command::new(rustc_executable)
40 .args(args)
41 .stdin(Stdio::inherit())
42 .stdout(Stdio::inherit())
43 .stderr(Stdio::inherit())
44 .spawn()?;
45 Ok(ExitCode(child.wait()?.code()))
46}
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 7a5bcb8c7..3c87782f2 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -57,7 +57,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
57 document_range_formatting_provider: None, 57 document_range_formatting_provider: None,
58 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { 58 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
59 first_trigger_character: "=".to_string(), 59 first_trigger_character: "=".to_string(),
60 more_trigger_character: Some(vec![".".to_string(), ">".to_string()]), 60 more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]),
61 }), 61 }),
62 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), 62 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
63 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), 63 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 9c59e7ee4..3f3134562 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -9,10 +9,11 @@ use std::{
9 9
10use hir::{ 10use hir::{
11 db::{AstDatabase, DefDatabase, HirDatabase}, 11 db::{AstDatabase, DefDatabase, HirDatabase},
12 AssocItem, Crate, HasSource, HirDisplay, ModuleDef, 12 AssocItem, Crate, Function, HasSource, HirDisplay, ModuleDef,
13}; 13};
14use hir_def::FunctionId; 14use hir_def::FunctionId;
15use hir_ty::TypeWalk; 15use hir_ty::{TyExt, TypeWalk};
16use ide::{AnalysisHost, RootDatabase};
16use ide_db::base_db::{ 17use ide_db::base_db::{
17 salsa::{self, ParallelDatabase}, 18 salsa::{self, ParallelDatabase},
18 SourceDatabaseExt, 19 SourceDatabaseExt,
@@ -24,6 +25,7 @@ use rayon::prelude::*;
24use rustc_hash::FxHashSet; 25use rustc_hash::FxHashSet;
25use stdx::format_to; 26use stdx::format_to;
26use syntax::AstNode; 27use syntax::AstNode;
28use vfs::Vfs;
27 29
28use crate::cli::{ 30use crate::cli::{
29 load_cargo::{load_workspace_at, LoadCargoConfig}, 31 load_cargo::{load_workspace_at, LoadCargoConfig},
@@ -51,6 +53,7 @@ pub struct AnalysisStatsCmd {
51 pub path: PathBuf, 53 pub path: PathBuf,
52 pub load_output_dirs: bool, 54 pub load_output_dirs: bool,
53 pub with_proc_macro: bool, 55 pub with_proc_macro: bool,
56 pub skip_inference: bool,
54} 57}
55 58
56impl AnalysisStatsCmd { 59impl AnalysisStatsCmd {
@@ -65,6 +68,7 @@ impl AnalysisStatsCmd {
65 cargo_config.no_sysroot = self.no_sysroot; 68 cargo_config.no_sysroot = self.no_sysroot;
66 let load_cargo_config = LoadCargoConfig { 69 let load_cargo_config = LoadCargoConfig {
67 load_out_dirs_from_check: self.load_output_dirs, 70 load_out_dirs_from_check: self.load_output_dirs,
71 wrap_rustc: false,
68 with_proc_macro: self.with_proc_macro, 72 with_proc_macro: self.with_proc_macro,
69 }; 73 };
70 let (host, vfs, _proc_macro) = 74 let (host, vfs, _proc_macro) =
@@ -128,6 +132,39 @@ impl AnalysisStatsCmd {
128 shuffle(&mut rng, &mut funcs); 132 shuffle(&mut rng, &mut funcs);
129 } 133 }
130 134
135 if !self.skip_inference {
136 self.run_inference(&host, db, &vfs, &funcs, verbosity);
137 }
138
139 let total_span = analysis_sw.elapsed();
140 eprintln!("{:<20} {}", "Total:", total_span);
141 report_metric("total time", total_span.time.as_millis() as u64, "ms");
142 if let Some(instructions) = total_span.instructions {
143 report_metric("total instructions", instructions, "#instr");
144 }
145 if let Some(memory) = total_span.memory {
146 report_metric("total memory", memory.allocated.megabytes() as u64, "MB");
147 }
148
149 if env::var("RA_COUNT").is_ok() {
150 eprintln!("{}", profile::countme::get_all());
151 }
152
153 if self.memory_usage && verbosity.is_verbose() {
154 print_memory_usage(host, vfs);
155 }
156
157 Ok(())
158 }
159
160 fn run_inference(
161 &self,
162 host: &AnalysisHost,
163 db: &RootDatabase,
164 vfs: &Vfs,
165 funcs: &[Function],
166 verbosity: Verbosity,
167 ) {
131 let mut bar = match verbosity { 168 let mut bar = match verbosity {
132 Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), 169 Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(),
133 _ if self.parallel => ProgressReport::hidden(), 170 _ if self.parallel => ProgressReport::hidden(),
@@ -154,7 +191,7 @@ impl AnalysisStatsCmd {
154 let mut num_exprs_unknown = 0; 191 let mut num_exprs_unknown = 0;
155 let mut num_exprs_partially_unknown = 0; 192 let mut num_exprs_partially_unknown = 0;
156 let mut num_type_mismatches = 0; 193 let mut num_type_mismatches = 0;
157 for f in funcs { 194 for f in funcs.iter().copied() {
158 let name = f.name(db); 195 let name = f.name(db);
159 let full_name = f 196 let full_name = f
160 .module(db) 197 .module(db)
@@ -296,26 +333,6 @@ impl AnalysisStatsCmd {
296 report_metric("type mismatches", num_type_mismatches, "#"); 333 report_metric("type mismatches", num_type_mismatches, "#");
297 334
298 eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed()); 335 eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed());
299
300 let total_span = analysis_sw.elapsed();
301 eprintln!("{:<20} {}", "Total:", total_span);
302 report_metric("total time", total_span.time.as_millis() as u64, "ms");
303 if let Some(instructions) = total_span.instructions {
304 report_metric("total instructions", instructions, "#instr");
305 }
306 if let Some(memory) = total_span.memory {
307 report_metric("total memory", memory.allocated.megabytes() as u64, "MB");
308 }
309
310 if env::var("RA_COUNT").is_ok() {
311 eprintln!("{}", profile::countme::get_all());
312 }
313
314 if self.memory_usage && verbosity.is_verbose() {
315 print_memory_usage(host, vfs);
316 }
317
318 Ok(())
319 } 336 }
320 337
321 fn stop_watch(&self) -> StopWatch { 338 fn stop_watch(&self) -> StopWatch {
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index 8b985716b..74f784338 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -34,7 +34,8 @@ pub fn diagnostics(
34 with_proc_macro: bool, 34 with_proc_macro: bool,
35) -> Result<()> { 35) -> Result<()> {
36 let cargo_config = Default::default(); 36 let cargo_config = Default::default();
37 let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check, with_proc_macro }; 37 let load_cargo_config =
38 LoadCargoConfig { load_out_dirs_from_check, with_proc_macro, wrap_rustc: false };
38 let (host, _vfs, _proc_macro) = 39 let (host, _vfs, _proc_macro) =
39 load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; 40 load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?;
40 let db = host.raw_database(); 41 let db = host.raw_database();
@@ -56,7 +57,8 @@ pub fn diagnostics(
56 let crate_name = 57 let crate_name =
57 module.krate().display_name(db).as_deref().unwrap_or("unknown").to_string(); 58 module.krate().display_name(db).as_deref().unwrap_or("unknown").to_string();
58 println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id)); 59 println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id));
59 for diagnostic in analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap() 60 for diagnostic in
61 analysis.diagnostics(&DiagnosticsConfig::default(), false, file_id).unwrap()
60 { 62 {
61 if matches!(diagnostic.severity, Severity::Error) { 63 if matches!(diagnostic.severity, Severity::Error) {
62 found_error = true; 64 found_error = true;
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 310c36904..75bad1112 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -15,6 +15,7 @@ use crate::reload::{ProjectFolders, SourceRootConfig};
15 15
16pub struct LoadCargoConfig { 16pub struct LoadCargoConfig {
17 pub load_out_dirs_from_check: bool, 17 pub load_out_dirs_from_check: bool,
18 pub wrap_rustc: bool,
18 pub with_proc_macro: bool, 19 pub with_proc_macro: bool,
19} 20}
20 21
@@ -52,7 +53,7 @@ pub fn load_workspace(
52 }; 53 };
53 54
54 let build_data = if config.load_out_dirs_from_check { 55 let build_data = if config.load_out_dirs_from_check {
55 let mut collector = BuildDataCollector::default(); 56 let mut collector = BuildDataCollector::new(config.wrap_rustc);
56 ws.collect_build_data_configs(&mut collector); 57 ws.collect_build_data_configs(&mut collector);
57 Some(collector.collect(progress)?) 58 Some(collector.collect(progress)?)
58 } else { 59 } else {
@@ -136,8 +137,11 @@ mod tests {
136 fn test_loading_rust_analyzer() -> Result<()> { 137 fn test_loading_rust_analyzer() -> Result<()> {
137 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); 138 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
138 let cargo_config = Default::default(); 139 let cargo_config = Default::default();
139 let load_cargo_config = 140 let load_cargo_config = LoadCargoConfig {
140 LoadCargoConfig { load_out_dirs_from_check: false, with_proc_macro: false }; 141 load_out_dirs_from_check: false,
142 wrap_rustc: false,
143 with_proc_macro: false,
144 };
141 let (host, _vfs, _proc_macro) = 145 let (host, _vfs, _proc_macro) =
142 load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?; 146 load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {})?;
143 147
diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs
index 79f426fff..1fd9b5a9b 100644
--- a/crates/rust-analyzer/src/cli/ssr.rs
+++ b/crates/rust-analyzer/src/cli/ssr.rs
@@ -9,8 +9,11 @@ use ide_ssr::{MatchFinder, SsrPattern, SsrRule};
9pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> { 9pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> {
10 use ide_db::base_db::SourceDatabaseExt; 10 use ide_db::base_db::SourceDatabaseExt;
11 let cargo_config = Default::default(); 11 let cargo_config = Default::default();
12 let load_cargo_config = 12 let load_cargo_config = LoadCargoConfig {
13 LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: true }; 13 load_out_dirs_from_check: true,
14 wrap_rustc: false,
15 with_proc_macro: true,
16 };
14 let (host, vfs, _proc_macro) = 17 let (host, vfs, _proc_macro) =
15 load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; 18 load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?;
16 let db = host.raw_database(); 19 let db = host.raw_database();
@@ -37,7 +40,7 @@ pub fn search_for_patterns(patterns: Vec<SsrPattern>, debug_snippet: Option<Stri
37 use ide_db::symbol_index::SymbolsDatabase; 40 use ide_db::symbol_index::SymbolsDatabase;
38 let cargo_config = Default::default(); 41 let cargo_config = Default::default();
39 let load_cargo_config = 42 let load_cargo_config =
40 LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro: true }; 43 LoadCargoConfig { load_out_dirs_from_check: true, wrap_rustc: true, with_proc_macro: true };
41 let (host, _vfs, _proc_macro) = 44 let (host, _vfs, _proc_macro) =
42 load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?; 45 load_workspace_at(&std::env::current_dir()?, &cargo_config, &load_cargo_config, &|_| {})?;
43 let db = host.raw_database(); 46 let db = host.raw_database();
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index cda272fd4..1109d2daf 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -17,7 +17,7 @@ use ide_db::helpers::{
17}; 17};
18use lsp_types::{ClientCapabilities, MarkupKind}; 18use lsp_types::{ClientCapabilities, MarkupKind};
19use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; 19use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource};
20use rustc_hash::FxHashSet; 20use rustc_hash::{FxHashMap, FxHashSet};
21use serde::{de::DeserializeOwned, Deserialize}; 21use serde::{de::DeserializeOwned, Deserialize};
22use vfs::AbsPathBuf; 22use vfs::AbsPathBuf;
23 23
@@ -48,6 +48,9 @@ config_data! {
48 /// Run build scripts (`build.rs`) for more precise code analysis. 48 /// Run build scripts (`build.rs`) for more precise code analysis.
49 cargo_runBuildScripts | 49 cargo_runBuildScripts |
50 cargo_loadOutDirsFromCheck: bool = "true", 50 cargo_loadOutDirsFromCheck: bool = "true",
51 /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
52 /// avoid compiling unnecessary things.
53 cargo_useRustcWrapperForBuildScripts: bool = "true",
51 /// Do not activate the `default` feature. 54 /// Do not activate the `default` feature.
52 cargo_noDefaultFeatures: bool = "false", 55 cargo_noDefaultFeatures: bool = "false",
53 /// Compilation target (target triple). 56 /// Compilation target (target triple).
@@ -96,6 +99,9 @@ config_data! {
96 diagnostics_enableExperimental: bool = "true", 99 diagnostics_enableExperimental: bool = "true",
97 /// List of rust-analyzer diagnostics to disable. 100 /// List of rust-analyzer diagnostics to disable.
98 diagnostics_disabled: FxHashSet<String> = "[]", 101 diagnostics_disabled: FxHashSet<String> = "[]",
102 /// Map of prefixes to be substituted when parsing diagnostic file paths.
103 /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
104 diagnostics_remapPrefix: FxHashMap<String, String> = "{}",
99 /// List of warnings that should be displayed with info severity. 105 /// List of warnings that should be displayed with info severity.
100 /// 106 ///
101 /// The warnings will be indicated by a blue squiggly underline in code 107 /// The warnings will be indicated by a blue squiggly underline in code
@@ -397,6 +403,17 @@ impl Config {
397 pub fn will_rename(&self) -> bool { 403 pub fn will_rename(&self) -> bool {
398 try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false) 404 try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false)
399 } 405 }
406 pub fn change_annotation_support(&self) -> bool {
407 try_!(self
408 .caps
409 .workspace
410 .as_ref()?
411 .workspace_edit
412 .as_ref()?
413 .change_annotation_support
414 .as_ref()?)
415 .is_some()
416 }
400 pub fn code_action_resolve(&self) -> bool { 417 pub fn code_action_resolve(&self) -> bool {
401 try_or!( 418 try_or!(
402 self.caps 419 self.caps
@@ -445,8 +462,8 @@ impl Config {
445 pub fn hover_actions(&self) -> bool { 462 pub fn hover_actions(&self) -> bool {
446 self.experimental("hoverActions") 463 self.experimental("hoverActions")
447 } 464 }
448 pub fn status_notification(&self) -> bool { 465 pub fn server_status_notification(&self) -> bool {
449 self.experimental("statusNotification") 466 self.experimental("serverStatusNotification")
450 } 467 }
451 468
452 pub fn publish_diagnostics(&self) -> bool { 469 pub fn publish_diagnostics(&self) -> bool {
@@ -460,6 +477,7 @@ impl Config {
460 } 477 }
461 pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { 478 pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
462 DiagnosticsMapConfig { 479 DiagnosticsMapConfig {
480 remap_prefix: self.data.diagnostics_remapPrefix.clone(),
463 warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(), 481 warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
464 warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(), 482 warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
465 } 483 }
@@ -493,6 +511,9 @@ impl Config {
493 pub fn run_build_scripts(&self) -> bool { 511 pub fn run_build_scripts(&self) -> bool {
494 self.data.cargo_runBuildScripts || self.data.procMacro_enable 512 self.data.cargo_runBuildScripts || self.data.procMacro_enable
495 } 513 }
514 pub fn wrap_rustc(&self) -> bool {
515 self.data.cargo_useRustcWrapperForBuildScripts
516 }
496 pub fn cargo(&self) -> CargoConfig { 517 pub fn cargo(&self) -> CargoConfig {
497 let rustc_source = self.data.rustcSource.as_ref().map(|rustc_src| { 518 let rustc_source = self.data.rustcSource.as_ref().map(|rustc_src| {
498 if rustc_src == "discover" { 519 if rustc_src == "discover" {
@@ -656,6 +677,19 @@ impl Config {
656 pub fn code_lens_refresh(&self) -> bool { 677 pub fn code_lens_refresh(&self) -> bool {
657 try_or!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?, false) 678 try_or!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?, false)
658 } 679 }
680 pub fn insert_replace_support(&self) -> bool {
681 try_or!(
682 self.caps
683 .text_document
684 .as_ref()?
685 .completion
686 .as_ref()?
687 .completion_item
688 .as_ref()?
689 .insert_replace_support?,
690 false
691 )
692 }
659} 693}
660 694
661#[derive(Deserialize, Debug, Clone)] 695#[derive(Deserialize, Debug, Clone)]
@@ -805,6 +839,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
805 "items": { "type": "string" }, 839 "items": { "type": "string" },
806 "uniqueItems": true, 840 "uniqueItems": true,
807 }, 841 },
842 "FxHashMap<String, String>" => set! {
843 "type": "object",
844 },
808 "Option<usize>" => set! { 845 "Option<usize>" => set! {
809 "type": ["null", "integer"], 846 "type": ["null", "integer"],
810 "minimum": 0, 847 "minimum": 0,
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index f01548c50..d4b9db362 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -12,6 +12,7 @@ pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>;
12 12
13#[derive(Debug, Default, Clone)] 13#[derive(Debug, Default, Clone)]
14pub struct DiagnosticsMapConfig { 14pub struct DiagnosticsMapConfig {
15 pub remap_prefix: FxHashMap<String, String>,
15 pub warnings_as_info: Vec<String>, 16 pub warnings_as_info: Vec<String>,
16 pub warnings_as_hint: Vec<String>, 17 pub warnings_as_hint: Vec<String>,
17} 18}
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
index 23ec2efba..227d96d51 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
@@ -326,6 +326,7 @@
326 }, 326 },
327 ), 327 ),
328 document_changes: None, 328 document_changes: None,
329 change_annotations: None,
329 }, 330 },
330 ), 331 ),
331 is_preferred: Some( 332 is_preferred: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
index b6acb5f42..f8adfad3b 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
@@ -179,6 +179,7 @@
179 }, 179 },
180 ), 180 ),
181 document_changes: None, 181 document_changes: None,
182 change_annotations: None,
182 }, 183 },
183 ), 184 ),
184 is_preferred: Some( 185 is_preferred: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
index d765257c4..5a70d2ed7 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
@@ -179,6 +179,7 @@
179 }, 179 },
180 ), 180 ),
181 document_changes: None, 181 document_changes: None,
182 change_annotations: None,
182 }, 183 },
183 ), 184 ),
184 is_preferred: Some( 185 is_preferred: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
index 6b0d94878..04ca0c9c2 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
@@ -179,6 +179,7 @@
179 }, 179 },
180 ), 180 ),
181 document_changes: None, 181 document_changes: None,
182 change_annotations: None,
182 }, 183 },
183 ), 184 ),
184 is_preferred: Some( 185 is_preferred: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
index a0cfb8d33..57d2f1ae3 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
@@ -339,6 +339,7 @@
339 }, 339 },
340 ), 340 ),
341 document_changes: None, 341 document_changes: None,
342 change_annotations: None,
342 }, 343 },
343 ), 344 ),
344 is_preferred: Some( 345 is_preferred: Some(
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index e2f319f6b..82dd0da9a 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -1,6 +1,9 @@
1//! This module provides the functionality needed to convert diagnostics from 1//! This module provides the functionality needed to convert diagnostics from
2//! `cargo check` json format to the LSP diagnostic format. 2//! `cargo check` json format to the LSP diagnostic format.
3use std::{collections::HashMap, path::Path}; 3use std::{
4 collections::HashMap,
5 path::{Path, PathBuf},
6};
4 7
5use flycheck::{DiagnosticLevel, DiagnosticSpan}; 8use flycheck::{DiagnosticLevel, DiagnosticSpan};
6use stdx::format_to; 9use stdx::format_to;
@@ -41,8 +44,12 @@ fn is_dummy_macro_file(file_name: &str) -> bool {
41} 44}
42 45
43/// Converts a Rust span to a LSP location 46/// Converts a Rust span to a LSP location
44fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { 47fn location(
45 let file_name = workspace_root.join(&span.file_name); 48 config: &DiagnosticsMapConfig,
49 workspace_root: &Path,
50 span: &DiagnosticSpan,
51) -> lsp_types::Location {
52 let file_name = resolve_path(config, workspace_root, &span.file_name);
46 let uri = url_from_abs_path(&file_name); 53 let uri = url_from_abs_path(&file_name);
47 54
48 // FIXME: this doesn't handle UTF16 offsets correctly 55 // FIXME: this doesn't handle UTF16 offsets correctly
@@ -58,32 +65,50 @@ fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location
58/// 65///
59/// This takes locations pointing into the standard library, or generally outside the current 66/// This takes locations pointing into the standard library, or generally outside the current
60/// workspace into account and tries to avoid those, in case macros are involved. 67/// workspace into account and tries to avoid those, in case macros are involved.
61fn primary_location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location { 68fn primary_location(
69 config: &DiagnosticsMapConfig,
70 workspace_root: &Path,
71 span: &DiagnosticSpan,
72) -> lsp_types::Location {
62 let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span)); 73 let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span));
63 for span in span_stack.clone() { 74 for span in span_stack.clone() {
64 let abs_path = workspace_root.join(&span.file_name); 75 let abs_path = resolve_path(config, workspace_root, &span.file_name);
65 if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) { 76 if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) {
66 return location(workspace_root, span); 77 return location(config, workspace_root, span);
67 } 78 }
68 } 79 }
69 80
70 // Fall back to the outermost macro invocation if no suitable span comes up. 81 // Fall back to the outermost macro invocation if no suitable span comes up.
71 let last_span = span_stack.last().unwrap(); 82 let last_span = span_stack.last().unwrap();
72 location(workspace_root, last_span) 83 location(config, workspace_root, last_span)
73} 84}
74 85
75/// Converts a secondary Rust span to a LSP related information 86/// Converts a secondary Rust span to a LSP related information
76/// 87///
77/// If the span is unlabelled this will return `None`. 88/// If the span is unlabelled this will return `None`.
78fn diagnostic_related_information( 89fn diagnostic_related_information(
90 config: &DiagnosticsMapConfig,
79 workspace_root: &Path, 91 workspace_root: &Path,
80 span: &DiagnosticSpan, 92 span: &DiagnosticSpan,
81) -> Option<lsp_types::DiagnosticRelatedInformation> { 93) -> Option<lsp_types::DiagnosticRelatedInformation> {
82 let message = span.label.clone()?; 94 let message = span.label.clone()?;
83 let location = location(workspace_root, span); 95 let location = location(config, workspace_root, span);
84 Some(lsp_types::DiagnosticRelatedInformation { location, message }) 96 Some(lsp_types::DiagnosticRelatedInformation { location, message })
85} 97}
86 98
99/// Resolves paths applying any matching path prefix remappings, and then
100/// joining the path to the workspace root.
101fn resolve_path(config: &DiagnosticsMapConfig, workspace_root: &Path, file_name: &str) -> PathBuf {
102 match config
103 .remap_prefix
104 .iter()
105 .find_map(|(from, to)| file_name.strip_prefix(from).map(|file_name| (to, file_name)))
106 {
107 Some((to, file_name)) => workspace_root.join(format!("{}{}", to, file_name)),
108 None => workspace_root.join(file_name),
109 }
110}
111
87struct SubDiagnostic { 112struct SubDiagnostic {
88 related: lsp_types::DiagnosticRelatedInformation, 113 related: lsp_types::DiagnosticRelatedInformation,
89 suggested_fix: Option<lsp_ext::CodeAction>, 114 suggested_fix: Option<lsp_ext::CodeAction>,
@@ -95,6 +120,7 @@ enum MappedRustChildDiagnostic {
95} 120}
96 121
97fn map_rust_child_diagnostic( 122fn map_rust_child_diagnostic(
123 config: &DiagnosticsMapConfig,
98 workspace_root: &Path, 124 workspace_root: &Path,
99 rd: &flycheck::Diagnostic, 125 rd: &flycheck::Diagnostic,
100) -> MappedRustChildDiagnostic { 126) -> MappedRustChildDiagnostic {
@@ -108,7 +134,7 @@ fn map_rust_child_diagnostic(
108 let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new(); 134 let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
109 for &span in &spans { 135 for &span in &spans {
110 if let Some(suggested_replacement) = &span.suggested_replacement { 136 if let Some(suggested_replacement) = &span.suggested_replacement {
111 let location = location(workspace_root, span); 137 let location = location(config, workspace_root, span);
112 let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone()); 138 let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
113 edit_map.entry(location.uri).or_default().push(edit); 139 edit_map.entry(location.uri).or_default().push(edit);
114 } 140 }
@@ -117,7 +143,7 @@ fn map_rust_child_diagnostic(
117 if edit_map.is_empty() { 143 if edit_map.is_empty() {
118 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { 144 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
119 related: lsp_types::DiagnosticRelatedInformation { 145 related: lsp_types::DiagnosticRelatedInformation {
120 location: location(workspace_root, spans[0]), 146 location: location(config, workspace_root, spans[0]),
121 message: rd.message.clone(), 147 message: rd.message.clone(),
122 }, 148 },
123 suggested_fix: None, 149 suggested_fix: None,
@@ -125,7 +151,7 @@ fn map_rust_child_diagnostic(
125 } else { 151 } else {
126 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic { 152 MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
127 related: lsp_types::DiagnosticRelatedInformation { 153 related: lsp_types::DiagnosticRelatedInformation {
128 location: location(workspace_root, spans[0]), 154 location: location(config, workspace_root, spans[0]),
129 message: rd.message.clone(), 155 message: rd.message.clone(),
130 }, 156 },
131 suggested_fix: Some(lsp_ext::CodeAction { 157 suggested_fix: Some(lsp_ext::CodeAction {
@@ -136,6 +162,7 @@ fn map_rust_child_diagnostic(
136 // FIXME: there's no good reason to use edit_map here.... 162 // FIXME: there's no good reason to use edit_map here....
137 changes: Some(edit_map), 163 changes: Some(edit_map),
138 document_changes: None, 164 document_changes: None,
165 change_annotations: None,
139 }), 166 }),
140 is_preferred: Some(true), 167 is_preferred: Some(true),
141 data: None, 168 data: None,
@@ -189,7 +216,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
189 let mut tags = Vec::new(); 216 let mut tags = Vec::new();
190 217
191 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { 218 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
192 let related = diagnostic_related_information(workspace_root, secondary_span); 219 let related = diagnostic_related_information(config, workspace_root, secondary_span);
193 if let Some(related) = related { 220 if let Some(related) = related {
194 subdiagnostics.push(SubDiagnostic { related, suggested_fix: None }); 221 subdiagnostics.push(SubDiagnostic { related, suggested_fix: None });
195 } 222 }
@@ -197,7 +224,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
197 224
198 let mut message = rd.message.clone(); 225 let mut message = rd.message.clone();
199 for child in &rd.children { 226 for child in &rd.children {
200 let child = map_rust_child_diagnostic(workspace_root, &child); 227 let child = map_rust_child_diagnostic(config, workspace_root, &child);
201 match child { 228 match child {
202 MappedRustChildDiagnostic::SubDiagnostic(sub) => { 229 MappedRustChildDiagnostic::SubDiagnostic(sub) => {
203 subdiagnostics.push(sub); 230 subdiagnostics.push(sub);
@@ -241,7 +268,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
241 primary_spans 268 primary_spans
242 .iter() 269 .iter()
243 .flat_map(|primary_span| { 270 .flat_map(|primary_span| {
244 let primary_location = primary_location(workspace_root, &primary_span); 271 let primary_location = primary_location(config, workspace_root, &primary_span);
245 272
246 let mut message = message.clone(); 273 let mut message = message.clone();
247 if needs_primary_span_label { 274 if needs_primary_span_label {
@@ -271,7 +298,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
271 // generated that code. 298 // generated that code.
272 let is_in_macro_call = i != 0; 299 let is_in_macro_call = i != 0;
273 300
274 let secondary_location = location(workspace_root, &span); 301 let secondary_location = location(config, workspace_root, &span);
275 if secondary_location == primary_location { 302 if secondary_location == primary_location {
276 continue; 303 continue;
277 } 304 }
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs
index 5b02b2598..712d5a9c2 100644
--- a/crates/rust-analyzer/src/from_proto.rs
+++ b/crates/rust-analyzer/src/from_proto.rs
@@ -42,27 +42,27 @@ pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Tex
42 TextRange::new(start, end) 42 TextRange::new(start, end)
43} 43}
44 44
45pub(crate) fn file_id(world: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> { 45pub(crate) fn file_id(snap: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> {
46 world.url_to_file_id(url) 46 snap.url_to_file_id(url)
47} 47}
48 48
49pub(crate) fn file_position( 49pub(crate) fn file_position(
50 world: &GlobalStateSnapshot, 50 snap: &GlobalStateSnapshot,
51 tdpp: lsp_types::TextDocumentPositionParams, 51 tdpp: lsp_types::TextDocumentPositionParams,
52) -> Result<FilePosition> { 52) -> Result<FilePosition> {
53 let file_id = file_id(world, &tdpp.text_document.uri)?; 53 let file_id = file_id(snap, &tdpp.text_document.uri)?;
54 let line_index = world.file_line_index(file_id)?; 54 let line_index = snap.file_line_index(file_id)?;
55 let offset = offset(&line_index, tdpp.position); 55 let offset = offset(&line_index, tdpp.position);
56 Ok(FilePosition { file_id, offset }) 56 Ok(FilePosition { file_id, offset })
57} 57}
58 58
59pub(crate) fn file_range( 59pub(crate) fn file_range(
60 world: &GlobalStateSnapshot, 60 snap: &GlobalStateSnapshot,
61 text_document_identifier: lsp_types::TextDocumentIdentifier, 61 text_document_identifier: lsp_types::TextDocumentIdentifier,
62 range: lsp_types::Range, 62 range: lsp_types::Range,
63) -> Result<FileRange> { 63) -> Result<FileRange> {
64 let file_id = file_id(world, &text_document_identifier.uri)?; 64 let file_id = file_id(snap, &text_document_identifier.uri)?;
65 let line_index = world.file_line_index(file_id)?; 65 let line_index = snap.file_line_index(file_id)?;
66 let range = text_range(&line_index, range); 66 let range = text_range(&line_index, range);
67 Ok(FileRange { file_id, range }) 67 Ok(FileRange { file_id, range })
68} 68}
@@ -82,7 +82,7 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind>
82} 82}
83 83
84pub(crate) fn annotation( 84pub(crate) fn annotation(
85 world: &GlobalStateSnapshot, 85 snap: &GlobalStateSnapshot,
86 code_lens: lsp_types::CodeLens, 86 code_lens: lsp_types::CodeLens,
87) -> Result<Annotation> { 87) -> Result<Annotation> {
88 let data = code_lens.data.unwrap(); 88 let data = code_lens.data.unwrap();
@@ -91,25 +91,25 @@ pub(crate) fn annotation(
91 match resolve { 91 match resolve {
92 lsp_ext::CodeLensResolveData::Impls(params) => { 92 lsp_ext::CodeLensResolveData::Impls(params) => {
93 let file_id = 93 let file_id =
94 world.url_to_file_id(&params.text_document_position_params.text_document.uri)?; 94 snap.url_to_file_id(&params.text_document_position_params.text_document.uri)?;
95 let line_index = world.file_line_index(file_id)?; 95 let line_index = snap.file_line_index(file_id)?;
96 96
97 Ok(Annotation { 97 Ok(Annotation {
98 range: text_range(&line_index, code_lens.range), 98 range: text_range(&line_index, code_lens.range),
99 kind: AnnotationKind::HasImpls { 99 kind: AnnotationKind::HasImpls {
100 position: file_position(world, params.text_document_position_params)?, 100 position: file_position(snap, params.text_document_position_params)?,
101 data: None, 101 data: None,
102 }, 102 },
103 }) 103 })
104 } 104 }
105 lsp_ext::CodeLensResolveData::References(params) => { 105 lsp_ext::CodeLensResolveData::References(params) => {
106 let file_id = world.url_to_file_id(&params.text_document.uri)?; 106 let file_id = snap.url_to_file_id(&params.text_document.uri)?;
107 let line_index = world.file_line_index(file_id)?; 107 let line_index = snap.file_line_index(file_id)?;
108 108
109 Ok(Annotation { 109 Ok(Annotation {
110 range: text_range(&line_index, code_lens.range), 110 range: text_range(&line_index, code_lens.range),
111 kind: AnnotationKind::HasReferences { 111 kind: AnnotationKind::HasReferences {
112 position: file_position(world, params)?, 112 position: file_position(snap, params)?,
113 data: None, 113 data: None,
114 }, 114 },
115 }) 115 })
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 52c249713..adeb7a97e 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -23,6 +23,7 @@ use crate::{
23 document::DocumentData, 23 document::DocumentData,
24 from_proto, 24 from_proto,
25 line_index::{LineEndings, LineIndex}, 25 line_index::{LineEndings, LineIndex},
26 lsp_ext,
26 main_loop::Task, 27 main_loop::Task,
27 op_queue::OpQueue, 28 op_queue::OpQueue,
28 reload::SourceRootConfig, 29 reload::SourceRootConfig,
@@ -32,20 +33,6 @@ use crate::{
32 Result, 33 Result,
33}; 34};
34 35
35#[derive(Eq, PartialEq, Copy, Clone)]
36pub(crate) enum Status {
37 Loading,
38 Ready { partial: bool },
39 Invalid,
40 NeedsReload,
41}
42
43impl Default for Status {
44 fn default() -> Self {
45 Status::Loading
46 }
47}
48
49// Enforces drop order 36// Enforces drop order
50pub(crate) struct Handle<H, C> { 37pub(crate) struct Handle<H, C> {
51 pub(crate) handle: H, 38 pub(crate) handle: H,
@@ -67,24 +54,37 @@ pub(crate) struct GlobalState {
67 req_queue: ReqQueue, 54 req_queue: ReqQueue,
68 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, 55 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
69 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, 56 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
70 pub(crate) vfs_config_version: u32,
71 pub(crate) flycheck: Vec<FlycheckHandle>,
72 pub(crate) flycheck_sender: Sender<flycheck::Message>,
73 pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
74 pub(crate) config: Arc<Config>, 57 pub(crate) config: Arc<Config>,
75 pub(crate) analysis_host: AnalysisHost, 58 pub(crate) analysis_host: AnalysisHost,
76 pub(crate) diagnostics: DiagnosticCollection, 59 pub(crate) diagnostics: DiagnosticCollection,
77 pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>, 60 pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>,
78 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>, 61 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
79 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
80 pub(crate) shutdown_requested: bool, 62 pub(crate) shutdown_requested: bool,
81 pub(crate) status: Status, 63 pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
82 pub(crate) source_root_config: SourceRootConfig, 64 pub(crate) source_root_config: SourceRootConfig,
83 pub(crate) proc_macro_client: Option<ProcMacroClient>, 65 pub(crate) proc_macro_client: Option<ProcMacroClient>,
66
67 pub(crate) flycheck: Vec<FlycheckHandle>,
68 pub(crate) flycheck_sender: Sender<flycheck::Message>,
69 pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
70
71 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
72 pub(crate) vfs_config_version: u32,
73 pub(crate) vfs_progress_config_version: u32,
74 pub(crate) vfs_progress_n_total: usize,
75 pub(crate) vfs_progress_n_done: usize,
76
77 /// For both `workspaces` and `workspace_build_data`, the field stores the
78 /// data we actually use, while the `OpQueue` stores the result of the last
79 /// fetch.
80 ///
81 /// If the fetch (partially) fails, we do not update the values.
84 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, 82 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
85 pub(crate) fetch_workspaces_queue: OpQueue<()>, 83 pub(crate) fetch_workspaces_queue: OpQueue<(), Vec<anyhow::Result<ProjectWorkspace>>>,
86 pub(crate) workspace_build_data: Option<BuildDataResult>, 84 pub(crate) workspace_build_data: Option<BuildDataResult>,
87 pub(crate) fetch_build_data_queue: OpQueue<BuildDataCollector>, 85 pub(crate) fetch_build_data_queue:
86 OpQueue<BuildDataCollector, Option<anyhow::Result<BuildDataResult>>>,
87
88 latest_requests: Arc<RwLock<LatestRequests>>, 88 latest_requests: Arc<RwLock<LatestRequests>>,
89} 89}
90 90
@@ -121,25 +121,32 @@ impl GlobalState {
121 GlobalState { 121 GlobalState {
122 sender, 122 sender,
123 req_queue: ReqQueue::default(), 123 req_queue: ReqQueue::default(),
124 vfs_config_version: 0,
125 task_pool, 124 task_pool,
126 loader, 125 loader,
127 flycheck: Vec::new(),
128 flycheck_sender,
129 flycheck_receiver,
130 config: Arc::new(config), 126 config: Arc::new(config),
131 analysis_host, 127 analysis_host,
132 diagnostics: Default::default(), 128 diagnostics: Default::default(),
133 mem_docs: FxHashMap::default(), 129 mem_docs: FxHashMap::default(),
134 semantic_tokens_cache: Arc::new(Default::default()), 130 semantic_tokens_cache: Arc::new(Default::default()),
135 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
136 shutdown_requested: false, 131 shutdown_requested: false,
137 status: Status::default(), 132 last_reported_status: None,
138 source_root_config: SourceRootConfig::default(), 133 source_root_config: SourceRootConfig::default(),
139 proc_macro_client: None, 134 proc_macro_client: None,
135
136 flycheck: Vec::new(),
137 flycheck_sender,
138 flycheck_receiver,
139
140 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
141 vfs_config_version: 0,
142 vfs_progress_config_version: 0,
143 vfs_progress_n_total: 0,
144 vfs_progress_n_done: 0,
145
140 workspaces: Arc::new(Vec::new()), 146 workspaces: Arc::new(Vec::new()),
141 fetch_workspaces_queue: OpQueue::default(), 147 fetch_workspaces_queue: OpQueue::default(),
142 workspace_build_data: None, 148 workspace_build_data: None,
149
143 fetch_build_data_queue: OpQueue::default(), 150 fetch_build_data_queue: OpQueue::default(),
144 latest_requests: Default::default(), 151 latest_requests: Default::default(),
145 } 152 }
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 53d29ddfc..1f59402e5 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -17,7 +17,7 @@ use lsp_server::ErrorCode;
17use lsp_types::{ 17use lsp_types::{
18 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, 18 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
19 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, 19 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
20 CodeActionKind, CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams, 20 CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams,
21 DocumentHighlight, FoldingRange, FoldingRangeParams, HoverContents, Location, NumberOrString, 21 DocumentHighlight, FoldingRange, FoldingRangeParams, HoverContents, Location, NumberOrString,
22 Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams, 22 Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensDeltaParams,
23 SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams, 23 SemanticTokensFullDeltaResult, SemanticTokensParams, SemanticTokensRangeParams,
@@ -36,7 +36,7 @@ use crate::{
36 diff::diff, 36 diff::diff,
37 from_proto, 37 from_proto,
38 global_state::{GlobalState, GlobalStateSnapshot}, 38 global_state::{GlobalState, GlobalStateSnapshot},
39 line_index::{LineEndings, LineIndex}, 39 line_index::LineEndings,
40 lsp_ext::{self, InlayHint, InlayHintsParams}, 40 lsp_ext::{self, InlayHint, InlayHintsParams},
41 lsp_utils::all_edits_are_disjoint, 41 lsp_utils::all_edits_are_disjoint,
42 to_proto, LspError, Result, 42 to_proto, LspError, Result,
@@ -231,7 +231,6 @@ pub(crate) fn handle_on_enter(
231 Ok(Some(edit)) 231 Ok(Some(edit))
232} 232}
233 233
234// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
235pub(crate) fn handle_on_type_formatting( 234pub(crate) fn handle_on_type_formatting(
236 snap: GlobalStateSnapshot, 235 snap: GlobalStateSnapshot,
237 params: lsp_types::DocumentOnTypeFormattingParams, 236 params: lsp_types::DocumentOnTypeFormattingParams,
@@ -665,10 +664,13 @@ pub(crate) fn handle_completion(
665 }; 664 };
666 let line_index = snap.file_line_index(position.file_id)?; 665 let line_index = snap.file_line_index(position.file_id)?;
667 666
667 let insert_replace_support =
668 snap.config.insert_replace_support().then(|| text_document_position.position);
668 let items: Vec<CompletionItem> = items 669 let items: Vec<CompletionItem> = items
669 .into_iter() 670 .into_iter()
670 .flat_map(|item| { 671 .flat_map(|item| {
671 let mut new_completion_items = to_proto::completion_item(&line_index, item.clone()); 672 let mut new_completion_items =
673 to_proto::completion_item(insert_replace_support, &line_index, item.clone());
672 674
673 if completion_config.enable_imports_on_the_fly { 675 if completion_config.enable_imports_on_the_fly {
674 for new_item in &mut new_completion_items { 676 for new_item in &mut new_completion_items {
@@ -927,19 +929,22 @@ pub(crate) fn handle_formatting(
927 let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default(); 929 let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default();
928 930
929 if !output.status.success() { 931 if !output.status.success() {
930 match output.status.code() { 932 let rustfmt_not_installed =
931 Some(1) if !captured_stderr.contains("not installed") => { 933 captured_stderr.contains("not installed") || captured_stderr.contains("not available");
934
935 return match output.status.code() {
936 Some(1) if !rustfmt_not_installed => {
932 // While `rustfmt` doesn't have a specific exit code for parse errors this is the 937 // While `rustfmt` doesn't have a specific exit code for parse errors this is the
933 // likely cause exiting with 1. Most Language Servers swallow parse errors on 938 // likely cause exiting with 1. Most Language Servers swallow parse errors on
934 // formatting because otherwise an error is surfaced to the user on top of the 939 // formatting because otherwise an error is surfaced to the user on top of the
935 // syntax error diagnostics they're already receiving. This is especially jarring 940 // syntax error diagnostics they're already receiving. This is especially jarring
936 // if they have format on save enabled. 941 // if they have format on save enabled.
937 log::info!("rustfmt exited with status 1, assuming parse error and ignoring"); 942 log::info!("rustfmt exited with status 1, assuming parse error and ignoring");
938 return Ok(None); 943 Ok(None)
939 } 944 }
940 _ => { 945 _ => {
941 // Something else happened - e.g. `rustfmt` is missing or caught a signal 946 // Something else happened - e.g. `rustfmt` is missing or caught a signal
942 return Err(LspError::new( 947 Err(LspError::new(
943 -32900, 948 -32900,
944 format!( 949 format!(
945 r#"rustfmt exited with: 950 r#"rustfmt exited with:
@@ -949,9 +954,9 @@ pub(crate) fn handle_formatting(
949 output.status, captured_stdout, captured_stderr, 954 output.status, captured_stdout, captured_stderr,
950 ), 955 ),
951 ) 956 )
952 .into()); 957 .into())
953 } 958 }
954 } 959 };
955 } 960 }
956 961
957 let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout); 962 let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout);
@@ -977,84 +982,52 @@ pub(crate) fn handle_code_action(
977 params: lsp_types::CodeActionParams, 982 params: lsp_types::CodeActionParams,
978) -> Result<Option<Vec<lsp_ext::CodeAction>>> { 983) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
979 let _p = profile::span("handle_code_action"); 984 let _p = profile::span("handle_code_action");
980 // We intentionally don't support command-based actions, as those either 985
981 // requires custom client-code anyway, or requires server-initiated edits.
982 // Server initiated edits break causality, so we avoid those as well.
983 if !snap.config.code_action_literals() { 986 if !snap.config.code_action_literals() {
987 // We intentionally don't support command-based actions, as those either
988 // require either custom client-code or server-initiated edits. Server
989 // initiated edits break causality, so we avoid those.
984 return Ok(None); 990 return Ok(None);
985 } 991 }
986 992
987 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?; 993 let line_index =
988 let line_index = snap.file_line_index(file_id)?; 994 snap.file_line_index(from_proto::file_id(&snap, &params.text_document.uri)?)?;
989 let range = from_proto::text_range(&line_index, params.range); 995 let frange = from_proto::file_range(&snap, params.text_document.clone(), params.range)?;
990 let frange = FileRange { file_id, range };
991 996
992 let mut assists_config = snap.config.assist(); 997 let mut assists_config = snap.config.assist();
993 assists_config.allowed = params 998 assists_config.allowed = params
994 .clone()
995 .context 999 .context
996 .only 1000 .only
1001 .clone()
997 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); 1002 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
998 1003
999 let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); 1004 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
1000 1005
1001 let include_quick_fixes = match &params.context.only { 1006 let code_action_resolve_cap = snap.config.code_action_resolve();
1002 Some(v) => v.iter().any(|it| { 1007 let assists = snap.analysis.assists_with_fixes(
1003 it == &lsp_types::CodeActionKind::EMPTY || it == &lsp_types::CodeActionKind::QUICKFIX 1008 &assists_config,
1004 }), 1009 &snap.config.diagnostics(),
1005 None => true, 1010 !code_action_resolve_cap,
1006 }; 1011 frange,
1007 if include_quick_fixes { 1012 )?;
1008 add_quick_fixes(&snap, frange, &line_index, &mut res)?; 1013 for (index, assist) in assists.into_iter().enumerate() {
1009 } 1014 let resolve_data =
1010 1015 if code_action_resolve_cap { Some((index, params.clone())) } else { None };
1011 if snap.config.code_action_resolve() { 1016 let code_action = to_proto::code_action(&snap, assist, resolve_data)?;
1012 for (index, assist) in 1017 res.push(code_action)
1013 snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate()
1014 {
1015 res.push(to_proto::unresolved_code_action(&snap, params.clone(), assist, index)?);
1016 }
1017 } else {
1018 for assist in snap.analysis.assists(&assists_config, true, frange)?.into_iter() {
1019 res.push(to_proto::resolved_code_action(&snap, assist)?);
1020 }
1021 }
1022
1023 Ok(Some(res))
1024}
1025
1026fn add_quick_fixes(
1027 snap: &GlobalStateSnapshot,
1028 frange: FileRange,
1029 line_index: &LineIndex,
1030 acc: &mut Vec<lsp_ext::CodeAction>,
1031) -> Result<()> {
1032 let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics(), frange.file_id)?;
1033
1034 for fix in diagnostics
1035 .into_iter()
1036 .filter_map(|d| d.fix)
1037 .filter(|fix| fix.fix_trigger_range.intersect(frange.range).is_some())
1038 {
1039 let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
1040 let action = lsp_ext::CodeAction {
1041 title: fix.label.to_string(),
1042 group: None,
1043 kind: Some(CodeActionKind::QUICKFIX),
1044 edit: Some(edit),
1045 is_preferred: Some(false),
1046 data: None,
1047 };
1048 acc.push(action);
1049 } 1018 }
1050 1019
1020 // Fixes from `cargo check`.
1051 for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() { 1021 for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() {
1022 // FIXME: this mapping is awkward and shouldn't exist. Refactor
1023 // `snap.check_fixes` to not convert to LSP prematurely.
1052 let fix_range = from_proto::text_range(&line_index, fix.range); 1024 let fix_range = from_proto::text_range(&line_index, fix.range);
1053 if fix_range.intersect(frange.range).is_some() { 1025 if fix_range.intersect(frange.range).is_some() {
1054 acc.push(fix.action.clone()); 1026 res.push(fix.action.clone());
1055 } 1027 }
1056 } 1028 }
1057 Ok(()) 1029
1030 Ok(Some(res))
1058} 1031}
1059 1032
1060pub(crate) fn handle_code_action_resolve( 1033pub(crate) fn handle_code_action_resolve(
@@ -1079,12 +1052,18 @@ pub(crate) fn handle_code_action_resolve(
1079 .only 1052 .only
1080 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); 1053 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
1081 1054
1082 let assists = snap.analysis.assists(&assists_config, true, frange)?; 1055 let assists = snap.analysis.assists_with_fixes(
1056 &assists_config,
1057 &snap.config.diagnostics(),
1058 true,
1059 frange,
1060 )?;
1061
1083 let (id, index) = split_once(&params.id, ':').unwrap(); 1062 let (id, index) = split_once(&params.id, ':').unwrap();
1084 let index = index.parse::<usize>().unwrap(); 1063 let index = index.parse::<usize>().unwrap();
1085 let assist = &assists[index]; 1064 let assist = &assists[index];
1086 assert!(assist.id.0 == id); 1065 assert!(assist.id.0 == id);
1087 let edit = to_proto::resolved_code_action(&snap, assist.clone())?.edit; 1066 let edit = to_proto::code_action(&snap, assist.clone(), None)?.edit;
1088 code_action.edit = edit; 1067 code_action.edit = edit;
1089 Ok(code_action) 1068 Ok(code_action)
1090} 1069}
@@ -1203,7 +1182,7 @@ pub(crate) fn publish_diagnostics(
1203 1182
1204 let diagnostics: Vec<Diagnostic> = snap 1183 let diagnostics: Vec<Diagnostic> = snap
1205 .analysis 1184 .analysis
1206 .diagnostics(&snap.config.diagnostics(), file_id)? 1185 .diagnostics(&snap.config.diagnostics(), false, file_id)?
1207 .into_iter() 1186 .into_iter()
1208 .map(|d| Diagnostic { 1187 .map(|d| Diagnostic {
1209 range: to_proto::range(&line_index, d.range), 1188 range: to_proto::range(&line_index, d.range),
@@ -1431,7 +1410,7 @@ pub(crate) fn handle_open_cargo_toml(
1431pub(crate) fn handle_move_item( 1410pub(crate) fn handle_move_item(
1432 snap: GlobalStateSnapshot, 1411 snap: GlobalStateSnapshot,
1433 params: lsp_ext::MoveItemParams, 1412 params: lsp_ext::MoveItemParams,
1434) -> Result<Option<lsp_types::TextDocumentEdit>> { 1413) -> Result<Vec<lsp_ext::SnippetTextEdit>> {
1435 let _p = profile::span("handle_move_item"); 1414 let _p = profile::span("handle_move_item");
1436 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?; 1415 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1437 let range = from_proto::file_range(&snap, params.text_document, params.range)?; 1416 let range = from_proto::file_range(&snap, params.text_document, params.range)?;
@@ -1442,8 +1421,11 @@ pub(crate) fn handle_move_item(
1442 }; 1421 };
1443 1422
1444 match snap.analysis.move_item(range, direction)? { 1423 match snap.analysis.move_item(range, direction)? {
1445 Some(text_edit) => Ok(Some(to_proto::text_document_edit(&snap, file_id, text_edit)?)), 1424 Some(text_edit) => {
1446 None => Ok(None), 1425 let line_index = snap.file_line_index(file_id)?;
1426 Ok(to_proto::snippet_text_edit_vec(&line_index, true, text_edit))
1427 }
1428 None => Ok(vec![]),
1447 } 1429 }
1448} 1430}
1449 1431
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 0e1fec209..b8835a534 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -241,26 +241,26 @@ pub struct SsrParams {
241 pub selections: Vec<lsp_types::Range>, 241 pub selections: Vec<lsp_types::Range>,
242} 242}
243 243
244pub enum StatusNotification {} 244pub enum ServerStatusNotification {}
245 245
246#[derive(Serialize, Deserialize)] 246impl Notification for ServerStatusNotification {
247#[serde(rename_all = "camelCase")] 247 type Params = ServerStatusParams;
248pub enum Status { 248 const METHOD: &'static str = "experimental/serverStatus";
249 Loading,
250 ReadyPartial,
251 Ready,
252 NeedsReload,
253 Invalid,
254} 249}
255 250
256#[derive(Deserialize, Serialize)] 251#[derive(Deserialize, Serialize, PartialEq, Eq, Clone)]
257pub struct StatusParams { 252pub struct ServerStatusParams {
258 pub status: Status, 253 pub health: Health,
254 pub quiescent: bool,
255 pub message: Option<String>,
259} 256}
260 257
261impl Notification for StatusNotification { 258#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
262 type Params = StatusParams; 259#[serde(rename_all = "camelCase")]
263 const METHOD: &'static str = "rust-analyzer/status"; 260pub enum Health {
261 Ok,
262 Warning,
263 Error,
264} 264}
265 265
266pub enum CodeActionRequest {} 266pub enum CodeActionRequest {}
@@ -312,6 +312,9 @@ pub struct SnippetWorkspaceEdit {
312 pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>, 312 pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>,
313 #[serde(skip_serializing_if = "Option::is_none")] 313 #[serde(skip_serializing_if = "Option::is_none")]
314 pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>, 314 pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>,
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub change_annotations:
317 Option<HashMap<lsp_types::ChangeAnnotationIdentifier, lsp_types::ChangeAnnotation>>,
315} 318}
316 319
317#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] 320#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
@@ -335,6 +338,9 @@ pub struct SnippetTextEdit {
335 pub new_text: String, 338 pub new_text: String,
336 #[serde(skip_serializing_if = "Option::is_none")] 339 #[serde(skip_serializing_if = "Option::is_none")]
337 pub insert_text_format: Option<lsp_types::InsertTextFormat>, 340 pub insert_text_format: Option<lsp_types::InsertTextFormat>,
341 /// The annotation id if this is an annotated
342 #[serde(skip_serializing_if = "Option::is_none")]
343 pub annotation_id: Option<lsp_types::ChangeAnnotationIdentifier>,
338} 344}
339 345
340pub enum HoverRequest {} 346pub enum HoverRequest {}
@@ -407,7 +413,7 @@ pub enum MoveItem {}
407 413
408impl Request for MoveItem { 414impl Request for MoveItem {
409 type Params = MoveItemParams; 415 type Params = MoveItemParams;
410 type Result = Option<lsp_types::TextDocumentEdit>; 416 type Result = Vec<SnippetTextEdit>;
411 const METHOD: &'static str = "experimental/moveItem"; 417 const METHOD: &'static str = "experimental/moveItem";
412} 418}
413 419
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 2ac487632..73c4193e8 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -150,8 +150,16 @@ pub(crate) fn all_edits_are_disjoint(
150 edit_ranges.push(edit.range); 150 edit_ranges.push(edit.range);
151 } 151 }
152 Some(lsp_types::CompletionTextEdit::InsertAndReplace(edit)) => { 152 Some(lsp_types::CompletionTextEdit::InsertAndReplace(edit)) => {
153 edit_ranges.push(edit.insert); 153 let replace = edit.replace;
154 edit_ranges.push(edit.replace); 154 let insert = edit.insert;
155 if replace.start != insert.start
156 || insert.start > insert.end
157 || insert.end > replace.end
158 {
159 // insert has to be a prefix of replace but it is not
160 return false;
161 }
162 edit_ranges.push(replace);
155 } 163 }
156 None => {} 164 None => {}
157 } 165 }
@@ -314,18 +322,6 @@ mod tests {
314 Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit { 322 Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit {
315 new_text: "new_text".to_string(), 323 new_text: "new_text".to_string(),
316 insert: disjoint_edit.range, 324 insert: disjoint_edit.range,
317 replace: joint_edit.range,
318 }));
319 completion_with_joint_edits.additional_text_edits = None;
320 assert!(
321 !all_edits_are_disjoint(&completion_with_joint_edits, &[]),
322 "Completion with disjoint edits fails the validation even with empty extra edits"
323 );
324
325 completion_with_joint_edits.text_edit =
326 Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit {
327 new_text: "new_text".to_string(),
328 insert: disjoint_edit.range,
329 replace: disjoint_edit_2.range, 325 replace: disjoint_edit_2.range,
330 })); 326 }));
331 completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit]); 327 completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit]);
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index e88f16cc1..6ea775d68 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -2,6 +2,7 @@
2//! requests/replies and notifications back to the client. 2//! requests/replies and notifications back to the client.
3use std::{ 3use std::{
4 env, fmt, 4 env, fmt,
5 sync::Arc,
5 time::{Duration, Instant}, 6 time::{Duration, Instant},
6}; 7};
7 8
@@ -12,6 +13,7 @@ use ide::{Canceled, FileId};
12use ide_db::base_db::VfsPath; 13use ide_db::base_db::VfsPath;
13use lsp_server::{Connection, Notification, Request, Response}; 14use lsp_server::{Connection, Notification, Request, Response};
14use lsp_types::notification::Notification as _; 15use lsp_types::notification::Notification as _;
16use project_model::BuildDataCollector;
15use vfs::ChangeKind; 17use vfs::ChangeKind;
16 18
17use crate::{ 19use crate::{
@@ -19,7 +21,7 @@ use crate::{
19 dispatch::{NotificationDispatcher, RequestDispatcher}, 21 dispatch::{NotificationDispatcher, RequestDispatcher},
20 document::DocumentData, 22 document::DocumentData,
21 from_proto, 23 from_proto,
22 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, 24 global_state::{file_id_to_url, url_to_file_id, GlobalState},
23 handlers, lsp_ext, 25 handlers, lsp_ext,
24 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, 26 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
25 reload::{BuildDataProgress, ProjectWorkspaceProgress}, 27 reload::{BuildDataProgress, ProjectWorkspaceProgress},
@@ -187,7 +189,7 @@ impl GlobalState {
187 log::info!("task queue len: {}", task_queue_len); 189 log::info!("task queue len: {}", task_queue_len);
188 } 190 }
189 191
190 let mut new_status = self.status; 192 let was_quiescent = self.is_quiescent();
191 match event { 193 match event {
192 Event::Lsp(msg) => match msg { 194 Event::Lsp(msg) => match msg {
193 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?, 195 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?,
@@ -227,12 +229,26 @@ impl GlobalState {
227 (Progress::Report, Some(msg)) 229 (Progress::Report, Some(msg))
228 } 230 }
229 ProjectWorkspaceProgress::End(workspaces) => { 231 ProjectWorkspaceProgress::End(workspaces) => {
230 self.fetch_workspaces_completed(); 232 self.fetch_workspaces_completed(workspaces);
231 self.switch_workspaces(workspaces, None); 233
234 let old = Arc::clone(&self.workspaces);
235 self.switch_workspaces();
236 let workspaces_updated = !Arc::ptr_eq(&old, &self.workspaces);
237
238 if self.config.run_build_scripts() && workspaces_updated {
239 let mut collector =
240 BuildDataCollector::new(self.config.wrap_rustc());
241 for ws in self.workspaces.iter() {
242 ws.collect_build_data_configs(&mut collector);
243 }
244 self.fetch_build_data_request(collector)
245 }
246
232 (Progress::End, None) 247 (Progress::End, None)
233 } 248 }
234 }; 249 };
235 self.report_progress("fetching", state, msg, None); 250
251 self.report_progress("Fetching", state, msg, None);
236 } 252 }
237 Task::FetchBuildData(progress) => { 253 Task::FetchBuildData(progress) => {
238 let (state, msg) = match progress { 254 let (state, msg) = match progress {
@@ -240,19 +256,21 @@ impl GlobalState {
240 BuildDataProgress::Report(msg) => { 256 BuildDataProgress::Report(msg) => {
241 (Some(Progress::Report), Some(msg)) 257 (Some(Progress::Report), Some(msg))
242 } 258 }
243 BuildDataProgress::End(collector) => { 259 BuildDataProgress::End(build_data_result) => {
244 self.fetch_build_data_completed(); 260 self.fetch_build_data_completed(build_data_result);
245 let workspaces = 261
246 (*self.workspaces).clone().into_iter().map(Ok).collect(); 262 self.switch_workspaces();
247 self.switch_workspaces(workspaces, Some(collector)); 263
248 (Some(Progress::End), None) 264 (Some(Progress::End), None)
249 } 265 }
250 }; 266 };
267
251 if let Some(state) = state { 268 if let Some(state) = state {
252 self.report_progress("loading", state, msg, None); 269 self.report_progress("Loading", state, msg, None);
253 } 270 }
254 } 271 }
255 } 272 }
273
256 // Coalesce multiple task events into one loop turn 274 // Coalesce multiple task events into one loop turn
257 task = match self.task_pool.receiver.try_recv() { 275 task = match self.task_pool.receiver.try_recv() {
258 Ok(task) => task, 276 Ok(task) => task,
@@ -280,7 +298,7 @@ impl GlobalState {
280 } 298 }
281 }; 299 };
282 300
283 self.report_progress("indexing", state, message, Some(fraction)); 301 self.report_progress("Indexing", state, message, Some(fraction));
284 } 302 }
285 } 303 }
286 Event::Vfs(mut task) => { 304 Event::Vfs(mut task) => {
@@ -298,30 +316,25 @@ impl GlobalState {
298 } 316 }
299 vfs::loader::Message::Progress { n_total, n_done, config_version } => { 317 vfs::loader::Message::Progress { n_total, n_done, config_version } => {
300 always!(config_version <= self.vfs_config_version); 318 always!(config_version <= self.vfs_config_version);
301 if n_total == 0 { 319
302 new_status = Status::Invalid; 320 self.vfs_progress_config_version = config_version;
321 self.vfs_progress_n_total = n_total;
322 self.vfs_progress_n_done = n_done;
323
324 let state = if n_done == 0 {
325 Progress::Begin
326 } else if n_done < n_total {
327 Progress::Report
303 } else { 328 } else {
304 let state = if n_done == 0 { 329 assert_eq!(n_done, n_total);
305 new_status = Status::Loading; 330 Progress::End
306 Progress::Begin 331 };
307 } else if n_done < n_total { 332 self.report_progress(
308 Progress::Report 333 "Roots Scanned",
309 } else { 334 state,
310 assert_eq!(n_done, n_total); 335 Some(format!("{}/{}", n_done, n_total)),
311 new_status = Status::Ready { 336 Some(Progress::fraction(n_done, n_total)),
312 partial: self.config.run_build_scripts() 337 )
313 && self.workspace_build_data.is_none()
314 || config_version < self.vfs_config_version,
315 };
316 Progress::End
317 };
318 self.report_progress(
319 "roots scanned",
320 state,
321 Some(format!("{}/{}", n_done, n_total)),
322 Some(Progress::fraction(n_done, n_total)),
323 )
324 }
325 } 338 }
326 } 339 }
327 // Coalesce many VFS event into a single loop turn 340 // Coalesce many VFS event into a single loop turn
@@ -397,18 +410,14 @@ impl GlobalState {
397 } 410 }
398 411
399 let state_changed = self.process_changes(); 412 let state_changed = self.process_changes();
400 let prev_status = self.status; 413
401 if prev_status != new_status { 414 if self.is_quiescent() && !was_quiescent {
402 self.transition(new_status);
403 }
404 let is_ready = matches!(self.status, Status::Ready { .. });
405 if prev_status == Status::Loading && is_ready {
406 for flycheck in &self.flycheck { 415 for flycheck in &self.flycheck {
407 flycheck.update(); 416 flycheck.update();
408 } 417 }
409 } 418 }
410 419
411 if is_ready && (state_changed || prev_status == Status::Loading) { 420 if self.is_quiescent() && (!was_quiescent || state_changed) {
412 self.update_file_notifications_on_threadpool(); 421 self.update_file_notifications_on_threadpool();
413 422
414 // Refresh semantic tokens if the client supports it. 423 // Refresh semantic tokens if the client supports it.
@@ -437,9 +446,13 @@ impl GlobalState {
437 } 446 }
438 } 447 }
439 448
440 self.fetch_workspaces_if_needed(); 449 if self.config.cargo_autoreload() {
450 self.fetch_workspaces_if_needed();
451 }
441 self.fetch_build_data_if_needed(); 452 self.fetch_build_data_if_needed();
442 453
454 self.report_new_status_if_needed();
455
443 let loop_duration = loop_start.elapsed(); 456 let loop_duration = loop_start.elapsed();
444 if loop_duration > Duration::from_millis(100) { 457 if loop_duration > Duration::from_millis(100) {
445 log::warn!("overly long loop turn: {:?}", loop_duration); 458 log::warn!("overly long loop turn: {:?}", loop_duration);
@@ -466,18 +479,23 @@ impl GlobalState {
466 return Ok(()); 479 return Ok(());
467 } 480 }
468 481
469 if self.status == Status::Loading && req.method != "shutdown" { 482 // Avoid flashing a bunch of unresolved references during initial load.
483 if self.workspaces.is_empty() && !self.is_quiescent() {
470 self.respond(lsp_server::Response::new_err( 484 self.respond(lsp_server::Response::new_err(
471 req.id, 485 req.id,
472 // FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion) 486 // FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion)
473 lsp_server::ErrorCode::ContentModified as i32, 487 lsp_server::ErrorCode::ContentModified as i32,
474 "Rust Analyzer is still loading...".to_owned(), 488 "waiting for cargo metadata or cargo check".to_owned(),
475 )); 489 ));
476 return Ok(()); 490 return Ok(());
477 } 491 }
478 492
479 RequestDispatcher { req: Some(req), global_state: self } 493 RequestDispatcher { req: Some(req), global_state: self }
480 .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces_request()))? 494 .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| {
495 s.fetch_workspaces_request();
496 s.fetch_workspaces_if_needed();
497 Ok(())
498 })?
481 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? 499 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
482 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? 500 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
483 .on_sync::<lsp_types::request::Shutdown>(|s, ()| { 501 .on_sync::<lsp_types::request::Shutdown>(|s, ()| {
diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs
index 865eaae9b..35eaffba8 100644
--- a/crates/rust-analyzer/src/markdown.rs
+++ b/crates/rust-analyzer/src/markdown.rs
@@ -1,17 +1,7 @@
1//! Transforms markdown 1//! Transforms markdown
2use ide_db::helpers::rust_doc::is_rust_fence;
2 3
3const RUSTDOC_FENCE: &str = "```"; 4const RUSTDOC_FENCE: &str = "```";
4const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] = &[
5 "",
6 "rust",
7 "should_panic",
8 "ignore",
9 "no_run",
10 "compile_fail",
11 "edition2015",
12 "edition2018",
13 "edition2021",
14];
15 5
16pub(crate) fn format_docs(src: &str) -> String { 6pub(crate) fn format_docs(src: &str) -> String {
17 let mut processed_lines = Vec::new(); 7 let mut processed_lines = Vec::new();
@@ -27,9 +17,7 @@ pub(crate) fn format_docs(src: &str) -> String {
27 in_code_block ^= true; 17 in_code_block ^= true;
28 18
29 if in_code_block { 19 if in_code_block {
30 is_rust = header 20 is_rust = is_rust_fence(header);
31 .split(',')
32 .all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC.contains(&sub.trim()));
33 21
34 if is_rust { 22 if is_rust {
35 line = "```rust"; 23 line = "```rust";
@@ -82,6 +70,12 @@ mod tests {
82 } 70 }
83 71
84 #[test] 72 #[test]
73 fn test_format_docs_handles_error_codes() {
74 let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```";
75 assert_eq!(format_docs(comment), "```rust\nlet b = 0 as *const _;\n```");
76 }
77
78 #[test]
85 fn test_format_docs_skips_comments_in_rust_block() { 79 fn test_format_docs_skips_comments_in_rust_block() {
86 let comment = 80 let comment =
87 "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```"; 81 "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```";
diff --git a/crates/rust-analyzer/src/op_queue.rs b/crates/rust-analyzer/src/op_queue.rs
index 761b9ad39..1d612a933 100644
--- a/crates/rust-analyzer/src/op_queue.rs
+++ b/crates/rust-analyzer/src/op_queue.rs
@@ -1,29 +1,43 @@
1//! Bookkeeping to make sure only one long-running operation is executed. 1//! Bookkeeping to make sure only one long-running operation is being executed
2//! at a time.
2 3
3pub(crate) struct OpQueue<D> { 4pub(crate) struct OpQueue<Args, Output> {
4 op_scheduled: Option<D>, 5 op_requested: Option<Args>,
5 op_in_progress: bool, 6 op_in_progress: bool,
7 last_op_result: Output,
6} 8}
7 9
8impl<D> Default for OpQueue<D> { 10impl<Args, Output: Default> Default for OpQueue<Args, Output> {
9 fn default() -> Self { 11 fn default() -> Self {
10 Self { op_scheduled: None, op_in_progress: false } 12 Self { op_requested: None, op_in_progress: false, last_op_result: Default::default() }
11 } 13 }
12} 14}
13 15
14impl<D> OpQueue<D> { 16impl<Args, Output> OpQueue<Args, Output> {
15 pub(crate) fn request_op(&mut self, data: D) { 17 pub(crate) fn request_op(&mut self, data: Args) {
16 self.op_scheduled = Some(data); 18 self.op_requested = Some(data);
17 } 19 }
18 pub(crate) fn should_start_op(&mut self) -> Option<D> { 20 pub(crate) fn should_start_op(&mut self) -> Option<Args> {
19 if self.op_in_progress { 21 if self.op_in_progress {
20 return None; 22 return None;
21 } 23 }
22 self.op_in_progress = self.op_scheduled.is_some(); 24 self.op_in_progress = self.op_requested.is_some();
23 self.op_scheduled.take() 25 self.op_requested.take()
24 } 26 }
25 pub(crate) fn op_completed(&mut self) { 27 pub(crate) fn op_completed(&mut self, result: Output) {
26 assert!(self.op_in_progress); 28 assert!(self.op_in_progress);
27 self.op_in_progress = false; 29 self.op_in_progress = false;
30 self.last_op_result = result;
31 }
32
33 #[allow(unused)]
34 pub(crate) fn last_op_result(&self) -> &Output {
35 &self.last_op_result
36 }
37 pub(crate) fn op_in_progress(&self) -> bool {
38 self.op_in_progress
39 }
40 pub(crate) fn op_requested(&self) -> bool {
41 self.op_requested.is_some()
28 } 42 }
29} 43}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 76fdbcddd..0ae2758cc 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -9,11 +9,10 @@ use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
9 9
10use crate::{ 10use crate::{
11 config::{Config, FilesWatcher, LinkedProject}, 11 config::{Config, FilesWatcher, LinkedProject},
12 global_state::{GlobalState, Status}, 12 global_state::GlobalState,
13 lsp_ext, 13 lsp_ext,
14 main_loop::Task, 14 main_loop::Task,
15}; 15};
16use lsp_ext::StatusParams;
17 16
18#[derive(Debug)] 17#[derive(Debug)]
19pub(crate) enum ProjectWorkspaceProgress { 18pub(crate) enum ProjectWorkspaceProgress {
@@ -30,6 +29,13 @@ pub(crate) enum BuildDataProgress {
30} 29}
31 30
32impl GlobalState { 31impl GlobalState {
32 pub(crate) fn is_quiescent(&self) -> bool {
33 !(self.fetch_workspaces_queue.op_in_progress()
34 || self.fetch_build_data_queue.op_in_progress()
35 || self.vfs_progress_config_version < self.vfs_config_version
36 || self.vfs_progress_n_done < self.vfs_progress_n_total)
37 }
38
33 pub(crate) fn update_configuration(&mut self, config: Config) { 39 pub(crate) fn update_configuration(&mut self, config: Config) {
34 let _p = profile::span("GlobalState::update_configuration"); 40 let _p = profile::span("GlobalState::update_configuration");
35 let old_config = mem::replace(&mut self.config, Arc::new(config)); 41 let old_config = mem::replace(&mut self.config, Arc::new(config));
@@ -46,25 +52,17 @@ impl GlobalState {
46 if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) { 52 if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
47 return; 53 return;
48 } 54 }
49 match self.status {
50 Status::Loading | Status::NeedsReload => return,
51 Status::Ready { .. } | Status::Invalid => (),
52 }
53 log::info!( 55 log::info!(
54 "Reloading workspace because of the following changes: {}", 56 "Requesting workspace reload because of the following changes: {}",
55 itertools::join( 57 itertools::join(
56 changes 58 changes
57 .iter() 59 .iter()
58 .filter(|(path, kind)| is_interesting(path, *kind)) 60 .filter(|(path, kind)| is_interesting(path, *kind))
59 .map(|(path, kind)| format!("{}/{:?}", path.display(), kind)), 61 .map(|(path, kind)| format!("{}: {:?}", path.display(), kind)),
60 ", " 62 ", "
61 ) 63 )
62 ); 64 );
63 if self.config.cargo_autoreload() { 65 self.fetch_workspaces_request();
64 self.fetch_workspaces_request();
65 } else {
66 self.transition(Status::NeedsReload);
67 }
68 66
69 fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool { 67 fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
70 const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"]; 68 const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
@@ -101,46 +99,41 @@ impl GlobalState {
101 false 99 false
102 } 100 }
103 } 101 }
104 pub(crate) fn transition(&mut self, new_status: Status) { 102 pub(crate) fn report_new_status_if_needed(&mut self) {
105 self.status = new_status; 103 let mut status = lsp_ext::ServerStatusParams {
106 if self.config.status_notification() { 104 health: lsp_ext::Health::Ok,
107 let lsp_status = match new_status { 105 quiescent: self.is_quiescent(),
108 Status::Loading => lsp_ext::Status::Loading, 106 message: None,
109 Status::Ready { partial: true } => lsp_ext::Status::ReadyPartial, 107 };
110 Status::Ready { partial: false } => lsp_ext::Status::Ready, 108
111 Status::Invalid => lsp_ext::Status::Invalid, 109 if let Some(error) = self.build_data_error() {
112 Status::NeedsReload => lsp_ext::Status::NeedsReload, 110 status.health = lsp_ext::Health::Warning;
113 }; 111 status.message = Some(error)
114 self.send_notification::<lsp_ext::StatusNotification>(StatusParams { 112 }
115 status: lsp_status, 113 if !self.config.cargo_autoreload()
116 }); 114 && self.is_quiescent()
115 && self.fetch_workspaces_queue.op_requested()
116 {
117 status.health = lsp_ext::Health::Warning;
118 status.message = Some("Workspace reload required".to_string())
117 } 119 }
118 }
119 120
120 pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) { 121 if let Some(error) = self.fetch_workspace_error() {
121 self.fetch_build_data_queue.request_op(build_data_collector); 122 status.health = lsp_ext::Health::Error;
122 } 123 status.message = Some(error)
124 }
123 125
124 pub(crate) fn fetch_build_data_if_needed(&mut self) { 126 if self.last_reported_status.as_ref() != Some(&status) {
125 let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() { 127 self.last_reported_status = Some(status.clone());
126 Some(it) => it,
127 None => return,
128 };
129 self.task_pool.handle.spawn_with_sender(move |sender| {
130 sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
131 128
132 let progress = { 129 if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message) {
133 let sender = sender.clone(); 130 self.show_message(lsp_types::MessageType::Error, message.clone());
134 move |msg| { 131 }
135 sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap() 132
136 } 133 if self.config.server_status_notification() {
137 }; 134 self.send_notification::<lsp_ext::ServerStatusNotification>(status);
138 let res = build_data_collector.collect(&progress); 135 }
139 sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap(); 136 }
140 });
141 }
142 pub(crate) fn fetch_build_data_completed(&mut self) {
143 self.fetch_build_data_queue.op_completed()
144 } 137 }
145 138
146 pub(crate) fn fetch_workspaces_request(&mut self) { 139 pub(crate) fn fetch_workspaces_request(&mut self) {
@@ -194,54 +187,69 @@ impl GlobalState {
194 } 187 }
195 }); 188 });
196 } 189 }
197 pub(crate) fn fetch_workspaces_completed(&mut self) { 190 pub(crate) fn fetch_workspaces_completed(
198 self.fetch_workspaces_queue.op_completed() 191 &mut self,
192 workspaces: Vec<anyhow::Result<ProjectWorkspace>>,
193 ) {
194 self.fetch_workspaces_queue.op_completed(workspaces)
195 }
196
197 pub(crate) fn fetch_build_data_request(&mut self, build_data_collector: BuildDataCollector) {
198 self.fetch_build_data_queue.request_op(build_data_collector);
199 } 199 }
200 pub(crate) fn fetch_build_data_if_needed(&mut self) {
201 let mut build_data_collector = match self.fetch_build_data_queue.should_start_op() {
202 Some(it) => it,
203 None => return,
204 };
205 self.task_pool.handle.spawn_with_sender(move |sender| {
206 sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
200 207
201 pub(crate) fn switch_workspaces( 208 let progress = {
209 let sender = sender.clone();
210 move |msg| {
211 sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
212 }
213 };
214 let res = build_data_collector.collect(&progress);
215 sender.send(Task::FetchBuildData(BuildDataProgress::End(res))).unwrap();
216 });
217 }
218 pub(crate) fn fetch_build_data_completed(
202 &mut self, 219 &mut self,
203 workspaces: Vec<anyhow::Result<ProjectWorkspace>>, 220 build_data: anyhow::Result<BuildDataResult>,
204 workspace_build_data: Option<anyhow::Result<BuildDataResult>>,
205 ) { 221 ) {
206 let _p = profile::span("GlobalState::switch_workspaces"); 222 self.fetch_build_data_queue.op_completed(Some(build_data))
207 log::info!("will switch workspaces: {:?}", workspaces); 223 }
208 224
209 let mut has_errors = false; 225 pub(crate) fn switch_workspaces(&mut self) {
210 let workspaces = workspaces 226 let _p = profile::span("GlobalState::switch_workspaces");
211 .into_iter() 227 log::info!("will switch workspaces");
212 .filter_map(|res| {
213 res.map_err(|err| {
214 has_errors = true;
215 log::error!("failed to load workspace: {:#}", err);
216 if self.workspaces.is_empty() {
217 self.show_message(
218 lsp_types::MessageType::Error,
219 format!("rust-analyzer failed to load workspace: {:#}", err),
220 );
221 }
222 })
223 .ok()
224 })
225 .collect::<Vec<_>>();
226 228
227 let workspace_build_data = match workspace_build_data { 229 if let Some(error_message) = self.fetch_workspace_error() {
228 Some(Ok(it)) => Some(it), 230 log::error!("failed to switch workspaces: {}", error_message);
229 Some(Err(err)) => { 231 if !self.workspaces.is_empty() {
230 log::error!("failed to fetch build data: {:#}", err);
231 self.show_message(
232 lsp_types::MessageType::Error,
233 format!("rust-analyzer failed to fetch build data: {:#}", err),
234 );
235 return; 232 return;
236 } 233 }
237 None => None, 234 }
238 };
239 235
240 if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data { 236 if let Some(error_message) = self.build_data_error() {
241 return; 237 log::error!("failed to switch build data: {}", error_message);
242 } 238 }
243 239
244 if !self.workspaces.is_empty() && has_errors { 240 let workspaces = self
241 .fetch_workspaces_queue
242 .last_op_result()
243 .iter()
244 .filter_map(|res| res.as_ref().ok().cloned())
245 .collect::<Vec<_>>();
246
247 let workspace_build_data = match self.fetch_build_data_queue.last_op_result() {
248 Some(Ok(it)) => Some(it.clone()),
249 None | Some(Err(_)) => None,
250 };
251
252 if *self.workspaces == workspaces && self.workspace_build_data == workspace_build_data {
245 return; 253 return;
246 } 254 }
247 255
@@ -314,6 +322,7 @@ impl GlobalState {
314 let loader = &mut self.loader; 322 let loader = &mut self.loader;
315 let mem_docs = &self.mem_docs; 323 let mem_docs = &self.mem_docs;
316 let mut load = |path: &AbsPath| { 324 let mut load = |path: &AbsPath| {
325 let _p = profile::span("GlobalState::load");
317 let vfs_path = vfs::VfsPath::from(path.to_path_buf()); 326 let vfs_path = vfs::VfsPath::from(path.to_path_buf());
318 if !mem_docs.contains_key(&vfs_path) { 327 if !mem_docs.contains_key(&vfs_path) {
319 let contents = loader.handle.load_sync(path); 328 let contents = loader.handle.load_sync(path);
@@ -337,14 +346,6 @@ impl GlobalState {
337 }; 346 };
338 change.set_crate_graph(crate_graph); 347 change.set_crate_graph(crate_graph);
339 348
340 if self.config.run_build_scripts() && workspace_build_data.is_none() {
341 let mut collector = BuildDataCollector::default();
342 for ws in &workspaces {
343 ws.collect_build_data_configs(&mut collector);
344 }
345 self.fetch_build_data_request(collector)
346 }
347
348 self.source_root_config = project_folders.source_root_config; 349 self.source_root_config = project_folders.source_root_config;
349 self.workspaces = Arc::new(workspaces); 350 self.workspaces = Arc::new(workspaces);
350 self.workspace_build_data = workspace_build_data; 351 self.workspace_build_data = workspace_build_data;
@@ -355,6 +356,32 @@ impl GlobalState {
355 log::info!("did switch workspaces"); 356 log::info!("did switch workspaces");
356 } 357 }
357 358
359 fn fetch_workspace_error(&self) -> Option<String> {
360 let mut buf = String::new();
361
362 for ws in self.fetch_workspaces_queue.last_op_result() {
363 if let Err(err) = ws {
364 stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err);
365 }
366 }
367
368 if buf.is_empty() {
369 return None;
370 }
371
372 Some(buf)
373 }
374
375 fn build_data_error(&self) -> Option<String> {
376 match self.fetch_build_data_queue.last_op_result() {
377 Some(Err(err)) => {
378 Some(format!("rust-analyzer failed to fetch build data: {:#}\n", err))
379 }
380 Some(Ok(data)) => data.error(),
381 None => None,
382 }
383 }
384
358 fn reload_flycheck(&mut self) { 385 fn reload_flycheck(&mut self) {
359 let _p = profile::span("GlobalState::reload_flycheck"); 386 let _p = profile::span("GlobalState::reload_flycheck");
360 let config = match self.config.flycheck() { 387 let config = match self.config.flycheck() {
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 2dc8a42f1..ecab89b2a 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -39,7 +39,9 @@ macro_rules! define_semantic_token_types {
39 39
40define_semantic_token_types![ 40define_semantic_token_types![
41 (ANGLE, "angle"), 41 (ANGLE, "angle"),
42 (ARITHMETIC, "arithmetic"),
42 (ATTRIBUTE, "attribute"), 43 (ATTRIBUTE, "attribute"),
44 (BITWISE, "bitwise"),
43 (BOOLEAN, "boolean"), 45 (BOOLEAN, "boolean"),
44 (BRACE, "brace"), 46 (BRACE, "brace"),
45 (BRACKET, "bracket"), 47 (BRACKET, "bracket"),
@@ -47,6 +49,7 @@ define_semantic_token_types![
47 (CHAR_LITERAL, "characterLiteral"), 49 (CHAR_LITERAL, "characterLiteral"),
48 (COLON, "colon"), 50 (COLON, "colon"),
49 (COMMA, "comma"), 51 (COMMA, "comma"),
52 (COMPARISON, "comparison"),
50 (CONST_PARAMETER, "constParameter"), 53 (CONST_PARAMETER, "constParameter"),
51 (DOT, "dot"), 54 (DOT, "dot"),
52 (ESCAPE_SEQUENCE, "escapeSequence"), 55 (ESCAPE_SEQUENCE, "escapeSequence"),
@@ -54,6 +57,8 @@ define_semantic_token_types![
54 (GENERIC, "generic"), 57 (GENERIC, "generic"),
55 (LABEL, "label"), 58 (LABEL, "label"),
56 (LIFETIME, "lifetime"), 59 (LIFETIME, "lifetime"),
60 (LOGICAL, "logical"),
61 (OPERATOR, "operator"),
57 (PARENTHESIS, "parenthesis"), 62 (PARENTHESIS, "parenthesis"),
58 (PUNCTUATION, "punctuation"), 63 (PUNCTUATION, "punctuation"),
59 (SELF_KEYWORD, "selfKeyword"), 64 (SELF_KEYWORD, "selfKeyword"),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index c3820944b..c2361b32e 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -1,15 +1,16 @@
1//! Conversion of rust-analyzer specific types to lsp_types equivalents. 1//! Conversion of rust-analyzer specific types to lsp_types equivalents.
2use std::{ 2use std::{
3 iter::once,
3 path::{self, Path}, 4 path::{self, Path},
4 sync::atomic::{AtomicU32, Ordering}, 5 sync::atomic::{AtomicU32, Ordering},
5}; 6};
6 7
7use ide::{ 8use ide::{
8 Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, 9 Annotation, AnnotationKind, Assist, AssistKind, CallInfo, Cancelable, CompletionItem,
9 CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, 10 CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
10 Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, 11 Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
11 Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange, 12 InlayKind, InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable,
12 StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, 13 Severity, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
13}; 14};
14use itertools::Itertools; 15use itertools::Itertools;
15use serde_json::to_value; 16use serde_json::to_value;
@@ -145,6 +146,23 @@ pub(crate) fn text_edit(line_index: &LineIndex, indel: Indel) -> lsp_types::Text
145 lsp_types::TextEdit { range, new_text } 146 lsp_types::TextEdit { range, new_text }
146} 147}
147 148
149pub(crate) fn completion_text_edit(
150 line_index: &LineIndex,
151 insert_replace_support: Option<lsp_types::Position>,
152 indel: Indel,
153) -> lsp_types::CompletionTextEdit {
154 let text_edit = text_edit(line_index, indel);
155 match insert_replace_support {
156 Some(cursor_pos) => lsp_types::InsertReplaceEdit {
157 new_text: text_edit.new_text,
158 insert: lsp_types::Range { start: text_edit.range.start, end: cursor_pos },
159 replace: text_edit.range,
160 }
161 .into(),
162 None => text_edit.into(),
163 }
164}
165
148pub(crate) fn snippet_text_edit( 166pub(crate) fn snippet_text_edit(
149 line_index: &LineIndex, 167 line_index: &LineIndex,
150 is_snippet: bool, 168 is_snippet: bool,
@@ -157,6 +175,7 @@ pub(crate) fn snippet_text_edit(
157 range: text_edit.range, 175 range: text_edit.range,
158 new_text: text_edit.new_text, 176 new_text: text_edit.new_text,
159 insert_text_format, 177 insert_text_format,
178 annotation_id: None,
160 } 179 }
161} 180}
162 181
@@ -179,6 +198,7 @@ pub(crate) fn snippet_text_edit_vec(
179} 198}
180 199
181pub(crate) fn completion_item( 200pub(crate) fn completion_item(
201 insert_replace_support: Option<lsp_types::Position>,
182 line_index: &LineIndex, 202 line_index: &LineIndex,
183 item: CompletionItem, 203 item: CompletionItem,
184) -> Vec<lsp_types::CompletionItem> { 204) -> Vec<lsp_types::CompletionItem> {
@@ -190,7 +210,7 @@ pub(crate) fn completion_item(
190 for indel in item.text_edit().iter() { 210 for indel in item.text_edit().iter() {
191 if indel.delete.contains_range(source_range) { 211 if indel.delete.contains_range(source_range) {
192 text_edit = Some(if indel.delete == source_range { 212 text_edit = Some(if indel.delete == source_range {
193 self::text_edit(line_index, indel.clone()) 213 self::completion_text_edit(line_index, insert_replace_support, indel.clone())
194 } else { 214 } else {
195 assert!(source_range.end() == indel.delete.end()); 215 assert!(source_range.end() == indel.delete.end());
196 let range1 = TextRange::new(indel.delete.start(), source_range.start()); 216 let range1 = TextRange::new(indel.delete.start(), source_range.start());
@@ -198,7 +218,7 @@ pub(crate) fn completion_item(
198 let indel1 = Indel::replace(range1, String::new()); 218 let indel1 = Indel::replace(range1, String::new());
199 let indel2 = Indel::replace(range2, indel.insert.clone()); 219 let indel2 = Indel::replace(range2, indel.insert.clone());
200 additional_text_edits.push(self::text_edit(line_index, indel1)); 220 additional_text_edits.push(self::text_edit(line_index, indel1));
201 self::text_edit(line_index, indel2) 221 self::completion_text_edit(line_index, insert_replace_support, indel2)
202 }) 222 })
203 } else { 223 } else {
204 assert!(source_range.intersect(indel.delete).is_none()); 224 assert!(source_range.intersect(indel.delete).is_none());
@@ -213,7 +233,7 @@ pub(crate) fn completion_item(
213 detail: item.detail().map(|it| it.to_string()), 233 detail: item.detail().map(|it| it.to_string()),
214 filter_text: Some(item.lookup().to_string()), 234 filter_text: Some(item.lookup().to_string()),
215 kind: item.kind().map(completion_item_kind), 235 kind: item.kind().map(completion_item_kind),
216 text_edit: Some(text_edit.into()), 236 text_edit: Some(text_edit),
217 additional_text_edits: Some(additional_text_edits), 237 additional_text_edits: Some(additional_text_edits),
218 documentation: item.documentation().map(documentation), 238 documentation: item.documentation().map(documentation),
219 deprecated: Some(item.deprecated()), 239 deprecated: Some(item.deprecated()),
@@ -445,7 +465,13 @@ fn semantic_token_type_and_modifiers(
445 HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, 465 HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
446 HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, 466 HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD,
447 HlTag::None => semantic_tokens::GENERIC, 467 HlTag::None => semantic_tokens::GENERIC,
448 HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR, 468 HlTag::Operator(op) => match op {
469 HlOperator::Bitwise => semantic_tokens::BITWISE,
470 HlOperator::Arithmetic => semantic_tokens::ARITHMETIC,
471 HlOperator::Logical => semantic_tokens::LOGICAL,
472 HlOperator::Comparison => semantic_tokens::COMPARISON,
473 HlOperator::Other => semantic_tokens::OPERATOR,
474 },
449 HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING, 475 HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING,
450 HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, 476 HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
451 HlTag::Punctuation(punct) => match punct { 477 HlTag::Punctuation(punct) => match punct {
@@ -497,7 +523,8 @@ pub(crate) fn folding_range(
497 | FoldKind::Block 523 | FoldKind::Block
498 | FoldKind::ArgList 524 | FoldKind::ArgList
499 | FoldKind::Consts 525 | FoldKind::Consts
500 | FoldKind::Statics => None, 526 | FoldKind::Statics
527 | FoldKind::Array => None,
501 }; 528 };
502 529
503 let range = range(line_index, fold.range); 530 let range = range(line_index, fold.range);
@@ -663,16 +690,8 @@ pub(crate) fn goto_definition_response(
663 } 690 }
664} 691}
665 692
666pub(crate) fn text_document_edit( 693fn outside_workspace_annotation_id() -> String {
667 snap: &GlobalStateSnapshot, 694 String::from("OutsideWorkspace")
668 file_id: FileId,
669 edit: TextEdit,
670) -> Result<lsp_types::TextDocumentEdit> {
671 let text_document = optional_versioned_text_document_identifier(snap, file_id);
672 let line_index = snap.file_line_index(file_id)?;
673 let edits =
674 edit.into_iter().map(|it| lsp_types::OneOf::Left(text_edit(&line_index, it))).collect();
675 Ok(lsp_types::TextDocumentEdit { text_document, edits })
676} 695}
677 696
678pub(crate) fn snippet_text_document_edit( 697pub(crate) fn snippet_text_document_edit(
@@ -683,14 +702,21 @@ pub(crate) fn snippet_text_document_edit(
683) -> Result<lsp_ext::SnippetTextDocumentEdit> { 702) -> Result<lsp_ext::SnippetTextDocumentEdit> {
684 let text_document = optional_versioned_text_document_identifier(snap, file_id); 703 let text_document = optional_versioned_text_document_identifier(snap, file_id);
685 let line_index = snap.file_line_index(file_id)?; 704 let line_index = snap.file_line_index(file_id)?;
686 let edits = edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect(); 705 let mut edits: Vec<_> =
706 edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect();
707
708 if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() {
709 for edit in &mut edits {
710 edit.annotation_id = Some(outside_workspace_annotation_id())
711 }
712 }
687 Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) 713 Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
688} 714}
689 715
690pub(crate) fn snippet_text_document_ops( 716pub(crate) fn snippet_text_document_ops(
691 snap: &GlobalStateSnapshot, 717 snap: &GlobalStateSnapshot,
692 file_system_edit: FileSystemEdit, 718 file_system_edit: FileSystemEdit,
693) -> Vec<lsp_ext::SnippetDocumentChangeOperation> { 719) -> Cancelable<Vec<lsp_ext::SnippetDocumentChangeOperation>> {
694 let mut ops = Vec::new(); 720 let mut ops = Vec::new();
695 match file_system_edit { 721 match file_system_edit {
696 FileSystemEdit::CreateFile { dst, initial_contents } => { 722 FileSystemEdit::CreateFile { dst, initial_contents } => {
@@ -708,6 +734,7 @@ pub(crate) fn snippet_text_document_ops(
708 range: lsp_types::Range::default(), 734 range: lsp_types::Range::default(),
709 new_text: initial_contents, 735 new_text: initial_contents,
710 insert_text_format: Some(lsp_types::InsertTextFormat::PlainText), 736 insert_text_format: Some(lsp_types::InsertTextFormat::PlainText),
737 annotation_id: None,
711 }; 738 };
712 let edit_file = 739 let edit_file =
713 lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] }; 740 lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] };
@@ -717,16 +744,19 @@ pub(crate) fn snippet_text_document_ops(
717 FileSystemEdit::MoveFile { src, dst } => { 744 FileSystemEdit::MoveFile { src, dst } => {
718 let old_uri = snap.file_id_to_url(src); 745 let old_uri = snap.file_id_to_url(src);
719 let new_uri = snap.anchored_path(&dst); 746 let new_uri = snap.anchored_path(&dst);
720 let rename_file = lsp_types::ResourceOp::Rename(lsp_types::RenameFile { 747 let mut rename_file =
721 old_uri, 748 lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None };
722 new_uri, 749 if snap.analysis.is_library_file(src) == Ok(true)
723 options: None, 750 && snap.config.change_annotation_support()
724 annotation_id: None, 751 {
725 }); 752 rename_file.annotation_id = Some(outside_workspace_annotation_id())
726 ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(rename_file)) 753 }
754 ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(lsp_types::ResourceOp::Rename(
755 rename_file,
756 )))
727 } 757 }
728 } 758 }
729 ops 759 Ok(ops)
730} 760}
731 761
732pub(crate) fn snippet_workspace_edit( 762pub(crate) fn snippet_workspace_edit(
@@ -734,16 +764,35 @@ pub(crate) fn snippet_workspace_edit(
734 source_change: SourceChange, 764 source_change: SourceChange,
735) -> Result<lsp_ext::SnippetWorkspaceEdit> { 765) -> Result<lsp_ext::SnippetWorkspaceEdit> {
736 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); 766 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
767
737 for op in source_change.file_system_edits { 768 for op in source_change.file_system_edits {
738 let ops = snippet_text_document_ops(snap, op); 769 let ops = snippet_text_document_ops(snap, op)?;
739 document_changes.extend_from_slice(&ops); 770 document_changes.extend_from_slice(&ops);
740 } 771 }
741 for (file_id, edit) in source_change.source_file_edits { 772 for (file_id, edit) in source_change.source_file_edits {
742 let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?; 773 let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?;
743 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); 774 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
744 } 775 }
745 let workspace_edit = 776 let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit {
746 lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes) }; 777 changes: None,
778 document_changes: Some(document_changes),
779 change_annotations: None,
780 };
781 if snap.config.change_annotation_support() {
782 workspace_edit.change_annotations = Some(
783 once((
784 outside_workspace_annotation_id(),
785 lsp_types::ChangeAnnotation {
786 label: String::from("Edit outside of the workspace"),
787 needs_confirmation: Some(true),
788 description: Some(String::from(
789 "This edit lies outside of the workspace and may affect dependencies",
790 )),
791 },
792 ))
793 .collect(),
794 )
795 }
747 Ok(workspace_edit) 796 Ok(workspace_edit)
748} 797}
749 798
@@ -771,16 +820,7 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
771 lsp_types::DocumentChangeOperation::Edit( 820 lsp_types::DocumentChangeOperation::Edit(
772 lsp_types::TextDocumentEdit { 821 lsp_types::TextDocumentEdit {
773 text_document: edit.text_document, 822 text_document: edit.text_document,
774 edits: edit 823 edits: edit.edits.into_iter().map(From::from).collect(),
775 .edits
776 .into_iter()
777 .map(|edit| {
778 lsp_types::OneOf::Left(lsp_types::TextEdit {
779 range: edit.range,
780 new_text: edit.new_text,
781 })
782 })
783 .collect(),
784 }, 824 },
785 ) 825 )
786 } 826 }
@@ -788,7 +828,23 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
788 .collect(), 828 .collect(),
789 ) 829 )
790 }), 830 }),
791 change_annotations: None, 831 change_annotations: snippet_workspace_edit.change_annotations,
832 }
833 }
834}
835
836impl From<lsp_ext::SnippetTextEdit>
837 for lsp_types::OneOf<lsp_types::TextEdit, lsp_types::AnnotatedTextEdit>
838{
839 fn from(
840 lsp_ext::SnippetTextEdit { annotation_id, insert_text_format:_, new_text, range }: lsp_ext::SnippetTextEdit,
841 ) -> Self {
842 match annotation_id {
843 Some(annotation_id) => lsp_types::OneOf::Right(lsp_types::AnnotatedTextEdit {
844 text_edit: lsp_types::TextEdit { range, new_text },
845 annotation_id,
846 }),
847 None => lsp_types::OneOf::Left(lsp_types::TextEdit { range, new_text }),
792 } 848 }
793 } 849 }
794} 850}
@@ -824,40 +880,31 @@ pub(crate) fn code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind {
824 } 880 }
825} 881}
826 882
827pub(crate) fn unresolved_code_action( 883pub(crate) fn code_action(
828 snap: &GlobalStateSnapshot, 884 snap: &GlobalStateSnapshot,
829 code_action_params: lsp_types::CodeActionParams,
830 assist: Assist, 885 assist: Assist,
831 index: usize, 886 resolve_data: Option<(usize, lsp_types::CodeActionParams)>,
832) -> Result<lsp_ext::CodeAction> { 887) -> Result<lsp_ext::CodeAction> {
833 assert!(assist.source_change.is_none()); 888 let mut res = lsp_ext::CodeAction {
834 let res = lsp_ext::CodeAction {
835 title: assist.label.to_string(), 889 title: assist.label.to_string(),
836 group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0), 890 group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0),
837 kind: Some(code_action_kind(assist.id.1)), 891 kind: Some(code_action_kind(assist.id.1)),
838 edit: None, 892 edit: None,
839 is_preferred: None, 893 is_preferred: None,
840 data: Some(lsp_ext::CodeActionData {
841 id: format!("{}:{}", assist.id.0, index.to_string()),
842 code_action_params,
843 }),
844 };
845 Ok(res)
846}
847
848pub(crate) fn resolved_code_action(
849 snap: &GlobalStateSnapshot,
850 assist: Assist,
851) -> Result<lsp_ext::CodeAction> {
852 let change = assist.source_change.unwrap();
853 let res = lsp_ext::CodeAction {
854 edit: Some(snippet_workspace_edit(snap, change)?),
855 title: assist.label.to_string(),
856 group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0),
857 kind: Some(code_action_kind(assist.id.1)),
858 is_preferred: None,
859 data: None, 894 data: None,
860 }; 895 };
896 match (assist.source_change, resolve_data) {
897 (Some(it), _) => res.edit = Some(snippet_workspace_edit(snap, it)?),
898 (None, Some((index, code_action_params))) => {
899 res.data = Some(lsp_ext::CodeActionData {
900 id: format!("{}:{}", assist.id.0, index.to_string()),
901 code_action_params,
902 });
903 }
904 (None, None) => {
905 stdx::never!("assist should always be resolved if client can't do lazy resolving")
906 }
907 };
861 Ok(res) 908 Ok(res)
862} 909}
863 910
@@ -1135,7 +1182,7 @@ mod tests {
1135 .unwrap() 1182 .unwrap()
1136 .into_iter() 1183 .into_iter()
1137 .filter(|c| c.label().ends_with("arg")) 1184 .filter(|c| c.label().ends_with("arg"))
1138 .map(|c| completion_item(&line_index, c)) 1185 .map(|c| completion_item(None, &line_index, c))
1139 .flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text))) 1186 .flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text)))
1140 .collect(); 1187 .collect();
1141 expect_test::expect![[r#" 1188 expect_test::expect![[r#"
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs
index 4442cbff6..9e89209ea 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/main.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs
@@ -340,7 +340,6 @@ fn main() {}
340 } 340 }
341 ] 341 ]
342 }, 342 },
343 "isPreferred": false,
344 "kind": "quickfix", 343 "kind": "quickfix",
345 "title": "Create module" 344 "title": "Create module"
346 }]), 345 }]),
@@ -411,7 +410,6 @@ fn main() {{}}
411 } 410 }
412 ] 411 ]
413 }, 412 },
414 "isPreferred": false,
415 "kind": "quickfix", 413 "kind": "quickfix",
416 "title": "Create module" 414 "title": "Create module"
417 }]), 415 }]),
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index 95bf26f01..75e677762 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -32,8 +32,12 @@ impl<'a> Project<'a> {
32 tmp_dir: None, 32 tmp_dir: None,
33 roots: vec![], 33 roots: vec![],
34 config: serde_json::json!({ 34 config: serde_json::json!({
35 // Loading standard library is costly, let's ignore it by default 35 "cargo": {
36 "cargo": { "noSysroot": true } 36 // Loading standard library is costly, let's ignore it by default
37 "noSysroot": true,
38 // Can't use test binary as rustc wrapper.
39 "useRustcWrapperForBuildScripts": false,
40 }
37 }), 41 }),
38 } 42 }
39 } 43 }
@@ -49,7 +53,17 @@ impl<'a> Project<'a> {
49 } 53 }
50 54
51 pub(crate) fn with_config(mut self, config: serde_json::Value) -> Project<'a> { 55 pub(crate) fn with_config(mut self, config: serde_json::Value) -> Project<'a> {
52 self.config = config; 56 fn merge(dst: &mut serde_json::Value, src: serde_json::Value) {
57 match (dst, src) {
58 (Value::Object(dst), Value::Object(src)) => {
59 for (k, v) in src {
60 merge(dst.entry(k).or_insert(v.clone()), v)
61 }
62 }
63 (dst, src) => *dst = src,
64 }
65 }
66 merge(&mut self.config, config);
53 self 67 self
54 } 68 }
55 69
@@ -103,7 +117,7 @@ impl<'a> Project<'a> {
103 ..Default::default() 117 ..Default::default()
104 }), 118 }),
105 experimental: Some(json!({ 119 experimental: Some(json!({
106 "statusNotification": true, 120 "serverStatusNotification": true,
107 })), 121 })),
108 ..Default::default() 122 ..Default::default()
109 }, 123 },
@@ -154,6 +168,7 @@ impl Server {
154 self.send_notification(r) 168 self.send_notification(r)
155 } 169 }
156 170
171 #[track_caller]
157 pub(crate) fn request<R>(&self, params: R::Params, expected_resp: Value) 172 pub(crate) fn request<R>(&self, params: R::Params, expected_resp: Value)
158 where 173 where
159 R: lsp_types::request::Request, 174 R: lsp_types::request::Request,
@@ -213,13 +228,12 @@ impl Server {
213 } 228 }
214 pub(crate) fn wait_until_workspace_is_loaded(self) -> Server { 229 pub(crate) fn wait_until_workspace_is_loaded(self) -> Server {
215 self.wait_for_message_cond(1, &|msg: &Message| match msg { 230 self.wait_for_message_cond(1, &|msg: &Message| match msg {
216 Message::Notification(n) if n.method == "rust-analyzer/status" => { 231 Message::Notification(n) if n.method == "experimental/serverStatus" => {
217 let status = n 232 let status = n
218 .clone() 233 .clone()
219 .extract::<lsp_ext::StatusParams>("rust-analyzer/status") 234 .extract::<lsp_ext::ServerStatusParams>("experimental/serverStatus")
220 .unwrap() 235 .unwrap();
221 .status; 236 status.quiescent
222 matches!(status, lsp_ext::Status::Ready)
223 } 237 }
224 _ => false, 238 _ => false,
225 }) 239 })
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml
index d28b5e658..f78c5da7c 100644
--- a/crates/stdx/Cargo.toml
+++ b/crates/stdx/Cargo.toml
@@ -10,10 +10,15 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13libc = "0.2.93"
13backtrace = { version = "0.3.44", optional = true } 14backtrace = { version = "0.3.44", optional = true }
14always-assert = { version = "0.1.2", features = ["log"] } 15always-assert = { version = "0.1.2", features = ["log"] }
15# Think twice before adding anything here 16# Think twice before adding anything here
16 17
18[target.'cfg(windows)'.dependencies]
19miow = "0.3.6"
20winapi = "0.3.9"
21
17[features] 22[features]
18# Uncomment to enable for the whole crate graph 23# Uncomment to enable for the whole crate graph
19# default = [ "backtrace" ] 24# default = [ "backtrace" ]
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index d26be4853..857567a85 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -1,7 +1,8 @@
1//! Missing batteries for standard libraries. 1//! Missing batteries for standard libraries.
2use std::{cmp::Ordering, ops, process, time::Instant}; 2use std::{cmp::Ordering, ops, time::Instant};
3 3
4mod macros; 4mod macros;
5pub mod process;
5pub mod panic_context; 6pub mod panic_context;
6 7
7pub use always_assert::{always, never}; 8pub use always_assert::{always, never};
@@ -178,17 +179,30 @@ where
178 start..start + len 179 start..start + len
179} 180}
180 181
181pub struct JodChild(pub process::Child); 182pub fn defer<F: FnOnce()>(f: F) -> impl Drop {
183 struct D<F: FnOnce()>(Option<F>);
184 impl<F: FnOnce()> Drop for D<F> {
185 fn drop(&mut self) {
186 if let Some(f) = self.0.take() {
187 f()
188 }
189 }
190 }
191 D(Some(f))
192}
193
194#[repr(transparent)]
195pub struct JodChild(pub std::process::Child);
182 196
183impl ops::Deref for JodChild { 197impl ops::Deref for JodChild {
184 type Target = process::Child; 198 type Target = std::process::Child;
185 fn deref(&self) -> &process::Child { 199 fn deref(&self) -> &std::process::Child {
186 &self.0 200 &self.0
187 } 201 }
188} 202}
189 203
190impl ops::DerefMut for JodChild { 204impl ops::DerefMut for JodChild {
191 fn deref_mut(&mut self) -> &mut process::Child { 205 fn deref_mut(&mut self) -> &mut std::process::Child {
192 &mut self.0 206 &mut self.0
193 } 207 }
194} 208}
@@ -200,6 +214,13 @@ impl Drop for JodChild {
200 } 214 }
201} 215}
202 216
217impl JodChild {
218 pub fn into_inner(self) -> std::process::Child {
219 // SAFETY: repr transparent
220 unsafe { std::mem::transmute::<JodChild, std::process::Child>(self) }
221 }
222}
223
203#[cfg(test)] 224#[cfg(test)]
204mod tests { 225mod tests {
205 use super::*; 226 use super::*;
diff --git a/crates/stdx/src/process.rs b/crates/stdx/src/process.rs
new file mode 100644
index 000000000..b0fa12f76
--- /dev/null
+++ b/crates/stdx/src/process.rs
@@ -0,0 +1,238 @@
1//! Read both stdout and stderr of child without deadlocks.
2//!
3//! https://github.com/rust-lang/cargo/blob/905af549966f23a9288e9993a85d1249a5436556/crates/cargo-util/src/read2.rs
4//! https://github.com/rust-lang/cargo/blob/58a961314437258065e23cb6316dfc121d96fb71/crates/cargo-util/src/process_builder.rs#L231
5
6use std::{
7 io,
8 process::{Command, Output, Stdio},
9};
10
11pub fn streaming_output(
12 mut cmd: Command,
13 on_stdout_line: &mut dyn FnMut(&str),
14 on_stderr_line: &mut dyn FnMut(&str),
15) -> io::Result<Output> {
16 let mut stdout = Vec::new();
17 let mut stderr = Vec::new();
18
19 let cmd = cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
20
21 let status = {
22 let mut child = cmd.spawn()?;
23 let out = child.stdout.take().unwrap();
24 let err = child.stderr.take().unwrap();
25 imp::read2(out, err, &mut |is_out, data, eof| {
26 let idx = if eof {
27 data.len()
28 } else {
29 match data.iter().rposition(|b| *b == b'\n') {
30 Some(i) => i + 1,
31 None => return,
32 }
33 };
34 {
35 // scope for new_lines
36 let new_lines = {
37 let dst = if is_out { &mut stdout } else { &mut stderr };
38 let start = dst.len();
39 let data = data.drain(..idx);
40 dst.extend(data);
41 &dst[start..]
42 };
43 for line in String::from_utf8_lossy(new_lines).lines() {
44 if is_out {
45 on_stdout_line(line)
46 } else {
47 on_stderr_line(line)
48 }
49 }
50 }
51 })?;
52 child.wait()?
53 };
54
55 Ok(Output { status, stdout, stderr })
56}
57
58#[cfg(unix)]
59mod imp {
60 use std::{
61 io::{self, prelude::*},
62 mem,
63 os::unix::prelude::*,
64 process::{ChildStderr, ChildStdout},
65 };
66
67 pub(crate) fn read2(
68 mut out_pipe: ChildStdout,
69 mut err_pipe: ChildStderr,
70 data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
71 ) -> io::Result<()> {
72 unsafe {
73 libc::fcntl(out_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
74 libc::fcntl(err_pipe.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK);
75 }
76
77 let mut out_done = false;
78 let mut err_done = false;
79 let mut out = Vec::new();
80 let mut err = Vec::new();
81
82 let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
83 fds[0].fd = out_pipe.as_raw_fd();
84 fds[0].events = libc::POLLIN;
85 fds[1].fd = err_pipe.as_raw_fd();
86 fds[1].events = libc::POLLIN;
87 let mut nfds = 2;
88 let mut errfd = 1;
89
90 while nfds > 0 {
91 // wait for either pipe to become readable using `select`
92 let r = unsafe { libc::poll(fds.as_mut_ptr(), nfds, -1) };
93 if r == -1 {
94 let err = io::Error::last_os_error();
95 if err.kind() == io::ErrorKind::Interrupted {
96 continue;
97 }
98 return Err(err);
99 }
100
101 // Read as much as we can from each pipe, ignoring EWOULDBLOCK or
102 // EAGAIN. If we hit EOF, then this will happen because the underlying
103 // reader will return Ok(0), in which case we'll see `Ok` ourselves. In
104 // this case we flip the other fd back into blocking mode and read
105 // whatever's leftover on that file descriptor.
106 let handle = |res: io::Result<_>| match res {
107 Ok(_) => Ok(true),
108 Err(e) => {
109 if e.kind() == io::ErrorKind::WouldBlock {
110 Ok(false)
111 } else {
112 Err(e)
113 }
114 }
115 };
116 if !err_done && fds[errfd].revents != 0 && handle(err_pipe.read_to_end(&mut err))? {
117 err_done = true;
118 nfds -= 1;
119 }
120 data(false, &mut err, err_done);
121 if !out_done && fds[0].revents != 0 && handle(out_pipe.read_to_end(&mut out))? {
122 out_done = true;
123 fds[0].fd = err_pipe.as_raw_fd();
124 errfd = 0;
125 nfds -= 1;
126 }
127 data(true, &mut out, out_done);
128 }
129 Ok(())
130 }
131}
132
133#[cfg(windows)]
134mod imp {
135 use std::{
136 io,
137 os::windows::prelude::*,
138 process::{ChildStderr, ChildStdout},
139 slice,
140 };
141
142 use miow::{
143 iocp::{CompletionPort, CompletionStatus},
144 pipe::NamedPipe,
145 Overlapped,
146 };
147 use winapi::shared::winerror::ERROR_BROKEN_PIPE;
148
149 struct Pipe<'a> {
150 dst: &'a mut Vec<u8>,
151 overlapped: Overlapped,
152 pipe: NamedPipe,
153 done: bool,
154 }
155
156 pub(crate) fn read2(
157 out_pipe: ChildStdout,
158 err_pipe: ChildStderr,
159 data: &mut dyn FnMut(bool, &mut Vec<u8>, bool),
160 ) -> io::Result<()> {
161 let mut out = Vec::new();
162 let mut err = Vec::new();
163
164 let port = CompletionPort::new(1)?;
165 port.add_handle(0, &out_pipe)?;
166 port.add_handle(1, &err_pipe)?;
167
168 unsafe {
169 let mut out_pipe = Pipe::new(out_pipe, &mut out);
170 let mut err_pipe = Pipe::new(err_pipe, &mut err);
171
172 out_pipe.read()?;
173 err_pipe.read()?;
174
175 let mut status = [CompletionStatus::zero(), CompletionStatus::zero()];
176
177 while !out_pipe.done || !err_pipe.done {
178 for status in port.get_many(&mut status, None)? {
179 if status.token() == 0 {
180 out_pipe.complete(status);
181 data(true, out_pipe.dst, out_pipe.done);
182 out_pipe.read()?;
183 } else {
184 err_pipe.complete(status);
185 data(false, err_pipe.dst, err_pipe.done);
186 err_pipe.read()?;
187 }
188 }
189 }
190
191 Ok(())
192 }
193 }
194
195 impl<'a> Pipe<'a> {
196 unsafe fn new<P: IntoRawHandle>(p: P, dst: &'a mut Vec<u8>) -> Pipe<'a> {
197 Pipe {
198 dst,
199 pipe: NamedPipe::from_raw_handle(p.into_raw_handle()),
200 overlapped: Overlapped::zero(),
201 done: false,
202 }
203 }
204
205 unsafe fn read(&mut self) -> io::Result<()> {
206 let dst = slice_to_end(self.dst);
207 match self.pipe.read_overlapped(dst, self.overlapped.raw()) {
208 Ok(_) => Ok(()),
209 Err(e) => {
210 if e.raw_os_error() == Some(ERROR_BROKEN_PIPE as i32) {
211 self.done = true;
212 Ok(())
213 } else {
214 Err(e)
215 }
216 }
217 }
218 }
219
220 unsafe fn complete(&mut self, status: &CompletionStatus) {
221 let prev = self.dst.len();
222 self.dst.set_len(prev + status.bytes_transferred() as usize);
223 if status.bytes_transferred() == 0 {
224 self.done = true;
225 }
226 }
227 }
228
229 unsafe fn slice_to_end(v: &mut Vec<u8>) -> &mut [u8] {
230 if v.capacity() == 0 {
231 v.reserve(16);
232 }
233 if v.capacity() == v.len() {
234 v.reserve(1);
235 }
236 slice::from_raw_parts_mut(v.as_mut_ptr().add(v.len()), v.capacity() - v.len())
237 }
238}
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 9f01acc26..556f80882 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -13,10 +13,10 @@ doctest = false
13[dependencies] 13[dependencies]
14cov-mark = { version = "1.1", features = ["thread-local"] } 14cov-mark = { version = "1.1", features = ["thread-local"] }
15itertools = "0.10.0" 15itertools = "0.10.0"
16rowan = "0.13.0-pre.3" 16rowan = "=0.13.0-pre.3"
17rustc_lexer = { version = "710.0.0", package = "rustc-ap-rustc_lexer" } 17rustc_lexer = { version = "716.0.0", package = "rustc-ap-rustc_lexer" }
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19arrayvec = "0.6" 19arrayvec = "0.7"
20once_cell = "1.3.1" 20once_cell = "1.3.1"
21indexmap = "1.4.0" 21indexmap = "1.4.0"
22smol_str = { version = "0.1.15", features = ["serde"] } 22smol_str = { version = "0.1.15", features = ["serde"] }
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index 529bd0eb1..04f97f368 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -14,10 +14,29 @@ use crate::{
14use super::NameOwner; 14use super::NameOwner;
15 15
16pub trait GenericParamsOwnerEdit: ast::GenericParamsOwner + AstNodeEdit { 16pub trait GenericParamsOwnerEdit: ast::GenericParamsOwner + AstNodeEdit {
17 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList;
17 fn get_or_create_where_clause(&self) -> ast::WhereClause; 18 fn get_or_create_where_clause(&self) -> ast::WhereClause;
18} 19}
19 20
20impl GenericParamsOwnerEdit for ast::Fn { 21impl GenericParamsOwnerEdit for ast::Fn {
22 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
23 match self.generic_param_list() {
24 Some(it) => it,
25 None => {
26 let position = if let Some(name) = self.name() {
27 Position::after(name.syntax)
28 } else if let Some(fn_token) = self.fn_token() {
29 Position::after(fn_token)
30 } else if let Some(param_list) = self.param_list() {
31 Position::before(param_list.syntax)
32 } else {
33 Position::last_child_of(self.syntax())
34 };
35 create_generic_param_list(position)
36 }
37 }
38 }
39
21 fn get_or_create_where_clause(&self) -> WhereClause { 40 fn get_or_create_where_clause(&self) -> WhereClause {
22 if self.where_clause().is_none() { 41 if self.where_clause().is_none() {
23 let position = if let Some(ty) = self.ret_type() { 42 let position = if let Some(ty) = self.ret_type() {
@@ -34,6 +53,20 @@ impl GenericParamsOwnerEdit for ast::Fn {
34} 53}
35 54
36impl GenericParamsOwnerEdit for ast::Impl { 55impl GenericParamsOwnerEdit for ast::Impl {
56 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
57 match self.generic_param_list() {
58 Some(it) => it,
59 None => {
60 let position = if let Some(imp_token) = self.impl_token() {
61 Position::after(imp_token)
62 } else {
63 Position::last_child_of(self.syntax())
64 };
65 create_generic_param_list(position)
66 }
67 }
68 }
69
37 fn get_or_create_where_clause(&self) -> WhereClause { 70 fn get_or_create_where_clause(&self) -> WhereClause {
38 if self.where_clause().is_none() { 71 if self.where_clause().is_none() {
39 let position = if let Some(items) = self.assoc_item_list() { 72 let position = if let Some(items) = self.assoc_item_list() {
@@ -48,6 +81,22 @@ impl GenericParamsOwnerEdit for ast::Impl {
48} 81}
49 82
50impl GenericParamsOwnerEdit for ast::Trait { 83impl GenericParamsOwnerEdit for ast::Trait {
84 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
85 match self.generic_param_list() {
86 Some(it) => it,
87 None => {
88 let position = if let Some(name) = self.name() {
89 Position::after(name.syntax)
90 } else if let Some(trait_token) = self.trait_token() {
91 Position::after(trait_token)
92 } else {
93 Position::last_child_of(self.syntax())
94 };
95 create_generic_param_list(position)
96 }
97 }
98 }
99
51 fn get_or_create_where_clause(&self) -> WhereClause { 100 fn get_or_create_where_clause(&self) -> WhereClause {
52 if self.where_clause().is_none() { 101 if self.where_clause().is_none() {
53 let position = if let Some(items) = self.assoc_item_list() { 102 let position = if let Some(items) = self.assoc_item_list() {
@@ -62,6 +111,22 @@ impl GenericParamsOwnerEdit for ast::Trait {
62} 111}
63 112
64impl GenericParamsOwnerEdit for ast::Struct { 113impl GenericParamsOwnerEdit for ast::Struct {
114 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
115 match self.generic_param_list() {
116 Some(it) => it,
117 None => {
118 let position = if let Some(name) = self.name() {
119 Position::after(name.syntax)
120 } else if let Some(struct_token) = self.struct_token() {
121 Position::after(struct_token)
122 } else {
123 Position::last_child_of(self.syntax())
124 };
125 create_generic_param_list(position)
126 }
127 }
128 }
129
65 fn get_or_create_where_clause(&self) -> WhereClause { 130 fn get_or_create_where_clause(&self) -> WhereClause {
66 if self.where_clause().is_none() { 131 if self.where_clause().is_none() {
67 let tfl = self.field_list().and_then(|fl| match fl { 132 let tfl = self.field_list().and_then(|fl| match fl {
@@ -84,6 +149,22 @@ impl GenericParamsOwnerEdit for ast::Struct {
84} 149}
85 150
86impl GenericParamsOwnerEdit for ast::Enum { 151impl GenericParamsOwnerEdit for ast::Enum {
152 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
153 match self.generic_param_list() {
154 Some(it) => it,
155 None => {
156 let position = if let Some(name) = self.name() {
157 Position::after(name.syntax)
158 } else if let Some(enum_token) = self.enum_token() {
159 Position::after(enum_token)
160 } else {
161 Position::last_child_of(self.syntax())
162 };
163 create_generic_param_list(position)
164 }
165 }
166 }
167
87 fn get_or_create_where_clause(&self) -> WhereClause { 168 fn get_or_create_where_clause(&self) -> WhereClause {
88 if self.where_clause().is_none() { 169 if self.where_clause().is_none() {
89 let position = if let Some(gpl) = self.generic_param_list() { 170 let position = if let Some(gpl) = self.generic_param_list() {
@@ -104,6 +185,37 @@ fn create_where_clause(position: Position) {
104 ted::insert(position, where_clause.syntax()); 185 ted::insert(position, where_clause.syntax());
105} 186}
106 187
188fn create_generic_param_list(position: Position) -> ast::GenericParamList {
189 let gpl = make::generic_param_list(empty()).clone_for_update();
190 ted::insert_raw(position, gpl.syntax());
191 gpl
192}
193
194impl ast::GenericParamList {
195 pub fn add_generic_param(&self, generic_param: ast::GenericParam) {
196 match self.generic_params().last() {
197 Some(last_param) => {
198 let mut elems = Vec::new();
199 if !last_param
200 .syntax()
201 .siblings_with_tokens(Direction::Next)
202 .any(|it| it.kind() == T![,])
203 {
204 elems.push(make::token(T![,]).into());
205 elems.push(make::tokens::single_space().into());
206 };
207 elems.push(generic_param.syntax().clone().into());
208 let after_last_param = Position::after(last_param.syntax());
209 ted::insert_all(after_last_param, elems);
210 }
211 None => {
212 let after_l_angle = Position::after(self.l_angle_token().unwrap());
213 ted::insert(after_l_angle, generic_param.syntax())
214 }
215 }
216 }
217}
218
107impl ast::WhereClause { 219impl ast::WhereClause {
108 pub fn add_predicate(&self, predicate: ast::WherePred) { 220 pub fn add_predicate(&self, predicate: ast::WherePred) {
109 if let Some(pred) = self.predicates().last() { 221 if let Some(pred) = self.predicates().last() {
@@ -164,3 +276,44 @@ impl ast::Use {
164 ted::remove(self.syntax()) 276 ted::remove(self.syntax())
165 } 277 }
166} 278}
279
280#[cfg(test)]
281mod tests {
282 use std::fmt;
283
284 use crate::SourceFile;
285
286 use super::*;
287
288 fn ast_mut_from_text<N: AstNode>(text: &str) -> N {
289 let parse = SourceFile::parse(text);
290 parse.tree().syntax().descendants().find_map(N::cast).unwrap().clone_for_update()
291 }
292
293 #[test]
294 fn test_create_generic_param_list() {
295 fn check_create_gpl<N: GenericParamsOwnerEdit + fmt::Display>(before: &str, after: &str) {
296 let gpl_owner = ast_mut_from_text::<N>(before);
297 gpl_owner.get_or_create_generic_param_list();
298 assert_eq!(gpl_owner.to_string(), after);
299 }
300
301 check_create_gpl::<ast::Fn>("fn foo", "fn foo<>");
302 check_create_gpl::<ast::Fn>("fn foo() {}", "fn foo<>() {}");
303
304 check_create_gpl::<ast::Impl>("impl", "impl<>");
305 check_create_gpl::<ast::Impl>("impl Struct {}", "impl<> Struct {}");
306 check_create_gpl::<ast::Impl>("impl Trait for Struct {}", "impl<> Trait for Struct {}");
307
308 check_create_gpl::<ast::Trait>("trait Trait<>", "trait Trait<>");
309 check_create_gpl::<ast::Trait>("trait Trait<> {}", "trait Trait<> {}");
310
311 check_create_gpl::<ast::Struct>("struct A", "struct A<>");
312 check_create_gpl::<ast::Struct>("struct A;", "struct A<>;");
313 check_create_gpl::<ast::Struct>("struct A();", "struct A<>();");
314 check_create_gpl::<ast::Struct>("struct A {}", "struct A<> {}");
315
316 check_create_gpl::<ast::Enum>("enum E", "enum E<>");
317 check_create_gpl::<ast::Enum>("enum E {", "enum E<> {");
318 }
319}
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 3a588e540..222b7e212 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -29,9 +29,13 @@ pub fn ty(text: &str) -> ast::Type {
29pub fn ty_unit() -> ast::Type { 29pub fn ty_unit() -> ast::Type {
30 ty("()") 30 ty("()")
31} 31}
32// FIXME: handle types of length == 1
33pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { 32pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
34 let contents = types.into_iter().join(", "); 33 let mut count: usize = 0;
34 let mut contents = types.into_iter().inspect(|_| count += 1).join(", ");
35 if count == 1 {
36 contents.push(',');
37 }
38
35 ty(&format!("({})", contents)) 39 ty(&format!("({})", contents))
36} 40}
37// FIXME: handle path to type 41// FIXME: handle path to type
@@ -301,13 +305,23 @@ pub fn wildcard_pat() -> ast::WildcardPat {
301 } 305 }
302} 306}
303 307
308pub fn literal_pat(lit: &str) -> ast::LiteralPat {
309 return from_text(lit);
310
311 fn from_text(text: &str) -> ast::LiteralPat {
312 ast_from_text(&format!("fn f() {{ match x {{ {} => {{}} }} }}", text))
313 }
314}
315
304/// Creates a tuple of patterns from an iterator of patterns. 316/// Creates a tuple of patterns from an iterator of patterns.
305/// 317///
306/// Invariant: `pats` must be length > 1 318/// Invariant: `pats` must be length > 0
307///
308/// FIXME handle `pats` length == 1
309pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat { 319pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat {
310 let pats_str = pats.into_iter().map(|p| p.to_string()).join(", "); 320 let mut count: usize = 0;
321 let mut pats_str = pats.into_iter().inspect(|_| count += 1).join(", ");
322 if count == 1 {
323 pats_str.push(',');
324 }
311 return from_text(&format!("({})", pats_str)); 325 return from_text(&format!("({})", pats_str));
312 326
313 fn from_text(text: &str) -> ast::TuplePat { 327 fn from_text(text: &str) -> ast::TuplePat {
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index ae98dbd26..171099661 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -125,6 +125,18 @@ pub enum AttrKind {
125 Outer, 125 Outer,
126} 126}
127 127
128impl AttrKind {
129 /// Returns `true` if the attr_kind is [`Inner`].
130 pub fn is_inner(&self) -> bool {
131 matches!(self, Self::Inner)
132 }
133
134 /// Returns `true` if the attr_kind is [`Outer`].
135 pub fn is_outer(&self) -> bool {
136 matches!(self, Self::Outer)
137 }
138}
139
128impl ast::Attr { 140impl ast::Attr {
129 pub fn as_simple_atom(&self) -> Option<SmolStr> { 141 pub fn as_simple_atom(&self) -> Option<SmolStr> {
130 if self.eq_token().is_some() || self.token_tree().is_some() { 142 if self.eq_token().is_some() || self.token_tree().is_some() {
diff --git a/crates/syntax/src/ted.rs b/crates/syntax/src/ted.rs
index 177d4ff67..450f2e447 100644
--- a/crates/syntax/src/ted.rs
+++ b/crates/syntax/src/ted.rs
@@ -165,6 +165,13 @@ fn ws_between(left: &SyntaxElement, right: &SyntaxElement) -> Option<SyntaxToken
165 if right.kind() == T![;] || right.kind() == T![,] { 165 if right.kind() == T![;] || right.kind() == T![,] {
166 return None; 166 return None;
167 } 167 }
168 if left.kind() == T![<] || right.kind() == T![>] {
169 return None;
170 }
171 if left.kind() == T![&] && right.kind() == SyntaxKind::LIFETIME {
172 return None;
173 }
174
168 if right.kind() == SyntaxKind::USE { 175 if right.kind() == SyntaxKind::USE {
169 let indent = IndentLevel::from_element(left); 176 let indent = IndentLevel::from_element(left);
170 return Some(make::tokens::whitespace(&format!("\n{}", indent))); 177 return Some(make::tokens::whitespace(&format!("\n{}", indent)));
diff --git a/crates/syntax/src/validation/block.rs b/crates/syntax/src/validation/block.rs
index ad9901468..40170014f 100644
--- a/crates/syntax/src/validation/block.rs
+++ b/crates/syntax/src/validation/block.rs
@@ -13,7 +13,7 @@ pub(crate) fn validate_block_expr(block: ast::BlockExpr, errors: &mut Vec<Syntax
13 _ => {} 13 _ => {}
14 } 14 }
15 } 15 }
16 errors.extend(block.attrs().map(|attr| { 16 errors.extend(block.attrs().filter(|attr| attr.kind().is_inner()).map(|attr| {
17 SyntaxError::new( 17 SyntaxError::new(
18 "A block in this position cannot accept inner attributes", 18 "A block in this position cannot accept inner attributes",
19 attr.syntax().text_range(), 19 attr.syntax().text_range(),
diff --git a/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast b/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast
index 3016a6574..1ff3f7656 100644
--- a/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0117_macro_call_type.rast
@@ -7,15 +7,16 @@ [email protected]
7 [email protected] " " 7 [email protected] " "
8 [email protected] "=" 8 [email protected] "="
9 [email protected] " " 9 [email protected] " "
10 [email protected] 10 [email protected]
11 [email protected] 11 [email protected]
12 [email protected] 12 [email protected]
13 [email protected] 13 [email protected]
14 [email protected] "foo" 14 [email protected]
15 [email protected] "!" 15 [email protected] "foo"
16 [email protected] 16 [email protected] "!"
17 [email protected] "(" 17 [email protected]
18 [email protected] ")" 18 [email protected] "("
19 [email protected] ")"
19 [email protected] ";" 20 [email protected] ";"
20 [email protected] "\n" 21 [email protected] "\n"
21 [email protected] 22 [email protected]
@@ -26,19 +27,20 @@ [email protected]
26 [email protected] " " 27 [email protected] " "
27 [email protected] "=" 28 [email protected] "="
28 [email protected] " " 29 [email protected] " "
29 [email protected] 30 [email protected]
30 [email protected] 31 [email protected]
31 [email protected] 32 [email protected]
32 [email protected] 33 [email protected]
33 [email protected] 34 [email protected]
34 [email protected] "crate" 35 [email protected]
35 [email protected] "::" 36 [email protected] "crate"
36 [email protected] 37 [email protected] "::"
37 [email protected] 38 [email protected]
38 [email protected] "foo" 39 [email protected]
39 [email protected] "!" 40 [email protected] "foo"
40 [email protected] 41 [email protected] "!"
41 [email protected] "(" 42 [email protected]
42 [email protected] ")" 43 [email protected] "("
44 [email protected] ")"
43 [email protected] ";" 45 [email protected] ";"
44 [email protected] "\n" 46 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast
index 925409bdf..e9202a612 100644
--- a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast
@@ -1,5 +1,5 @@
1SOURCE_FILE@0..63 1SOURCE_FILE@0..102
2 FN@0..62 2 FN@0..101
3 [email protected] "fn" 3 [email protected] "fn"
4 [email protected] " " 4 [email protected] " "
5 [email protected] 5 [email protected]
@@ -8,7 +8,7 @@ [email protected]
8 [email protected] "(" 8 [email protected] "("
9 [email protected] ")" 9 [email protected] ")"
10 [email protected] " " 10 [email protected] " "
11 BLOCK_EXPR@9..62 11 BLOCK_EXPR@9..101
12 [email protected] "{" 12 [email protected] "{"
13 [email protected] "\n " 13 [email protected] "\n "
14 [email protected] 14 [email protected]
@@ -70,6 +70,52 @@ [email protected]
70 [email protected] "(" 70 [email protected] "("
71 [email protected] ")" 71 [email protected] ")"
72 [email protected] ";" 72 [email protected] ";"
73 [email protected] "\n" 73 [email protected] "\n "
74 [email protected] "}" 74 [email protected]
75 [email protected] "\n" 75 [email protected] "let"
76 [email protected] " "
77 [email protected]
78 [email protected]
79 [email protected]
80 [email protected]
81 [email protected] "S"
82 [email protected] " "
83 [email protected]
84 [email protected] "{"
85 [email protected] " "
86 [email protected]
87 [email protected]
88 [email protected] "#"
89 [email protected] "["
90 [email protected]
91 [email protected]
92 [email protected]
93 [email protected] "cfg"
94 [email protected]
95 [email protected] "("
96 [email protected] "any"
97 [email protected]
98 [email protected] "("
99 [email protected] ")"
100 [email protected] ")"
101 [email protected] "]"
102 [email protected] " "
103 [email protected]
104 [email protected] "x"
105 [email protected] ":"
106 [email protected] " "
107 [email protected]
108 [email protected]
109 [email protected] "1"
110 [email protected] " "
111 [email protected] "}"
112 [email protected] " "
113 [email protected] "="
114 [email protected] " "
115 [email protected]
116 [email protected] "("
117 [email protected] ")"
118 [email protected] ";"
119 [email protected] "\n"
120 [email protected] "}"
121 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs
index 26b1d5f89..53cfdc22d 100644
--- a/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs
@@ -1,4 +1,5 @@
1fn foo() { 1fn foo() {
2 let S { 0: 1 } = (); 2 let S { 0: 1 } = ();
3 let S { x: 1 } = (); 3 let S { x: 1 } = ();
4 let S { #[cfg(any())] x: 1 } = ();
4} 5}
diff --git a/crates/syntax/test_data/parser/ok/0045_block_attrs.rast b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast
new file mode 100644
index 000000000..50ab52d32
--- /dev/null
+++ b/crates/syntax/test_data/parser/ok/0045_block_attrs.rast
@@ -0,0 +1,218 @@
1[email protected]
2 [email protected]
3 [email protected] "fn"
4 [email protected] " "
5 [email protected]
6 [email protected] "inner"
7 [email protected]
8 [email protected] "("
9 [email protected] ")"
10 [email protected] " "
11 [email protected]
12 [email protected] "{"
13 [email protected] "\n "
14 [email protected]
15 [email protected] "#"
16 [email protected] "!"
17 [email protected] "["
18 [email protected]
19 [email protected]
20 [email protected]
21 [email protected] "doc"
22 [email protected]
23 [email protected] "("
24 [email protected] "\"Inner attributes all ..."
25 [email protected] ")"
26 [email protected] "]"
27 [email protected] "\n "
28 [email protected] "//! As are ModuleDoc ..."
29 [email protected] "\n "
30 [email protected]
31 [email protected]
32 [email protected] "{"
33 [email protected] "\n "
34 [email protected]
35 [email protected] "#"
36 [email protected] "!"
37 [email protected] "["
38 [email protected]
39 [email protected]
40 [email protected]
41 [email protected] "doc"
42 [email protected]
43 [email protected] "("
44 [email protected] "\"Inner attributes are ..."
45 [email protected] ")"
46 [email protected] "]"
47 [email protected] "\n "
48 [email protected]
49 [email protected] "#"
50 [email protected] "!"
51 [email protected] "["
52 [email protected]
53 [email protected]
54 [email protected]
55 [email protected] "doc"
56 [email protected]
57 [email protected] "("
58 [email protected] "\"Being validated is n ..."
59 [email protected] ")"
60 [email protected] "]"
61 [email protected] "\n "
62 [email protected] "//! As are ModuleDoc ..."
63 [email protected] "\n "
64 [email protected] "}"
65 [email protected] ";"
66 [email protected] "\n "
67 [email protected]
68 [email protected] "{"
69 [email protected] "\n "
70 [email protected]
71 [email protected] "#"
72 [email protected] "!"
73 [email protected] "["
74 [email protected]
75 [email protected]
76 [email protected]
77 [email protected] "doc"
78 [email protected]
79 [email protected] "("
80 [email protected] "\"Inner attributes are ..."
81 [email protected] ")"
82 [email protected] "]"
83 [email protected] "\n "
84 [email protected] "//! As are ModuleDoc ..."
85 [email protected] "\n "
86 [email protected] "}"
87 [email protected] "\n"
88 [email protected] "}"
89 [email protected] "\n\n"
90 [email protected]
91 [email protected] "fn"
92 [email protected] " "
93 [email protected]
94 [email protected] "outer"
95 [email protected]
96 [email protected] "("
97 [email protected] ")"
98 [email protected] " "
99 [email protected]
100 [email protected] "{"
101 [email protected] "\n "
102 [email protected]
103 [email protected] "let"
104 [email protected] " "
105 [email protected]
106 [email protected] "_"
107 [email protected] " "
108 [email protected] "="
109 [email protected] " "
110 [email protected]
111 [email protected]
112 [email protected] "#"
113 [email protected] "["
114 [email protected]
115 [email protected]
116 [email protected]
117 [email protected] "doc"
118 [email protected]
119 [email protected] "("
120 [email protected] "\"Outer attributes are ..."
121 [email protected] ")"
122 [email protected] "]"
123 [email protected] " "
124 [email protected] "{"
125 [email protected] "}"
126 [email protected] ";"
127 [email protected] "\n"
128 [email protected] "}"
129 [email protected] "\n\n"
130 [email protected] "// https://github.com ..."
131 [email protected] "\n"
132 [email protected]
133 [email protected] "impl"
134 [email protected] " "
135 [email protected]
136 [email protected]
137 [email protected]
138 [email protected]
139 [email protected] "Whatever"
140 [email protected] " "
141 [email protected]
142 [email protected] "{"
143 [email protected] "\n "
144 [email protected]
145 [email protected] "fn"
146 [email protected] " "
147 [email protected]
148 [email protected] "salsa_event"
149 [email protected]
150 [email protected] "("
151 [email protected]
152 [email protected] "&"
153 [email protected]
154 [email protected] "self"
155 [email protected] ","
156 [email protected] " "
157 [email protected]
158 [email protected]
159 [email protected]
160 [email protected] "event_fn"
161 [email protected] ":"
162 [email protected] " "
163 [email protected]
164 [email protected] "impl"
165 [email protected] " "
166 [email protected]
167 [email protected]
168 [email protected]
169 [email protected]
170 [email protected]
171 [email protected]
172 [email protected] "Fn"
173 [email protected]
174 [email protected] "("
175 [email protected] ")"
176 [email protected] " "
177 [email protected]
178 [email protected] "->"
179 [email protected] " "
180 [email protected]
181 [email protected]
182 [email protected]
183 [email protected]
184 [email protected] "Event"
185 [email protected]
186 [email protected] "<"
187 [email protected]
188 [email protected]
189 [email protected]
190 [email protected]
191 [email protected]
192 [email protected] "Self"
193 [email protected] ">"
194 [email protected] ")"
195 [email protected] " "
196 [email protected]
197 [email protected] "{"
198 [email protected] "\n "
199 [email protected]
200 [email protected] "#"
201 [email protected] "!"
202 [email protected] "["
203 [email protected]
204 [email protected]
205 [email protected]
206 [email protected] "allow"
207 [email protected]
208 [email protected] "("
209 [email protected] "unused_variables"
210 [email protected] ")"
211 [email protected] "]"
212 [email protected] " "
213 [email protected] "// this is `inner_at ..."
214 [email protected] "\n "
215 [email protected] "}"
216 [email protected] "\n"
217 [email protected] "}"
218 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rs b/crates/syntax/test_data/parser/ok/0045_block_attrs.rs
index 88df8138e..ed4593759 100644
--- a/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rs
+++ b/crates/syntax/test_data/parser/ok/0045_block_attrs.rs
@@ -1,4 +1,4 @@
1fn block() { 1fn inner() {
2 #![doc("Inner attributes allowed here")] 2 #![doc("Inner attributes allowed here")]
3 //! As are ModuleDoc style comments 3 //! As are ModuleDoc style comments
4 { 4 {
@@ -12,6 +12,10 @@ fn block() {
12 } 12 }
13} 13}
14 14
15fn outer() {
16 let _ = #[doc("Outer attributes are always allowed")] {};
17}
18
15// https://github.com/rust-analyzer/rust-analyzer/issues/689 19// https://github.com/rust-analyzer/rust-analyzer/issues/689
16impl Whatever { 20impl Whatever {
17 fn salsa_event(&self, event_fn: impl Fn() -> Event<Self>) { 21 fn salsa_event(&self, event_fn: impl Fn() -> Event<Self>) {
diff --git a/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rast b/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rast
deleted file mode 100644
index 6afed5f05..000000000
--- a/crates/syntax/test_data/parser/ok/0045_block_inner_attrs.rast
+++ /dev/null
@@ -1,178 +0,0 @@
1[email protected]
2 [email protected]
3 [email protected] "fn"
4 [email protected] " "
5 [email protected]
6 [email protected] "block"
7 [email protected]
8 [email protected] "("
9 [email protected] ")"
10 [email protected] " "
11 [email protected]
12 [email protected] "{"
13 [email protected] "\n "
14 [email protected]
15 [email protected] "#"
16 [email protected] "!"
17 [email protected] "["
18 [email protected]
19 [email protected]
20 [email protected]
21 [email protected] "doc"
22 [email protected]
23 [email protected] "("
24 [email protected] "\"Inner attributes all ..."
25 [email protected] ")"
26 [email protected] "]"
27 [email protected] "\n "
28 [email protected] "//! As are ModuleDoc ..."
29 [email protected] "\n "
30 [email protected]
31 [email protected]
32 [email protected] "{"
33 [email protected] "\n "
34 [email protected]
35 [email protected] "#"
36 [email protected] "!"
37 [email protected] "["
38 [email protected]
39 [email protected]
40 [email protected]
41 [email protected] "doc"
42 [email protected]
43 [email protected] "("
44 [email protected] "\"Inner attributes are ..."
45 [email protected] ")"
46 [email protected] "]"
47 [email protected] "\n "
48 [email protected]
49 [email protected] "#"
50 [email protected] "!"
51 [email protected] "["
52 [email protected]
53 [email protected]
54 [email protected]
55 [email protected] "doc"
56 [email protected]
57 [email protected] "("
58 [email protected] "\"Being validated is n ..."
59 [email protected] ")"
60 [email protected] "]"
61 [email protected] "\n "
62 [email protected] "//! As are ModuleDoc ..."
63 [email protected] "\n "
64 [email protected] "}"
65 [email protected] ";"
66 [email protected] "\n "
67 [email protected]
68 [email protected] "{"
69 [email protected] "\n "
70 [email protected]
71 [email protected] "#"
72 [email protected] "!"
73 [email protected] "["
74 [email protected]
75 [email protected]
76 [email protected]
77 [email protected] "doc"
78 [email protected]
79 [email protected] "("
80 [email protected] "\"Inner attributes are ..."
81 [email protected] ")"
82 [email protected] "]"
83 [email protected] "\n "
84 [email protected] "//! As are ModuleDoc ..."
85 [email protected] "\n "
86 [email protected] "}"
87 [email protected] "\n"
88 [email protected] "}"
89 [email protected] "\n\n"
90 [email protected] "// https://github.com ..."
91 [email protected] "\n"
92 [email protected]
93 [email protected] "impl"
94 [email protected] " "
95 [email protected]
96 [email protected]
97 [email protected]
98 [email protected]
99 [email protected] "Whatever"
100 [email protected] " "
101 [email protected]
102 [email protected] "{"
103 [email protected] "\n "
104 [email protected]
105 [email protected] "fn"
106 [email protected] " "
107 [email protected]
108 [email protected] "salsa_event"
109 [email protected]
110 [email protected] "("
111 [email protected]
112 [email protected] "&"
113 [email protected]
114 [email protected] "self"
115 [email protected] ","
116 [email protected] " "
117 [email protected]
118 [email protected]
119 [email protected]
120 [email protected] "event_fn"
121 [email protected] ":"
122 [email protected] " "
123 [email protected]
124 [email protected] "impl"
125 [email protected] " "
126 [email protected]
127 [email protected]
128 [email protected]
129 [email protected]
130 [email protected]
131 [email protected]
132 [email protected] "Fn"
133 [email protected]
134 [email protected] "("
135 [email protected] ")"
136 [email protected] " "
137 [email protected]
138 [email protected] "->"
139 [email protected] " "
140 [email protected]
141 [email protected]
142 [email protected]
143 [email protected]
144 [email protected] "Event"
145 [email protected]
146 [email protected] "<"
147 [email protected]
148 [email protected]
149 [email protected]
150 [email protected]
151 [email protected]
152 [email protected] "Self"
153 [email protected] ">"
154 [email protected] ")"
155 [email protected] " "
156 [email protected]
157 [email protected] "{"
158 [email protected] "\n "
159 [email protected]
160 [email protected] "#"
161 [email protected] "!"
162 [email protected] "["
163 [email protected]
164 [email protected]
165 [email protected]
166 [email protected] "allow"
167 [email protected]
168 [email protected] "("
169 [email protected] "unused_variables"
170 [email protected] ")"
171 [email protected] "]"
172 [email protected] " "
173 [email protected] "// this is `inner_at ..."
174 [email protected] "\n "
175 [email protected] "}"
176 [email protected] "\n"
177 [email protected] "}"
178 [email protected] "\n"
diff --git a/crates/test_utils/src/assert_linear.rs b/crates/test_utils/src/assert_linear.rs
new file mode 100644
index 000000000..6ecc232e1
--- /dev/null
+++ b/crates/test_utils/src/assert_linear.rs
@@ -0,0 +1,112 @@
1//! Checks that a set of measurements looks like a linear function rather than
2//! like a quadratic function. Algorithm:
3//!
4//! 1. Linearly scale input to be in [0; 1)
5//! 2. Using linear regression, compute the best linear function approximating
6//! the input.
7//! 3. Compute RMSE and maximal absolute error.
8//! 4. Check that errors are within tolerances and that the constant term is not
9//! too negative.
10//!
11//! Ideally, we should use a proper "model selection" to directly compare
12//! quadratic and linear models, but that sounds rather complicated:
13//!
14//! https://stats.stackexchange.com/questions/21844/selecting-best-model-based-on-linear-quadratic-and-cubic-fit-of-data
15//!
16//! We might get false positives on a VM, but never false negatives. So, if the
17//! first round fails, we repeat the ordeal three more times and fail only if
18//! every time there's a fault.
19use stdx::format_to;
20
21#[derive(Default)]
22pub struct AssertLinear {
23 rounds: Vec<Round>,
24}
25
26#[derive(Default)]
27struct Round {
28 samples: Vec<(f64, f64)>,
29 plot: String,
30 linear: bool,
31}
32
33impl AssertLinear {
34 pub fn next_round(&mut self) -> bool {
35 if let Some(round) = self.rounds.last_mut() {
36 round.finish();
37 }
38 if self.rounds.iter().any(|it| it.linear) || self.rounds.len() == 4 {
39 return false;
40 }
41 self.rounds.push(Round::default());
42 true
43 }
44
45 pub fn sample(&mut self, x: f64, y: f64) {
46 self.rounds.last_mut().unwrap().samples.push((x, y))
47 }
48}
49
50impl Drop for AssertLinear {
51 fn drop(&mut self) {
52 assert!(!self.rounds.is_empty());
53 if self.rounds.iter().all(|it| !it.linear) {
54 for round in &self.rounds {
55 eprintln!("\n{}", round.plot);
56 }
57 panic!("Doesn't look linear!")
58 }
59 }
60}
61
62impl Round {
63 fn finish(&mut self) {
64 let (mut xs, mut ys): (Vec<_>, Vec<_>) = self.samples.iter().copied().unzip();
65 normalize(&mut xs);
66 normalize(&mut ys);
67 let xy = xs.iter().copied().zip(ys.iter().copied());
68
69 // Linear regression: finding a and b to fit y = a + b*x.
70
71 let mean_x = mean(&xs);
72 let mean_y = mean(&ys);
73
74 let b = {
75 let mut num = 0.0;
76 let mut denom = 0.0;
77 for (x, y) in xy.clone() {
78 num += (x - mean_x) * (y - mean_y);
79 denom += (x - mean_x).powi(2);
80 }
81 num / denom
82 };
83
84 let a = mean_y - b * mean_x;
85
86 self.plot = format!("y_pred = {:.3} + {:.3} * x\n\nx y y_pred\n", a, b);
87
88 let mut se = 0.0;
89 let mut max_error = 0.0f64;
90 for (x, y) in xy {
91 let y_pred = a + b * x;
92 se += (y - y_pred).powi(2);
93 max_error = max_error.max((y_pred - y).abs());
94
95 format_to!(self.plot, "{:.3} {:.3} {:.3}\n", x, y, y_pred);
96 }
97
98 let rmse = (se / xs.len() as f64).sqrt();
99 format_to!(self.plot, "\nrmse = {:.3} max error = {:.3}", rmse, max_error);
100
101 self.linear = rmse < 0.05 && max_error < 0.1 && a > -0.1;
102
103 fn normalize(xs: &mut Vec<f64>) {
104 let max = xs.iter().copied().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap();
105 xs.iter_mut().for_each(|it| *it /= max);
106 }
107
108 fn mean(xs: &[f64]) -> f64 {
109 xs.iter().copied().sum::<f64>() / (xs.len() as f64)
110 }
111 }
112}
diff --git a/crates/test_utils/src/bench_fixture.rs b/crates/test_utils/src/bench_fixture.rs
index 3a37c4473..979156263 100644
--- a/crates/test_utils/src/bench_fixture.rs
+++ b/crates/test_utils/src/bench_fixture.rs
@@ -8,7 +8,10 @@ use crate::project_root;
8 8
9pub fn big_struct() -> String { 9pub fn big_struct() -> String {
10 let n = 1_000; 10 let n = 1_000;
11 big_struct_n(n)
12}
11 13
14pub fn big_struct_n(n: u32) -> String {
12 let mut buf = "pub struct RegisterBlock {".to_string(); 15 let mut buf = "pub struct RegisterBlock {".to_string();
13 for i in 0..n { 16 for i in 0..n {
14 format_to!(buf, " /// Doc comment for {}.\n", i); 17 format_to!(buf, " /// Doc comment for {}.\n", i);
diff --git a/crates/test_utils/src/fixture.rs b/crates/test_utils/src/fixture.rs
index 6bc824e94..099baeca2 100644
--- a/crates/test_utils/src/fixture.rs
+++ b/crates/test_utils/src/fixture.rs
@@ -1,5 +1,65 @@
1//! Defines `Fixture` -- a convenient way to describe the initial state of 1//! Defines `Fixture` -- a convenient way to describe the initial state of
2//! rust-analyzer database from a single string. 2//! rust-analyzer database from a single string.
3//!
4//! Fixtures are strings containing rust source code with optional metadata.
5//! A fixture without metadata is parsed into a single source file.
6//! Use this to test functionality local to one file.
7//!
8//! Simple Example:
9//! ```
10//! r#"
11//! fn main() {
12//! println!("Hello World")
13//! }
14//! "#
15//! ```
16//!
17//! Metadata can be added to a fixture after a `//-` comment.
18//! The basic form is specifying filenames,
19//! which is also how to define multiple files in a single test fixture
20//!
21//! Example using two files in the same crate:
22//! ```
23//! "
24//! //- /main.rs
25//! mod foo;
26//! fn main() {
27//! foo::bar();
28//! }
29//!
30//! //- /foo.rs
31//! pub fn bar() {}
32//! "
33//! ```
34//!
35//! Example using two crates with one file each, with one crate depending on the other:
36//! ```
37//! r#"
38//! //- /main.rs crate:a deps:b
39//! fn main() {
40//! b::foo();
41//! }
42//! //- /lib.rs crate:b
43//! pub fn b() {
44//! println!("Hello World")
45//! }
46//! "#
47//! ```
48//!
49//! Metadata allows specifying all settings and variables
50//! that are available in a real rust project:
51//! - crate names via `crate:cratename`
52//! - dependencies via `deps:dep1,dep2`
53//! - configuration settings via `cfg:dbg=false,opt_level=2`
54//! - environment variables via `env:PATH=/bin,RUST_LOG=debug`
55//!
56//! Example using all available metadata:
57//! ```
58//! "
59//! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
60//! fn insert_source_code_here() {}
61//! "
62//! ```
3 63
4use rustc_hash::FxHashMap; 64use rustc_hash::FxHashMap;
5use stdx::{lines_with_ends, split_once, trim_indent}; 65use stdx::{lines_with_ends, split_once, trim_indent};
@@ -24,7 +84,7 @@ impl Fixture {
24 /// //- some meta 84 /// //- some meta
25 /// line 1 85 /// line 1
26 /// line 2 86 /// line 2
27 /// // - other meta 87 /// //- other meta
28 /// ``` 88 /// ```
29 pub fn parse(ra_fixture: &str) -> Vec<Fixture> { 89 pub fn parse(ra_fixture: &str) -> Vec<Fixture> {
30 let fixture = trim_indent(ra_fixture); 90 let fixture = trim_indent(ra_fixture);
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index c5f859790..72466c957 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -8,6 +8,7 @@
8 8
9pub mod bench_fixture; 9pub mod bench_fixture;
10mod fixture; 10mod fixture;
11mod assert_linear;
11 12
12use std::{ 13use std::{
13 convert::{TryFrom, TryInto}, 14 convert::{TryFrom, TryInto},
@@ -22,7 +23,7 @@ use text_size::{TextRange, TextSize};
22pub use dissimilar::diff as __diff; 23pub use dissimilar::diff as __diff;
23pub use rustc_hash::FxHashMap; 24pub use rustc_hash::FxHashMap;
24 25
25pub use crate::fixture::Fixture; 26pub use crate::{assert_linear::AssertLinear, fixture::Fixture};
26 27
27pub const CURSOR_MARKER: &str = "$0"; 28pub const CURSOR_MARKER: &str = "$0";
28pub const ESCAPED_CURSOR_MARKER: &str = "\\$0"; 29pub const ESCAPED_CURSOR_MARKER: &str = "\\$0";
diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml
index c318a68f7..894944b18 100644
--- a/crates/vfs/Cargo.toml
+++ b/crates/vfs/Cargo.toml
@@ -14,3 +14,4 @@ rustc-hash = "1.0"
14fst = "0.4" 14fst = "0.4"
15 15
16paths = { path = "../paths", version = "0.0.0" } 16paths = { path = "../paths", version = "0.0.0" }
17indexmap = "1.6.2"
diff --git a/crates/vfs/src/path_interner.rs b/crates/vfs/src/path_interner.rs
index 2189e5e25..6e049f0d4 100644
--- a/crates/vfs/src/path_interner.rs
+++ b/crates/vfs/src/path_interner.rs
@@ -1,15 +1,22 @@
1//! Maps paths to compact integer ids. We don't care about clearings paths which 1//! Maps paths to compact integer ids. We don't care about clearings paths which
2//! no longer exist -- the assumption is total size of paths we ever look at is 2//! no longer exist -- the assumption is total size of paths we ever look at is
3//! not too big. 3//! not too big.
4use rustc_hash::FxHashMap; 4use std::hash::BuildHasherDefault;
5
6use indexmap::IndexSet;
7use rustc_hash::FxHasher;
5 8
6use crate::{FileId, VfsPath}; 9use crate::{FileId, VfsPath};
7 10
8/// Structure to map between [`VfsPath`] and [`FileId`]. 11/// Structure to map between [`VfsPath`] and [`FileId`].
9#[derive(Default)]
10pub(crate) struct PathInterner { 12pub(crate) struct PathInterner {
11 map: FxHashMap<VfsPath, FileId>, 13 map: IndexSet<VfsPath, BuildHasherDefault<FxHasher>>,
12 vec: Vec<VfsPath>, 14}
15
16impl Default for PathInterner {
17 fn default() -> Self {
18 Self { map: IndexSet::default() }
19 }
13} 20}
14 21
15impl PathInterner { 22impl PathInterner {
@@ -17,7 +24,7 @@ impl PathInterner {
17 /// 24 ///
18 /// If `path` does not exists in `self`, returns [`None`]. 25 /// If `path` does not exists in `self`, returns [`None`].
19 pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> { 26 pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> {
20 self.map.get(path).copied() 27 self.map.get_index_of(path).map(|i| FileId(i as u32))
21 } 28 }
22 29
23 /// Insert `path` in `self`. 30 /// Insert `path` in `self`.
@@ -25,13 +32,9 @@ impl PathInterner {
25 /// - If `path` already exists in `self`, returns its associated id; 32 /// - If `path` already exists in `self`, returns its associated id;
26 /// - Else, returns a newly allocated id. 33 /// - Else, returns a newly allocated id.
27 pub(crate) fn intern(&mut self, path: VfsPath) -> FileId { 34 pub(crate) fn intern(&mut self, path: VfsPath) -> FileId {
28 if let Some(id) = self.get(&path) { 35 let (id, _added) = self.map.insert_full(path);
29 return id; 36 assert!(id < u32::MAX as usize);
30 } 37 FileId(id as u32)
31 let id = FileId(self.vec.len() as u32);
32 self.map.insert(path.clone(), id);
33 self.vec.push(path);
34 id
35 } 38 }
36 39
37 /// Returns the path corresponding to `id`. 40 /// Returns the path corresponding to `id`.
@@ -40,6 +43,6 @@ impl PathInterner {
40 /// 43 ///
41 /// Panics if `id` does not exists in `self`. 44 /// Panics if `id` does not exists in `self`.
42 pub(crate) fn lookup(&self, id: FileId) -> &VfsPath { 45 pub(crate) fn lookup(&self, id: FileId) -> &VfsPath {
43 &self.vec[id.0 as usize] 46 self.map.get_index(id.0 as usize).unwrap()
44 } 47 }
45} 48}
diff --git a/docs/dev/README.md b/docs/dev/README.md
index eab21a765..16b23adc6 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -24,7 +24,7 @@ Rust Analyzer is a part of [RLS-2.0 working
24group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0). 24group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0).
25Discussion happens in this Zulip stream: 25Discussion happens in this Zulip stream:
26 26
27https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0 27https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
28 28
29# Issue Labels 29# Issue Labels
30 30
@@ -32,6 +32,8 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0
32 are good issues to get into the project. 32 are good issues to get into the project.
33* [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions) 33* [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions)
34 issues have links to the code in question and tests. 34 issues have links to the code in question and tests.
35* [Broken Window](https://github.com/rust-analyzer/rust-analyzer/issues?q=is:issue+is:open+label:%22Broken+Window%22)
36 are issues which are not critical by themselves, but which should be fixed ASAP regardless, to avoid accumulation of technical debt.
35* [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy), 37* [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy),
36 [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium), 38 [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium),
37 [E-hard](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-hard), 39 [E-hard](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-hard),
@@ -206,20 +208,26 @@ Release process is handled by `release`, `dist` and `promote` xtasks, `release`
206 208
207Additionally, it assumes that remote for `rust-analyzer` is called `upstream` (I use `origin` to point to my fork). 209Additionally, it assumes that remote for `rust-analyzer` is called `upstream` (I use `origin` to point to my fork).
208 210
211`release` calls the GitHub API calls to scrape pull request comments and categorize them in the changelog.
212This step uses the `curl` and `jq` applications, which need to be available in `PATH`.
213Finally, you need to obtain a GitHub personal access token and set the `GITHUB_TOKEN` environment variable.
214
209Release steps: 215Release steps:
210 216
2111. Inside rust-analyzer, run `cargo xtask release`. This will: 2171. Set the `GITHUB_TOKEN` environment variable.
2182. Inside rust-analyzer, run `cargo xtask release`. This will:
212 * checkout the `release` branch 219 * checkout the `release` branch
213 * reset it to `upstream/nightly` 220 * reset it to `upstream/nightly`
214 * push it to `upstream`. This triggers GitHub Actions which: 221 * push it to `upstream`. This triggers GitHub Actions which:
215 * runs `cargo xtask dist` to package binaries and VS Code extension 222 * runs `cargo xtask dist` to package binaries and VS Code extension
216 * makes a GitHub release 223 * makes a GitHub release
217 * pushes VS Code extension to the marketplace 224 * pushes VS Code extension to the marketplace
218 * create new changelog in `rust-analyzer.github.io` 225 * call the GitHub API for PR details
2192. While the release is in progress, fill in the changelog 226 * create a new changelog in `rust-analyzer.github.io`
2203. Commit & push the changelog 2273. While the release is in progress, fill in the changelog
2214. Tweet 2284. Commit & push the changelog
2225. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's submodule. 2295. Tweet
2306. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's submodule.
223 Self-approve the PR. 231 Self-approve the PR.
224 232
225If the GitHub Actions release fails because of a transient problem like a timeout, you can re-run the job from the Actions console. 233If the GitHub Actions release fails because of a transient problem like a timeout, you can re-run the job from the Actions console.
@@ -227,18 +235,27 @@ If it fails because of something that needs to be fixed, remove the release tag
227Make sure to remove the new changelog post created when running `cargo xtask release` a second time. 235Make sure to remove the new changelog post created when running `cargo xtask release` a second time.
228 236
229We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week. 237We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week.
238
230We don't do "patch" releases, unless something truly egregious comes up. 239We don't do "patch" releases, unless something truly egregious comes up.
240To do a patch release, cherry-pick the fix on top of the current `release` branch and push the branch.
241There's no need to write a changelog for a patch release, it's OK to include the notes about the fix into the next weekly one.
242Note: we tag releases by dates, releasing a patch release on the same day should work (by overwriting a tag), but I am not 100% sure.
231 243
232## Permissions 244## Permissions
233 245
234There are three sets of people with extra permissions: 246There are three sets of people with extra permissions:
235 247
236* rust-analyzer GitHub organization **admins** (which include current t-compiler leads). 248* rust-analyzer GitHub organization [**admins**](https://github.com/orgs/rust-analyzer/people?query=role:owner) (which include current t-compiler leads).
237 Admins have full access to the org. 249 Admins have full access to the org.
238* **review** team in the organization. 250* [**review**](https://github.com/orgs/rust-analyzer/teams/review) team in the organization.
239 Reviewers have `r+` access to all of organization's repositories and publish rights on crates.io. 251 Reviewers have `r+` access to all of organization's repositories and publish rights on crates.io.
240 They also have direct commit access, but all changes should via bors queue. 252 They also have direct commit access, but all changes should via bors queue.
241 It's ok to self-approve if you think you know what you are doing! 253 It's ok to self-approve if you think you know what you are doing!
242 bors should automatically sync the permissions. 254 bors should automatically sync the permissions.
243* **triage** team in the organization. 255 Feel free to request a review or assign any PR to a reviewer with the relevant expertise to bring the work to their attention.
256 Don't feel pressured to review assigned PRs though.
257 If you don't feel like reviewing for whatever reason, someone else will pick the review up!
258* [**triage**](https://github.com/orgs/rust-analyzer/teams/triage) team in the organization.
244 This team can label and close issues. 259 This team can label and close issues.
260
261Note that at the time being you need to be a member of the org yourself to view the links.
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md
index fb991133a..39edf9e19 100644
--- a/docs/dev/architecture.md
+++ b/docs/dev/architecture.md
@@ -42,7 +42,7 @@ The underlying engine makes sure that model is computed lazily (on-demand) and c
42## Entry Points 42## Entry Points
43 43
44`crates/rust-analyzer/src/bin/main.rs` contains the main function which spawns LSP. 44`crates/rust-analyzer/src/bin/main.rs` contains the main function which spawns LSP.
45This is *the* entry point, but it front-loads a lot of complexity, so its fine to just skim through it. 45This is *the* entry point, but it front-loads a lot of complexity, so it's fine to just skim through it.
46 46
47`crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP. 47`crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP.
48 48
@@ -67,7 +67,7 @@ They are handled by Rust code in the xtask directory.
67 67
68VS Code plugin. 68VS Code plugin.
69 69
70### `libs/` 70### `lib/`
71 71
72rust-analyzer independent libraries which we publish to crates.io. 72rust-analyzer independent libraries which we publish to crates.io.
73It's not heavily utilized at the moment. 73It's not heavily utilized at the moment.
@@ -139,7 +139,8 @@ If an AST method returns an `Option`, it *can* be `None` at runtime, even if thi
139### `crates/base_db` 139### `crates/base_db`
140 140
141We use the [salsa](https://github.com/salsa-rs/salsa) crate for incremental and on-demand computation. 141We use the [salsa](https://github.com/salsa-rs/salsa) crate for incremental and on-demand computation.
142Roughly, you can think of salsa as a key-value store, but it can also compute derived values using specified functions. The `base_db` crate provides basic infrastructure for interacting with salsa. 142Roughly, you can think of salsa as a key-value store, but it can also compute derived values using specified functions.
143The `base_db` crate provides basic infrastructure for interacting with salsa.
143Crucially, it defines most of the "input" queries: facts supplied by the client of the analyzer. 144Crucially, it defines most of the "input" queries: facts supplied by the client of the analyzer.
144Reading the docs of the `base_db::input` module should be useful: everything else is strictly derived from those inputs. 145Reading the docs of the `base_db::input` module should be useful: everything else is strictly derived from those inputs.
145 146
@@ -221,7 +222,7 @@ Internally, `ide` is split across several crates. `ide_assists`, `ide_completion
221The `ide` contains a public API/façade, as well as implementation for a plethora of smaller features. 222The `ide` contains a public API/façade, as well as implementation for a plethora of smaller features.
222 223
223**Architecture Invariant:** `ide` crate strives to provide a _perfect_ API. 224**Architecture Invariant:** `ide` crate strives to provide a _perfect_ API.
224Although at the moment it has only one consumer, the LSP server, LSP *does not* influence it's API design. 225Although at the moment it has only one consumer, the LSP server, LSP *does not* influence its API design.
225Instead, we keep in mind a hypothetical _ideal_ client -- an IDE tailored specifically for rust, every nook and cranny of which is packed with Rust-specific goodies. 226Instead, we keep in mind a hypothetical _ideal_ client -- an IDE tailored specifically for rust, every nook and cranny of which is packed with Rust-specific goodies.
226 227
227### `crates/rust-analyzer` 228### `crates/rust-analyzer`
@@ -307,7 +308,7 @@ This sections talks about the things which are everywhere and nowhere in particu
307 308
308### Code generation 309### Code generation
309 310
310Some of the components of this repository are generated through automatic processes. 311Some components in this repository are generated through automatic processes.
311Generated code is updated automatically on `cargo test`. 312Generated code is updated automatically on `cargo test`.
312Generated code is generally committed to the git repository. 313Generated code is generally committed to the git repository.
313 314
@@ -389,7 +390,7 @@ fn spam() {
389``` 390```
390 391
391To specify input data, we use a single string literal in a special format, which can describe a set of rust files. 392To specify input data, we use a single string literal in a special format, which can describe a set of rust files.
392See the `Fixture` type. 393See the `Fixture` its module for fixture examples and documentation.
393 394
394**Architecture Invariant:** all code invariants are tested by `#[test]` tests. 395**Architecture Invariant:** all code invariants are tested by `#[test]` tests.
395There's no additional checks in CI, formatting and tidy tests are run with `cargo test`. 396There's no additional checks in CI, formatting and tidy tests are run with `cargo test`.
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 8a6f9f06e..f0f981802 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,8 +1,8 @@
1<!--- 1<!---
2lsp_ext.rs hash: e8a7502bd2b2c2f5 2lsp_ext.rs hash: 28a9d5a24b7ca396
3 3
4If you need to change the above hash to make the test pass, please check if you 4If you need to change the above hash to make the test pass, please check if you
5need to adjust this doc as well and ping this issue: 5need to adjust this doc as well and ping this issue:
6 6
7 https://github.com/rust-analyzer/rust-analyzer/issues/4604 7 https://github.com/rust-analyzer/rust-analyzer/issues/4604
8 8
@@ -46,13 +46,14 @@ If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests
46```typescript 46```typescript
47interface SnippetTextEdit extends TextEdit { 47interface SnippetTextEdit extends TextEdit {
48 insertTextFormat?: InsertTextFormat; 48 insertTextFormat?: InsertTextFormat;
49 annotationId?: ChangeAnnotationIdentifier;
49} 50}
50``` 51```
51 52
52```typescript 53```typescript
53export interface TextDocumentEdit { 54export interface TextDocumentEdit {
54 textDocument: OptionalVersionedTextDocumentIdentifier; 55 textDocument: OptionalVersionedTextDocumentIdentifier;
55 edits: (TextEdit | SnippetTextEdit)[]; 56 edits: (TextEdit | SnippetTextEdit)[];
56} 57}
57``` 58```
58 59
@@ -145,9 +146,9 @@ mod foo;
145### Unresolved Question 146### Unresolved Question
146 147
147* An alternative would be to use a more general "gotoSuper" request, which would work for super methods, super classes and super modules. 148* An alternative would be to use a more general "gotoSuper" request, which would work for super methods, super classes and super modules.
148 This is the approach IntelliJ Rust is takeing. 149 This is the approach IntelliJ Rust is taking.
149 However, experience shows that super module (which generally has a feeling of navigation between files) should be separate. 150 However, experience shows that super module (which generally has a feeling of navigation between files) should be separate.
150 If you want super module, but the cursor happens to be inside an overriden function, the behavior with single "gotoSuper" request is surprising. 151 If you want super module, but the cursor happens to be inside an overridden function, the behavior with single "gotoSuper" request is surprising.
151 152
152## Join Lines 153## Join Lines
153 154
@@ -193,7 +194,7 @@ fn main() {
193### Unresolved Question 194### Unresolved Question
194 195
195* What is the position of the cursor after `joinLines`? 196* What is the position of the cursor after `joinLines`?
196 Currently this is left to editor's discretion, but it might be useful to specify on the server via snippets. 197 Currently, this is left to editor's discretion, but it might be useful to specify on the server via snippets.
197 However, it then becomes unclear how it works with multi cursor. 198 However, it then becomes unclear how it works with multi cursor.
198 199
199## On Enter 200## On Enter
@@ -330,7 +331,7 @@ Moreover, it would be cool if editors didn't need to implement even basic langua
330 331
331### Unresolved Question 332### Unresolved Question
332 333
333* Should we return a a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair? 334* Should we return a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
334 This is how `SelectionRange` request works. 335 This is how `SelectionRange` request works.
335* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs? 336* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
336 337
@@ -419,23 +420,42 @@ Returns internal status message, mostly for debugging purposes.
419 420
420Reloads project information (that is, re-executes `cargo metadata`). 421Reloads project information (that is, re-executes `cargo metadata`).
421 422
422## Status Notification 423## Server Status
423 424
424**Experimental Client Capability:** `{ "statusNotification": boolean }` 425**Experimental Client Capability:** `{ "serverStatusNotification": boolean }`
425 426
426**Method:** `rust-analyzer/status` 427**Method:** `experimental/serverStatus`
427 428
428**Notification:** 429**Notification:**
429 430
430```typescript 431```typescript
431interface StatusParams { 432interface ServerStatusParams {
432 status: "loading" | "readyPartial" | "ready" | "invalid" | "needsReload", 433 /// `ok` means that the server is completely functional.
434 ///
435 /// `warning` means that the server is partially functional.
436 /// It can answer correctly to most requests, but some results
437 /// might be wrong due to, for example, some missing dependencies.
438 ///
439 /// `error` means that the server is not functional. For example,
440 /// there's a fatal build configuration problem. The server might
441 /// still give correct answers to simple requests, but most results
442 /// will be incomplete or wrong.
443 health: "ok" | "warning" | "error",
444 /// Is there any pending background work which might change the status?
445 /// For example, are dependencies being downloaded?
446 quiescent: bool,
447 /// Explanatory message to show on hover.
448 message?: string,
433} 449}
434``` 450```
435 451
436This notification is sent from server to client. 452This notification is sent from server to client.
437The client can use it to display persistent status to the user (in modline). 453The client can use it to display *persistent* status to the user (in modline).
438For `needsReload` state, the client can provide a context-menu action to run `rust-analyzer/reloadWorkspace` request. 454It is similar to the `showMessage`, but is intended for stares rather than point-in-time events.
455
456Note that this functionality is intended primarily to inform the end user about the state of the server.
457In particular, it's valid for the client to completely ignore this extension.
458Clients are discouraged from but are allowed to use the `health` status to decide if it's worth sending a request to the server.
439 459
440## Syntax Tree 460## Syntax Tree
441 461
@@ -497,7 +517,7 @@ Expands macro call at a given position.
497This request is sent from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types. 517This request is sent from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types.
498Generally, the client should re-query inlay hints after every modification. 518Generally, the client should re-query inlay hints after every modification.
499Note that we plan to move this request to `experimental/inlayHints`, as it is not really Rust-specific, but the current API is not necessary the right one. 519Note that we plan to move this request to `experimental/inlayHints`, as it is not really Rust-specific, but the current API is not necessary the right one.
500Upstream issue: https://github.com/microsoft/language-server-protocol/issues/956 520Upstream issues: https://github.com/microsoft/language-server-protocol/issues/956 , https://github.com/rust-analyzer/rust-analyzer/issues/2797
501 521
502**Request:** 522**Request:**
503 523
@@ -602,12 +622,11 @@ interface TestInfo {
602 622
603This request is sent from client to server to move item under cursor or selection in some direction. 623This request is sent from client to server to move item under cursor or selection in some direction.
604 624
605**Method:** `experimental/moveItemUp` 625**Method:** `experimental/moveItem`
606**Method:** `experimental/moveItemDown`
607 626
608**Request:** `MoveItemParams` 627**Request:** `MoveItemParams`
609 628
610**Response:** `TextDocumentEdit | null` 629**Response:** `SnippetTextEdit[]`
611 630
612```typescript 631```typescript
613export interface MoveItemParams { 632export interface MoveItemParams {
diff --git a/docs/dev/style.md b/docs/dev/style.md
index c594946be..6ab60b50e 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -53,9 +53,9 @@ https://www.tedinski.com/2018/02/06/system-boundaries.html
53## Crates.io Dependencies 53## Crates.io Dependencies
54 54
55We try to be very conservative with usage of crates.io dependencies. 55We try to be very conservative with usage of crates.io dependencies.
56Don't use small "helper" crates (exception: `itertools` is allowed). 56Don't use small "helper" crates (exception: `itertools` and `either` are allowed).
57If there's some general reusable bit of code you need, consider adding it to the `stdx` crate. 57If there's some general reusable bit of code you need, consider adding it to the `stdx` crate.
58A useful exercise is to read Cargo.lock and see if some of the *transitive* dependencies do not make sense for rust-analyzer. 58A useful exercise is to read Cargo.lock and see if some *transitive* dependencies do not make sense for rust-analyzer.
59 59
60**Rationale:** keep compile times low, create ecosystem pressure for faster compiles, reduce the number of things which might break. 60**Rationale:** keep compile times low, create ecosystem pressure for faster compiles, reduce the number of things which might break.
61 61
@@ -83,8 +83,19 @@ This makes it easier to prepare a changelog.
83 83
84If the change adds a new user-visible functionality, consider recording a GIF with [peek](https://github.com/phw/peek) and pasting it into the PR description. 84If the change adds a new user-visible functionality, consider recording a GIF with [peek](https://github.com/phw/peek) and pasting it into the PR description.
85 85
86To make writing the release notes easier, you can mark a pull request as a feature, fix, internal change, or minor.
87Minor changes are excluded from the release notes, while the other types are distributed in their corresponding sections.
88There are two ways to mark this:
89
90* use a `feat: `, `feature: `, `fix: `, `internal: ` or `minor: ` prefix in the PR title
91* write `changelog [feature|fix|internal|skip] [description]` in a comment or in the PR description; the description is optional, and will replace the title if included.
92
93These comments don't have to be added by the PR author.
94Editing a comment or the PR description or title is also fine, as long as it happens before the release.
95
86**Rationale:** clean history is potentially useful, but rarely used. 96**Rationale:** clean history is potentially useful, but rarely used.
87But many users read changelogs. 97But many users read changelogs.
98Including a description and GIF suitable for the changelog means less work for the maintainers on the release day.
88 99
89## Clippy 100## Clippy
90 101
@@ -152,6 +163,16 @@ Do not reuse marks between several tests.
152 163
153**Rationale:** marks provide an easy way to find the canonical test for each bit of code. 164**Rationale:** marks provide an easy way to find the canonical test for each bit of code.
154This makes it much easier to understand. 165This makes it much easier to understand.
166More than one mark per test / code branch doesn't add significantly to understanding.
167
168## `#[should_panic]`
169
170Do not use `#[should_panic]` tests.
171Instead, explicitly check for `None`, `Err`, etc.
172
173**Rationale:** `#[should_panic]` is a tool for library authors, to makes sure that API does not fail silently, when misused.
174`rust-analyzer` is not a library, we don't need to test for API misuse, and we have to handle any user input without panics.
175Panic messages in the logs from the `#[should_panic]` tests are confusing.
155 176
156## Function Preconditions 177## Function Preconditions
157 178
@@ -330,7 +351,7 @@ When implementing `do_thing`, it might be very useful to create a context object
330 351
331```rust 352```rust
332pub fn do_thing(arg1: Arg1, arg2: Arg2) -> Res { 353pub fn do_thing(arg1: Arg1, arg2: Arg2) -> Res {
333 let mut ctx = Ctx { arg1, arg2 } 354 let mut ctx = Ctx { arg1, arg2 };
334 ctx.run() 355 ctx.run()
335} 356}
336 357
@@ -586,7 +607,7 @@ use super::{}
586 607
587**Rationale:** consistency. 608**Rationale:** consistency.
588Reading order is important for new contributors. 609Reading order is important for new contributors.
589Grouping by crate allows to spot unwanted dependencies easier. 610Grouping by crate allows spotting unwanted dependencies easier.
590 611
591## Import Style 612## Import Style
592 613
@@ -779,7 +800,7 @@ assert!(x < y);
779assert!(x > 0); 800assert!(x > 0);
780 801
781// BAD 802// BAD
782assert!(x >= lo && x <= hi>); 803assert!(x >= lo && x <= hi);
783assert!(r1 < l2 || l1 > r2); 804assert!(r1 < l2 || l1 > r2);
784assert!(y > x); 805assert!(y > x);
785assert!(0 > x); 806assert!(0 > x);
@@ -842,7 +863,26 @@ Re-using originally single-purpose function often leads to bad coupling.
842 863
843## Helper Variables 864## Helper Variables
844 865
845Introduce helper variables freely, especially for multiline conditions. 866Introduce helper variables freely, especially for multiline conditions:
867
868```rust
869// GOOD
870let rustfmt_not_installed =
871 captured_stderr.contains("not installed") || captured_stderr.contains("not available");
872
873match output.status.code() {
874 Some(1) if !rustfmt_not_installed => Ok(None),
875 _ => Err(format_err!("rustfmt failed:\n{}", captured_stderr)),
876};
877
878// BAD
879match output.status.code() {
880 Some(1)
881 if !captured_stderr.contains("not installed")
882 && !captured_stderr.contains("not available") => Ok(None),
883 _ => Err(format_err!("rustfmt failed:\n{}", captured_stderr)),
884};
885```
846 886
847**Rationale:** like blocks, single-use variables are a cognitively cheap abstraction, as they have access to all the context. 887**Rationale:** like blocks, single-use variables are a cognitively cheap abstraction, as they have access to all the context.
848Extra variables help during debugging, they make it easy to print/view important intermediate results. 888Extra variables help during debugging, they make it easy to print/view important intermediate results.
diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md
index 737cc7a72..f7a0c09fc 100644
--- a/docs/dev/syntax.md
+++ b/docs/dev/syntax.md
@@ -145,7 +145,7 @@ Another alternative (used by swift and roslyn) is to explicitly divide the set o
145 145
146```rust 146```rust
147struct Token { 147struct Token {
148 kind: NonTriviaTokenKind 148 kind: NonTriviaTokenKind,
149 text: String, 149 text: String,
150 leading_trivia: Vec<TriviaToken>, 150 leading_trivia: Vec<TriviaToken>,
151 trailing_trivia: Vec<TriviaToken>, 151 trailing_trivia: Vec<TriviaToken>,
@@ -240,7 +240,7 @@ impl SyntaxNode {
240 let child_offset = offset; 240 let child_offset = offset;
241 offset += green_child.text_len; 241 offset += green_child.text_len;
242 Arc::new(SyntaxData { 242 Arc::new(SyntaxData {
243 offset: child_offset; 243 offset: child_offset,
244 parent: Some(Arc::clone(self)), 244 parent: Some(Arc::clone(self)),
245 green: Arc::clone(green_child), 245 green: Arc::clone(green_child),
246 }) 246 })
@@ -249,7 +249,7 @@ impl SyntaxNode {
249} 249}
250 250
251impl PartialEq for SyntaxNode { 251impl PartialEq for SyntaxNode {
252 fn eq(&self, other: &SyntaxNode) { 252 fn eq(&self, other: &SyntaxNode) -> bool {
253 self.offset == other.offset 253 self.offset == other.offset
254 && Arc::ptr_eq(&self.green, &other.green) 254 && Arc::ptr_eq(&self.green, &other.green)
255 } 255 }
@@ -273,7 +273,7 @@ This is OK because trees traversals mostly (always, in case of rust-analyzer) ru
273The other thread can restore the `SyntaxNode` by traversing from the root green node and looking for a node with specified range. 273The other thread can restore the `SyntaxNode` by traversing from the root green node and looking for a node with specified range.
274You can also use the similar trick to store a `SyntaxNode`. 274You can also use the similar trick to store a `SyntaxNode`.
275That is, a data structure that holds a `(GreenNode, Range<usize>)` will be `Sync`. 275That is, a data structure that holds a `(GreenNode, Range<usize>)` will be `Sync`.
276However rust-analyzer goes even further. 276However, rust-analyzer goes even further.
277It treats trees as semi-transient and instead of storing a `GreenNode`, it generally stores just the id of the file from which the tree originated: `(FileId, Range<usize>)`. 277It treats trees as semi-transient and instead of storing a `GreenNode`, it generally stores just the id of the file from which the tree originated: `(FileId, Range<usize>)`.
278The `SyntaxNode` is the restored by reparsing the file and traversing it from root. 278The `SyntaxNode` is the restored by reparsing the file and traversing it from root.
279With this trick, rust-analyzer holds only a small amount of trees in memory at the same time, which reduces memory usage. 279With this trick, rust-analyzer holds only a small amount of trees in memory at the same time, which reduces memory usage.
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 871c65add..e28423e99 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -39,6 +39,12 @@ List of features to activate.
39-- 39--
40Run build scripts (`build.rs`) for more precise code analysis. 40Run build scripts (`build.rs`) for more precise code analysis.
41-- 41--
42[[rust-analyzer.cargo.useRustcWrapperForBuildScripts]]rust-analyzer.cargo.useRustcWrapperForBuildScripts (default: `true`)::
43+
44--
45Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
46avoid compiling unnecessary things.
47--
42[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`):: 48[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`)::
43+ 49+
44-- 50--
@@ -141,6 +147,12 @@ have more false positives than usual.
141-- 147--
142List of rust-analyzer diagnostics to disable. 148List of rust-analyzer diagnostics to disable.
143-- 149--
150[[rust-analyzer.diagnostics.remapPrefix]]rust-analyzer.diagnostics.remapPrefix (default: `{}`)::
151+
152--
153Map of prefixes to be substituted when parsing diagnostic file paths.
154This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
155--
144[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: 156[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`)::
145+ 157+
146-- 158--
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index e74b287fb..54195adb7 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -429,24 +429,32 @@ However, if you use some other build system, you'll have to describe the structu
429[source,TypeScript] 429[source,TypeScript]
430---- 430----
431interface JsonProject { 431interface JsonProject {
432 /// Path to the directory with *source code* of sysroot crates. 432 /// Path to the directory with *source code* of
433 /// sysroot crates.
434 ///
435 /// It should point to the directory where std,
436 /// core, and friends can be found:
433 /// 437 ///
434 /// It should point to the directory where std, core, and friends can be found:
435 /// https://github.com/rust-lang/rust/tree/master/library. 438 /// https://github.com/rust-lang/rust/tree/master/library.
436 /// 439 ///
437 /// If provided, rust-analyzer automatically adds dependencies on sysroot 440 /// If provided, rust-analyzer automatically adds
438 /// crates. Conversely, if you omit this path, you can specify sysroot 441 /// dependencies on sysroot crates. Conversely,
439 /// dependencies yourself and, for example, have several different "sysroots" in 442 /// if you omit this path, you can specify sysroot
440 /// one graph of crates. 443 /// dependencies yourself and, for example, have
444 /// several different "sysroots" in one graph of
445 /// crates.
441 sysroot_src?: string; 446 sysroot_src?: string;
442 /// The set of crates comprising the current project. 447 /// The set of crates comprising the current
443 /// Must include all transitive dependencies as well as sysroot crate (libstd, libcore and such). 448 /// project. Must include all transitive
449 /// dependencies as well as sysroot crate (libstd,
450 /// libcore and such).
444 crates: Crate[]; 451 crates: Crate[];
445} 452}
446 453
447interface Crate { 454interface Crate {
448 /// Optional crate name used for display purposes, without affecting semantics. 455 /// Optional crate name used for display purposes,
449 /// See the `deps` key for semantically-significant crate names. 456 /// without affecting semantics. See the `deps`
457 /// key for semantically-significant crate names.
450 display_name?: string; 458 display_name?: string;
451 /// Path to the root module of the crate. 459 /// Path to the root module of the crate.
452 root_module: string; 460 root_module: string;
@@ -454,45 +462,59 @@ interface Crate {
454 edition: "2015" | "2018" | "2021"; 462 edition: "2015" | "2018" | "2021";
455 /// Dependencies 463 /// Dependencies
456 deps: Dep[]; 464 deps: Dep[];
457 /// Should this crate be treated as a member of current "workspace". 465 /// Should this crate be treated as a member of
466 /// current "workspace".
458 /// 467 ///
459 /// By default, inferred from the `root_module` (members are the crates which reside 468 /// By default, inferred from the `root_module`
460 /// inside the directory opened in the editor). 469 /// (members are the crates which reside inside
470 /// the directory opened in the editor).
461 /// 471 ///
462 /// Set this to `false` for things like standard library and 3rd party crates to 472 /// Set this to `false` for things like standard
463 /// enable performance optimizations (rust-analyzer assumes that non-member crates 473 /// library and 3rd party crates to enable
464 /// don't change). 474 /// performance optimizations (rust-analyzer
475 /// assumes that non-member crates don't change).
465 is_workspace_member?: boolean; 476 is_workspace_member?: boolean;
466 /// Optionally specify the (super)set of `.rs` files comprising this crate. 477 /// Optionally specify the (super)set of `.rs`
478 /// files comprising this crate.
467 /// 479 ///
468 /// By default, rust-analyzer assumes that only files under `root_module.parent` can belong to a crate. 480 /// By default, rust-analyzer assumes that only
469 /// `include_dirs` are included recursively, unless a subdirectory is in `exclude_dirs`. 481 /// files under `root_module.parent` can belong
482 /// to a crate. `include_dirs` are included
483 /// recursively, unless a subdirectory is in
484 /// `exclude_dirs`.
470 /// 485 ///
471 /// Different crates can share the same `source`. 486 /// Different crates can share the same `source`.
472 /// 487 ///
473 /// If two crates share an `.rs` file in common, they *must* have the same `source`. 488 /// If two crates share an `.rs` file in common,
474 /// rust-analyzer assumes that files from one source can't refer to files in another source. 489 /// they *must* have the same `source`.
490 /// rust-analyzer assumes that files from one
491 /// source can't refer to files in another source.
475 source?: { 492 source?: {
476 include_dirs: string[], 493 include_dirs: string[],
477 exclude_dirs: string[], 494 exclude_dirs: string[],
478 }, 495 },
479 /// The set of cfgs activated for a given crate, like `["unix", "feature=\"foo\"", "feature=\"bar\""]`. 496 /// The set of cfgs activated for a given crate, like
497 /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`.
480 cfg: string[]; 498 cfg: string[];
481 /// Target triple for this Crate. 499 /// Target triple for this Crate.
482 /// 500 ///
483 /// Used when running `rustc --print cfg` to get target-specific cfgs. 501 /// Used when running `rustc --print cfg`
502 /// to get target-specific cfgs.
484 target?: string; 503 target?: string;
485 /// Environment variables, used for the `env!` macro 504 /// Environment variables, used for
505 /// the `env!` macro
486 env: : { [key: string]: string; }, 506 env: : { [key: string]: string; },
487 507
488 /// For proc-macro crates, path to compiles proc-macro (.so file). 508 /// For proc-macro crates, path to compiled
509 /// proc-macro (.so file).
489 proc_macro_dylib_path?: string; 510 proc_macro_dylib_path?: string;
490} 511}
491 512
492interface Dep { 513interface Dep {
493 /// Index of a crate in the `crates` array. 514 /// Index of a crate in the `crates` array.
494 crate: number, 515 crate: number,
495 /// Name as should appear in the (implicit) `extern crate name` declaration. 516 /// Name as should appear in the (implicit)
517 /// `extern crate name` declaration.
496 name: string, 518 name: string,
497} 519}
498---- 520----
@@ -541,7 +563,7 @@ include::./generated_assists.adoc[]
541== Diagnostics 563== Diagnostics
542 564
543While most errors and warnings provided by rust-analyzer come from the `cargo check` integration, there's a growing number of diagnostics implemented using rust-analyzer's own analysis. 565While most errors and warnings provided by rust-analyzer come from the `cargo check` integration, there's a growing number of diagnostics implemented using rust-analyzer's own analysis.
544These diagnostics don't respect `#[allow]` or `#[deny]` attributes yet, but can be turned off using the `rust-analyzer.diagnostics.enable`, `rust-analyzer.diagnostics.enableExperimental` or `rust-analyzer.diagnostics.disabled` settings. 566Some of these diagnostics don't respect `\#[allow]` or `\#[deny]` attributes yet, but can be turned off using the `rust-analyzer.diagnostics.enable`, `rust-analyzer.diagnostics.enableExperimental` or `rust-analyzer.diagnostics.disabled` settings.
545 567
546include::./generated_diagnostic.adoc[] 568include::./generated_diagnostic.adoc[]
547 569
diff --git a/editors/code/package.json b/editors/code/package.json
index d263610f5..fa5632f90 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -434,6 +434,11 @@
434 "default": true, 434 "default": true,
435 "type": "boolean" 435 "type": "boolean"
436 }, 436 },
437 "rust-analyzer.cargo.useRustcWrapperForBuildScripts": {
438 "markdownDescription": "Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to\navoid compiling unnecessary things.",
439 "default": true,
440 "type": "boolean"
441 },
437 "rust-analyzer.cargo.noDefaultFeatures": { 442 "rust-analyzer.cargo.noDefaultFeatures": {
438 "markdownDescription": "Do not activate the `default` feature.", 443 "markdownDescription": "Do not activate the `default` feature.",
439 "default": false, 444 "default": false,
@@ -560,6 +565,11 @@
560 }, 565 },
561 "uniqueItems": true 566 "uniqueItems": true
562 }, 567 },
568 "rust-analyzer.diagnostics.remapPrefix": {
569 "markdownDescription": "Map of prefixes to be substituted when parsing diagnostic file paths.\nThis should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.",
570 "default": {},
571 "type": "object"
572 },
563 "rust-analyzer.diagnostics.warningsAsHint": { 573 "rust-analyzer.diagnostics.warningsAsHint": {
564 "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.", 574 "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.",
565 "default": [], 575 "default": [],
@@ -1190,4 +1200,4 @@
1190 ] 1200 ]
1191 } 1201 }
1192 } 1202 }
1193} \ No newline at end of file 1203}
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 0771ca3b6..116f41df6 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -159,7 +159,7 @@ class ExperimentalFeatures implements lc.StaticFeature {
159 caps.snippetTextEdit = true; 159 caps.snippetTextEdit = true;
160 caps.codeActionGroup = true; 160 caps.codeActionGroup = true;
161 caps.hoverActions = true; 161 caps.hoverActions = true;
162 caps.statusNotification = true; 162 caps.serverStatusNotification = true;
163 capabilities.experimental = caps; 163 capabilities.experimental = caps;
164 } 164 }
165 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { 165 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 1a0805bd3..4092435db 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -148,34 +148,16 @@ export function moveItem(ctx: Ctx, direction: ra.Direction): Cmd {
148 const client = ctx.client; 148 const client = ctx.client;
149 if (!editor || !client) return; 149 if (!editor || !client) return;
150 150
151 const edit = await client.sendRequest(ra.moveItem, { 151 const lcEdits = await client.sendRequest(ra.moveItem, {
152 range: client.code2ProtocolConverter.asRange(editor.selection), 152 range: client.code2ProtocolConverter.asRange(editor.selection),
153 textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), 153 textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document),
154 direction 154 direction
155 }); 155 });
156 156
157 if (!edit) return; 157 if (!lcEdits) return;
158 158
159 let cursor: vscode.Position | null = null; 159 const edits = client.protocol2CodeConverter.asTextEdits(lcEdits);
160 160 await applySnippetTextEdits(editor, edits);
161 await editor.edit((builder) => {
162 client.protocol2CodeConverter.asTextEdits(edit.edits).forEach((edit: any) => {
163 builder.replace(edit.range, edit.newText);
164
165 if (direction === ra.Direction.Up) {
166 if (!cursor || edit.range.end.isBeforeOrEqual(cursor)) {
167 cursor = edit.range.end;
168 }
169 } else {
170 if (!cursor || edit.range.end.isAfterOrEqual(cursor)) {
171 cursor = edit.range.end;
172 }
173 }
174 });
175 }).then(() => {
176 const newPosition = cursor ?? editor.selection.start;
177 editor.selection = new vscode.Selection(newPosition, newPosition);
178 });
179 }; 161 };
180} 162}
181 163
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index c07583cfa..bd023f803 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -5,7 +5,7 @@ import * as ra from './lsp_ext';
5import { Config } from './config'; 5import { Config } from './config';
6import { createClient } from './client'; 6import { createClient } from './client';
7import { isRustEditor, RustEditor } from './util'; 7import { isRustEditor, RustEditor } from './util';
8import { Status } from './lsp_ext'; 8import { ServerStatusParams } from './lsp_ext';
9 9
10export class Ctx { 10export class Ctx {
11 private constructor( 11 private constructor(
@@ -36,7 +36,7 @@ export class Ctx {
36 36
37 res.pushCleanup(client.start()); 37 res.pushCleanup(client.start());
38 await client.onReady(); 38 await client.onReady();
39 client.onNotification(ra.status, (params) => res.setStatus(params.status)); 39 client.onNotification(ra.serverStatus, (params) => res.setServerStatus(params));
40 return res; 40 return res;
41 } 41 }
42 42
@@ -66,39 +66,28 @@ export class Ctx {
66 return this.extCtx.subscriptions; 66 return this.extCtx.subscriptions;
67 } 67 }
68 68
69 setStatus(status: Status) { 69 setServerStatus(status: ServerStatusParams) {
70 switch (status) { 70 this.statusBar.tooltip = status.message ?? "Ready";
71 case "loading": 71 let icon = "";
72 this.statusBar.text = "$(sync~spin) rust-analyzer"; 72 switch (status.health) {
73 this.statusBar.tooltip = "Loading the project"; 73 case "ok":
74 this.statusBar.command = undefined;
75 this.statusBar.color = undefined; 74 this.statusBar.color = undefined;
76 break; 75 break;
77 case "readyPartial": 76 case "warning":
78 this.statusBar.text = "rust-analyzer"; 77 this.statusBar.tooltip += "\nClick to reload.";
79 this.statusBar.tooltip = "Ready (Partial)";
80 this.statusBar.command = undefined;
81 this.statusBar.color = undefined;
82 break;
83 case "ready":
84 this.statusBar.text = "rust-analyzer";
85 this.statusBar.tooltip = "Ready";
86 this.statusBar.command = undefined;
87 this.statusBar.color = undefined;
88 break;
89 case "invalid":
90 this.statusBar.text = "$(error) rust-analyzer";
91 this.statusBar.tooltip = "Failed to load the project";
92 this.statusBar.command = undefined;
93 this.statusBar.color = new vscode.ThemeColor("notificationsErrorIcon.foreground");
94 break;
95 case "needsReload":
96 this.statusBar.text = "$(warning) rust-analyzer";
97 this.statusBar.tooltip = "Click to reload";
98 this.statusBar.command = "rust-analyzer.reloadWorkspace"; 78 this.statusBar.command = "rust-analyzer.reloadWorkspace";
99 this.statusBar.color = new vscode.ThemeColor("notificationsWarningIcon.foreground"); 79 this.statusBar.color = new vscode.ThemeColor("notificationsWarningIcon.foreground");
80 icon = "$(warning) ";
81 break;
82 case "error":
83 this.statusBar.tooltip += "\nClick to reload.";
84 this.statusBar.command = "rust-analyzer.reloadWorkspace";
85 this.statusBar.color = new vscode.ThemeColor("notificationsErrorIcon.foreground");
86 icon = "$(error) ";
100 break; 87 break;
101 } 88 }
89 if (!status.quiescent) icon = "$(sync~spin) ";
90 this.statusBar.text = `${icon} rust-analyzer`;
102 } 91 }
103 92
104 pushCleanup(d: Disposable) { 93 pushCleanup(d: Disposable) {
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 00e128b8c..f78de894b 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -10,11 +10,12 @@ export interface AnalyzerStatusParams {
10export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>("rust-analyzer/analyzerStatus"); 10export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>("rust-analyzer/analyzerStatus");
11export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage"); 11export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage");
12 12
13export type Status = "loading" | "ready" | "readyPartial" | "invalid" | "needsReload"; 13export interface ServerStatusParams {
14export interface StatusParams { 14 health: "ok" | "warning" | "error";
15 status: Status; 15 quiescent: boolean;
16 message?: string;
16} 17}
17export const status = new lc.NotificationType<StatusParams>("rust-analyzer/status"); 18export const serverStatus = new lc.NotificationType<ServerStatusParams>("experimental/serverStatus");
18 19
19export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace"); 20export const reloadWorkspace = new lc.RequestType0<null, void>("rust-analyzer/reloadWorkspace");
20 21
@@ -128,7 +129,7 @@ export interface OpenCargoTomlParams {
128 textDocument: lc.TextDocumentIdentifier; 129 textDocument: lc.TextDocumentIdentifier;
129} 130}
130 131
131export const moveItem = new lc.RequestType<MoveItemParams, lc.TextDocumentEdit | void, void>("experimental/moveItem"); 132export const moveItem = new lc.RequestType<MoveItemParams, lc.TextEdit[], void>("experimental/moveItem");
132 133
133export interface MoveItemParams { 134export interface MoveItemParams {
134 textDocument: lc.TextDocumentIdentifier; 135 textDocument: lc.TextDocumentIdentifier;
diff --git a/lib/arena/src/lib.rs b/lib/arena/src/lib.rs
index bce15c867..1720537cb 100644
--- a/lib/arena/src/lib.rs
+++ b/lib/arena/src/lib.rs
@@ -90,7 +90,7 @@ impl<T> Idx<T> {
90} 90}
91 91
92/// Yet another index-based arena. 92/// Yet another index-based arena.
93#[derive(Clone, PartialEq, Eq)] 93#[derive(Clone, PartialEq, Eq, Hash)]
94pub struct Arena<T> { 94pub struct Arena<T> {
95 data: Vec<T>, 95 data: Vec<T>,
96} 96}
diff --git a/xtask/src/release.rs b/xtask/src/release.rs
index dde5d14ee..22bb50467 100644
--- a/xtask/src/release.rs
+++ b/xtask/src/release.rs
@@ -1,4 +1,4 @@
1use std::fmt::Write; 1mod changelog;
2 2
3use xshell::{cmd, cp, pushd, read_dir, write_file}; 3use xshell::{cmd, cp, pushd, read_dir, write_file};
4 4
@@ -38,42 +38,7 @@ impl flags::Release {
38 let tags = cmd!("git tag --list").read()?; 38 let tags = cmd!("git tag --list").read()?;
39 let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); 39 let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap();
40 40
41 let git_log = cmd!("git log {prev_tag}..HEAD --merges --reverse").read()?; 41 let contents = changelog::get_changelog(changelog_n, &commit, prev_tag, &today)?;
42 let mut git_log_summary = String::new();
43 for line in git_log.lines() {
44 let line = line.trim_start();
45 if let Some(p) = line.find(':') {
46 if let Ok(pr) = line[..p].parse::<u32>() {
47 writeln!(git_log_summary, "* pr:{}[]{}", pr, &line[p + 1..]).unwrap();
48 }
49 }
50 }
51
52 let contents = format!(
53 "\
54= Changelog #{}
55:sectanchors:
56:page-layout: post
57
58Commit: commit:{}[] +
59Release: release:{}[]
60
61== Sponsors
62
63**Become a sponsor:** On https://opencollective.com/rust-analyzer/[OpenCollective] or
64https://github.com/sponsors/rust-analyzer[GitHub Sponsors].
65
66== New Features
67
68{}
69
70== Fixes
71
72== Internal Improvements
73",
74 changelog_n, commit, today, git_log_summary
75 );
76
77 let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n)); 42 let path = changelog_dir.join(format!("{}-changelog-{}.adoc", today, changelog_n));
78 write_file(&path, &contents)?; 43 write_file(&path, &contents)?;
79 44
diff --git a/xtask/src/release/changelog.rs b/xtask/src/release/changelog.rs
new file mode 100644
index 000000000..ffcae2cf7
--- /dev/null
+++ b/xtask/src/release/changelog.rs
@@ -0,0 +1,159 @@
1use std::fmt::Write;
2use std::{env, iter};
3
4use anyhow::{bail, Result};
5use xshell::cmd;
6
7pub(crate) fn get_changelog(
8 changelog_n: usize,
9 commit: &str,
10 prev_tag: &str,
11 today: &str,
12) -> Result<String> {
13 let git_log = cmd!("git log {prev_tag}..HEAD --merges --reverse").read()?;
14 let mut features = String::new();
15 let mut fixes = String::new();
16 let mut internal = String::new();
17 let mut others = String::new();
18 for line in git_log.lines() {
19 let line = line.trim_start();
20 if let Some(p) = line.find(':') {
21 let pr = &line[..p];
22 if let Ok(pr_num) = pr.parse::<u32>() {
23 let accept = "Accept: application/vnd.github.v3+json";
24 let token = match env::var("GITHUB_TOKEN") {
25 Ok(token) => token,
26 Err(_) => bail!("Please obtain a personal access token from https://github.com/settings/tokens and set the `GITHUB_TOKEN` environment variable."),
27 };
28 let authorization = format!("Authorization: token {}", token);
29 let pr_url = "https://api.github.com/repos/rust-analyzer/rust-analyzer/issues";
30
31 // we don't use an HTTPS client or JSON parser to keep the build times low
32 let pr_json =
33 cmd!("curl -s -H {accept} -H {authorization} {pr_url}/{pr}").read()?;
34 let pr_title = cmd!("jq .title").stdin(&pr_json).read()?;
35 let pr_title = unescape(&pr_title[1..pr_title.len() - 1]);
36 let pr_comment = cmd!("jq .body").stdin(pr_json).read()?;
37
38 let comments_json =
39 cmd!("curl -s -H {accept} -H {authorization} {pr_url}/{pr}/comments").read()?;
40 let pr_comments = cmd!("jq .[].body").stdin(comments_json).read()?;
41
42 let l = iter::once(pr_comment.as_str())
43 .chain(pr_comments.lines())
44 .rev()
45 .find_map(|it| {
46 let it = unescape(&it[1..it.len() - 1]);
47 it.lines().find_map(parse_changelog_line)
48 })
49 .into_iter()
50 .next()
51 .unwrap_or_else(|| parse_title_line(&pr_title));
52 let s = match l.kind {
53 PrKind::Feature => &mut features,
54 PrKind::Fix => &mut fixes,
55 PrKind::Internal => &mut internal,
56 PrKind::Other => &mut others,
57 PrKind::Skip => continue,
58 };
59 writeln!(s, "* pr:{}[] {}", pr_num, l.message.as_deref().unwrap_or(&pr_title))
60 .unwrap();
61 }
62 }
63 }
64
65 let contents = format!(
66 "\
67= Changelog #{}
68:sectanchors:
69:page-layout: post
70
71Commit: commit:{}[] +
72Release: release:{}[]
73
74== Sponsors
75
76**Become a sponsor:** On https://opencollective.com/rust-analyzer/[OpenCollective] or
77https://github.com/sponsors/rust-analyzer[GitHub Sponsors].
78
79== New Features
80
81{}
82
83== Fixes
84
85{}
86
87== Internal Improvements
88
89{}
90
91== Others
92
93{}
94",
95 changelog_n, commit, today, features, fixes, internal, others
96 );
97 Ok(contents)
98}
99
100#[derive(Clone, Copy)]
101enum PrKind {
102 Feature,
103 Fix,
104 Internal,
105 Other,
106 Skip,
107}
108
109struct PrInfo {
110 message: Option<String>,
111 kind: PrKind,
112}
113
114fn unescape(s: &str) -> String {
115 s.replace(r#"\""#, "").replace(r#"\n"#, "\n").replace(r#"\r"#, "")
116}
117
118fn parse_changelog_line(s: &str) -> Option<PrInfo> {
119 let parts = s.splitn(3, ' ').collect::<Vec<_>>();
120 if parts.len() < 2 || parts[0] != "changelog" {
121 return None;
122 }
123 let message = parts.get(2).map(|it| it.to_string());
124 let kind = match parts[1].trim_end_matches(':') {
125 "feature" => PrKind::Feature,
126 "fix" => PrKind::Fix,
127 "internal" => PrKind::Internal,
128 "skip" => PrKind::Skip,
129 _ => {
130 let kind = PrKind::Other;
131 let message = format!("{} {}", parts[1], message.unwrap_or_default());
132 return Some(PrInfo { kind, message: Some(message) });
133 }
134 };
135 let res = PrInfo { kind, message };
136 Some(res)
137}
138
139fn parse_title_line(s: &str) -> PrInfo {
140 let lower = s.to_ascii_lowercase();
141 const PREFIXES: [(&str, PrKind); 5] = [
142 ("feat: ", PrKind::Feature),
143 ("feature: ", PrKind::Feature),
144 ("fix: ", PrKind::Fix),
145 ("internal: ", PrKind::Internal),
146 ("minor: ", PrKind::Skip),
147 ];
148
149 for &(prefix, kind) in &PREFIXES {
150 if lower.starts_with(prefix) {
151 let message = match &kind {
152 PrKind::Skip => None,
153 _ => Some(s[prefix.len()..].to_string()),
154 };
155 return PrInfo { kind, message };
156 }
157 }
158 PrInfo { kind: PrKind::Other, message: Some(s.to_string()) }
159}
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs
index 50d9efccd..c3c785eff 100644
--- a/xtask/src/tidy.rs
+++ b/xtask/src/tidy.rs
@@ -224,7 +224,7 @@ Apache-2.0 OR BSL-1.0
224Apache-2.0 OR MIT 224Apache-2.0 OR MIT
225Apache-2.0/MIT 225Apache-2.0/MIT
226BSD-3-Clause 226BSD-3-Clause
227CC0-1.0 227CC0-1.0 OR Artistic-2.0
228ISC 228ISC
229MIT 229MIT
230MIT / Apache-2.0 230MIT / Apache-2.0