Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #132314 - lnicola:sync-from-ra, r=lnicola
Subtree update of `rust-analyzer` r? `@ghost`
bors 2024-10-29
parent 0dc8c6c · parent 2eaf49b · commit e8398b1
-rw-r--r--.github/workflows/ci.yaml37
-rw-r--r--.gitignore3
-rw-r--r--Cargo.lock45
-rw-r--r--Cargo.toml15
-rw-r--r--crates/cfg/Cargo.toml1
-rw-r--r--crates/cfg/src/lib.rs32
-rw-r--r--crates/hir-def/Cargo.toml1
-rw-r--r--crates/hir-def/src/body.rs267
-rw-r--r--crates/hir-def/src/body/lower.rs693
-rw-r--r--crates/hir-def/src/body/lower/asm.rs4
-rw-r--r--crates/hir-def/src/body/pretty.rs50
-rw-r--r--crates/hir-def/src/body/scope.rs65
-rw-r--r--crates/hir-def/src/body/tests.rs34
-rw-r--r--crates/hir-def/src/data.rs42
-rw-r--r--crates/hir-def/src/data/adt.rs46
-rw-r--r--crates/hir-def/src/db.rs26
-rw-r--r--crates/hir-def/src/expander.rs36
-rw-r--r--crates/hir-def/src/find_path.rs4
-rw-r--r--crates/hir-def/src/generics.rs341
-rw-r--r--crates/hir-def/src/hir.rs228
-rw-r--r--crates/hir-def/src/hir/type_ref.rs228
-rw-r--r--crates/hir-def/src/item_tree.rs279
-rw-r--r--crates/hir-def/src/item_tree/lower.rs388
-rw-r--r--crates/hir-def/src/item_tree/pretty.rs100
-rw-r--r--crates/hir-def/src/lib.rs8
-rw-r--r--crates/hir-def/src/lower.rs63
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mod.rs2
-rw-r--r--crates/hir-def/src/nameres/collector.rs49
-rw-r--r--crates/hir-def/src/nameres/path_resolution.rs207
-rw-r--r--crates/hir-def/src/nameres/tests.rs46
-rw-r--r--crates/hir-def/src/nameres/tests/globs.rs39
-rw-r--r--crates/hir-def/src/nameres/tests/incremental.rs5
-rw-r--r--crates/hir-def/src/path.rs168
-rw-r--r--crates/hir-def/src/path/lower.rs55
-rw-r--r--crates/hir-def/src/pretty.rs99
-rw-r--r--crates/hir-def/src/resolver.rs88
-rw-r--r--crates/hir-def/src/test_db.rs5
-rw-r--r--crates/hir-def/src/visibility.rs42
-rw-r--r--crates/hir-expand/src/attrs.rs5
-rw-r--r--crates/hir-expand/src/hygiene.rs2
-rw-r--r--crates/hir-expand/src/lib.rs81
-rw-r--r--crates/hir-expand/src/name.rs2
-rw-r--r--crates/hir-ty/src/autoderef.rs2
-rw-r--r--crates/hir-ty/src/chalk_db.rs7
-rw-r--r--crates/hir-ty/src/consteval.rs10
-rw-r--r--crates/hir-ty/src/diagnostics/decl_check.rs4
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs15
-rw-r--r--crates/hir-ty/src/diagnostics/match_check.rs2
-rw-r--r--crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs2
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs50
-rw-r--r--crates/hir-ty/src/display.rs187
-rw-r--r--crates/hir-ty/src/dyn_compatibility.rs2
-rw-r--r--crates/hir-ty/src/generics.rs17
-rw-r--r--crates/hir-ty/src/infer.rs108
-rw-r--r--crates/hir-ty/src/infer/closure.rs120
-rw-r--r--crates/hir-ty/src/infer/expr.rs245
-rw-r--r--crates/hir-ty/src/infer/mutability.rs29
-rw-r--r--crates/hir-ty/src/infer/pat.rs169
-rw-r--r--crates/hir-ty/src/infer/path.rs50
-rw-r--r--crates/hir-ty/src/lower.rs331
-rw-r--r--crates/hir-ty/src/mir.rs37
-rw-r--r--crates/hir-ty/src/mir/borrowck.rs12
-rw-r--r--crates/hir-ty/src/mir/eval.rs29
-rw-r--r--crates/hir-ty/src/mir/lower.rs272
-rw-r--r--crates/hir-ty/src/mir/lower/as_place.rs11
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs98
-rw-r--r--crates/hir-ty/src/mir/monomorphization.rs4
-rw-r--r--crates/hir-ty/src/mir/pretty.rs4
-rw-r--r--crates/hir-ty/src/tests/simple.rs86
-rw-r--r--crates/hir-ty/src/utils.rs12
-rw-r--r--crates/hir/src/db.rs50
-rw-r--r--crates/hir/src/diagnostics.rs111
-rw-r--r--crates/hir/src/display.rs99
-rw-r--r--crates/hir/src/lib.rs38
-rw-r--r--crates/hir/src/semantics.rs98
-rw-r--r--crates/hir/src/semantics/source_to_def.rs5
-rw-r--r--crates/hir/src/source_analyzer.rs255
-rw-r--r--crates/hir/src/symbols.rs12
-rw-r--r--crates/ide-assists/Cargo.toml1
-rw-r--r--crates/ide-assists/src/handlers/bool_to_enum.rs2
-rw-r--r--crates/ide-assists/src/handlers/destructure_struct_binding.rs2
-rw-r--r--crates/ide-assists/src/handlers/destructure_tuple_binding.rs2
-rw-r--r--crates/ide-assists/src/handlers/remove_dbg.rs2
-rw-r--r--crates/ide-assists/src/handlers/remove_unused_imports.rs2
-rw-r--r--crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs2
-rw-r--r--crates/ide-assists/src/handlers/unmerge_match_arm.rs3
-rw-r--r--crates/ide-assists/src/lib.rs5
-rw-r--r--crates/ide-completion/Cargo.toml1
-rw-r--r--crates/ide-completion/src/completions/item_list/trait_impl.rs2
-rw-r--r--crates/ide-completion/src/completions/mod_.rs3
-rw-r--r--crates/ide-completion/src/completions/postfix.rs2
-rw-r--r--crates/ide-completion/src/context.rs4
-rw-r--r--crates/ide-completion/src/item.rs9
-rw-r--r--crates/ide-completion/src/lib.rs9
-rw-r--r--crates/ide-completion/src/render.rs2
-rw-r--r--crates/ide-completion/src/tests/attribute.rs2
-rw-r--r--crates/ide-db/Cargo.toml1
-rw-r--r--crates/ide-db/src/apply_change.rs20
-rw-r--r--crates/ide-db/src/defs.rs45
-rw-r--r--crates/ide-db/src/documentation.rs2
-rw-r--r--crates/ide-db/src/lib.rs34
-rw-r--r--crates/ide-db/src/rename.rs2
-rw-r--r--crates/ide-db/src/source_change.rs11
-rw-r--r--crates/ide-db/src/syntax_helpers/tree_diff.rs559
-rw-r--r--crates/ide-db/src/text_edit.rs (renamed from crates/text-edit/src/lib.rs)2
-rw-r--r--crates/ide-diagnostics/Cargo.toml1
-rw-r--r--crates/ide-diagnostics/src/handlers/field_shorthand.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/inactive_code.rs16
-rw-r--r--crates/ide-diagnostics/src/handlers/json_is_not_rust.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/macro_error.rs13
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_fields.rs12
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_unsafe.rs5
-rw-r--r--crates/ide-diagnostics/src/handlers/mutability_errors.rs55
-rw-r--r--crates/ide-diagnostics/src/handlers/no_such_field.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/remove_trailing_return.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/typed_hole.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/undeclared_label.rs30
-rw-r--r--crates/ide-diagnostics/src/handlers/unlinked_file.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_field.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_ident.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs25
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_method.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/unused_variables.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/useless_braces.rs2
-rw-r--r--crates/ide-ssr/Cargo.toml3
-rw-r--r--crates/ide-ssr/src/lib.rs2
-rw-r--r--crates/ide-ssr/src/parsing.rs2
-rw-r--r--crates/ide-ssr/src/replacing.rs2
-rw-r--r--crates/ide/Cargo.toml1
-rw-r--r--crates/ide/src/call_hierarchy.rs5
-rw-r--r--crates/ide/src/file_structure.rs94
-rw-r--r--crates/ide/src/goto_definition.rs190
-rw-r--r--crates/ide/src/hover.rs24
-rw-r--r--crates/ide/src/hover/render.rs2
-rw-r--r--crates/ide/src/hover/tests.rs161
-rw-r--r--crates/ide/src/inlay_hints.rs27
-rw-r--r--crates/ide/src/inlay_hints/adjustment.rs277
-rw-r--r--crates/ide/src/inlay_hints/binding_mode.rs106
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs2
-rw-r--r--crates/ide/src/inlay_hints/closure_captures.rs121
-rw-r--r--crates/ide/src/inlay_hints/discriminant.rs11
-rw-r--r--crates/ide/src/inlay_hints/implicit_static.rs3
-rw-r--r--crates/ide/src/join_lines.rs2
-rw-r--r--crates/ide/src/lib.rs2
-rw-r--r--crates/ide/src/move_item.rs7
-rw-r--r--crates/ide/src/references.rs8
-rw-r--r--crates/ide/src/rename.rs4
-rw-r--r--crates/ide/src/syntax_highlighting.rs25
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html2
-rw-r--r--crates/ide/src/typing.rs4
-rw-r--r--crates/ide/src/typing/on_enter.rs2
-rw-r--r--crates/parser/src/grammar/generic_params.rs25
-rw-r--r--crates/parser/src/grammar/patterns.rs18
-rw-r--r--crates/parser/src/lexed_str.rs13
-rw-r--r--crates/parser/src/syntax_kind/generated.rs2
-rw-r--r--crates/parser/src/tests.rs17
-rw-r--r--crates/parser/src/tests/top_entries.rs32
-rw-r--r--crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rast4
-rw-r--r--crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rs3
-rw-r--r--crates/parser/test_data/parser/inline/ok/match_arm.rast15
-rw-r--r--crates/parser/test_data/parser/inline/ok/precise_capturing.rast16
-rw-r--r--crates/parser/test_data/parser/inline/ok/precise_capturing.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/slice_pat.rast11
-rw-r--r--crates/parser/test_data/parser/inline/ok/tuple_pat.rast15
-rw-r--r--crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast11
-rw-r--r--crates/proc-macro-api/src/msg/flat.rs4
-rw-r--r--crates/project-model/src/rustc_cfg.rs7
-rw-r--r--crates/project-model/src/workspace.rs23
-rw-r--r--crates/project-model/test_data/output/cargo_hello_world_project_model.txt6
-rw-r--r--crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt6
-rw-r--r--crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt6
-rw-r--r--crates/project-model/test_data/output/rust_project_cfg_groups.txt12
-rw-r--r--crates/project-model/test_data/output/rust_project_hello_world_project_model.txt11
-rw-r--r--crates/ra-salsa/src/derived/slot.rs2
-rw-r--r--crates/ra-salsa/src/derived_lru/slot.rs2
-rw-r--r--crates/ra-salsa/src/interned.rs2
-rw-r--r--crates/ra-salsa/src/lib.rs6
-rw-r--r--crates/rust-analyzer/src/bin/main.rs4
-rw-r--r--crates/rust-analyzer/src/cli/lsif.rs35
-rw-r--r--crates/rust-analyzer/src/config.rs22
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs38
-rw-r--r--crates/rust-analyzer/src/handlers/dispatch.rs69
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs83
-rw-r--r--crates/rust-analyzer/src/lsp/capabilities.rs23
-rw-r--r--crates/rust-analyzer/src/main_loop.rs31
-rw-r--r--crates/rust-analyzer/src/tracing/config.rs20
-rw-r--r--crates/rust-analyzer/src/tracing/hprof.rs2
-rw-r--r--crates/rust-analyzer/tests/slow-tests/cli.rs131
-rw-r--r--crates/rust-analyzer/tests/slow-tests/main.rs1
-rw-r--r--crates/rust-analyzer/tests/slow-tests/support.rs42
-rw-r--r--crates/span/src/ast_id.rs3
-rw-r--r--crates/stdx/src/lib.rs17
-rw-r--r--crates/stdx/src/thin_vec.rs472
-rw-r--r--crates/syntax/Cargo.toml1
-rw-r--r--crates/syntax/rust.ungram11
-rw-r--r--crates/syntax/src/algo.rs561
-rw-r--r--crates/syntax/src/ast/expr_ext.rs4
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs81
-rw-r--r--crates/syntax/src/ast/node_ext.rs16
-rw-r--r--crates/syntax/src/fuzz.rs9
-rw-r--r--crates/syntax/src/lib.rs23
-rw-r--r--crates/syntax/src/parsing/reparsing.rs59
-rw-r--r--crates/text-edit/Cargo.toml20
-rw-r--r--crates/tt/src/buffer.rs2
-rw-r--r--editors/code/.vscodeignore1
-rw-r--r--editors/code/package.json7
-rw-r--r--editors/code/src/ctx.ts2
-rw-r--r--editors/code/walkthrough-setup-tips.md10
-rw-r--r--rust-bors.toml1
-rw-r--r--rust-version2
-rw-r--r--xtask/src/codegen.rs4
-rw-r--r--xtask/src/dist.rs3
-rw-r--r--xtask/src/release/changelog.rs5
-rw-r--r--xtask/src/tidy.rs2
218 files changed, 7243 insertions, 3366 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 5cf4a8fd43..d82e46016d 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -1,14 +1,10 @@
-# Please make sure that the `needs` fields for both `end-success` and `end-failure`
+# Please make sure that the `needs` field for the `conclusion` job
# are updated when adding new jobs!
name: CI
on:
pull_request:
- push:
- branches:
- - auto
- - try
- - automation/bors/try
+ merge_group:
env:
CARGO_INCREMENTAL: 0
@@ -237,20 +233,21 @@ jobs:
- name: check for typos
run: typos
- end-success:
- name: bors build finished
- if: github.event.pusher.name == 'bors' && success()
- runs-on: ubuntu-latest
+ conclusion:
needs: [rust, rust-cross, typescript, typo-check]
- steps:
- - name: Mark the job as successful
- run: exit 0
-
- end-failure:
- name: bors build finished
- if: github.event.pusher.name == 'bors' && !success()
+ # We need to ensure this job does *not* get skipped if its dependencies fail,
+ # because a skipped job is considered a success by GitHub. So we have to
+ # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
+ # when the workflow is canceled manually.
+ #
+ # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
+ if: ${{ !cancelled() }}
runs-on: ubuntu-latest
- needs: [rust, rust-cross, typescript, typo-check]
steps:
- - name: Mark the job as a failure
- run: exit 1
+ # Manually check the status of all dependencies. `if: failure()` does not work.
+ - name: Conclusion
+ run: |
+ # Print the dependent jobs to see them in the CI log
+ jq -C <<< '${{ toJson(needs) }}'
+ # Check if all jobs that we depend on (in the needs array) were successful.
+ jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
diff --git a/.gitignore b/.gitignore
index 68c87a6b1e..c4470a4507 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
-/target/
+target/
/dist/
-crates/*/target
**/*.rs.bk
**/*.rs.pending-snap
.idea/*
diff --git a/Cargo.lock b/Cargo.lock
index fd569571b3..695c37f6d7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -164,6 +164,7 @@ dependencies = [
"rustc-hash 2.0.0",
"syntax",
"syntax-bridge",
+ "tracing",
"tt",
]
@@ -556,6 +557,7 @@ dependencies = [
"syntax-bridge",
"test-fixture",
"test-utils",
+ "text-size",
"tracing",
"triomphe",
"tt",
@@ -670,7 +672,6 @@ dependencies = [
"syntax",
"test-fixture",
"test-utils",
- "text-edit",
"toolchain",
"tracing",
"triomphe",
@@ -692,7 +693,6 @@ dependencies = [
"syntax",
"test-fixture",
"test-utils",
- "text-edit",
"tracing",
]
@@ -711,7 +711,6 @@ dependencies = [
"syntax",
"test-fixture",
"test-utils",
- "text-edit",
"tracing",
]
@@ -743,7 +742,6 @@ dependencies = [
"syntax",
"test-fixture",
"test-utils",
- "text-edit",
"tracing",
"triomphe",
]
@@ -765,7 +763,6 @@ dependencies = [
"syntax",
"test-fixture",
"test-utils",
- "text-edit",
"tracing",
]
@@ -784,7 +781,6 @@ dependencies = [
"syntax",
"test-fixture",
"test-utils",
- "text-edit",
"triomphe",
]
@@ -1497,9 +1493,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_abi"
-version = "0.73.0"
+version = "0.75.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879ece0781e3c1cb670b9f29775c81a43a16db789d1296fad6bc5c74065b2fac"
+checksum = "d5bc2cfc7264d84215a08875ef90a1d35f76b5c9ad1993515d2da7e4e40b2b4b"
dependencies = [
"bitflags 2.6.0",
"ra-ap-rustc_index",
@@ -1508,9 +1504,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_index"
-version = "0.73.0"
+version = "0.75.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6910087ff89bb9f3db114bfcd86b5139042731fe7278d3ff4ceaa69a140154a7"
+checksum = "e8929140697812e5dd09e19cf446d85146332363f0dbc125d4214834c34ead96"
dependencies = [
"arrayvec",
"ra-ap-rustc_index_macros",
@@ -1519,9 +1515,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_index_macros"
-version = "0.73.0"
+version = "0.75.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b6f7bd12b678fbb37444ba77f3b0cfc13b7394a6dc7b0c799491fc9df0a9997"
+checksum = "514a3f5d04c8b4a2750f29746cc9abb1f78deb7e72e4ad1dc95bbc608f3db157"
dependencies = [
"proc-macro2",
"quote",
@@ -1530,9 +1526,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_lexer"
-version = "0.73.0"
+version = "0.75.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "119bc05b5b6bc3e7f5b67ce8b8080e185da94bd83c447f91b6b3f3ecf60cbab1"
+checksum = "276fcb1205da071a0cd64416f3f0e198043c11f176c5b501a45dbf0cb33979f2"
dependencies = [
"unicode-properties",
"unicode-xid",
@@ -1540,9 +1536,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_parse_format"
-version = "0.73.0"
+version = "0.75.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ed6150ae71d905c064dc88d7824ebb0fa81083f45d7477cba7b57176f2f635"
+checksum = "961b30b22cfac296b14b72e9f95e79c16cebc8c926872755fb1568a6c4243a62"
dependencies = [
"ra-ap-rustc_index",
"ra-ap-rustc_lexer",
@@ -1550,9 +1546,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_pattern_analysis"
-version = "0.73.0"
+version = "0.75.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e830862a0ec85fce211d34735315686bb8d6a12d418d6d735fb534aa1cd3293"
+checksum = "614232513814a4b714fea7f11345d31c0c277bca3089bb6ca1ec20870bfc022a"
dependencies = [
"ra-ap-rustc_index",
"rustc-hash 2.0.0",
@@ -1884,9 +1880,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "smol_str"
-version = "0.3.1"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66eaf762c5af19db3108300515c8aa7a50efc90ff745f4c62288052ebf9fdd25"
+checksum = "9676b89cd56310a87b93dec47b11af744f34d5fc9f367b829474eec0a891350d"
dependencies = [
"borsh",
"serde",
@@ -1978,7 +1974,6 @@ dependencies = [
"smol_str",
"stdx",
"test-utils",
- "text-edit",
"tracing",
"triomphe",
]
@@ -2027,14 +2022,6 @@ dependencies = [
]
[[package]]
-name = "text-edit"
-version = "0.0.0"
-dependencies = [
- "itertools",
- "text-size",
-]
-
-[[package]]
name = "text-size"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 9db62de9ab..3aa93b7b7b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
resolver = "2"
[workspace.package]
-rust-version = "1.81"
+rust-version = "1.82"
edition = "2021"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer team"]
@@ -79,17 +79,16 @@ span = { path = "./crates/span", version = "0.0.0" }
stdx = { path = "./crates/stdx", version = "0.0.0" }
syntax = { path = "./crates/syntax", version = "0.0.0" }
syntax-bridge = { path = "./crates/syntax-bridge", version = "0.0.0" }
-text-edit = { path = "./crates/text-edit", version = "0.0.0" }
toolchain = { path = "./crates/toolchain", version = "0.0.0" }
tt = { path = "./crates/tt", version = "0.0.0" }
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
vfs = { path = "./crates/vfs", version = "0.0.0" }
-ra-ap-rustc_lexer = { version = "0.73", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.73", default-features = false }
-ra-ap-rustc_index = { version = "0.73", default-features = false }
-ra-ap-rustc_abi = { version = "0.73", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.73", default-features = false }
+ra-ap-rustc_lexer = { version = "0.75", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.75", default-features = false }
+ra-ap-rustc_index = { version = "0.75", default-features = false }
+ra-ap-rustc_abi = { version = "0.75", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.75", default-features = false }
# local crates that aren't published to crates.io. These should not have versions.
test-fixture = { path = "./crates/test-fixture" }
@@ -145,7 +144,7 @@ smallvec = { version = "1.10.0", features = [
"union",
"const_generics",
] }
-smol_str = "0.3.1"
+smol_str = "0.3.2"
snap = "1.1.0"
text-size = "1.1.1"
tracing = "0.1.40"
diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml
index 29b7ad6f8f..040bddbd7f 100644
--- a/crates/cfg/Cargo.toml
+++ b/crates/cfg/Cargo.toml
@@ -14,6 +14,7 @@ doctest = false
[dependencies]
rustc-hash.workspace = true
+tracing.workspace = true
# locals deps
tt = { workspace = true, optional = true }
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index c2d4008605..6a6213a871 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -9,7 +9,7 @@ use std::fmt;
use rustc_hash::FxHashSet;
-use intern::Symbol;
+use intern::{sym, Symbol};
pub use cfg_expr::{CfgAtom, CfgExpr};
pub use dnf::DnfExpr;
@@ -24,11 +24,17 @@ pub use dnf::DnfExpr;
/// of key and value in `key_values`.
///
/// See: <https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options>
-#[derive(Clone, PartialEq, Eq, Default)]
+#[derive(Clone, PartialEq, Eq)]
pub struct CfgOptions {
enabled: FxHashSet<CfgAtom>,
}
+impl Default for CfgOptions {
+ fn default() -> Self {
+ Self { enabled: FxHashSet::from_iter([CfgAtom::Flag(sym::true_.clone())]) }
+ }
+}
+
impl fmt::Debug for CfgOptions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut items = self
@@ -54,23 +60,37 @@ impl CfgOptions {
}
pub fn insert_atom(&mut self, key: Symbol) {
- self.enabled.insert(CfgAtom::Flag(key));
+ self.insert_any_atom(CfgAtom::Flag(key));
}
pub fn insert_key_value(&mut self, key: Symbol, value: Symbol) {
- self.enabled.insert(CfgAtom::KeyValue { key, value });
+ self.insert_any_atom(CfgAtom::KeyValue { key, value });
}
pub fn apply_diff(&mut self, diff: CfgDiff) {
for atom in diff.enable {
- self.enabled.insert(atom);
+ self.insert_any_atom(atom);
}
for atom in diff.disable {
+ let (CfgAtom::Flag(sym) | CfgAtom::KeyValue { key: sym, .. }) = &atom;
+ if *sym == sym::true_ || *sym == sym::false_ {
+ tracing::error!("cannot remove `true` or `false` from cfg");
+ continue;
+ }
self.enabled.remove(&atom);
}
}
+ fn insert_any_atom(&mut self, atom: CfgAtom) {
+ let (CfgAtom::Flag(sym) | CfgAtom::KeyValue { key: sym, .. }) = &atom;
+ if *sym == sym::true_ || *sym == sym::false_ {
+ tracing::error!("cannot insert `true` or `false` to cfg");
+ return;
+ }
+ self.enabled.insert(atom);
+ }
+
pub fn get_cfg_keys(&self) -> impl Iterator<Item = &Symbol> {
self.enabled.iter().map(|it| match it {
CfgAtom::Flag(key) => key,
@@ -88,7 +108,7 @@ impl CfgOptions {
impl Extend<CfgAtom> for CfgOptions {
fn extend<T: IntoIterator<Item = CfgAtom>>(&mut self, iter: T) {
- iter.into_iter().for_each(|cfg_flag| _ = self.enabled.insert(cfg_flag));
+ iter.into_iter().for_each(|cfg_flag| self.insert_any_atom(cfg_flag));
}
}
diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml
index c8ba5da449..375f18d9fe 100644
--- a/crates/hir-def/Cargo.toml
+++ b/crates/hir-def/Cargo.toml
@@ -29,6 +29,7 @@ smallvec.workspace = true
hashbrown.workspace = true
triomphe.workspace = true
rustc_apfloat = "0.2.0"
+text-size.workspace = true
ra-ap-rustc_parse_format.workspace = true
ra-ap-rustc_abi.workspace = true
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index 9535b5aea7..5a386f6cf8 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/body.rs
@@ -10,6 +10,7 @@ use std::ops::{Deref, Index};
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
+use either::Either;
use hir_expand::{name::Name, ExpandError, InFile};
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
use rustc_hash::FxHashMap;
@@ -22,15 +23,33 @@ use crate::{
db::DefDatabase,
expander::Expander,
hir::{
- dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
+ dummy_expr_id, Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label,
+ LabelId, Pat, PatId, RecordFieldPat, Statement,
},
item_tree::AttrOwner,
nameres::DefMap,
path::{ModPath, Path},
src::HasSource,
+ type_ref::{TypeRef, TypeRefId, TypesMap, TypesSourceMap},
BlockId, DefWithBodyId, HasModule, Lookup,
};
+/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct HygieneId(pub(crate) span::SyntaxContextId);
+
+impl HygieneId {
+ pub const ROOT: Self = Self(span::SyntaxContextId::ROOT);
+
+ pub fn new(ctx: span::SyntaxContextId) -> Self {
+ Self(ctx)
+ }
+
+ pub(crate) fn is_root(self) -> bool {
+ self.0.is_root()
+ }
+}
+
/// The body of an item (function, const etc.).
#[derive(Debug, Eq, PartialEq)]
pub struct Body {
@@ -51,8 +70,25 @@ pub struct Body {
pub self_param: Option<BindingId>,
/// The `ExprId` of the actual body expression.
pub body_expr: ExprId,
+ pub types: TypesMap,
/// Block expressions in this body that may contain inner items.
block_scopes: Vec<BlockId>,
+
+ /// A map from binding to its hygiene ID.
+ ///
+ /// Bindings that don't come from macro expansion are not allocated to save space, so not all bindings appear here.
+ /// If a binding does not appear here it has `SyntaxContextId::ROOT`.
+ ///
+ /// Note that this may not be the direct `SyntaxContextId` of the binding's expansion, because transparent
+ /// expansions are attributed to their parent expansion (recursively).
+ binding_hygiene: FxHashMap<BindingId, HygieneId>,
+ /// A map from an variable usages to their hygiene ID.
+ ///
+ /// Expressions that can be recorded here are single segment path, although not all single segments path refer
+ /// to variables and have hygiene (some refer to items, we don't know at this stage).
+ expr_hygiene: FxHashMap<ExprId, HygieneId>,
+ /// A map from a destructuring assignment possible variable usages to their hygiene ID.
+ pat_hygiene: FxHashMap<PatId, HygieneId>,
}
pub type ExprPtr = AstPtr<ast::Expr>;
@@ -67,9 +103,12 @@ pub type LabelSource = InFile<LabelPtr>;
pub type FieldPtr = AstPtr<ast::RecordExprField>;
pub type FieldSource = InFile<FieldPtr>;
-pub type PatFieldPtr = AstPtr<ast::RecordPatField>;
+pub type PatFieldPtr = AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>;
pub type PatFieldSource = InFile<PatFieldPtr>;
+pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>;
+pub type ExprOrPatSource = InFile<ExprOrPatPtr>;
+
/// An item body together with the mapping from syntax nodes to HIR expression
/// IDs. This is needed to go from e.g. a position in a file to the HIR
/// expression containing it; but for type inference etc., we want to operate on
@@ -83,11 +122,13 @@ pub type PatFieldSource = InFile<PatFieldPtr>;
/// this properly for macros.
#[derive(Default, Debug, Eq, PartialEq)]
pub struct BodySourceMap {
- expr_map: FxHashMap<ExprSource, ExprId>,
+ // AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
+ // to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
+ expr_map: FxHashMap<ExprSource, ExprOrPatId>,
expr_map_back: ArenaMap<ExprId, ExprSource>,
pat_map: FxHashMap<PatSource, PatId>,
- pat_map_back: ArenaMap<PatId, PatSource>,
+ pat_map_back: ArenaMap<PatId, ExprOrPatSource>,
label_map: FxHashMap<LabelSource, LabelId>,
label_map_back: ArenaMap<LabelId, LabelSource>,
@@ -100,10 +141,13 @@ pub struct BodySourceMap {
field_map_back: FxHashMap<ExprId, FieldSource>,
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
+ types: TypesSourceMap,
+
+ // FIXME: Make this a sane struct.
template_map: Option<
Box<(
// format_args!
- FxHashMap<ExprId, Vec<(syntax::TextRange, Name)>>,
+ FxHashMap<ExprId, (HygieneId, Vec<(syntax::TextRange, Name)>)>,
// asm!
FxHashMap<ExprId, Vec<Vec<(syntax::TextRange, usize)>>>,
)>,
@@ -261,6 +305,10 @@ impl Body {
pats,
bindings,
binding_owners,
+ binding_hygiene,
+ expr_hygiene,
+ pat_hygiene,
+ types,
} = self;
block_scopes.shrink_to_fit();
exprs.shrink_to_fit();
@@ -268,6 +316,10 @@ impl Body {
pats.shrink_to_fit();
bindings.shrink_to_fit();
binding_owners.shrink_to_fit();
+ binding_hygiene.shrink_to_fit();
+ expr_hygiene.shrink_to_fit();
+ pat_hygiene.shrink_to_fit();
+ types.shrink_to_fit();
}
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
@@ -286,7 +338,8 @@ impl Body {
| Pat::Path(..)
| Pat::ConstBlock(..)
| Pat::Wild
- | Pat::Missing => {}
+ | Pat::Missing
+ | Pat::Expr(_) => {}
&Pat::Bind { subpat, .. } => {
if let Some(subpat) = subpat {
f(subpat);
@@ -322,6 +375,162 @@ impl Body {
None => true,
}
}
+
+ pub fn walk_child_exprs(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) {
+ let expr = &self[expr_id];
+ match expr {
+ Expr::Continue { .. }
+ | Expr::Const(_)
+ | Expr::Missing
+ | Expr::Path(_)
+ | Expr::OffsetOf(_)
+ | Expr::Literal(_)
+ | Expr::Underscore => {}
+ Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
+ AsmOperand::In { expr, .. }
+ | AsmOperand::Out { expr: Some(expr), .. }
+ | AsmOperand::InOut { expr, .. } => f(*expr),
+ AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+ f(*in_expr);
+ if let Some(out_expr) = out_expr {
+ f(*out_expr);
+ }
+ }
+ AsmOperand::Out { expr: None, .. }
+ | AsmOperand::Const(_)
+ | AsmOperand::Label(_)
+ | AsmOperand::Sym(_) => (),
+ }),
+ Expr::If { condition, then_branch, else_branch } => {
+ f(*condition);
+ f(*then_branch);
+ if let &Some(else_branch) = else_branch {
+ f(else_branch);
+ }
+ }
+ Expr::Let { expr, .. } => {
+ f(*expr);
+ }
+ Expr::Block { statements, tail, .. }
+ | Expr::Unsafe { statements, tail, .. }
+ | Expr::Async { statements, tail, .. } => {
+ for stmt in statements.iter() {
+ match stmt {
+ Statement::Let { initializer, else_branch, pat, .. } => {
+ if let &Some(expr) = initializer {
+ f(expr);
+ }
+ if let &Some(expr) = else_branch {
+ f(expr);
+ }
+ self.walk_exprs_in_pat(*pat, &mut f);
+ }
+ Statement::Expr { expr: expression, .. } => f(*expression),
+ Statement::Item(_) => (),
+ }
+ }
+ if let &Some(expr) = tail {
+ f(expr);
+ }
+ }
+ Expr::Loop { body, .. } => f(*body),
+ Expr::Call { callee, args, .. } => {
+ f(*callee);
+ args.iter().copied().for_each(f);
+ }
+ Expr::MethodCall { receiver, args, .. } => {
+ f(*receiver);
+ args.iter().copied().for_each(f);
+ }
+ Expr::Match { expr, arms } => {
+ f(*expr);
+ arms.iter().map(|arm| arm.expr).for_each(f);
+ }
+ Expr::Break { expr, .. }
+ | Expr::Return { expr }
+ | Expr::Yield { expr }
+ | Expr::Yeet { expr } => {
+ if let &Some(expr) = expr {
+ f(expr);
+ }
+ }
+ Expr::Become { expr } => f(*expr),
+ Expr::RecordLit { fields, spread, .. } => {
+ for field in fields.iter() {
+ f(field.expr);
+ }
+ if let &Some(expr) = spread {
+ f(expr);
+ }
+ }
+ Expr::Closure { body, .. } => {
+ f(*body);
+ }
+ Expr::BinaryOp { lhs, rhs, .. } => {
+ f(*lhs);
+ f(*rhs);
+ }
+ Expr::Range { lhs, rhs, .. } => {
+ if let &Some(lhs) = rhs {
+ f(lhs);
+ }
+ if let &Some(rhs) = lhs {
+ f(rhs);
+ }
+ }
+ Expr::Index { base, index, .. } => {
+ f(*base);
+ f(*index);
+ }
+ Expr::Field { expr, .. }
+ | Expr::Await { expr }
+ | Expr::Cast { expr, .. }
+ | Expr::Ref { expr, .. }
+ | Expr::UnaryOp { expr, .. }
+ | Expr::Box { expr } => {
+ f(*expr);
+ }
+ Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
+ Expr::Array(a) => match a {
+ Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
+ Array::Repeat { initializer, repeat } => {
+ f(*initializer);
+ f(*repeat)
+ }
+ },
+ &Expr::Assignment { target, value } => {
+ self.walk_exprs_in_pat(target, &mut f);
+ f(value);
+ }
+ }
+ }
+
+ pub fn walk_exprs_in_pat(&self, pat_id: PatId, f: &mut impl FnMut(ExprId)) {
+ self.walk_pats(pat_id, &mut |pat| {
+ if let Pat::Expr(expr) | Pat::ConstBlock(expr) = self[pat] {
+ f(expr);
+ }
+ });
+ }
+
+ fn binding_hygiene(&self, binding: BindingId) -> HygieneId {
+ self.binding_hygiene.get(&binding).copied().unwrap_or(HygieneId::ROOT)
+ }
+
+ pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId {
+ self.expr_hygiene.get(&expr).copied().unwrap_or(HygieneId::ROOT)
+ }
+
+ pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId {
+ self.pat_hygiene.get(&pat).copied().unwrap_or(HygieneId::ROOT)
+ }
+
+ pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId {
+ match id {
+ ExprOrPatId::ExprId(id) => self.expr_path_hygiene(id),
+ ExprOrPatId::PatId(id) => self.pat_path_hygiene(id),
+ }
+ }
}
impl Default for Body {
@@ -336,6 +545,10 @@ impl Default for Body {
block_scopes: Default::default(),
binding_owners: Default::default(),
self_param: Default::default(),
+ binding_hygiene: Default::default(),
+ expr_hygiene: Default::default(),
+ pat_hygiene: Default::default(),
+ types: Default::default(),
}
}
}
@@ -372,14 +585,29 @@ impl Index<BindingId> for Body {
}
}
+impl Index<TypeRefId> for Body {
+ type Output = TypeRef;
+
+ fn index(&self, b: TypeRefId) -> &TypeRef {
+ &self.types[b]
+ }
+}
+
// FIXME: Change `node_` prefix to something more reasonable.
// Perhaps `expr_syntax` and `expr_id`?
impl BodySourceMap {
+ pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
+ match id {
+ ExprOrPatId::ExprId(id) => self.expr_syntax(id).map(|it| it.map(AstPtr::wrap_left)),
+ ExprOrPatId::PatId(id) => self.pat_syntax(id),
+ }
+ }
+
pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
self.expr_map_back.get(expr).cloned().ok_or(SyntheticSyntax)
}
- pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
+ pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprOrPatId> {
let src = node.map(AstPtr::new);
self.expr_map.get(&src).cloned()
}
@@ -395,7 +623,7 @@ impl BodySourceMap {
self.expansions.iter().map(|(&a, &b)| (a, b))
}
- pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
+ pub fn pat_syntax(&self, pat: PatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
}
@@ -428,7 +656,7 @@ impl BodySourceMap {
self.pat_field_map_back[&pat]
}
- pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprId> {
+ pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprOrPatId> {
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::MacroExpr>).map(AstPtr::upcast);
self.expr_map.get(&src).copied()
}
@@ -442,9 +670,11 @@ impl BodySourceMap {
pub fn implicit_format_args(
&self,
node: InFile<&ast::FormatArgsExpr>,
- ) -> Option<&[(syntax::TextRange, Name)]> {
+ ) -> Option<(HygieneId, &[(syntax::TextRange, Name)])> {
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
- self.template_map.as_ref()?.0.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref)
+ let (hygiene, names) =
+ self.template_map.as_ref()?.0.get(&self.expr_map.get(&src)?.as_expr()?)?;
+ Some((*hygiene, &**names))
}
pub fn asm_template_args(
@@ -452,8 +682,8 @@ impl BodySourceMap {
node: InFile<&ast::AsmExpr>,
) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> {
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
- let expr = self.expr_map.get(&src)?;
- Some(*expr).zip(self.template_map.as_ref()?.1.get(expr).map(std::ops::Deref::deref))
+ let expr = self.expr_map.get(&src)?.as_expr()?;
+ Some(expr).zip(self.template_map.as_ref()?.1.get(&expr).map(std::ops::Deref::deref))
}
/// Get a reference to the body source map's diagnostics.
@@ -476,6 +706,7 @@ impl BodySourceMap {
template_map,
diagnostics,
binding_definitions,
+ types,
} = self;
if let Some(template_map) = template_map {
template_map.0.shrink_to_fit();
@@ -492,14 +723,6 @@ impl BodySourceMap {
expansions.shrink_to_fit();
diagnostics.shrink_to_fit();
binding_definitions.shrink_to_fit();
- }
-
- pub fn template_map(
- &self,
- ) -> Option<&(
- FxHashMap<Idx<Expr>, Vec<(tt::TextRange, Name)>>,
- FxHashMap<Idx<Expr>, Vec<Vec<(tt::TextRange, usize)>>>,
- )> {
- self.template_map.as_deref()
+ types.shrink_to_fit();
}
}
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 9c547574ec..0b108b54e6 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -9,9 +9,10 @@ use base_db::CrateId;
use either::Either;
use hir_expand::{
name::{AsName, Name},
- InFile,
+ span_map::{ExpansionSpanMap, SpanMap},
+ InFile, MacroDefId,
};
-use intern::{sym, Interned, Symbol};
+use intern::{sym, Symbol};
use rustc_hash::FxHashMap;
use span::AstIdMap;
use stdx::never;
@@ -22,10 +23,11 @@ use syntax::{
},
AstNode, AstPtr, AstToken as _, SyntaxNodePtr,
};
+use text_size::TextSize;
use triomphe::Arc;
use crate::{
- body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
+ body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr},
builtin_type::BuiltinUint,
data::adt::StructKind,
db::DefDatabase,
@@ -37,8 +39,8 @@ use crate::{
FormatPlaceholder, FormatSign, FormatTrait,
},
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
- Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat,
- PatId, RecordFieldPat, RecordLitField, Statement,
+ Expr, ExprId, Item, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
+ OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
},
item_scope::BuiltinShadowMode,
lang_item::LangItem,
@@ -46,7 +48,7 @@ use crate::{
nameres::{DefMap, MacroSubNs},
path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef},
- AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
+ AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, MacroId, ModuleDefId, UnresolvedMacro,
};
type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
@@ -60,6 +62,17 @@ pub(super) fn lower(
krate: CrateId,
is_async_fn: bool,
) -> (Body, BodySourceMap) {
+ // We cannot leave the root span map empty and let any identifier from it be treated as root,
+ // because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved
+ // with the inner macro, and that will cause confusion because they won't be the same as `ROOT`
+ // even though they should be the same. Also, when the body comes from multiple expansions, their
+ // hygiene is different.
+ let span_map = expander.current_file_id().macro_file().map(|_| {
+ let SpanMap::ExpansionSpanMap(span_map) = expander.span_map(db) else {
+ panic!("in a macro file there should be `ExpansionSpanMap`");
+ };
+ Arc::clone(span_map)
+ });
ExprCollector {
db,
owner,
@@ -70,11 +83,12 @@ pub(super) fn lower(
body: Body::default(),
expander,
current_try_block_label: None,
- is_lowering_assignee_expr: false,
is_lowering_coroutine: false,
label_ribs: Vec::new(),
current_binding_owner: None,
awaitable_context: None,
+ current_span_map: span_map,
+ current_block_legacy_macro_defs_count: FxHashMap::default(),
}
.collect(params, body, is_async_fn)
}
@@ -89,9 +103,14 @@ struct ExprCollector<'a> {
body: Body,
source_map: BodySourceMap,
- is_lowering_assignee_expr: bool,
is_lowering_coroutine: bool,
+ /// Legacy (`macro_rules!`) macros can have multiple definitions and shadow each other,
+ /// and we need to find the current definition. So we track the number of definitions we saw.
+ current_block_legacy_macro_defs_count: FxHashMap<Name, usize>,
+
+ current_span_map: Option<Arc<ExpansionSpanMap>>,
+
current_try_block_label: Option<LabelId>,
// points to the expression that a try expression will target (replaces current_try_block_label)
// catch_scope: Option<ExprId>,
@@ -110,31 +129,27 @@ struct ExprCollector<'a> {
#[derive(Clone, Debug)]
struct LabelRib {
kind: RibKind,
- // Once we handle macro hygiene this will need to be a map
- label: Option<(Name, LabelId)>,
}
impl LabelRib {
fn new(kind: RibKind) -> Self {
- LabelRib { kind, label: None }
- }
- fn new_normal(label: (Name, LabelId)) -> Self {
- LabelRib { kind: RibKind::Normal, label: Some(label) }
+ LabelRib { kind }
}
}
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
enum RibKind {
- Normal,
+ Normal(Name, LabelId, HygieneId),
Closure,
Constant,
+ MacroDef(Box<MacroDefId>),
}
impl RibKind {
/// This rib forbids referring to labels defined in upwards ribs.
- fn is_label_barrier(self) -> bool {
+ fn is_label_barrier(&self) -> bool {
match self {
- RibKind::Normal => false,
+ RibKind::Normal(..) | RibKind::MacroDef(_) => false,
RibKind::Closure | RibKind::Constant => true,
}
}
@@ -147,7 +162,7 @@ enum Awaitable {
#[derive(Debug, Default)]
struct BindingList {
- map: FxHashMap<Name, BindingId>,
+ map: FxHashMap<(Name, HygieneId), BindingId>,
is_used: FxHashMap<BindingId, bool>,
reject_new: bool,
}
@@ -157,9 +172,16 @@ impl BindingList {
&mut self,
ec: &mut ExprCollector<'_>,
name: Name,
+ hygiene: HygieneId,
mode: BindingAnnotation,
) -> BindingId {
- let id = *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode));
+ let id = *self.map.entry((name, hygiene)).or_insert_with_key(|(name, _)| {
+ let id = ec.alloc_binding(name.clone(), mode);
+ if !hygiene.is_root() {
+ ec.body.binding_hygiene.insert(id, hygiene);
+ }
+ id
+ });
if ec.body.bindings[id].mode != mode {
ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
}
@@ -213,6 +235,13 @@ impl ExprCollector<'_> {
Name::new_symbol_root(sym::self_.clone()),
BindingAnnotation::new(is_mutable, false),
);
+ let hygiene = self_param
+ .name()
+ .map(|name| self.hygiene_id_for(name.syntax().text_range().start()))
+ .unwrap_or(HygieneId::ROOT);
+ if !hygiene.is_root() {
+ self.body.binding_hygiene.insert(binding_id, hygiene);
+ }
self.body.self_param = Some(binding_id);
self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param)));
}
@@ -245,8 +274,8 @@ impl ExprCollector<'_> {
(self.body, self.source_map)
}
- fn ctx(&self) -> LowerCtx<'_> {
- self.expander.ctx(self.db)
+ fn ctx(&mut self) -> LowerCtx<'_> {
+ self.expander.ctx(self.db, &mut self.body.types, &mut self.source_map.types)
}
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@@ -290,13 +319,14 @@ impl ExprCollector<'_> {
})
}
Some(ast::BlockModifier::Label(label)) => {
- let label = self.collect_label(label);
- self.with_labeled_rib(label, |this| {
+ let label_hygiene = self.hygiene_id_for(label.syntax().text_range().start());
+ let label_id = self.collect_label(label);
+ self.with_labeled_rib(label_id, label_hygiene, |this| {
this.collect_block_(e, |id, statements, tail| Expr::Block {
id,
statements,
tail,
- label: Some(label),
+ label: Some(label_id),
})
})
}
@@ -338,9 +368,14 @@ impl ExprCollector<'_> {
None => self.collect_block(e),
},
ast::Expr::LoopExpr(e) => {
- let label = e.label().map(|label| self.collect_label(label));
+ let label = e.label().map(|label| {
+ (
+ self.hygiene_id_for(label.syntax().text_range().start()),
+ self.collect_label(label),
+ )
+ });
let body = self.collect_labelled_block_opt(label, e.loop_body());
- self.alloc_expr(Expr::Loop { body, label }, syntax_ptr)
+ self.alloc_expr(Expr::Loop { body, label: label.map(|it| it.1) }, syntax_ptr)
}
ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e),
ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e),
@@ -359,14 +394,7 @@ impl ExprCollector<'_> {
} else {
Box::default()
};
- self.alloc_expr(
- Expr::Call {
- callee,
- args,
- is_assignee_expr: self.is_lowering_assignee_expr,
- },
- syntax_ptr,
- )
+ self.alloc_expr(Expr::Call { callee, args }, syntax_ptr)
}
}
ast::Expr::MethodCallExpr(e) => {
@@ -407,12 +435,15 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
}
ast::Expr::PathExpr(e) => {
- let path = e
- .path()
- .and_then(|path| self.expander.parse_path(self.db, path))
- .map(Expr::Path)
- .unwrap_or(Expr::Missing);
- self.alloc_expr(path, syntax_ptr)
+ let (path, hygiene) = self
+ .collect_expr_path(e)
+ .map(|(path, hygiene)| (Expr::Path(path), hygiene))
+ .unwrap_or((Expr::Missing, HygieneId::ROOT));
+ let expr_id = self.alloc_expr(path, syntax_ptr);
+ if !hygiene.is_root() {
+ self.body.expr_hygiene.insert(expr_id, hygiene);
+ }
+ expr_id
}
ast::Expr::ContinueExpr(e) => {
let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
@@ -433,7 +464,7 @@ impl ExprCollector<'_> {
let inner = self.collect_expr_opt(e.expr());
// make the paren expr point to the inner expression as well for IDE resolution
let src = self.expander.in_file(syntax_ptr);
- self.source_map.expr_map.insert(src, inner);
+ self.source_map.expr_map.insert(src, inner.into());
inner
}
ast::Expr::ReturnExpr(e) => {
@@ -455,9 +486,7 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::Yeet { expr }, syntax_ptr)
}
ast::Expr::RecordExpr(e) => {
- let path =
- e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
- let is_assignee_expr = self.is_lowering_assignee_expr;
+ let path = e.path().and_then(|path| self.parse_path(path)).map(Box::new);
let record_lit = if let Some(nfl) = e.record_expr_field_list() {
let fields = nfl
.fields()
@@ -476,16 +505,9 @@ impl ExprCollector<'_> {
})
.collect();
let spread = nfl.spread().map(|s| self.collect_expr(s));
- let ellipsis = nfl.dotdot_token().is_some();
- Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr }
+ Expr::RecordLit { path, fields, spread }
} else {
- Expr::RecordLit {
- path,
- fields: Box::default(),
- spread: None,
- ellipsis: false,
- is_assignee_expr,
- }
+ Expr::RecordLit { path, fields: Box::default(), spread: None }
};
self.alloc_expr(record_lit, syntax_ptr)
@@ -511,7 +533,7 @@ impl ExprCollector<'_> {
ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e),
ast::Expr::CastExpr(e) => {
let expr = self.collect_expr_opt(e.expr());
- let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
+ let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.ty());
self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
}
ast::Expr::RefExpr(e) => {
@@ -550,16 +572,13 @@ impl ExprCollector<'_> {
arg_types.reserve_exact(num_params);
for param in pl.params() {
let pat = this.collect_pat_top(param.pat());
- let type_ref =
- param.ty().map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
+ let type_ref = param.ty().map(|it| TypeRef::from_ast(&this.ctx(), it));
args.push(pat);
arg_types.push(type_ref);
}
}
- let ret_type = e
- .ret_type()
- .and_then(|r| r.ty())
- .map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
+ let ret_type =
+ e.ret_type().and_then(|r| r.ty()).map(|it| TypeRef::from_ast(&this.ctx(), it));
let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine);
let prev_try_block_label = this.current_try_block_label.take();
@@ -602,12 +621,14 @@ impl ExprCollector<'_> {
ast::Expr::BinExpr(e) => {
let op = e.op_kind();
if let Some(ast::BinaryOp::Assignment { op: None }) = op {
- self.is_lowering_assignee_expr = true;
+ let target = self.collect_expr_as_pat_opt(e.lhs());
+ let value = self.collect_expr_opt(e.rhs());
+ self.alloc_expr(Expr::Assignment { target, value }, syntax_ptr)
+ } else {
+ let lhs = self.collect_expr_opt(e.lhs());
+ let rhs = self.collect_expr_opt(e.rhs());
+ self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
}
- let lhs = self.collect_expr_opt(e.lhs());
- self.is_lowering_assignee_expr = false;
- let rhs = self.collect_expr_opt(e.rhs());
- self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
}
ast::Expr::TupleExpr(e) => {
let mut exprs: Vec<_> = e.fields().map(|expr| self.collect_expr(expr)).collect();
@@ -617,13 +638,7 @@ impl ExprCollector<'_> {
exprs.insert(0, self.missing_expr());
}
- self.alloc_expr(
- Expr::Tuple {
- exprs: exprs.into_boxed_slice(),
- is_assignee_expr: self.is_lowering_assignee_expr,
- },
- syntax_ptr,
- )
+ self.alloc_expr(Expr::Tuple { exprs: exprs.into_boxed_slice() }, syntax_ptr)
}
ast::Expr::ArrayExpr(e) => {
let kind = e.kind();
@@ -631,13 +646,7 @@ impl ExprCollector<'_> {
match kind {
ArrayExprKind::ElementList(e) => {
let elements = e.map(|expr| self.collect_expr(expr)).collect();
- self.alloc_expr(
- Expr::Array(Array::ElementList {
- elements,
- is_assignee_expr: self.is_lowering_assignee_expr,
- }),
- syntax_ptr,
- )
+ self.alloc_expr(Expr::Array(Array::ElementList { elements }), syntax_ptr)
}
ArrayExprKind::Repeat { initializer, repeat } => {
let initializer = self.collect_expr_opt(initializer);
@@ -664,8 +673,7 @@ impl ExprCollector<'_> {
ast::Expr::IndexExpr(e) => {
let base = self.collect_expr_opt(e.base());
let index = self.collect_expr_opt(e.index());
- let is_assignee_expr = self.is_lowering_assignee_expr;
- self.alloc_expr(Expr::Index { base, index, is_assignee_expr }, syntax_ptr)
+ self.alloc_expr(Expr::Index { base, index }, syntax_ptr)
}
ast::Expr::RangeExpr(e) => {
let lhs = e.start().map(|lhs| self.collect_expr(lhs));
@@ -688,7 +696,7 @@ impl ExprCollector<'_> {
// Make the macro-call point to its expanded expression so we can query
// semantics on syntax pointers to the macro
let src = self.expander.in_file(syntax_ptr);
- self.source_map.expr_map.insert(src, id);
+ self.source_map.expr_map.insert(src, id.into());
id
}
None => self.alloc_expr(Expr::Missing, syntax_ptr),
@@ -697,7 +705,7 @@ impl ExprCollector<'_> {
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
ast::Expr::AsmExpr(e) => self.lower_inline_asm(e, syntax_ptr),
ast::Expr::OffsetOfExpr(e) => {
- let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
+ let container = TypeRef::from_ast_opt(&self.ctx(), e.ty());
let fields = e.fields().map(|it| it.as_name()).collect();
self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr)
}
@@ -705,6 +713,200 @@ impl ExprCollector<'_> {
})
}
+ fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
+ self.expander.parse_path(self.db, path, &mut self.body.types, &mut self.source_map.types)
+ }
+
+ fn collect_expr_path(&mut self, e: ast::PathExpr) -> Option<(Path, HygieneId)> {
+ e.path().and_then(|path| {
+ let path = self.parse_path(path)?;
+ // Need to enable `mod_path.len() < 1` for `self`.
+ let may_be_variable = matches!(&path, Path::BarePath(mod_path) if mod_path.len() <= 1);
+ let hygiene = if may_be_variable {
+ self.hygiene_id_for(e.syntax().text_range().start())
+ } else {
+ HygieneId::ROOT
+ };
+ Some((path, hygiene))
+ })
+ }
+
+ fn collect_expr_as_pat_opt(&mut self, expr: Option<ast::Expr>) -> PatId {
+ match expr {
+ Some(expr) => self.collect_expr_as_pat(expr),
+ _ => self.missing_pat(),
+ }
+ }
+
+ fn collect_expr_as_pat(&mut self, expr: ast::Expr) -> PatId {
+ self.maybe_collect_expr_as_pat(&expr).unwrap_or_else(|| {
+ let src = self.expander.in_file(AstPtr::new(&expr).wrap_left());
+ let expr = self.collect_expr(expr);
+ // Do not use `alloc_pat_from_expr()` here, it will override the entry in `expr_map`.
+ let id = self.body.pats.alloc(Pat::Expr(expr));
+ self.source_map.pat_map_back.insert(id, src);
+ id
+ })
+ }
+
+ fn maybe_collect_expr_as_pat(&mut self, expr: &ast::Expr) -> Option<PatId> {
+ self.check_cfg(expr)?;
+ let syntax_ptr = AstPtr::new(expr);
+
+ let result = match expr {
+ ast::Expr::UnderscoreExpr(_) => self.alloc_pat_from_expr(Pat::Wild, syntax_ptr),
+ ast::Expr::ParenExpr(e) => {
+ // We special-case `(..)` for consistency with patterns.
+ if let Some(ast::Expr::RangeExpr(range)) = e.expr() {
+ if range.is_range_full() {
+ return Some(self.alloc_pat_from_expr(
+ Pat::Tuple { args: Box::default(), ellipsis: Some(0) },
+ syntax_ptr,
+ ));
+ }
+ }
+ return e.expr().and_then(|expr| self.maybe_collect_expr_as_pat(&expr));
+ }
+ ast::Expr::TupleExpr(e) => {
+ let (ellipsis, args) = collect_tuple(self, e.fields());
+ self.alloc_pat_from_expr(Pat::Tuple { args, ellipsis }, syntax_ptr)
+ }
+ ast::Expr::ArrayExpr(e) => {
+ if e.semicolon_token().is_some() {
+ return None;
+ }
+
+ let mut elements = e.exprs();
+ let prefix = elements
+ .by_ref()
+ .map_while(|elem| collect_possibly_rest(self, elem).left())
+ .collect();
+ let suffix = elements.map(|elem| self.collect_expr_as_pat(elem)).collect();
+ self.alloc_pat_from_expr(Pat::Slice { prefix, slice: None, suffix }, syntax_ptr)
+ }
+ ast::Expr::CallExpr(e) => {
+ let path = collect_path(self, e.expr()?)?;
+ let path = path.path().and_then(|path| self.parse_path(path)).map(Box::new);
+ let (ellipsis, args) = collect_tuple(self, e.arg_list()?.args());
+ self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr)
+ }
+ ast::Expr::PathExpr(e) => {
+ let (path, hygiene) = self
+ .collect_expr_path(e.clone())
+ .map(|(path, hygiene)| (Pat::Path(path), hygiene))
+ .unwrap_or((Pat::Missing, HygieneId::ROOT));
+ let pat_id = self.alloc_pat_from_expr(path, syntax_ptr);
+ if !hygiene.is_root() {
+ self.body.pat_hygiene.insert(pat_id, hygiene);
+ }
+ pat_id
+ }
+ ast::Expr::MacroExpr(e) => {
+ let e = e.macro_call()?;
+ let macro_ptr = AstPtr::new(&e);
+ let src = self.expander.in_file(AstPtr::new(expr));
+ let id = self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
+ this.collect_expr_as_pat_opt(expansion)
+ });
+ self.source_map.expr_map.insert(src, id.into());
+ id
+ }
+ ast::Expr::RecordExpr(e) => {
+ let path = e.path().and_then(|path| self.parse_path(path)).map(Box::new);
+ let record_field_list = e.record_expr_field_list()?;
+ let ellipsis = record_field_list.dotdot_token().is_some();
+ // FIXME: Report an error here if `record_field_list.spread().is_some()`.
+ let args = record_field_list
+ .fields()
+ .filter_map(|f| {
+ self.check_cfg(&f)?;
+ let field_expr = f.expr()?;
+ let pat = self.collect_expr_as_pat(field_expr);
+ let name = f.field_name()?.as_name();
+ let src = self.expander.in_file(AstPtr::new(&f).wrap_left());
+ self.source_map.pat_field_map_back.insert(pat, src);
+ Some(RecordFieldPat { name, pat })
+ })
+ .collect();
+ self.alloc_pat_from_expr(Pat::Record { path, args, ellipsis }, syntax_ptr)
+ }
+ _ => return None,
+ };
+ return Some(result);
+
+ fn collect_path(this: &mut ExprCollector<'_>, expr: ast::Expr) -> Option<ast::PathExpr> {
+ match expr {
+ ast::Expr::PathExpr(e) => Some(e),
+ ast::Expr::MacroExpr(mac) => {
+ let call = mac.macro_call()?;
+ {
+ let macro_ptr = AstPtr::new(&call);
+ this.collect_macro_call(call, macro_ptr, true, |this, expanded_path| {
+ collect_path(this, expanded_path?)
+ })
+ }
+ }
+ _ => None,
+ }
+ }
+
+ fn collect_possibly_rest(
+ this: &mut ExprCollector<'_>,
+ expr: ast::Expr,
+ ) -> Either<PatId, ()> {
+ match &expr {
+ ast::Expr::RangeExpr(e) if e.is_range_full() => Either::Right(()),
+ ast::Expr::MacroExpr(mac) => match mac.macro_call() {
+ Some(call) => {
+ let macro_ptr = AstPtr::new(&call);
+ let pat = this.collect_macro_call(
+ call,
+ macro_ptr,
+ true,
+ |this, expanded_expr| match expanded_expr {
+ Some(expanded_pat) => collect_possibly_rest(this, expanded_pat),
+ None => Either::Left(this.missing_pat()),
+ },
+ );
+ if let Either::Left(pat) = pat {
+ let src = this.expander.in_file(AstPtr::new(&expr).wrap_left());
+ this.source_map.pat_map_back.insert(pat, src);
+ }
+ pat
+ }
+ None => {
+ let ptr = AstPtr::new(&expr);
+ Either::Left(this.alloc_pat_from_expr(Pat::Missing, ptr))
+ }
+ },
+ _ => Either::Left(this.collect_expr_as_pat(expr)),
+ }
+ }
+
+ fn collect_tuple(
+ this: &mut ExprCollector<'_>,
+ fields: ast::AstChildren<ast::Expr>,
+ ) -> (Option<u32>, Box<[la_arena::Idx<Pat>]>) {
+ let mut ellipsis = None;
+ let args = fields
+ .enumerate()
+ .filter_map(|(idx, elem)| {
+ match collect_possibly_rest(this, elem) {
+ Either::Left(pat) => Some(pat),
+ Either::Right(()) => {
+ if ellipsis.is_none() {
+ ellipsis = Some(idx as u32);
+ }
+ // FIXME: Report an error here otherwise.
+ None
+ }
+ }
+ })
+ .collect();
+ (ellipsis, args)
+ }
+ }
+
fn initialize_binding_owner(
&mut self,
syntax_ptr: AstPtr<ast::Expr>,
@@ -744,7 +946,7 @@ impl ExprCollector<'_> {
let old_label = self.current_try_block_label.replace(label);
let ptr = AstPtr::new(&e).upcast();
- let (btail, expr_id) = self.with_labeled_rib(label, |this| {
+ let (btail, expr_id) = self.with_labeled_rib(label, HygieneId::ROOT, |this| {
let mut btail = None;
let block = this.collect_block_(e, |id, statements, tail| {
btail = tail;
@@ -755,17 +957,13 @@ impl ExprCollector<'_> {
let callee = self.alloc_expr_desugared_with_ptr(Expr::Path(try_from_output), ptr);
let next_tail = match btail {
- Some(tail) => self.alloc_expr_desugared_with_ptr(
- Expr::Call { callee, args: Box::new([tail]), is_assignee_expr: false },
- ptr,
- ),
+ Some(tail) => self
+ .alloc_expr_desugared_with_ptr(Expr::Call { callee, args: Box::new([tail]) }, ptr),
None => {
- let unit = self.alloc_expr_desugared_with_ptr(
- Expr::Tuple { exprs: Box::new([]), is_assignee_expr: false },
- ptr,
- );
+ let unit =
+ self.alloc_expr_desugared_with_ptr(Expr::Tuple { exprs: Box::new([]) }, ptr);
self.alloc_expr_desugared_with_ptr(
- Expr::Call { callee, args: Box::new([unit]), is_assignee_expr: false },
+ Expr::Call { callee, args: Box::new([unit]) },
ptr,
)
}
@@ -792,7 +990,9 @@ impl ExprCollector<'_> {
/// FIXME: Rustc wraps the condition in a construct equivalent to `{ let _t = <cond>; _t }`
/// to preserve drop semantics. We should probably do the same in future.
fn collect_while_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::WhileExpr) -> ExprId {
- let label = e.label().map(|label| self.collect_label(label));
+ let label = e.label().map(|label| {
+ (self.hygiene_id_for(label.syntax().text_range().start()), self.collect_label(label))
+ });
let body = self.collect_labelled_block_opt(label, e.loop_body());
// Labels can also be used in the condition expression, like this:
@@ -809,9 +1009,9 @@ impl ExprCollector<'_> {
// }
// ```
let condition = match label {
- Some(label) => {
- self.with_labeled_rib(label, |this| this.collect_expr_opt(e.condition()))
- }
+ Some((label_hygiene, label)) => self.with_labeled_rib(label, label_hygiene, |this| {
+ this.collect_expr_opt(e.condition())
+ }),
None => self.collect_expr_opt(e.condition()),
};
@@ -820,7 +1020,7 @@ impl ExprCollector<'_> {
Expr::If { condition, then_branch: body, else_branch: Some(break_expr) },
syntax_ptr,
);
- self.alloc_expr(Expr::Loop { body: if_expr, label }, syntax_ptr)
+ self.alloc_expr(Expr::Loop { body: if_expr, label: label.map(|it| it.1) }, syntax_ptr)
}
/// Desugar `ast::ForExpr` from: `[opt_ident]: for <pat> in <head> <body>` into:
@@ -851,15 +1051,11 @@ impl ExprCollector<'_> {
let head = self.collect_expr_opt(e.iterable());
let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr);
let iterator = self.alloc_expr(
- Expr::Call {
- callee: into_iter_fn_expr,
- args: Box::new([head]),
- is_assignee_expr: false,
- },
+ Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) },
syntax_ptr,
);
let none_arm = MatchArm {
- pat: self.alloc_pat_desugared(Pat::Path(Box::new(option_none))),
+ pat: self.alloc_pat_desugared(Pat::Path(option_none)),
guard: None,
expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr),
};
@@ -868,7 +1064,9 @@ impl ExprCollector<'_> {
args: Box::new([self.collect_pat_top(e.pat())]),
ellipsis: None,
};
- let label = e.label().map(|label| self.collect_label(label));
+ let label = e.label().map(|label| {
+ (self.hygiene_id_for(label.syntax().text_range().start()), self.collect_label(label))
+ });
let some_arm = MatchArm {
pat: self.alloc_pat_desugared(some_pat),
guard: None,
@@ -884,11 +1082,7 @@ impl ExprCollector<'_> {
);
let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr);
let iter_next_expr = self.alloc_expr(
- Expr::Call {
- callee: iter_next_fn_expr,
- args: Box::new([iter_expr_mut]),
- is_assignee_expr: false,
- },
+ Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) },
syntax_ptr,
);
let loop_inner = self.alloc_expr(
@@ -904,7 +1098,8 @@ impl ExprCollector<'_> {
},
syntax_ptr,
);
- let loop_outer = self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr);
+ let loop_outer = self
+ .alloc_expr(Expr::Loop { body: loop_inner, label: label.map(|it| it.1) }, syntax_ptr);
let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable);
let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None });
self.add_definition_to_binding(iter_binding, iter_pat);
@@ -942,10 +1137,8 @@ impl ExprCollector<'_> {
};
let operand = self.collect_expr_opt(e.expr());
let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr);
- let expr = self.alloc_expr(
- Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false },
- syntax_ptr,
- );
+ let expr = self
+ .alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr);
let continue_name = Name::generate_new_name(self.body.bindings.len());
let continue_binding =
self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated);
@@ -975,10 +1168,8 @@ impl ExprCollector<'_> {
expr: {
let it = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr);
let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr);
- let result = self.alloc_expr(
- Expr::Call { callee, args: Box::new([it]), is_assignee_expr: false },
- syntax_ptr,
- );
+ let result =
+ self.alloc_expr(Expr::Call { callee, args: Box::new([it]) }, syntax_ptr);
self.alloc_expr(
match self.current_try_block_label {
Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
@@ -1065,7 +1256,14 @@ impl ExprCollector<'_> {
// FIXME: Report parse errors here
}
+ let SpanMap::ExpansionSpanMap(new_span_map) = self.expander.span_map(self.db)
+ else {
+ panic!("just expanded a macro, ExpansionSpanMap should be available");
+ };
+ let old_span_map =
+ mem::replace(&mut self.current_span_map, Some(new_span_map.clone()));
let id = collector(self, Some(expansion.tree()));
+ self.current_span_map = old_span_map;
self.ast_id_map = prev_ast_id_map;
self.expander.exit(mark);
id
@@ -1108,7 +1306,7 @@ impl ExprCollector<'_> {
// Make the macro-call point to its expanded expression so we can query
// semantics on syntax pointers to the macro
let src = self.expander.in_file(syntax_ptr);
- self.source_map.expr_map.insert(src, tail);
+ self.source_map.expr_map.insert(src, tail.into());
})
}
@@ -1119,8 +1317,7 @@ impl ExprCollector<'_> {
return;
}
let pat = self.collect_pat_top(stmt.pat());
- let type_ref =
- stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+ let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
let else_branch = stmt
.let_else()
@@ -1145,10 +1342,46 @@ impl ExprCollector<'_> {
statements.push(Statement::Expr { expr, has_semi });
}
}
- ast::Stmt::Item(_item) => statements.push(Statement::Item),
+ ast::Stmt::Item(ast::Item::MacroDef(macro_)) => {
+ let Some(name) = macro_.name() else {
+ statements.push(Statement::Item(Item::Other));
+ return;
+ };
+ let name = name.as_name();
+ let macro_id = self.def_map.modules[DefMap::ROOT].scope.get(&name).take_macros();
+ self.collect_macro_def(statements, macro_id);
+ }
+ ast::Stmt::Item(ast::Item::MacroRules(macro_)) => {
+ let Some(name) = macro_.name() else {
+ statements.push(Statement::Item(Item::Other));
+ return;
+ };
+ let name = name.as_name();
+ let macro_defs_count =
+ self.current_block_legacy_macro_defs_count.entry(name.clone()).or_insert(0);
+ let macro_id = self.def_map.modules[DefMap::ROOT]
+ .scope
+ .get_legacy_macro(&name)
+ .and_then(|it| it.get(*macro_defs_count))
+ .copied();
+ *macro_defs_count += 1;
+ self.collect_macro_def(statements, macro_id);
+ }
+ ast::Stmt::Item(_item) => statements.push(Statement::Item(Item::Other)),
}
}
+ fn collect_macro_def(&mut self, statements: &mut Vec<Statement>, macro_id: Option<MacroId>) {
+ let Some(macro_id) = macro_id else {
+ never!("def map should have macro definition, but it doesn't");
+ statements.push(Statement::Item(Item::Other));
+ return;
+ };
+ let macro_id = self.db.macro_def(macro_id);
+ statements.push(Statement::Item(Item::MacroDef(Box::new(macro_id))));
+ self.label_ribs.push(LabelRib::new(RibKind::MacroDef(Box::new(macro_id))));
+ }
+
fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
self.collect_block_(block, |id, statements, tail| Expr::Block {
id,
@@ -1194,6 +1427,7 @@ impl ExprCollector<'_> {
};
let prev_def_map = mem::replace(&mut self.def_map, def_map);
let prev_local_module = mem::replace(&mut self.expander.module, module);
+ let prev_legacy_macros_count = mem::take(&mut self.current_block_legacy_macro_defs_count);
let mut statements = Vec::new();
block.statements().for_each(|s| self.collect_stmt(&mut statements, s));
@@ -1216,6 +1450,7 @@ impl ExprCollector<'_> {
self.def_map = prev_def_map;
self.expander.module = prev_local_module;
+ self.current_block_legacy_macro_defs_count = prev_legacy_macros_count;
expr_id
}
@@ -1228,11 +1463,13 @@ impl ExprCollector<'_> {
fn collect_labelled_block_opt(
&mut self,
- label: Option<LabelId>,
+ label: Option<(HygieneId, LabelId)>,
expr: Option<ast::BlockExpr>,
) -> ExprId {
match label {
- Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)),
+ Some((hygiene, label)) => {
+ self.with_labeled_rib(label, hygiene, |this| this.collect_block_opt(expr))
+ }
None => self.collect_block_opt(expr),
}
}
@@ -1250,6 +1487,10 @@ impl ExprCollector<'_> {
let pattern = match &pat {
ast::Pat::IdentPat(bp) => {
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
+ let hygiene = bp
+ .name()
+ .map(|name| self.hygiene_id_for(name.syntax().text_range().start()))
+ .unwrap_or(HygieneId::ROOT);
let annotation =
BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
@@ -1285,12 +1526,12 @@ impl ExprCollector<'_> {
}
// shadowing statics is an error as well, so we just ignore that case here
_ => {
- let id = binding_list.find(self, name, annotation);
+ let id = binding_list.find(self, name, hygiene, annotation);
(Some(id), Pat::Bind { id, subpat })
}
}
} else {
- let id = binding_list.find(self, name, annotation);
+ let id = binding_list.find(self, name, hygiene, annotation);
(Some(id), Pat::Bind { id, subpat })
};
@@ -1302,8 +1543,7 @@ impl ExprCollector<'_> {
return pat;
}
ast::Pat::TupleStructPat(p) => {
- let path =
- p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
+ let path = p.path().and_then(|path| self.parse_path(path)).map(Box::new);
let (args, ellipsis) = self.collect_tuple_pat(
p.fields(),
comma_follows_token(p.l_paren_token()),
@@ -1317,8 +1557,7 @@ impl ExprCollector<'_> {
Pat::Ref { pat, mutability }
}
ast::Pat::PathPat(p) => {
- let path =
- p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
+ let path = p.path().and_then(|path| self.parse_path(path));
path.map(Pat::Path).unwrap_or(Pat::Missing)
}
ast::Pat::OrPat(p) => 'b: {
@@ -1348,6 +1587,10 @@ impl ExprCollector<'_> {
for (id, _) in current_is_used.into_iter() {
binding_list.check_is_used(self, id);
}
+ if let &[pat] = &*pats {
+ // Leading pipe without real OR pattern. Leaving an one-item OR pattern may confuse later stages.
+ return pat;
+ }
Pat::Or(pats.into())
}
ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
@@ -1361,8 +1604,7 @@ impl ExprCollector<'_> {
}
ast::Pat::WildcardPat(_) => Pat::Wild,
ast::Pat::RecordPat(p) => {
- let path =
- p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
+ let path = p.path().and_then(|path| self.parse_path(path)).map(Box::new);
let record_pat_field_list =
&p.record_pat_field_list().expect("every struct should have a field list");
let args = record_pat_field_list
@@ -1372,7 +1614,7 @@ impl ExprCollector<'_> {
let ast_pat = f.pat()?;
let pat = self.collect_pat(ast_pat, binding_list);
let name = f.field_name()?.as_name();
- let src = self.expander.in_file(AstPtr::new(&f));
+ let src = self.expander.in_file(AstPtr::new(&f).wrap_right());
self.source_map.pat_field_map_back.insert(pat, src);
Some(RecordFieldPat { name, pat })
})
@@ -1569,20 +1811,51 @@ impl ExprCollector<'_> {
lifetime: Option<ast::Lifetime>,
) -> Result<Option<LabelId>, BodyDiagnostic> {
let Some(lifetime) = lifetime else { return Ok(None) };
+ let (mut hygiene_id, mut hygiene_info) = match &self.current_span_map {
+ None => (HygieneId::ROOT, None),
+ Some(span_map) => {
+ let span = span_map.span_at(lifetime.syntax().text_range().start());
+ let ctx = self.db.lookup_intern_syntax_context(span.ctx);
+ let hygiene_id = HygieneId::new(ctx.opaque_and_semitransparent);
+ let hygiene_info = ctx.outer_expn.map(|expansion| {
+ let expansion = self.db.lookup_intern_macro_call(expansion);
+ (ctx.parent, expansion.def)
+ });
+ (hygiene_id, hygiene_info)
+ }
+ };
let name = Name::new_lifetime(&lifetime);
for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
- if let Some((label_name, id)) = &rib.label {
- if *label_name == name {
- return if self.is_label_valid_from_rib(rib_idx) {
- Ok(Some(*id))
- } else {
- Err(BodyDiagnostic::UnreachableLabel {
- name,
- node: self.expander.in_file(AstPtr::new(&lifetime)),
- })
- };
+ match &rib.kind {
+ RibKind::Normal(label_name, id, label_hygiene) => {
+ if *label_name == name && *label_hygiene == hygiene_id {
+ return if self.is_label_valid_from_rib(rib_idx) {
+ Ok(Some(*id))
+ } else {
+ Err(BodyDiagnostic::UnreachableLabel {
+ name,
+ node: self.expander.in_file(AstPtr::new(&lifetime)),
+ })
+ };
+ }
}
+ RibKind::MacroDef(macro_id) => {
+ if let Some((parent_ctx, label_macro_id)) = hygiene_info {
+ if label_macro_id == **macro_id {
+ // A macro is allowed to refer to labels from before its declaration.
+ // Therefore, if we got to the rib of its declaration, give up its hygiene
+ // and use its parent expansion.
+ let parent_ctx = self.db.lookup_intern_syntax_context(parent_ctx);
+ hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent);
+ hygiene_info = parent_ctx.outer_expn.map(|expansion| {
+ let expansion = self.db.lookup_intern_macro_call(expansion);
+ (parent_ctx.parent, expansion.def)
+ });
+ }
+ }
+ }
+ _ => {}
}
}
@@ -1596,28 +1869,44 @@ impl ExprCollector<'_> {
!self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
}
+ fn pop_label_rib(&mut self) {
+ // We need to pop all macro defs, plus one rib.
+ while let Some(LabelRib { kind: RibKind::MacroDef(_) }) = self.label_ribs.pop() {
+ // Do nothing.
+ }
+ }
+
fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
self.label_ribs.push(LabelRib::new(kind));
let res = f(self);
- self.label_ribs.pop();
+ self.pop_label_rib();
res
}
- fn with_labeled_rib<T>(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T {
- self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label)));
+ fn with_labeled_rib<T>(
+ &mut self,
+ label: LabelId,
+ hygiene: HygieneId,
+ f: impl FnOnce(&mut Self) -> T,
+ ) -> T {
+ self.label_ribs.push(LabelRib::new(RibKind::Normal(
+ self.body[label].name.clone(),
+ label,
+ hygiene,
+ )));
let res = f(self);
- self.label_ribs.pop();
+ self.pop_label_rib();
res
}
fn with_opt_labeled_rib<T>(
&mut self,
- label: Option<LabelId>,
+ label: Option<(HygieneId, LabelId)>,
f: impl FnOnce(&mut Self) -> T,
) -> T {
match label {
None => f(self),
- Some(label) => self.with_labeled_rib(label, f),
+ Some((hygiene, label)) => self.with_labeled_rib(label, hygiene, f),
}
}
// endregion: labels
@@ -1666,28 +1955,39 @@ impl ExprCollector<'_> {
_ => None,
});
let mut mappings = vec![];
- let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) {
+ let (fmt, hygiene) = match template.and_then(|it| self.expand_macros_to_string(it)) {
Some((s, is_direct_literal)) => {
let call_ctx = self.expander.syntax_context();
- format_args::parse(
+ let hygiene = self.hygiene_id_for(s.syntax().text_range().start());
+ let fmt = format_args::parse(
&s,
fmt_snippet,
args,
is_direct_literal,
- |name| self.alloc_expr_desugared(Expr::Path(Path::from(name))),
+ |name| {
+ let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));
+ if !hygiene.is_root() {
+ self.body.expr_hygiene.insert(expr_id, hygiene);
+ }
+ expr_id
+ },
|name, span| {
if let Some(span) = span {
mappings.push((span, name))
}
},
call_ctx,
- )
+ );
+ (fmt, hygiene)
}
- None => FormatArgs {
- template: Default::default(),
- arguments: args.finish(),
- orphans: Default::default(),
- },
+ None => (
+ FormatArgs {
+ template: Default::default(),
+ arguments: args.finish(),
+ orphans: Default::default(),
+ },
+ HygieneId::ROOT,
+ ),
};
// Create a list of all _unique_ (argument, format trait) combinations.
@@ -1723,10 +2023,8 @@ impl ExprCollector<'_> {
}
})
.collect();
- let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
- elements: lit_pieces,
- is_assignee_expr: false,
- }));
+ let lit_pieces =
+ self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: lit_pieces }));
let lit_pieces = self.alloc_expr_desugared(Expr::Ref {
expr: lit_pieces,
rawness: Rawness::Ref,
@@ -1743,10 +2041,7 @@ impl ExprCollector<'_> {
Some(self.make_format_spec(placeholder, &mut argmap))
})
.collect();
- let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
- elements,
- is_assignee_expr: false,
- }));
+ let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements }));
self.alloc_expr_desugared(Expr::Ref {
expr: array,
rawness: Rawness::Ref,
@@ -1756,10 +2051,8 @@ impl ExprCollector<'_> {
let arguments = &*fmt.arguments.arguments;
let args = if arguments.is_empty() {
- let expr = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
- elements: Box::default(),
- is_assignee_expr: false,
- }));
+ let expr = self
+ .alloc_expr_desugared(Expr::Array(Array::ElementList { elements: Box::default() }));
self.alloc_expr_desugared(Expr::Ref {
expr,
rawness: Rawness::Ref,
@@ -1786,10 +2079,8 @@ impl ExprCollector<'_> {
self.make_argument(arg, ty)
})
.collect();
- let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
- elements: args,
- is_assignee_expr: false,
- }));
+ let array =
+ self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
self.alloc_expr_desugared(Expr::Ref {
expr: array,
rawness: Rawness::Ref,
@@ -1822,11 +2113,8 @@ impl ExprCollector<'_> {
let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted));
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new));
- let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call {
- callee: unsafe_arg_new,
- args: Box::default(),
- is_assignee_expr: false,
- });
+ let unsafe_arg_new =
+ self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() });
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
id: None,
// We collect the unused expressions here so that we still infer them instead of
@@ -1843,11 +2131,14 @@ impl ExprCollector<'_> {
Expr::Call {
callee: new_v1_formatted,
args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
- is_assignee_expr: false,
},
syntax_ptr,
);
- self.source_map.template_map.get_or_insert_with(Default::default).0.insert(idx, mappings);
+ self.source_map
+ .template_map
+ .get_or_insert_with(Default::default)
+ .0
+ .insert(idx, (hygiene, mappings));
idx
}
@@ -1938,7 +2229,6 @@ impl ExprCollector<'_> {
self.alloc_expr_desugared(Expr::Call {
callee: format_placeholder_new,
args: Box::new([position, fill, align, flags, precision, width]),
- is_assignee_expr: false,
})
}
@@ -1980,11 +2270,7 @@ impl ExprCollector<'_> {
Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)),
None => self.missing_expr(),
};
- self.alloc_expr_desugared(Expr::Call {
- callee: count_is,
- args: Box::new([args]),
- is_assignee_expr: false,
- })
+ self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]) })
}
Some(FormatCount::Argument(arg)) => {
if let Ok(arg_index) = arg.index {
@@ -2005,7 +2291,6 @@ impl ExprCollector<'_> {
self.alloc_expr_desugared(Expr::Call {
callee: count_param,
args: Box::new([args]),
- is_assignee_expr: false,
})
} else {
// FIXME: This drops arg causing it to potentially not be resolved/type checked
@@ -2054,11 +2339,7 @@ impl ExprCollector<'_> {
Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)),
None => self.missing_expr(),
};
- self.alloc_expr_desugared(Expr::Call {
- callee: new_fn,
- args: Box::new([arg]),
- is_assignee_expr: false,
- })
+ self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) })
}
// endregion: format
@@ -2082,7 +2363,7 @@ impl ExprCollector<'_> {
let src = self.expander.in_file(ptr);
let id = self.body.exprs.alloc(expr);
self.source_map.expr_map_back.insert(id, src);
- self.source_map.expr_map.insert(src, id);
+ self.source_map.expr_map.insert(src, id.into());
id
}
// FIXME: desugared exprs don't have ptr, that's wrong and should be fixed.
@@ -2110,10 +2391,17 @@ impl ExprCollector<'_> {
binding
}
+ fn alloc_pat_from_expr(&mut self, pat: Pat, ptr: ExprPtr) -> PatId {
+ let src = self.expander.in_file(ptr);
+ let id = self.body.pats.alloc(pat);
+ self.source_map.expr_map.insert(src, id.into());
+ self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
+ id
+ }
fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
let src = self.expander.in_file(ptr);
let id = self.body.pats.alloc(pat);
- self.source_map.pat_map_back.insert(id, src);
+ self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
self.source_map.pat_map.insert(src, id);
id
}
@@ -2151,6 +2439,17 @@ impl ExprCollector<'_> {
self.awaitable_context = orig;
res
}
+
+ /// If this returns `HygieneId::ROOT`, do not allocate to save space.
+ fn hygiene_id_for(&self, span_start: TextSize) -> HygieneId {
+ match &self.current_span_map {
+ None => HygieneId::ROOT,
+ Some(span_map) => {
+ let ctx = span_map.span_at(span_start).ctx;
+ HygieneId(self.db.lookup_intern_syntax_context(ctx).opaque_and_semitransparent)
+ }
+ }
+ }
}
fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
diff --git a/crates/hir-def/src/body/lower/asm.rs b/crates/hir-def/src/body/lower/asm.rs
index 4213370ac1..c1b58dbdd0 100644
--- a/crates/hir-def/src/body/lower/asm.rs
+++ b/crates/hir-def/src/body/lower/asm.rs
@@ -158,9 +158,7 @@ impl ExprCollector<'_> {
AsmOperand::Const(self.collect_expr_opt(c.expr()))
}
ast::AsmOperand::AsmSym(s) => {
- let Some(path) =
- s.path().and_then(|p| self.expander.parse_path(self.db, p))
- else {
+ let Some(path) = s.path().and_then(|p| self.parse_path(p)) else {
continue;
};
AsmOperand::Sym(path)
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs
index 37167fcb81..f8b6eef342 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/body/pretty.rs
@@ -11,7 +11,6 @@ use crate::{
Statement,
},
pretty::{print_generic_args, print_path, print_type_ref},
- type_ref::TypeRef,
};
use super::*;
@@ -69,20 +68,20 @@ pub(super) fn print_body_hir(
};
if let DefWithBodyId::FunctionId(it) = owner {
p.buf.push('(');
- let function_data = &db.function_data(it);
+ let function_data = db.function_data(it);
let (mut params, ret_type) = (function_data.params.iter(), &function_data.ret_type);
if let Some(self_param) = body.self_param {
p.print_binding(self_param);
p.buf.push_str(": ");
if let Some(ty) = params.next() {
- p.print_type_ref(ty);
+ p.print_type_ref(*ty, &function_data.types_map);
p.buf.push_str(", ");
}
}
body.params.iter().zip(params).for_each(|(&param, ty)| {
p.print_pat(param);
p.buf.push_str(": ");
- p.print_type_ref(ty);
+ p.print_type_ref(*ty, &function_data.types_map);
p.buf.push_str(", ");
});
// remove the last ", " in param list
@@ -92,7 +91,7 @@ pub(super) fn print_body_hir(
p.buf.push(')');
// return type
p.buf.push_str(" -> ");
- p.print_type_ref(ret_type);
+ p.print_type_ref(*ret_type, &function_data.types_map);
p.buf.push(' ');
}
p.print_expr(body.body_expr);
@@ -242,7 +241,7 @@ impl Printer<'_> {
Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
Expr::OffsetOf(offset_of) => {
w!(self, "builtin#offset_of(");
- self.print_type_ref(&offset_of.container);
+ self.print_type_ref(offset_of.container, &self.body.types);
let edition = self.edition;
w!(
self,
@@ -277,7 +276,7 @@ impl Printer<'_> {
w!(self, "loop ");
self.print_expr(*body);
}
- Expr::Call { callee, args, is_assignee_expr: _ } => {
+ Expr::Call { callee, args } => {
self.print_expr(*callee);
w!(self, "(");
if !args.is_empty() {
@@ -296,7 +295,7 @@ impl Printer<'_> {
if let Some(args) = generic_args {
w!(self, "::<");
let edition = self.edition;
- print_generic_args(self.db, args, self, edition).unwrap();
+ print_generic_args(self.db, args, &self.body.types, self, edition).unwrap();
w!(self, ">");
}
w!(self, "(");
@@ -372,7 +371,7 @@ impl Printer<'_> {
self.print_expr(*expr);
}
}
- Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr: _ } => {
+ Expr::RecordLit { path, fields, spread } => {
match path {
Some(path) => self.print_path(path),
None => w!(self, "�"),
@@ -391,9 +390,6 @@ impl Printer<'_> {
p.print_expr(*spread);
wln!(p);
}
- if *ellipsis {
- wln!(p, "..");
- }
});
w!(self, "}}");
}
@@ -408,7 +404,7 @@ impl Printer<'_> {
Expr::Cast { expr, type_ref } => {
self.print_expr(*expr);
w!(self, " as ");
- self.print_type_ref(type_ref);
+ self.print_type_ref(*type_ref, &self.body.types);
}
Expr::Ref { expr, rawness, mutability } => {
w!(self, "&");
@@ -466,7 +462,7 @@ impl Printer<'_> {
w!(self, ") ");
}
}
- Expr::Index { base, index, is_assignee_expr: _ } => {
+ Expr::Index { base, index } => {
self.print_expr(*base);
w!(self, "[");
self.print_expr(*index);
@@ -496,18 +492,18 @@ impl Printer<'_> {
self.print_pat(*pat);
if let Some(ty) = ty {
w!(self, ": ");
- self.print_type_ref(ty);
+ self.print_type_ref(*ty, &self.body.types);
}
}
w!(self, "|");
if let Some(ret_ty) = ret_type {
w!(self, " -> ");
- self.print_type_ref(ret_ty);
+ self.print_type_ref(*ret_ty, &self.body.types);
}
self.whitespace();
self.print_expr(*body);
}
- Expr::Tuple { exprs, is_assignee_expr: _ } => {
+ Expr::Tuple { exprs } => {
w!(self, "(");
for expr in exprs.iter() {
self.print_expr(*expr);
@@ -519,7 +515,7 @@ impl Printer<'_> {
w!(self, "[");
if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) {
self.indented(|p| match arr {
- Array::ElementList { elements, is_assignee_expr: _ } => {
+ Array::ElementList { elements } => {
for elem in elements.iter() {
p.print_expr(*elem);
w!(p, ", ");
@@ -551,6 +547,11 @@ impl Printer<'_> {
Expr::Const(id) => {
w!(self, "const {{ /* {id:?} */ }}");
}
+ &Expr::Assignment { target, value } => {
+ self.print_pat(target);
+ w!(self, " = ");
+ self.print_expr(value);
+ }
}
}
@@ -719,6 +720,9 @@ impl Printer<'_> {
w!(self, "const ");
self.print_expr(*c);
}
+ Pat::Expr(expr) => {
+ self.print_expr(*expr);
+ }
}
}
@@ -729,7 +733,7 @@ impl Printer<'_> {
self.print_pat(*pat);
if let Some(ty) = type_ref {
w!(self, ": ");
- self.print_type_ref(ty);
+ self.print_type_ref(*ty, &self.body.types);
}
if let Some(init) = initializer {
w!(self, " = ");
@@ -748,7 +752,7 @@ impl Printer<'_> {
}
wln!(self);
}
- Statement::Item => (),
+ Statement::Item(_) => (),
}
}
@@ -787,14 +791,14 @@ impl Printer<'_> {
}
}
- fn print_type_ref(&mut self, ty: &TypeRef) {
+ fn print_type_ref(&mut self, ty: TypeRefId, map: &TypesMap) {
let edition = self.edition;
- print_type_ref(self.db, ty, self, edition).unwrap();
+ print_type_ref(self.db, ty, map, self, edition).unwrap();
}
fn print_path(&mut self, path: &Path) {
let edition = self.edition;
- print_path(self.db, path, self, edition).unwrap();
+ print_path(self.db, path, &self.body.types, self, edition).unwrap();
}
fn print_binding(&mut self, id: BindingId) {
diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs
index bf201ca834..63a7a9af20 100644
--- a/crates/hir-def/src/body/scope.rs
+++ b/crates/hir-def/src/body/scope.rs
@@ -1,12 +1,12 @@
//! Name resolution for expressions.
-use hir_expand::name::Name;
+use hir_expand::{name::Name, MacroDefId};
use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx};
use triomphe::Arc;
use crate::{
- body::Body,
+ body::{Body, HygieneId},
db::DefDatabase,
- hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
+ hir::{Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement},
BlockId, ConstBlockId, DefWithBodyId,
};
@@ -22,6 +22,7 @@ pub struct ExprScopes {
#[derive(Debug, PartialEq, Eq)]
pub struct ScopeEntry {
name: Name,
+ hygiene: HygieneId,
binding: BindingId,
}
@@ -30,6 +31,10 @@ impl ScopeEntry {
&self.name
}
+ pub(crate) fn hygiene(&self) -> HygieneId {
+ self.hygiene
+ }
+
pub fn binding(&self) -> BindingId {
self.binding
}
@@ -40,6 +45,8 @@ pub struct ScopeData {
parent: Option<ScopeId>,
block: Option<BlockId>,
label: Option<(LabelId, Name)>,
+ // FIXME: We can compress this with an enum for this and `label`/`block` if memory usage matters.
+ macro_def: Option<Box<MacroDefId>>,
entries: IdxRange<ScopeEntry>,
}
@@ -62,6 +69,12 @@ impl ExprScopes {
self.scopes[scope].block
}
+ /// If `scope` refers to a macro def scope, returns the corresponding `MacroId`.
+ #[allow(clippy::borrowed_box)] // If we return `&MacroDefId` we need to move it, this way we just clone the `Box`.
+ pub fn macro_def(&self, scope: ScopeId) -> Option<&Box<MacroDefId>> {
+ self.scopes[scope].macro_def.as_ref()
+ }
+
/// If `scope` refers to a labeled expression scope, returns the corresponding `Label`.
pub fn label(&self, scope: ScopeId) -> Option<(LabelId, Name)> {
self.scopes[scope].label.clone()
@@ -102,7 +115,7 @@ impl ExprScopes {
};
let mut root = scopes.root_scope();
if let Some(self_param) = body.self_param {
- scopes.add_bindings(body, root, self_param);
+ scopes.add_bindings(body, root, self_param, body.binding_hygiene(self_param));
}
scopes.add_params_bindings(body, root, &body.params);
compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root, resolve_const_block);
@@ -114,6 +127,7 @@ impl ExprScopes {
parent: None,
block: None,
label: None,
+ macro_def: None,
entries: empty_entries(self.scope_entries.len()),
})
}
@@ -123,6 +137,7 @@ impl ExprScopes {
parent: Some(parent),
block: None,
label: None,
+ macro_def: None,
entries: empty_entries(self.scope_entries.len()),
})
}
@@ -132,6 +147,7 @@ impl ExprScopes {
parent: Some(parent),
block: None,
label,
+ macro_def: None,
entries: empty_entries(self.scope_entries.len()),
})
}
@@ -146,21 +162,38 @@ impl ExprScopes {
parent: Some(parent),
block,
label,
+ macro_def: None,
entries: empty_entries(self.scope_entries.len()),
})
}
- fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) {
+ fn new_macro_def_scope(&mut self, parent: ScopeId, macro_id: Box<MacroDefId>) -> ScopeId {
+ self.scopes.alloc(ScopeData {
+ parent: Some(parent),
+ block: None,
+ label: None,
+ macro_def: Some(macro_id),
+ entries: empty_entries(self.scope_entries.len()),
+ })
+ }
+
+ fn add_bindings(
+ &mut self,
+ body: &Body,
+ scope: ScopeId,
+ binding: BindingId,
+ hygiene: HygieneId,
+ ) {
let Binding { name, .. } = &body.bindings[binding];
- let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding });
+ let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding, hygiene });
self.scopes[scope].entries =
IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
}
fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
let pattern = &body[pat];
- if let Pat::Bind { id, .. } = pattern {
- self.add_bindings(body, scope, *id);
+ if let Pat::Bind { id, .. } = *pattern {
+ self.add_bindings(body, scope, id, body.binding_hygiene(id));
}
pattern.walk_child_pats(|pat| self.add_pat_bindings(body, scope, pat));
@@ -206,7 +239,10 @@ fn compute_block_scopes(
Statement::Expr { expr, .. } => {
compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block);
}
- Statement::Item => (),
+ Statement::Item(Item::MacroDef(macro_id)) => {
+ *scope = scopes.new_macro_def_scope(*scope, macro_id.clone());
+ }
+ Statement::Item(Item::Other) => (),
}
}
if let Some(expr) = tail {
@@ -282,7 +318,7 @@ fn compute_expr_scopes(
*scope = scopes.new_scope(*scope);
scopes.add_pat_bindings(body, *scope, pat);
}
- e => e.walk_child_exprs(|e| compute_expr_scopes(scopes, e, scope)),
+ _ => body.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)),
};
}
@@ -333,6 +369,8 @@ mod tests {
let expr_id = source_map
.node_expr(InFile { file_id: file_id.into(), value: &marker.into() })
+ .unwrap()
+ .as_expr()
.unwrap();
let scope = scopes.scope_for(expr_id);
@@ -488,8 +526,11 @@ fn foo() {
let expr_scope = {
let expr_ast = name_ref.syntax().ancestors().find_map(ast::Expr::cast).unwrap();
- let expr_id =
- source_map.node_expr(InFile { file_id: file_id.into(), value: &expr_ast }).unwrap();
+ let expr_id = source_map
+ .node_expr(InFile { file_id: file_id.into(), value: &expr_ast })
+ .unwrap()
+ .as_expr()
+ .unwrap();
scopes.scope_for(expr_id).unwrap()
};
diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs
index dd3e79c874..3b29d98d19 100644
--- a/crates/hir-def/src/body/tests.rs
+++ b/crates/hir-def/src/body/tests.rs
@@ -370,3 +370,37 @@ fn f(a: i32, b: u32) -> String {
}"#]]
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
}
+
+#[test]
+fn destructuring_assignment_tuple_macro() {
+ // This is a funny one. `let m!()() = Bar()` is an error in rustc, because `m!()()` isn't a valid pattern,
+ // but in destructuring assignment it is valid, because `m!()()` is a valid expression, and destructuring
+ // assignments start their lives as expressions. So we have to do the same.
+
+ let (db, body, def) = lower(
+ r#"
+struct Bar();
+
+macro_rules! m {
+ () => { Bar };
+}
+
+fn foo() {
+ m!()() = Bar();
+}
+"#,
+ );
+
+ let (_, source_map) = db.body_with_source_map(def);
+ assert_eq!(source_map.diagnostics(), &[]);
+
+ for (_, def_map) in body.blocks(&db) {
+ assert_eq!(def_map.diagnostics(), &[]);
+ }
+
+ expect![[r#"
+ fn foo() -> () {
+ Bar() = Bar();
+ }"#]]
+ .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
+}
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index 263fad51d7..f49018eaf3 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -6,7 +6,7 @@ use base_db::CrateId;
use hir_expand::{
name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind,
};
-use intern::{sym, Interned, Symbol};
+use intern::{sym, Symbol};
use la_arena::{Idx, RawIdx};
use smallvec::SmallVec;
use syntax::{ast, Parse};
@@ -25,7 +25,7 @@ use crate::{
DefMap, MacroSubNs,
},
path::ImportAlias,
- type_ref::{TraitRef, TypeBound, TypeRef},
+ type_ref::{TraitRef, TypeBound, TypeRefId, TypesMap},
visibility::RawVisibility,
AssocItemId, AstIdWithPath, ConstId, ConstLoc, ExternCrateId, FunctionId, FunctionLoc,
HasModule, ImplId, Intern, ItemContainerId, ItemLoc, Lookup, Macro2Id, MacroRulesId, ModuleId,
@@ -35,13 +35,14 @@ use crate::{
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunctionData {
pub name: Name,
- pub params: Box<[Interned<TypeRef>]>,
- pub ret_type: Interned<TypeRef>,
+ pub params: Box<[TypeRefId]>,
+ pub ret_type: TypeRefId,
pub attrs: Attrs,
pub visibility: RawVisibility,
pub abi: Option<Symbol>,
pub legacy_const_generics_indices: Option<Box<Box<[u32]>>>,
pub rustc_allow_incoherent_impl: bool,
+ pub types_map: Arc<TypesMap>,
flags: FnFlags,
}
@@ -110,13 +111,14 @@ impl FunctionData {
.filter(|&(idx, _)| {
item_tree.attrs(db, krate, attr_owner(idx)).is_cfg_enabled(cfg_options)
})
- .filter_map(|(_, param)| param.type_ref.clone())
+ .filter_map(|(_, param)| param.type_ref)
.collect(),
- ret_type: func.ret_type.clone(),
+ ret_type: func.ret_type,
attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
visibility,
abi: func.abi.clone(),
legacy_const_generics_indices,
+ types_map: func.types_map.clone(),
flags,
rustc_allow_incoherent_impl,
})
@@ -182,13 +184,14 @@ fn parse_rustc_legacy_const_generics(tt: &crate::tt::Subtree) -> Box<[u32]> {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypeAliasData {
pub name: Name,
- pub type_ref: Option<Interned<TypeRef>>,
+ pub type_ref: Option<TypeRefId>,
pub visibility: RawVisibility,
pub is_extern: bool,
pub rustc_has_incoherent_inherent_impls: bool,
pub rustc_allow_incoherent_impl: bool,
/// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
- pub bounds: Box<[Interned<TypeBound>]>,
+ pub bounds: Box<[TypeBound]>,
+ pub types_map: Arc<TypesMap>,
}
impl TypeAliasData {
@@ -216,12 +219,13 @@ impl TypeAliasData {
Arc::new(TypeAliasData {
name: typ.name.clone(),
- type_ref: typ.type_ref.clone(),
+ type_ref: typ.type_ref,
visibility,
is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
rustc_has_incoherent_inherent_impls,
rustc_allow_incoherent_impl,
bounds: typ.bounds.clone(),
+ types_map: typ.types_map.clone(),
})
}
}
@@ -343,13 +347,14 @@ impl TraitAliasData {
#[derive(Debug, PartialEq, Eq)]
pub struct ImplData {
- pub target_trait: Option<Interned<TraitRef>>,
- pub self_ty: Interned<TypeRef>,
+ pub target_trait: Option<TraitRef>,
+ pub self_ty: TypeRefId,
pub items: Box<[AssocItemId]>,
pub is_negative: bool,
pub is_unsafe: bool,
// box it as the vec is usually empty anyways
pub macro_calls: Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
+ pub types_map: Arc<TypesMap>,
}
impl ImplData {
@@ -368,7 +373,7 @@ impl ImplData {
let item_tree = tree_id.item_tree(db);
let impl_def = &item_tree[tree_id.value];
let target_trait = impl_def.target_trait.clone();
- let self_ty = impl_def.self_ty.clone();
+ let self_ty = impl_def.self_ty;
let is_negative = impl_def.is_negative;
let is_unsafe = impl_def.is_unsafe;
@@ -387,6 +392,7 @@ impl ImplData {
is_negative,
is_unsafe,
macro_calls,
+ types_map: impl_def.types_map.clone(),
}),
DefDiagnostics::new(diagnostics),
)
@@ -532,10 +538,11 @@ impl ExternCrateDeclData {
pub struct ConstData {
/// `None` for `const _: () = ();`
pub name: Option<Name>,
- pub type_ref: Interned<TypeRef>,
+ pub type_ref: TypeRefId,
pub visibility: RawVisibility,
pub rustc_allow_incoherent_impl: bool,
pub has_body: bool,
+ pub types_map: Arc<TypesMap>,
}
impl ConstData {
@@ -556,10 +563,11 @@ impl ConstData {
Arc::new(ConstData {
name: konst.name.clone(),
- type_ref: konst.type_ref.clone(),
+ type_ref: konst.type_ref,
visibility,
rustc_allow_incoherent_impl,
has_body: konst.has_body,
+ types_map: konst.types_map.clone(),
})
}
}
@@ -567,12 +575,13 @@ impl ConstData {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StaticData {
pub name: Name,
- pub type_ref: Interned<TypeRef>,
+ pub type_ref: TypeRefId,
pub visibility: RawVisibility,
pub mutable: bool,
pub is_extern: bool,
pub has_safe_kw: bool,
pub has_unsafe_kw: bool,
+ pub types_map: Arc<TypesMap>,
}
impl StaticData {
@@ -583,12 +592,13 @@ impl StaticData {
Arc::new(StaticData {
name: statik.name.clone(),
- type_ref: statik.type_ref.clone(),
+ type_ref: statik.type_ref,
visibility: item_tree[statik.visibility].clone(),
mutable: statik.mutable,
is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
has_safe_kw: statik.has_safe_kw,
has_unsafe_kw: statik.has_unsafe_kw,
+ types_map: statik.types_map.clone(),
})
}
}
diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs
index ba54451e59..068ebb3b7e 100644
--- a/crates/hir-def/src/data/adt.rs
+++ b/crates/hir-def/src/data/adt.rs
@@ -6,7 +6,7 @@ use cfg::CfgOptions;
use either::Either;
use hir_expand::name::Name;
-use intern::{sym, Interned};
+use intern::sym;
use la_arena::Arena;
use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
use triomphe::Arc;
@@ -21,7 +21,7 @@ use crate::{
lang_item::LangItem,
nameres::diagnostics::{DefDiagnostic, DefDiagnostics},
tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree},
- type_ref::TypeRef,
+ type_ref::{TypeRefId, TypesMap},
visibility::RawVisibility,
EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, VariantId,
};
@@ -73,8 +73,8 @@ pub struct EnumVariantData {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VariantData {
- Record(Arena<FieldData>),
- Tuple(Arena<FieldData>),
+ Record { fields: Arena<FieldData>, types_map: Arc<TypesMap> },
+ Tuple { fields: Arena<FieldData>, types_map: Arc<TypesMap> },
Unit,
}
@@ -82,7 +82,7 @@ pub enum VariantData {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FieldData {
pub name: Name,
- pub type_ref: Interned<TypeRef>,
+ pub type_ref: TypeRefId,
pub visibility: RawVisibility,
}
@@ -208,7 +208,7 @@ impl StructData {
}
let strukt = &item_tree[loc.id.value];
- let (data, diagnostics) = lower_fields(
+ let (fields, diagnostics) = lower_fields(
db,
krate,
loc.container.local_id,
@@ -219,12 +219,13 @@ impl StructData {
&strukt.fields,
None,
);
+ let types_map = strukt.types_map.clone();
(
Arc::new(StructData {
name: strukt.name.clone(),
variant_data: Arc::new(match strukt.shape {
- FieldsShape::Record => VariantData::Record(data),
- FieldsShape::Tuple => VariantData::Tuple(data),
+ FieldsShape::Record => VariantData::Record { fields, types_map },
+ FieldsShape::Tuple => VariantData::Tuple { fields, types_map },
FieldsShape::Unit => VariantData::Unit,
}),
repr,
@@ -258,7 +259,7 @@ impl StructData {
}
let union = &item_tree[loc.id.value];
- let (data, diagnostics) = lower_fields(
+ let (fields, diagnostics) = lower_fields(
db,
krate,
loc.container.local_id,
@@ -269,10 +270,11 @@ impl StructData {
&union.fields,
None,
);
+ let types_map = union.types_map.clone();
(
Arc::new(StructData {
name: union.name.clone(),
- variant_data: Arc::new(VariantData::Record(data)),
+ variant_data: Arc::new(VariantData::Record { fields, types_map }),
repr,
visibility: item_tree[union.visibility].clone(),
flags,
@@ -360,7 +362,7 @@ impl EnumVariantData {
let item_tree = loc.id.item_tree(db);
let variant = &item_tree[loc.id.value];
- let (data, diagnostics) = lower_fields(
+ let (fields, diagnostics) = lower_fields(
db,
krate,
container.local_id,
@@ -371,13 +373,14 @@ impl EnumVariantData {
&variant.fields,
Some(item_tree[loc.parent.lookup(db).id.value].visibility),
);
+ let types_map = variant.types_map.clone();
(
Arc::new(EnumVariantData {
name: variant.name.clone(),
variant_data: Arc::new(match variant.shape {
- FieldsShape::Record => VariantData::Record(data),
- FieldsShape::Tuple => VariantData::Tuple(data),
+ FieldsShape::Record => VariantData::Record { fields, types_map },
+ FieldsShape::Tuple => VariantData::Tuple { fields, types_map },
FieldsShape::Unit => VariantData::Unit,
}),
}),
@@ -390,11 +393,20 @@ impl VariantData {
pub fn fields(&self) -> &Arena<FieldData> {
const EMPTY: &Arena<FieldData> = &Arena::new();
match &self {
- VariantData::Record(fields) | VariantData::Tuple(fields) => fields,
+ VariantData::Record { fields, .. } | VariantData::Tuple { fields, .. } => fields,
_ => EMPTY,
}
}
+ pub fn types_map(&self) -> &TypesMap {
+ match &self {
+ VariantData::Record { types_map, .. } | VariantData::Tuple { types_map, .. } => {
+ types_map
+ }
+ VariantData::Unit => TypesMap::EMPTY,
+ }
+ }
+
// FIXME: Linear lookup
pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
@@ -402,8 +414,8 @@ impl VariantData {
pub fn kind(&self) -> StructKind {
match self {
- VariantData::Record(_) => StructKind::Record,
- VariantData::Tuple(_) => StructKind::Tuple,
+ VariantData::Record { .. } => StructKind::Record,
+ VariantData::Tuple { .. } => StructKind::Tuple,
VariantData::Unit => StructKind::Unit,
}
}
@@ -463,7 +475,7 @@ fn lower_field(
) -> FieldData {
FieldData {
name: field.name.clone(),
- type_ref: field.type_ref.clone(),
+ type_ref: field.type_ref,
visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(),
}
}
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index aeda302f35..d7e83ce33e 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -2,7 +2,7 @@
use base_db::{ra_salsa, CrateId, SourceDatabase, Upcast};
use either::Either;
use hir_expand::{db::ExpandDatabase, HirFileId, MacroDefId};
-use intern::{sym, Interned};
+use intern::sym;
use la_arena::ArenaMap;
use span::{EditionedFileId, MacroCallId};
use syntax::{ast, AstPtr};
@@ -18,9 +18,10 @@ use crate::{
},
generics::GenericParams,
import_map::ImportMap,
- item_tree::{AttrOwner, ItemTree},
+ item_tree::{AttrOwner, ItemTree, ItemTreeSourceMaps},
lang_item::{self, LangItem, LangItemTarget, LangItems},
nameres::{diagnostics::DefDiagnostics, DefMap},
+ type_ref::TypesSourceMap,
visibility::{self, Visibility},
AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId,
EnumId, EnumLoc, EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId,
@@ -91,6 +92,18 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[ra_salsa::invoke(ItemTree::block_item_tree_query)]
fn block_item_tree(&self, block_id: BlockId) -> Arc<ItemTree>;
+ #[ra_salsa::invoke(ItemTree::file_item_tree_with_source_map_query)]
+ fn file_item_tree_with_source_map(
+ &self,
+ file_id: HirFileId,
+ ) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>);
+
+ #[ra_salsa::invoke(ItemTree::block_item_tree_with_source_map_query)]
+ fn block_item_tree_with_source_map(
+ &self,
+ block_id: BlockId,
+ ) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>);
+
#[ra_salsa::invoke(DefMap::crate_def_map_query)]
fn crate_def_map(&self, krate: CrateId) -> Arc<DefMap>;
@@ -187,7 +200,14 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
fn expr_scopes(&self, def: DefWithBodyId) -> Arc<ExprScopes>;
#[ra_salsa::invoke(GenericParams::generic_params_query)]
- fn generic_params(&self, def: GenericDefId) -> Interned<GenericParams>;
+ fn generic_params(&self, def: GenericDefId) -> Arc<GenericParams>;
+
+ /// If this returns `None` for the source map, that means it is the same as with the item tree.
+ #[ra_salsa::invoke(GenericParams::generic_params_with_source_map_query)]
+ fn generic_params_with_source_map(
+ &self,
+ def: GenericDefId,
+ ) -> (Arc<GenericParams>, Option<Arc<TypesSourceMap>>);
// region:attrs
diff --git a/crates/hir-def/src/expander.rs b/crates/hir-def/src/expander.rs
index 6d8b4445f7..d430733fca 100644
--- a/crates/hir-def/src/expander.rs
+++ b/crates/hir-def/src/expander.rs
@@ -14,6 +14,7 @@ use span::SyntaxContextId;
use syntax::{ast, Parse};
use triomphe::Arc;
+use crate::type_ref::{TypesMap, TypesSourceMap};
use crate::{
attr::Attrs, db::DefDatabase, lower::LowerCtx, path::Path, AsMacroCall, MacroId, ModuleId,
UnresolvedMacro,
@@ -49,6 +50,10 @@ impl Expander {
}
}
+ pub(crate) fn span_map(&self, db: &dyn DefDatabase) -> &SpanMap {
+ self.span_map.get_or_init(|| db.span_map(self.current_file_id))
+ }
+
pub fn krate(&self) -> CrateId {
self.module.krate
}
@@ -110,8 +115,19 @@ impl Expander {
mark.bomb.defuse();
}
- pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> {
- LowerCtx::with_span_map_cell(db, self.current_file_id, self.span_map.clone())
+ pub fn ctx<'a>(
+ &self,
+ db: &'a dyn DefDatabase,
+ types_map: &'a mut TypesMap,
+ types_source_map: &'a mut TypesSourceMap,
+ ) -> LowerCtx<'a> {
+ LowerCtx::with_span_map_cell(
+ db,
+ self.current_file_id,
+ self.span_map.clone(),
+ types_map,
+ types_source_map,
+ )
}
pub(crate) fn in_file<T>(&self, value: T) -> InFile<T> {
@@ -138,8 +154,20 @@ impl Expander {
self.current_file_id
}
- pub(crate) fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
- let ctx = LowerCtx::with_span_map_cell(db, self.current_file_id, self.span_map.clone());
+ pub(crate) fn parse_path(
+ &mut self,
+ db: &dyn DefDatabase,
+ path: ast::Path,
+ types_map: &mut TypesMap,
+ types_source_map: &mut TypesSourceMap,
+ ) -> Option<Path> {
+ let ctx = LowerCtx::with_span_map_cell(
+ db,
+ self.current_file_id,
+ self.span_map.clone(),
+ types_map,
+ types_source_map,
+ );
Path::from_src(&ctx, path)
}
diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs
index f5e03e5281..a615abd1bb 100644
--- a/crates/hir-def/src/find_path.rs
+++ b/crates/hir-def/src/find_path.rs
@@ -1025,7 +1025,7 @@ pub mod ast {
check_found_path(
r#"
mod bar {
- mod foo { pub(super) struct S; }
+ mod foo { pub(crate) struct S; }
pub(crate) use foo::*;
}
$0
@@ -1047,7 +1047,7 @@ $0
check_found_path(
r#"
mod bar {
- mod foo { pub(super) struct S; }
+ mod foo { pub(crate) struct S; }
pub(crate) use foo::S as U;
}
$0
diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs
index 6c34ee086a..6b79850e9c 100644
--- a/crates/hir-def/src/generics.rs
+++ b/crates/hir-def/src/generics.rs
@@ -3,16 +3,18 @@
//! generic parameters. See also the `Generics` type and the `generics_of` query
//! in rustc.
-use std::ops;
+use std::{ops, sync::LazyLock};
use either::Either;
use hir_expand::{
name::{AsName, Name},
ExpandResult,
};
-use intern::Interned;
use la_arena::{Arena, RawIdx};
-use stdx::impl_from;
+use stdx::{
+ impl_from,
+ thin_vec::{EmptyOptimizedThinVec, ThinVec},
+};
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
use triomphe::Arc;
@@ -22,7 +24,11 @@ use crate::{
item_tree::{AttrOwner, FileItemTreeId, GenericModItem, GenericsItemTreeNode, ItemTree},
lower::LowerCtx,
nameres::{DefMap, MacroSubNs},
- type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
+ path::{AssociatedTypeBinding, GenericArg, GenericArgs, NormalPath, Path},
+ type_ref::{
+ ArrayType, ConstRef, FnType, LifetimeRef, RefType, TypeBound, TypeRef, TypeRefId, TypesMap,
+ TypesSourceMap,
+ },
AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId,
LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
};
@@ -37,7 +43,7 @@ pub struct TypeParamData {
/// [`None`] only if the type ref is an [`TypeRef::ImplTrait`]. FIXME: Might be better to just
/// make it always be a value, giving impl trait a special name.
pub name: Option<Name>,
- pub default: Option<Interned<TypeRef>>,
+ pub default: Option<TypeRefId>,
pub provenance: TypeParamProvenance,
}
@@ -51,7 +57,7 @@ pub struct LifetimeParamData {
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct ConstParamData {
pub name: Name,
- pub ty: Interned<TypeRef>,
+ pub ty: TypeRefId,
pub default: Option<ConstRef>,
}
@@ -161,6 +167,7 @@ pub struct GenericParams {
type_or_consts: Arena<TypeOrConstParamData>,
lifetimes: Arena<LifetimeParamData>,
where_predicates: Box<[WherePredicate]>,
+ pub types_map: TypesMap,
}
impl ops::Index<LocalTypeOrConstParamId> for GenericParams {
@@ -183,24 +190,14 @@ impl ops::Index<LocalLifetimeParamId> for GenericParams {
/// associated type bindings like `Iterator<Item = u32>`.
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub enum WherePredicate {
- TypeBound {
- target: WherePredicateTypeTarget,
- bound: Interned<TypeBound>,
- },
- Lifetime {
- target: LifetimeRef,
- bound: LifetimeRef,
- },
- ForLifetime {
- lifetimes: Box<[Name]>,
- target: WherePredicateTypeTarget,
- bound: Interned<TypeBound>,
- },
+ TypeBound { target: WherePredicateTypeTarget, bound: TypeBound },
+ Lifetime { target: LifetimeRef, bound: LifetimeRef },
+ ForLifetime { lifetimes: Box<[Name]>, target: WherePredicateTypeTarget, bound: TypeBound },
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub enum WherePredicateTypeTarget {
- TypeRef(Interned<TypeRef>),
+ TypeRef(TypeRefId),
/// For desugared where predicates that can directly refer to a type param.
TypeOrConstParam(LocalTypeOrConstParamId),
}
@@ -300,7 +297,14 @@ impl GenericParams {
pub(crate) fn generic_params_query(
db: &dyn DefDatabase,
def: GenericDefId,
- ) -> Interned<GenericParams> {
+ ) -> Arc<GenericParams> {
+ db.generic_params_with_source_map(def).0
+ }
+
+ pub(crate) fn generic_params_with_source_map_query(
+ db: &dyn DefDatabase,
+ def: GenericDefId,
+ ) -> (Arc<GenericParams>, Option<Arc<TypesSourceMap>>) {
let _p = tracing::info_span!("generic_params_query").entered();
let krate = def.krate(db);
@@ -309,7 +313,7 @@ impl GenericParams {
// Returns the generic parameters that are enabled under the current `#[cfg]` options
let enabled_params =
- |params: &Interned<GenericParams>, item_tree: &ItemTree, parent: GenericModItem| {
+ |params: &Arc<GenericParams>, item_tree: &ItemTree, parent: GenericModItem| {
let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options);
let attr_owner_ct = |param| AttrOwner::TypeOrConstParamData(parent, param);
let attr_owner_lt = |param| AttrOwner::LifetimeParamData(parent, param);
@@ -325,7 +329,7 @@ impl GenericParams {
if all_type_or_consts_enabled && all_lifetimes_enabled {
params.clone()
} else {
- Interned::new(GenericParams {
+ Arc::new(GenericParams {
type_or_consts: all_type_or_consts_enabled
.then(|| params.type_or_consts.clone())
.unwrap_or_else(|| {
@@ -347,6 +351,7 @@ impl GenericParams {
.collect()
}),
where_predicates: params.where_predicates.clone(),
+ types_map: params.types_map.clone(),
})
}
};
@@ -357,18 +362,18 @@ impl GenericParams {
Data = impl ItemTreeLoc<Id = Id>,
>,
enabled_params: impl Fn(
- &Interned<GenericParams>,
+ &Arc<GenericParams>,
&ItemTree,
GenericModItem,
- ) -> Interned<GenericParams>,
- ) -> Interned<GenericParams>
+ ) -> Arc<GenericParams>,
+ ) -> (Arc<GenericParams>, Option<Arc<TypesSourceMap>>)
where
FileItemTreeId<Id>: Into<GenericModItem>,
{
let id = id.lookup(db).item_tree_id();
let tree = id.item_tree(db);
let item = &tree[id.value];
- enabled_params(item.generic_params(), &tree, id.value.into())
+ (enabled_params(item.generic_params(), &tree, id.value.into()), None)
}
match def {
@@ -383,28 +388,37 @@ impl GenericParams {
let module = loc.container.module(db);
let func_data = db.function_data(id);
if func_data.params.is_empty() {
- enabled_params
+ (enabled_params, None)
} else {
+ let source_maps = loc.id.item_tree_with_source_map(db).1;
+ let item_source_maps = source_maps.function(loc.id.value);
let mut generic_params = GenericParamsCollector {
type_or_consts: enabled_params.type_or_consts.clone(),
lifetimes: enabled_params.lifetimes.clone(),
where_predicates: enabled_params.where_predicates.clone().into(),
};
+ let (mut types_map, mut types_source_maps) =
+ (enabled_params.types_map.clone(), item_source_maps.generics().clone());
// Don't create an `Expander` if not needed since this
// could cause a reparse after the `ItemTree` has been created due to the spanmap.
let mut expander = None;
- for param in func_data.params.iter() {
+ for &param in func_data.params.iter() {
generic_params.fill_implicit_impl_trait_args(
db,
+ &mut types_map,
+ &mut types_source_maps,
&mut expander,
&mut || {
(module.def_map(db), Expander::new(db, loc.id.file_id(), module))
},
param,
+ &item.types_map,
+ item_source_maps.item(),
);
}
- Interned::new(generic_params.finish())
+ let generics = generic_params.finish(types_map, &mut types_source_maps);
+ (generics, Some(Arc::new(types_source_maps)))
}
}
GenericDefId::AdtId(AdtId::StructId(id)) => id_to_generics(db, id, enabled_params),
@@ -414,11 +428,15 @@ impl GenericParams {
GenericDefId::TraitAliasId(id) => id_to_generics(db, id, enabled_params),
GenericDefId::TypeAliasId(id) => id_to_generics(db, id, enabled_params),
GenericDefId::ImplId(id) => id_to_generics(db, id, enabled_params),
- GenericDefId::ConstId(_) => Interned::new(GenericParams {
- type_or_consts: Default::default(),
- lifetimes: Default::default(),
- where_predicates: Default::default(),
- }),
+ GenericDefId::ConstId(_) => (
+ Arc::new(GenericParams {
+ type_or_consts: Default::default(),
+ lifetimes: Default::default(),
+ where_predicates: Default::default(),
+ types_map: Default::default(),
+ }),
+ None,
+ ),
}
}
}
@@ -452,7 +470,7 @@ impl GenericParamsCollector {
&mut self,
lower_ctx: &LowerCtx<'_>,
type_bounds: Option<ast::TypeBoundList>,
- target: Either<TypeRef, LifetimeRef>,
+ target: Either<TypeRefId, LifetimeRef>,
) {
for bound in type_bounds.iter().flat_map(|type_bound_list| type_bound_list.bounds()) {
self.add_where_predicate_from_bound(lower_ctx, bound, None, target.clone());
@@ -473,16 +491,15 @@ impl GenericParamsCollector {
ast::TypeOrConstParam::Type(type_param) => {
let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
// FIXME: Use `Path::from_src`
- let default = type_param
- .default_type()
- .map(|it| Interned::new(TypeRef::from_ast(lower_ctx, it)));
+ let default =
+ type_param.default_type().map(|it| TypeRef::from_ast(lower_ctx, it));
let param = TypeParamData {
name: Some(name.clone()),
default,
provenance: TypeParamProvenance::TypeParamList,
};
let idx = self.type_or_consts.alloc(param.into());
- let type_ref = TypeRef::Path(name.into());
+ let type_ref = lower_ctx.alloc_type_ref_desugared(TypeRef::Path(name.into()));
self.fill_bounds(
lower_ctx,
type_param.type_bound_list(),
@@ -492,12 +509,10 @@ impl GenericParamsCollector {
}
ast::TypeOrConstParam::Const(const_param) => {
let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
- let ty = const_param
- .ty()
- .map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it));
+ let ty = TypeRef::from_ast_opt(lower_ctx, const_param.ty());
let param = ConstParamData {
name,
- ty: Interned::new(ty),
+ ty,
default: ConstRef::from_const_param(lower_ctx, &const_param),
};
let idx = self.type_or_consts.alloc(param.into());
@@ -557,7 +572,7 @@ impl GenericParamsCollector {
lower_ctx: &LowerCtx<'_>,
bound: ast::TypeBound,
hrtb_lifetimes: Option<&[Name]>,
- target: Either<TypeRef, LifetimeRef>,
+ target: Either<TypeRefId, LifetimeRef>,
) {
let bound = TypeBound::from_ast(lower_ctx, bound);
self.fill_impl_trait_bounds(lower_ctx.take_impl_traits_bounds());
@@ -565,12 +580,12 @@ impl GenericParamsCollector {
(Either::Left(type_ref), bound) => match hrtb_lifetimes {
Some(hrtb_lifetimes) => WherePredicate::ForLifetime {
lifetimes: hrtb_lifetimes.to_vec().into_boxed_slice(),
- target: WherePredicateTypeTarget::TypeRef(Interned::new(type_ref)),
- bound: Interned::new(bound),
+ target: WherePredicateTypeTarget::TypeRef(type_ref),
+ bound,
},
None => WherePredicate::TypeBound {
- target: WherePredicateTypeTarget::TypeRef(Interned::new(type_ref)),
- bound: Interned::new(bound),
+ target: WherePredicateTypeTarget::TypeRef(type_ref),
+ bound,
},
},
(Either::Right(lifetime), TypeBound::Lifetime(bound)) => {
@@ -581,7 +596,7 @@ impl GenericParamsCollector {
self.where_predicates.push(predicate);
}
- fn fill_impl_trait_bounds(&mut self, impl_bounds: Vec<Vec<Interned<TypeBound>>>) {
+ fn fill_impl_trait_bounds(&mut self, impl_bounds: Vec<ThinVec<TypeBound>>) {
for bounds in impl_bounds {
let param = TypeParamData {
name: None,
@@ -589,10 +604,10 @@ impl GenericParamsCollector {
provenance: TypeParamProvenance::ArgumentImplTrait,
};
let param_id = self.type_or_consts.alloc(param.into());
- for bound in bounds {
+ for bound in &bounds {
self.where_predicates.push(WherePredicate::TypeBound {
target: WherePredicateTypeTarget::TypeOrConstParam(param_id),
- bound,
+ bound: bound.clone(),
});
}
}
@@ -601,12 +616,16 @@ impl GenericParamsCollector {
fn fill_implicit_impl_trait_args(
&mut self,
db: &dyn DefDatabase,
+ generics_types_map: &mut TypesMap,
+ generics_types_source_map: &mut TypesSourceMap,
// FIXME: Change this back to `LazyCell` if https://github.com/rust-lang/libs-team/issues/429 is accepted.
exp: &mut Option<(Arc<DefMap>, Expander)>,
exp_fill: &mut dyn FnMut() -> (Arc<DefMap>, Expander),
- type_ref: &TypeRef,
+ type_ref: TypeRefId,
+ types_map: &TypesMap,
+ types_source_map: &TypesSourceMap,
) {
- type_ref.walk(&mut |type_ref| {
+ TypeRef::walk(type_ref, types_map, &mut |type_ref| {
if let TypeRef::ImplTrait(bounds) = type_ref {
let param = TypeParamData {
name: None,
@@ -615,12 +634,20 @@ impl GenericParamsCollector {
};
let param_id = self.type_or_consts.alloc(param.into());
for bound in bounds {
+ let bound = copy_type_bound(
+ bound,
+ types_map,
+ types_source_map,
+ generics_types_map,
+ generics_types_source_map,
+ );
self.where_predicates.push(WherePredicate::TypeBound {
target: WherePredicateTypeTarget::TypeOrConstParam(param_id),
- bound: bound.clone(),
+ bound,
});
}
}
+
if let TypeRef::Macro(mc) = type_ref {
let macro_call = mc.to_node(db.upcast());
let (def_map, expander) = exp.get_or_insert_with(&mut *exp_fill);
@@ -641,23 +668,217 @@ impl GenericParamsCollector {
if let Ok(ExpandResult { value: Some((mark, expanded)), .. }) =
expander.enter_expand(db, macro_call, resolver)
{
- let ctx = expander.ctx(db);
+ let (mut macro_types_map, mut macro_types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let ctx = expander.ctx(db, &mut macro_types_map, &mut macro_types_source_map);
let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
- self.fill_implicit_impl_trait_args(db, &mut *exp, exp_fill, &type_ref);
+ self.fill_implicit_impl_trait_args(
+ db,
+ generics_types_map,
+ generics_types_source_map,
+ &mut *exp,
+ exp_fill,
+ type_ref,
+ &macro_types_map,
+ &macro_types_source_map,
+ );
exp.get_or_insert_with(&mut *exp_fill).1.exit(mark);
}
}
});
}
- pub(crate) fn finish(self) -> GenericParams {
- let Self { mut lifetimes, mut type_or_consts, where_predicates } = self;
+ pub(crate) fn finish(
+ self,
+ mut generics_types_map: TypesMap,
+ generics_types_source_map: &mut TypesSourceMap,
+ ) -> Arc<GenericParams> {
+ let Self { mut lifetimes, mut type_or_consts, mut where_predicates } = self;
+
+ if lifetimes.is_empty() && type_or_consts.is_empty() && where_predicates.is_empty() {
+ static EMPTY: LazyLock<Arc<GenericParams>> = LazyLock::new(|| {
+ Arc::new(GenericParams {
+ lifetimes: Arena::new(),
+ type_or_consts: Arena::new(),
+ where_predicates: Box::default(),
+ types_map: TypesMap::default(),
+ })
+ });
+ return Arc::clone(&EMPTY);
+ }
+
lifetimes.shrink_to_fit();
type_or_consts.shrink_to_fit();
- GenericParams {
+ where_predicates.shrink_to_fit();
+ generics_types_map.shrink_to_fit();
+ generics_types_source_map.shrink_to_fit();
+ Arc::new(GenericParams {
type_or_consts,
lifetimes,
where_predicates: where_predicates.into_boxed_slice(),
+ types_map: generics_types_map,
+ })
+ }
+}
+
+/// Copies a `TypeRef` from a `TypesMap` (accompanied with `TypesSourceMap`) into another `TypesMap`
+/// (and `TypesSourceMap`).
+fn copy_type_ref(
+ type_ref: TypeRefId,
+ from: &TypesMap,
+ from_source_map: &TypesSourceMap,
+ to: &mut TypesMap,
+ to_source_map: &mut TypesSourceMap,
+) -> TypeRefId {
+ let result = match &from[type_ref] {
+ TypeRef::Fn(fn_) => {
+ let params = fn_.params().iter().map(|(name, param_type)| {
+ (name.clone(), copy_type_ref(*param_type, from, from_source_map, to, to_source_map))
+ });
+ TypeRef::Fn(FnType::new(fn_.is_varargs(), fn_.is_unsafe(), fn_.abi().clone(), params))
}
+ TypeRef::Tuple(types) => TypeRef::Tuple(EmptyOptimizedThinVec::from_iter(
+ types.iter().map(|&t| copy_type_ref(t, from, from_source_map, to, to_source_map)),
+ )),
+ &TypeRef::RawPtr(type_ref, mutbl) => TypeRef::RawPtr(
+ copy_type_ref(type_ref, from, from_source_map, to, to_source_map),
+ mutbl,
+ ),
+ TypeRef::Reference(ref_) => TypeRef::Reference(Box::new(RefType {
+ ty: copy_type_ref(ref_.ty, from, from_source_map, to, to_source_map),
+ lifetime: ref_.lifetime.clone(),
+ mutability: ref_.mutability,
+ })),
+ TypeRef::Array(array) => TypeRef::Array(Box::new(ArrayType {
+ ty: copy_type_ref(array.ty, from, from_source_map, to, to_source_map),
+ len: array.len.clone(),
+ })),
+ &TypeRef::Slice(type_ref) => {
+ TypeRef::Slice(copy_type_ref(type_ref, from, from_source_map, to, to_source_map))
+ }
+ TypeRef::ImplTrait(bounds) => TypeRef::ImplTrait(ThinVec::from_iter(copy_type_bounds(
+ bounds,
+ from,
+ from_source_map,
+ to,
+ to_source_map,
+ ))),
+ TypeRef::DynTrait(bounds) => TypeRef::DynTrait(ThinVec::from_iter(copy_type_bounds(
+ bounds,
+ from,
+ from_source_map,
+ to,
+ to_source_map,
+ ))),
+ TypeRef::Path(path) => {
+ TypeRef::Path(copy_path(path, from, from_source_map, to, to_source_map))
+ }
+ TypeRef::Never => TypeRef::Never,
+ TypeRef::Placeholder => TypeRef::Placeholder,
+ TypeRef::Macro(macro_call) => TypeRef::Macro(*macro_call),
+ TypeRef::Error => TypeRef::Error,
+ };
+ let id = to.types.alloc(result);
+ if let Some(&ptr) = from_source_map.types_map_back.get(id) {
+ to_source_map.types_map_back.insert(id, ptr);
+ }
+ id
+}
+
+fn copy_path(
+ path: &Path,
+ from: &TypesMap,
+ from_source_map: &TypesSourceMap,
+ to: &mut TypesMap,
+ to_source_map: &mut TypesSourceMap,
+) -> Path {
+ match path {
+ Path::BarePath(mod_path) => Path::BarePath(mod_path.clone()),
+ Path::Normal(path) => {
+ let type_anchor = path
+ .type_anchor()
+ .map(|type_ref| copy_type_ref(type_ref, from, from_source_map, to, to_source_map));
+ let mod_path = path.mod_path().clone();
+ let generic_args = path.generic_args().iter().map(|generic_args| {
+ copy_generic_args(generic_args, from, from_source_map, to, to_source_map)
+ });
+ Path::Normal(NormalPath::new(type_anchor, mod_path, generic_args))
+ }
+ Path::LangItem(lang_item, name) => Path::LangItem(*lang_item, name.clone()),
+ }
+}
+
+fn copy_generic_args(
+ generic_args: &Option<GenericArgs>,
+ from: &TypesMap,
+ from_source_map: &TypesSourceMap,
+ to: &mut TypesMap,
+ to_source_map: &mut TypesSourceMap,
+) -> Option<GenericArgs> {
+ generic_args.as_ref().map(|generic_args| {
+ let args = generic_args
+ .args
+ .iter()
+ .map(|arg| match arg {
+ &GenericArg::Type(ty) => {
+ GenericArg::Type(copy_type_ref(ty, from, from_source_map, to, to_source_map))
+ }
+ GenericArg::Lifetime(lifetime) => GenericArg::Lifetime(lifetime.clone()),
+ GenericArg::Const(konst) => GenericArg::Const(konst.clone()),
+ })
+ .collect();
+ let bindings = generic_args
+ .bindings
+ .iter()
+ .map(|binding| {
+ let name = binding.name.clone();
+ let args =
+ copy_generic_args(&binding.args, from, from_source_map, to, to_source_map);
+ let type_ref = binding.type_ref.map(|type_ref| {
+ copy_type_ref(type_ref, from, from_source_map, to, to_source_map)
+ });
+ let bounds =
+ copy_type_bounds(&binding.bounds, from, from_source_map, to, to_source_map)
+ .collect();
+ AssociatedTypeBinding { name, args, type_ref, bounds }
+ })
+ .collect();
+ GenericArgs {
+ args,
+ has_self_type: generic_args.has_self_type,
+ bindings,
+ desugared_from_fn: generic_args.desugared_from_fn,
+ }
+ })
+}
+
+fn copy_type_bounds<'a>(
+ bounds: &'a [TypeBound],
+ from: &'a TypesMap,
+ from_source_map: &'a TypesSourceMap,
+ to: &'a mut TypesMap,
+ to_source_map: &'a mut TypesSourceMap,
+) -> impl stdx::thin_vec::TrustedLen<Item = TypeBound> + 'a {
+ bounds.iter().map(|bound| copy_type_bound(bound, from, from_source_map, to, to_source_map))
+}
+
+fn copy_type_bound(
+ bound: &TypeBound,
+ from: &TypesMap,
+ from_source_map: &TypesSourceMap,
+ to: &mut TypesMap,
+ to_source_map: &mut TypesSourceMap,
+) -> TypeBound {
+ match bound {
+ TypeBound::Path(path, modifier) => {
+ TypeBound::Path(copy_path(path, from, from_source_map, to, to_source_map), *modifier)
+ }
+ TypeBound::ForLifetime(lifetimes, path) => TypeBound::ForLifetime(
+ lifetimes.clone(),
+ copy_path(path, from, from_source_map, to, to_source_map),
+ ),
+ TypeBound::Lifetime(lifetime) => TypeBound::Lifetime(lifetime.clone()),
+ TypeBound::Use(use_args) => TypeBound::Use(use_args.clone()),
+ TypeBound::Error => TypeBound::Error,
}
}
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index d9358a2882..8596346943 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -17,16 +17,17 @@ pub mod type_ref;
use std::fmt;
-use hir_expand::name::Name;
-use intern::{Interned, Symbol};
+use hir_expand::{name::Name, MacroDefId};
+use intern::Symbol;
use la_arena::{Idx, RawIdx};
use rustc_apfloat::ieee::{Half as f16, Quad as f128};
use syntax::ast;
+use type_ref::TypeRefId;
use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
path::{GenericArgs, Path},
- type_ref::{Mutability, Rawness, TypeRef},
+ type_ref::{Mutability, Rawness},
BlockId, ConstBlockId,
};
@@ -48,6 +49,22 @@ pub enum ExprOrPatId {
ExprId(ExprId),
PatId(PatId),
}
+
+impl ExprOrPatId {
+ pub fn as_expr(self) -> Option<ExprId> {
+ match self {
+ Self::ExprId(v) => Some(v),
+ _ => None,
+ }
+ }
+
+ pub fn as_pat(self) -> Option<PatId> {
+ match self {
+ Self::PatId(v) => Some(v),
+ _ => None,
+ }
+ }
+}
stdx::impl_from!(ExprId, PatId for ExprOrPatId);
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -204,7 +221,6 @@ pub enum Expr {
Call {
callee: ExprId,
args: Box<[ExprId]>,
- is_assignee_expr: bool,
},
MethodCall {
receiver: ExprId,
@@ -239,8 +255,6 @@ pub enum Expr {
path: Option<Box<Path>>,
fields: Box<[RecordLitField]>,
spread: Option<ExprId>,
- ellipsis: bool,
- is_assignee_expr: bool,
},
Field {
expr: ExprId,
@@ -251,7 +265,7 @@ pub enum Expr {
},
Cast {
expr: ExprId,
- type_ref: Interned<TypeRef>,
+ type_ref: TypeRefId,
},
Ref {
expr: ExprId,
@@ -265,11 +279,17 @@ pub enum Expr {
expr: ExprId,
op: UnaryOp,
},
+ /// `op` cannot be bare `=` (but can be `op=`), these are lowered to `Assignment` instead.
BinaryOp {
lhs: ExprId,
rhs: ExprId,
op: Option<BinaryOp>,
},
+ // Assignments need a special treatment because of destructuring assignment.
+ Assignment {
+ target: PatId,
+ value: ExprId,
+ },
Range {
lhs: Option<ExprId>,
rhs: Option<ExprId>,
@@ -278,19 +298,17 @@ pub enum Expr {
Index {
base: ExprId,
index: ExprId,
- is_assignee_expr: bool,
},
Closure {
args: Box<[PatId]>,
- arg_types: Box<[Option<Interned<TypeRef>>]>,
- ret_type: Option<Interned<TypeRef>>,
+ arg_types: Box<[Option<TypeRefId>]>,
+ ret_type: Option<TypeRefId>,
body: ExprId,
closure_kind: ClosureKind,
capture_by: CaptureBy,
},
Tuple {
exprs: Box<[ExprId]>,
- is_assignee_expr: bool,
},
Array(Array),
Literal(Literal),
@@ -301,7 +319,7 @@ pub enum Expr {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OffsetOf {
- pub container: Interned<TypeRef>,
+ pub container: TypeRefId,
pub fields: Box<[Name]>,
}
@@ -446,7 +464,7 @@ pub enum Movability {
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Array {
- ElementList { elements: Box<[ExprId]>, is_assignee_expr: bool },
+ ElementList { elements: Box<[ExprId]> },
Repeat { initializer: ExprId, repeat: ExprId },
}
@@ -467,7 +485,7 @@ pub struct RecordLitField {
pub enum Statement {
Let {
pat: PatId,
- type_ref: Option<Interned<TypeRef>>,
+ type_ref: Option<TypeRefId>,
initializer: Option<ExprId>,
else_branch: Option<ExprId>,
},
@@ -475,133 +493,13 @@ pub enum Statement {
expr: ExprId,
has_semi: bool,
},
- // At the moment, we only use this to figure out if a return expression
- // is really the last statement of a block. See #16566
- Item,
+ Item(Item),
}
-impl Expr {
- pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
- match self {
- Expr::Missing => {}
- Expr::Path(_) | Expr::OffsetOf(_) => {}
- Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
- AsmOperand::In { expr, .. }
- | AsmOperand::Out { expr: Some(expr), .. }
- | AsmOperand::InOut { expr, .. } => f(*expr),
- AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
- f(*in_expr);
- if let Some(out_expr) = out_expr {
- f(*out_expr);
- }
- }
- AsmOperand::Out { expr: None, .. }
- | AsmOperand::Const(_)
- | AsmOperand::Label(_)
- | AsmOperand::Sym(_) => (),
- }),
- Expr::If { condition, then_branch, else_branch } => {
- f(*condition);
- f(*then_branch);
- if let &Some(else_branch) = else_branch {
- f(else_branch);
- }
- }
- Expr::Let { expr, .. } => {
- f(*expr);
- }
- Expr::Const(_) => (),
- Expr::Block { statements, tail, .. }
- | Expr::Unsafe { statements, tail, .. }
- | Expr::Async { statements, tail, .. } => {
- for stmt in statements.iter() {
- match stmt {
- Statement::Let { initializer, else_branch, .. } => {
- if let &Some(expr) = initializer {
- f(expr);
- }
- if let &Some(expr) = else_branch {
- f(expr);
- }
- }
- Statement::Expr { expr: expression, .. } => f(*expression),
- Statement::Item => (),
- }
- }
- if let &Some(expr) = tail {
- f(expr);
- }
- }
- Expr::Loop { body, .. } => f(*body),
- Expr::Call { callee, args, .. } => {
- f(*callee);
- args.iter().copied().for_each(f);
- }
- Expr::MethodCall { receiver, args, .. } => {
- f(*receiver);
- args.iter().copied().for_each(f);
- }
- Expr::Match { expr, arms } => {
- f(*expr);
- arms.iter().map(|arm| arm.expr).for_each(f);
- }
- Expr::Continue { .. } => {}
- Expr::Break { expr, .. }
- | Expr::Return { expr }
- | Expr::Yield { expr }
- | Expr::Yeet { expr } => {
- if let &Some(expr) = expr {
- f(expr);
- }
- }
- Expr::Become { expr } => f(*expr),
- Expr::RecordLit { fields, spread, .. } => {
- for field in fields.iter() {
- f(field.expr);
- }
- if let &Some(expr) = spread {
- f(expr);
- }
- }
- Expr::Closure { body, .. } => {
- f(*body);
- }
- Expr::BinaryOp { lhs, rhs, .. } => {
- f(*lhs);
- f(*rhs);
- }
- Expr::Range { lhs, rhs, .. } => {
- if let &Some(lhs) = rhs {
- f(lhs);
- }
- if let &Some(rhs) = lhs {
- f(rhs);
- }
- }
- Expr::Index { base, index, .. } => {
- f(*base);
- f(*index);
- }
- Expr::Field { expr, .. }
- | Expr::Await { expr }
- | Expr::Cast { expr, .. }
- | Expr::Ref { expr, .. }
- | Expr::UnaryOp { expr, .. }
- | Expr::Box { expr } => {
- f(*expr);
- }
- Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
- Expr::Array(a) => match a {
- Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
- Array::Repeat { initializer, repeat } => {
- f(*initializer);
- f(*repeat)
- }
- },
- Expr::Literal(_) => {}
- Expr::Underscore => {}
- }
- }
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Item {
+ MacroDef(Box<MacroDefId>),
+ Other,
}
/// Explicit binding annotations given in the HIR for a binding. Note
@@ -665,18 +563,49 @@ pub struct RecordFieldPat {
pub enum Pat {
Missing,
Wild,
- Tuple { args: Box<[PatId]>, ellipsis: Option<u32> },
+ Tuple {
+ args: Box<[PatId]>,
+ ellipsis: Option<u32>,
+ },
Or(Box<[PatId]>),
- Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool },
- Range { start: Option<Box<LiteralOrConst>>, end: Option<Box<LiteralOrConst>> },
- Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
- Path(Box<Path>),
+ Record {
+ path: Option<Box<Path>>,
+ args: Box<[RecordFieldPat]>,
+ ellipsis: bool,
+ },
+ Range {
+ start: Option<Box<LiteralOrConst>>,
+ end: Option<Box<LiteralOrConst>>,
+ },
+ Slice {
+ prefix: Box<[PatId]>,
+ slice: Option<PatId>,
+ suffix: Box<[PatId]>,
+ },
+ /// This might refer to a variable if a single segment path (specifically, on destructuring assignment).
+ Path(Path),
Lit(ExprId),
- Bind { id: BindingId, subpat: Option<PatId> },
- TupleStruct { path: Option<Box<Path>>, args: Box<[PatId]>, ellipsis: Option<u32> },
- Ref { pat: PatId, mutability: Mutability },
- Box { inner: PatId },
+ Bind {
+ id: BindingId,
+ subpat: Option<PatId>,
+ },
+ TupleStruct {
+ path: Option<Box<Path>>,
+ args: Box<[PatId]>,
+ ellipsis: Option<u32>,
+ },
+ Ref {
+ pat: PatId,
+ mutability: Mutability,
+ },
+ Box {
+ inner: PatId,
+ },
ConstBlock(ExprId),
+ /// An expression inside a pattern. That can only occur inside assignments.
+ ///
+ /// E.g. in `(a, *b) = (1, &mut 2)`, `*b` is an expression.
+ Expr(ExprId),
}
impl Pat {
@@ -687,7 +616,8 @@ impl Pat {
| Pat::Path(..)
| Pat::ConstBlock(..)
| Pat::Wild
- | Pat::Missing => {}
+ | Pat::Missing
+ | Pat::Expr(_) => {}
Pat::Bind { subpat, .. } => {
subpat.iter().copied().for_each(f);
}
diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs
index b74cd90f69..2582340c0f 100644
--- a/crates/hir-def/src/hir/type_ref.rs
+++ b/crates/hir-def/src/hir/type_ref.rs
@@ -2,22 +2,27 @@
//! be directly created from an ast::TypeRef, without further queries.
use core::fmt;
-use std::fmt::Write;
+use std::{fmt::Write, ops::Index};
use hir_expand::{
db::ExpandDatabase,
name::{AsName, Name},
- AstId,
+ AstId, InFile,
};
-use intern::{sym, Interned, Symbol};
+use intern::{sym, Symbol};
+use la_arena::{Arena, ArenaMap, Idx};
use span::Edition;
-use syntax::ast::{self, HasGenericArgs, HasName, IsString};
+use stdx::thin_vec::{thin_vec_with_header_struct, EmptyOptimizedThinVec, ThinVec};
+use syntax::{
+ ast::{self, HasGenericArgs, HasName, IsString},
+ AstPtr,
+};
use crate::{
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
hir::Literal,
lower::LowerCtx,
- path::Path,
+ path::{GenericArg, Path},
};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@@ -104,35 +109,90 @@ impl TraitRef {
}
}
+thin_vec_with_header_struct! {
+ pub new(pub(crate)) struct FnType, FnTypeHeader {
+ pub params: [(Option<Name>, TypeRefId)],
+ pub is_varargs: bool,
+ pub is_unsafe: bool,
+ pub abi: Option<Symbol>; ref,
+ }
+}
+
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+pub struct ArrayType {
+ pub ty: TypeRefId,
+ // FIXME: This should be Ast<ConstArg>
+ pub len: ConstRef,
+}
+
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+pub struct RefType {
+ pub ty: TypeRefId,
+ pub lifetime: Option<LifetimeRef>,
+ pub mutability: Mutability,
+}
+
/// Compare ty::Ty
-///
-/// Note: Most users of `TypeRef` that end up in the salsa database intern it using
-/// `Interned<TypeRef>` to save space. But notably, nested `TypeRef`s are not interned, since that
-/// does not seem to save any noticeable amount of memory.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TypeRef {
Never,
Placeholder,
- Tuple(Vec<TypeRef>),
+ Tuple(EmptyOptimizedThinVec<TypeRefId>),
Path(Path),
- RawPtr(Box<TypeRef>, Mutability),
- Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
- // FIXME: This should be Array(Box<TypeRef>, Ast<ConstArg>),
- Array(Box<TypeRef>, ConstRef),
- Slice(Box<TypeRef>),
+ RawPtr(TypeRefId, Mutability),
+ Reference(Box<RefType>),
+ Array(Box<ArrayType>),
+ Slice(TypeRefId),
/// A fn pointer. Last element of the vector is the return type.
- Fn(
- Box<[(Option<Name>, TypeRef)]>,
- bool, /*varargs*/
- bool, /*is_unsafe*/
- Option<Symbol>, /* abi */
- ),
- ImplTrait(Vec<Interned<TypeBound>>),
- DynTrait(Vec<Interned<TypeBound>>),
+ Fn(FnType),
+ ImplTrait(ThinVec<TypeBound>),
+ DynTrait(ThinVec<TypeBound>),
Macro(AstId<ast::MacroCall>),
Error,
}
+#[cfg(target_arch = "x86_64")]
+const _: () = assert!(size_of::<TypeRef>() == 16);
+
+pub type TypeRefId = Idx<TypeRef>;
+
+#[derive(Default, Clone, PartialEq, Eq, Debug, Hash)]
+pub struct TypesMap {
+ pub(crate) types: Arena<TypeRef>,
+}
+
+impl TypesMap {
+ pub const EMPTY: &TypesMap = &TypesMap { types: Arena::new() };
+
+ pub(crate) fn shrink_to_fit(&mut self) {
+ let TypesMap { types } = self;
+ types.shrink_to_fit();
+ }
+}
+
+impl Index<TypeRefId> for TypesMap {
+ type Output = TypeRef;
+
+ fn index(&self, index: TypeRefId) -> &Self::Output {
+ &self.types[index]
+ }
+}
+
+pub type TypePtr = AstPtr<ast::Type>;
+pub type TypeSource = InFile<TypePtr>;
+
+#[derive(Default, Clone, PartialEq, Eq, Debug, Hash)]
+pub struct TypesSourceMap {
+ pub(crate) types_map_back: ArenaMap<TypeRefId, TypeSource>,
+}
+
+impl TypesSourceMap {
+ pub(crate) fn shrink_to_fit(&mut self) {
+ let TypesSourceMap { types_map_back } = self;
+ types_map_back.shrink_to_fit();
+ }
+}
+
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct LifetimeRef {
pub name: Name,
@@ -157,12 +217,22 @@ pub enum TypeBound {
Path(Path, TraitBoundModifier),
ForLifetime(Box<[Name]>, Path),
Lifetime(LifetimeRef),
+ Use(Box<[UseArgRef]>),
Error,
}
+#[cfg(target_pointer_width = "64")]
+const _: [(); 32] = [(); ::std::mem::size_of::<TypeBound>()];
+
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+pub enum UseArgRef {
+ Name(Name),
+ Lifetime(LifetimeRef),
+}
+
/// A modifier on a bound, currently this is only used for `?Sized`, where the
/// modifier is `Maybe`.
-#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum TraitBoundModifier {
None,
Maybe,
@@ -170,12 +240,12 @@ pub enum TraitBoundModifier {
impl TypeRef {
/// Converts an `ast::TypeRef` to a `hir::TypeRef`.
- pub fn from_ast(ctx: &LowerCtx<'_>, node: ast::Type) -> Self {
- match node {
- ast::Type::ParenType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
- ast::Type::TupleType(inner) => {
- TypeRef::Tuple(inner.fields().map(|it| TypeRef::from_ast(ctx, it)).collect())
- }
+ pub fn from_ast(ctx: &LowerCtx<'_>, node: ast::Type) -> TypeRefId {
+ let ty = match &node {
+ ast::Type::ParenType(inner) => return TypeRef::from_ast_opt(ctx, inner.ty()),
+ ast::Type::TupleType(inner) => TypeRef::Tuple(EmptyOptimizedThinVec::from_iter(
+ Vec::from_iter(inner.fields().map(|it| TypeRef::from_ast(ctx, it))),
+ )),
ast::Type::NeverType(..) => TypeRef::Never,
ast::Type::PathType(inner) => {
// FIXME: Use `Path::from_src`
@@ -188,20 +258,21 @@ impl TypeRef {
ast::Type::PtrType(inner) => {
let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
let mutability = Mutability::from_mutable(inner.mut_token().is_some());
- TypeRef::RawPtr(Box::new(inner_ty), mutability)
+ TypeRef::RawPtr(inner_ty, mutability)
}
ast::Type::ArrayType(inner) => {
let len = ConstRef::from_const_arg(ctx, inner.const_arg());
- TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
- }
- ast::Type::SliceType(inner) => {
- TypeRef::Slice(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())))
+ TypeRef::Array(Box::new(ArrayType {
+ ty: TypeRef::from_ast_opt(ctx, inner.ty()),
+ len,
+ }))
}
+ ast::Type::SliceType(inner) => TypeRef::Slice(TypeRef::from_ast_opt(ctx, inner.ty())),
ast::Type::RefType(inner) => {
let inner_ty = TypeRef::from_ast_opt(ctx, inner.ty());
let lifetime = inner.lifetime().map(|lt| LifetimeRef::new(&lt));
let mutability = Mutability::from_mutable(inner.mut_token().is_some());
- TypeRef::Reference(Box::new(inner_ty), lifetime, mutability)
+ TypeRef::Reference(Box::new(RefType { ty: inner_ty, lifetime, mutability }))
}
ast::Type::InferType(_inner) => TypeRef::Placeholder,
ast::Type::FnPtrType(inner) => {
@@ -209,7 +280,7 @@ impl TypeRef {
.ret_type()
.and_then(|rt| rt.ty())
.map(|it| TypeRef::from_ast(ctx, it))
- .unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
+ .unwrap_or_else(|| ctx.alloc_type_ref_desugared(TypeRef::unit()));
let mut is_varargs = false;
let mut params = if let Some(pl) = inner.param_list() {
if let Some(param) = pl.params().last() {
@@ -241,10 +312,10 @@ impl TypeRef {
let abi = inner.abi().map(lower_abi);
params.push((None, ret_ty));
- TypeRef::Fn(params.into(), is_varargs, inner.unsafe_token().is_some(), abi)
+ TypeRef::Fn(FnType::new(is_varargs, inner.unsafe_token().is_some(), abi, params))
}
// for types are close enough for our purposes to the inner type for now...
- ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()),
+ ast::Type::ForType(inner) => return TypeRef::from_ast_opt(ctx, inner.ty()),
ast::Type::ImplTraitType(inner) => {
if ctx.outer_impl_trait() {
// Disallow nested impl traits
@@ -261,74 +332,74 @@ impl TypeRef {
Some(mc) => TypeRef::Macro(ctx.ast_id(&mc)),
None => TypeRef::Error,
},
- }
+ };
+ ctx.alloc_type_ref(ty, AstPtr::new(&node))
}
- pub(crate) fn from_ast_opt(ctx: &LowerCtx<'_>, node: Option<ast::Type>) -> Self {
+ pub(crate) fn from_ast_opt(ctx: &LowerCtx<'_>, node: Option<ast::Type>) -> TypeRefId {
match node {
Some(node) => TypeRef::from_ast(ctx, node),
- None => TypeRef::Error,
+ None => ctx.alloc_error_type(),
}
}
pub(crate) fn unit() -> TypeRef {
- TypeRef::Tuple(Vec::new())
+ TypeRef::Tuple(EmptyOptimizedThinVec::empty())
}
- pub fn walk(&self, f: &mut impl FnMut(&TypeRef)) {
- go(self, f);
+ pub fn walk(this: TypeRefId, map: &TypesMap, f: &mut impl FnMut(&TypeRef)) {
+ go(this, f, map);
- fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
+ fn go(type_ref: TypeRefId, f: &mut impl FnMut(&TypeRef), map: &TypesMap) {
+ let type_ref = &map[type_ref];
f(type_ref);
match type_ref {
- TypeRef::Fn(params, _, _, _) => {
- params.iter().for_each(|(_, param_type)| go(param_type, f))
+ TypeRef::Fn(fn_) => {
+ fn_.params().iter().for_each(|&(_, param_type)| go(param_type, f, map))
}
- TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)),
- TypeRef::RawPtr(type_ref, _)
- | TypeRef::Reference(type_ref, ..)
- | TypeRef::Array(type_ref, _)
- | TypeRef::Slice(type_ref) => go(type_ref, f),
+ TypeRef::Tuple(types) => types.iter().for_each(|&t| go(t, f, map)),
+ TypeRef::RawPtr(type_ref, _) | TypeRef::Slice(type_ref) => go(*type_ref, f, map),
+ TypeRef::Reference(it) => go(it.ty, f, map),
+ TypeRef::Array(it) => go(it.ty, f, map),
TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
for bound in bounds {
- match bound.as_ref() {
+ match bound {
TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
- go_path(path, f)
+ go_path(path, f, map)
}
- TypeBound::Lifetime(_) | TypeBound::Error => (),
+ TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (),
}
}
}
- TypeRef::Path(path) => go_path(path, f),
+ TypeRef::Path(path) => go_path(path, f, map),
TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
};
}
- fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef)) {
+ fn go_path(path: &Path, f: &mut impl FnMut(&TypeRef), map: &TypesMap) {
if let Some(type_ref) = path.type_anchor() {
- go(type_ref, f);
+ go(type_ref, f, map);
}
for segment in path.segments().iter() {
if let Some(args_and_bindings) = segment.args_and_bindings {
for arg in args_and_bindings.args.iter() {
match arg {
- crate::path::GenericArg::Type(type_ref) => {
- go(type_ref, f);
+ GenericArg::Type(type_ref) => {
+ go(*type_ref, f, map);
}
- crate::path::GenericArg::Const(_)
- | crate::path::GenericArg::Lifetime(_) => {}
+ GenericArg::Const(_) | GenericArg::Lifetime(_) => {}
}
}
for binding in args_and_bindings.bindings.iter() {
- if let Some(type_ref) = &binding.type_ref {
- go(type_ref, f);
+ if let Some(type_ref) = binding.type_ref {
+ go(type_ref, f, map);
}
for bound in binding.bounds.iter() {
- match bound.as_ref() {
+ match bound {
TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
- go_path(path, f)
+ go_path(path, f, map)
}
- TypeBound::Lifetime(_) | TypeBound::Error => (),
+ TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (),
}
}
}
@@ -341,11 +412,13 @@ impl TypeRef {
pub(crate) fn type_bounds_from_ast(
lower_ctx: &LowerCtx<'_>,
type_bounds_opt: Option<ast::TypeBoundList>,
-) -> Vec<Interned<TypeBound>> {
+) -> ThinVec<TypeBound> {
if let Some(type_bounds) = type_bounds_opt {
- type_bounds.bounds().map(|it| Interned::new(TypeBound::from_ast(lower_ctx, it))).collect()
+ ThinVec::from_iter(Vec::from_iter(
+ type_bounds.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)),
+ ))
} else {
- vec![]
+ ThinVec::from_iter([])
}
}
@@ -380,7 +453,16 @@ impl TypeBound {
None => TypeBound::Error,
}
}
- ast::TypeBoundKind::Use(_) => TypeBound::Error,
+ ast::TypeBoundKind::Use(gal) => TypeBound::Use(
+ gal.use_bound_generic_args()
+ .map(|p| match p {
+ ast::UseBoundGenericArg::Lifetime(l) => {
+ UseArgRef::Lifetime(LifetimeRef::new(&l))
+ }
+ ast::UseBoundGenericArg::NameRef(n) => UseArgRef::Name(n.as_name()),
+ })
+ .collect(),
+ ),
ast::TypeBoundKind::Lifetime(lifetime) => {
TypeBound::Lifetime(LifetimeRef::new(&lifetime))
}
@@ -391,7 +473,7 @@ impl TypeBound {
match self {
TypeBound::Path(p, m) => Some((p, m)),
TypeBound::ForLifetime(_, p) => Some((p, &TraitBoundModifier::None)),
- TypeBound::Lifetime(_) | TypeBound::Error => None,
+ TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => None,
}
}
}
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 7cb833fdce..b5bf2feb82 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -61,7 +61,7 @@ use crate::{
db::DefDatabase,
generics::GenericParams,
path::{GenericArgs, ImportAlias, ModPath, Path, PathKind},
- type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
+ type_ref::{Mutability, TraitRef, TypeBound, TypeRefId, TypesMap, TypesSourceMap},
visibility::{RawVisibility, VisibilityExplicitness},
BlockId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup,
};
@@ -100,14 +100,20 @@ pub struct ItemTree {
impl ItemTree {
pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
- let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered();
- static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
+ db.file_item_tree_with_source_map(file_id).0
+ }
- let syntax = db.parse_or_expand(file_id);
+ pub(crate) fn file_item_tree_with_source_map_query(
+ db: &dyn DefDatabase,
+ file_id: HirFileId,
+ ) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>) {
+ let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered();
+ static EMPTY: OnceLock<(Arc<ItemTree>, Arc<ItemTreeSourceMaps>)> = OnceLock::new();
let ctx = lower::Ctx::new(db, file_id);
+ let syntax = db.parse_or_expand(file_id);
let mut top_attrs = None;
- let mut item_tree = match_ast! {
+ let (mut item_tree, source_maps) = match_ast! {
match syntax {
ast::SourceFile(file) => {
top_attrs = Some(RawAttrs::new(db.upcast(), &file, ctx.span_map()));
@@ -137,42 +143,55 @@ impl ItemTree {
{
EMPTY
.get_or_init(|| {
- Arc::new(ItemTree {
- top_level: SmallVec::new_const(),
- attrs: FxHashMap::default(),
- data: None,
- })
+ (
+ Arc::new(ItemTree {
+ top_level: SmallVec::new_const(),
+ attrs: FxHashMap::default(),
+ data: None,
+ }),
+ Arc::default(),
+ )
})
.clone()
} else {
item_tree.shrink_to_fit();
- Arc::new(item_tree)
+ (Arc::new(item_tree), Arc::new(source_maps))
}
}
pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
+ db.block_item_tree_with_source_map(block).0
+ }
+
+ pub(crate) fn block_item_tree_with_source_map_query(
+ db: &dyn DefDatabase,
+ block: BlockId,
+ ) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>) {
let _p = tracing::info_span!("block_item_tree_query", ?block).entered();
- static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
+ static EMPTY: OnceLock<(Arc<ItemTree>, Arc<ItemTreeSourceMaps>)> = OnceLock::new();
let loc = block.lookup(db);
let block = loc.ast_id.to_node(db.upcast());
let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
- let mut item_tree = ctx.lower_block(&block);
+ let (mut item_tree, source_maps) = ctx.lower_block(&block);
if item_tree.data.is_none() && item_tree.top_level.is_empty() && item_tree.attrs.is_empty()
{
EMPTY
.get_or_init(|| {
- Arc::new(ItemTree {
- top_level: SmallVec::new_const(),
- attrs: FxHashMap::default(),
- data: None,
- })
+ (
+ Arc::new(ItemTree {
+ top_level: SmallVec::new_const(),
+ attrs: FxHashMap::default(),
+ data: None,
+ }),
+ Arc::default(),
+ )
})
.clone()
} else {
item_tree.shrink_to_fit();
- Arc::new(item_tree)
+ (Arc::new(item_tree), Arc::new(source_maps))
}
}
@@ -309,6 +328,160 @@ struct ItemTreeData {
vis: ItemVisibilities,
}
+#[derive(Default, Debug, Eq, PartialEq)]
+pub struct ItemTreeSourceMaps {
+ all_concatenated: Box<[TypesSourceMap]>,
+ structs_offset: u32,
+ unions_offset: u32,
+ enum_generics_offset: u32,
+ variants_offset: u32,
+ consts_offset: u32,
+ statics_offset: u32,
+ trait_generics_offset: u32,
+ trait_alias_generics_offset: u32,
+ impls_offset: u32,
+ type_aliases_offset: u32,
+}
+
+#[derive(Clone, Copy)]
+pub struct GenericItemSourceMap<'a>(&'a [TypesSourceMap; 2]);
+
+impl<'a> GenericItemSourceMap<'a> {
+ #[inline]
+ pub fn item(self) -> &'a TypesSourceMap {
+ &self.0[0]
+ }
+
+ #[inline]
+ pub fn generics(self) -> &'a TypesSourceMap {
+ &self.0[1]
+ }
+}
+
+#[derive(Default, Debug, Eq, PartialEq)]
+pub struct GenericItemSourceMapBuilder {
+ pub item: TypesSourceMap,
+ pub generics: TypesSourceMap,
+}
+
+#[derive(Default, Debug, Eq, PartialEq)]
+struct ItemTreeSourceMapsBuilder {
+ functions: Vec<GenericItemSourceMapBuilder>,
+ structs: Vec<GenericItemSourceMapBuilder>,
+ unions: Vec<GenericItemSourceMapBuilder>,
+ enum_generics: Vec<TypesSourceMap>,
+ variants: Vec<TypesSourceMap>,
+ consts: Vec<TypesSourceMap>,
+ statics: Vec<TypesSourceMap>,
+ trait_generics: Vec<TypesSourceMap>,
+ trait_alias_generics: Vec<TypesSourceMap>,
+ impls: Vec<GenericItemSourceMapBuilder>,
+ type_aliases: Vec<GenericItemSourceMapBuilder>,
+}
+
+impl ItemTreeSourceMapsBuilder {
+ fn build(self) -> ItemTreeSourceMaps {
+ let ItemTreeSourceMapsBuilder {
+ functions,
+ structs,
+ unions,
+ enum_generics,
+ variants,
+ consts,
+ statics,
+ trait_generics,
+ trait_alias_generics,
+ impls,
+ type_aliases,
+ } = self;
+ let structs_offset = functions.len() as u32 * 2;
+ let unions_offset = structs_offset + (structs.len() as u32 * 2);
+ let enum_generics_offset = unions_offset + (unions.len() as u32 * 2);
+ let variants_offset = enum_generics_offset + (enum_generics.len() as u32);
+ let consts_offset = variants_offset + (variants.len() as u32);
+ let statics_offset = consts_offset + (consts.len() as u32);
+ let trait_generics_offset = statics_offset + (statics.len() as u32);
+ let trait_alias_generics_offset = trait_generics_offset + (trait_generics.len() as u32);
+ let impls_offset = trait_alias_generics_offset + (trait_alias_generics.len() as u32);
+ let type_aliases_offset = impls_offset + (impls.len() as u32 * 2);
+ let all_concatenated = generics_concat(functions)
+ .chain(generics_concat(structs))
+ .chain(generics_concat(unions))
+ .chain(enum_generics)
+ .chain(variants)
+ .chain(consts)
+ .chain(statics)
+ .chain(trait_generics)
+ .chain(trait_alias_generics)
+ .chain(generics_concat(impls))
+ .chain(generics_concat(type_aliases))
+ .collect();
+ return ItemTreeSourceMaps {
+ all_concatenated,
+ structs_offset,
+ unions_offset,
+ enum_generics_offset,
+ variants_offset,
+ consts_offset,
+ statics_offset,
+ trait_generics_offset,
+ trait_alias_generics_offset,
+ impls_offset,
+ type_aliases_offset,
+ };
+
+ fn generics_concat(
+ source_maps: Vec<GenericItemSourceMapBuilder>,
+ ) -> impl Iterator<Item = TypesSourceMap> {
+ source_maps.into_iter().flat_map(|it| [it.item, it.generics])
+ }
+ }
+}
+
+impl ItemTreeSourceMaps {
+ #[inline]
+ fn generic_item(&self, offset: u32, index: u32) -> GenericItemSourceMap<'_> {
+ GenericItemSourceMap(
+ self.all_concatenated[(offset + (index * 2)) as usize..][..2].try_into().unwrap(),
+ )
+ }
+
+ #[inline]
+ fn non_generic_item(&self, offset: u32, index: u32) -> &TypesSourceMap {
+ &self.all_concatenated[(offset + index) as usize]
+ }
+
+ #[inline]
+ pub fn function(&self, index: FileItemTreeId<Function>) -> GenericItemSourceMap<'_> {
+ self.generic_item(0, index.0.into_raw().into_u32())
+ }
+}
+
+macro_rules! index_item_source_maps {
+ ( $( $name:ident; $field:ident[$tree_id:ident]; $fn:ident; $ret:ty, )* ) => {
+ impl ItemTreeSourceMaps {
+ $(
+ #[inline]
+ pub fn $name(&self, index: FileItemTreeId<$tree_id>) -> $ret {
+ self.$fn(self.$field, index.0.into_raw().into_u32())
+ }
+ )*
+ }
+ };
+}
+index_item_source_maps! {
+ strukt; structs_offset[Struct]; generic_item; GenericItemSourceMap<'_>,
+ union; unions_offset[Union]; generic_item; GenericItemSourceMap<'_>,
+ enum_generic; enum_generics_offset[Enum]; non_generic_item; &TypesSourceMap,
+ variant; variants_offset[Variant]; non_generic_item; &TypesSourceMap,
+ konst; consts_offset[Const]; non_generic_item; &TypesSourceMap,
+ statik; statics_offset[Static]; non_generic_item; &TypesSourceMap,
+ trait_generic; trait_generics_offset[Trait]; non_generic_item; &TypesSourceMap,
+ trait_alias_generic; trait_alias_generics_offset[TraitAlias]; non_generic_item; &TypesSourceMap,
+ impl_; impls_offset[Impl]; generic_item; GenericItemSourceMap<'_>,
+ type_alias; type_aliases_offset[TypeAlias]; generic_item; GenericItemSourceMap<'_>,
+}
+
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum AttrOwner {
/// Attributes on an item.
@@ -364,7 +537,7 @@ pub trait ItemTreeNode: Clone {
fn attr_owner(id: FileItemTreeId<Self>) -> AttrOwner;
}
pub trait GenericsItemTreeNode: ItemTreeNode {
- fn generic_params(&self) -> &Interned<GenericParams>;
+ fn generic_params(&self) -> &Arc<GenericParams>;
}
pub struct FileItemTreeId<N>(Idx<N>);
@@ -429,6 +602,16 @@ impl TreeId {
}
}
+ pub fn item_tree_with_source_map(
+ &self,
+ db: &dyn DefDatabase,
+ ) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>) {
+ match self.block {
+ Some(block) => db.block_item_tree_with_source_map(block),
+ None => db.file_item_tree_with_source_map(self.file),
+ }
+ }
+
pub fn file_id(self) -> HirFileId {
self.file
}
@@ -461,6 +644,13 @@ impl<N> ItemTreeId<N> {
self.tree.item_tree(db)
}
+ pub fn item_tree_with_source_map(
+ self,
+ db: &dyn DefDatabase,
+ ) -> (Arc<ItemTree>, Arc<ItemTreeSourceMaps>) {
+ self.tree.item_tree_with_source_map(db)
+ }
+
pub fn resolved<R>(self, db: &dyn DefDatabase, cb: impl FnOnce(&N) -> R) -> R
where
ItemTree: Index<FileItemTreeId<N>, Output = N>,
@@ -593,7 +783,7 @@ macro_rules! mod_items {
$(
impl GenericsItemTreeNode for $typ {
- fn generic_params(&self) -> &Interned<GenericParams> {
+ fn generic_params(&self) -> &Arc<GenericParams> {
&self.$generic_params
}
}
@@ -731,17 +921,18 @@ pub struct ExternBlock {
pub struct Function {
pub name: Name,
pub visibility: RawVisibilityId,
- pub explicit_generic_params: Interned<GenericParams>,
+ pub explicit_generic_params: Arc<GenericParams>,
pub abi: Option<Symbol>,
pub params: Box<[Param]>,
- pub ret_type: Interned<TypeRef>,
+ pub ret_type: TypeRefId,
pub ast_id: FileAstId<ast::Fn>,
+ pub types_map: Arc<TypesMap>,
pub(crate) flags: FnFlags,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Param {
- pub type_ref: Option<Interned<TypeRef>>,
+ pub type_ref: Option<TypeRefId>,
}
bitflags::bitflags! {
@@ -762,26 +953,28 @@ bitflags::bitflags! {
pub struct Struct {
pub name: Name,
pub visibility: RawVisibilityId,
- pub generic_params: Interned<GenericParams>,
+ pub generic_params: Arc<GenericParams>,
pub fields: Box<[Field]>,
pub shape: FieldsShape,
pub ast_id: FileAstId<ast::Struct>,
+ pub types_map: Arc<TypesMap>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Union {
pub name: Name,
pub visibility: RawVisibilityId,
- pub generic_params: Interned<GenericParams>,
+ pub generic_params: Arc<GenericParams>,
pub fields: Box<[Field]>,
pub ast_id: FileAstId<ast::Union>,
+ pub types_map: Arc<TypesMap>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Enum {
pub name: Name,
pub visibility: RawVisibilityId,
- pub generic_params: Interned<GenericParams>,
+ pub generic_params: Arc<GenericParams>,
pub variants: Range<FileItemTreeId<Variant>>,
pub ast_id: FileAstId<ast::Enum>,
}
@@ -792,6 +985,7 @@ pub struct Variant {
pub fields: Box<[Field]>,
pub shape: FieldsShape,
pub ast_id: FileAstId<ast::Variant>,
+ pub types_map: Arc<TypesMap>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -805,7 +999,7 @@ pub enum FieldsShape {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Field {
pub name: Name,
- pub type_ref: Interned<TypeRef>,
+ pub type_ref: TypeRefId,
pub visibility: RawVisibilityId,
}
@@ -814,9 +1008,10 @@ pub struct Const {
/// `None` for `const _: () = ();`
pub name: Option<Name>,
pub visibility: RawVisibilityId,
- pub type_ref: Interned<TypeRef>,
+ pub type_ref: TypeRefId,
pub ast_id: FileAstId<ast::Const>,
pub has_body: bool,
+ pub types_map: Arc<TypesMap>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -827,15 +1022,16 @@ pub struct Static {
pub mutable: bool,
pub has_safe_kw: bool,
pub has_unsafe_kw: bool,
- pub type_ref: Interned<TypeRef>,
+ pub type_ref: TypeRefId,
pub ast_id: FileAstId<ast::Static>,
+ pub types_map: Arc<TypesMap>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Trait {
pub name: Name,
pub visibility: RawVisibilityId,
- pub generic_params: Interned<GenericParams>,
+ pub generic_params: Arc<GenericParams>,
pub is_auto: bool,
pub is_unsafe: bool,
pub items: Box<[AssocItem]>,
@@ -846,19 +1042,20 @@ pub struct Trait {
pub struct TraitAlias {
pub name: Name,
pub visibility: RawVisibilityId,
- pub generic_params: Interned<GenericParams>,
+ pub generic_params: Arc<GenericParams>,
pub ast_id: FileAstId<ast::TraitAlias>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Impl {
- pub generic_params: Interned<GenericParams>,
- pub target_trait: Option<Interned<TraitRef>>,
- pub self_ty: Interned<TypeRef>,
+ pub generic_params: Arc<GenericParams>,
+ pub target_trait: Option<TraitRef>,
+ pub self_ty: TypeRefId,
pub is_negative: bool,
pub is_unsafe: bool,
pub items: Box<[AssocItem]>,
pub ast_id: FileAstId<ast::Impl>,
+ pub types_map: Arc<TypesMap>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -866,10 +1063,11 @@ pub struct TypeAlias {
pub name: Name,
pub visibility: RawVisibilityId,
/// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`.
- pub bounds: Box<[Interned<TypeBound>]>,
- pub generic_params: Interned<GenericParams>,
- pub type_ref: Option<Interned<TypeRef>>,
+ pub bounds: Box<[TypeBound]>,
+ pub generic_params: Arc<GenericParams>,
+ pub type_ref: Option<TypeRefId>,
pub ast_id: FileAstId<ast::TypeAlias>,
+ pub types_map: Arc<TypesMap>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -968,6 +1166,11 @@ impl UseTree {
self.expand_impl(None, &mut cb)
}
+ /// The [`UseTreeKind`] of this `UseTree`.
+ pub fn kind(&self) -> &UseTreeKind {
+ &self.kind
+ }
+
fn expand_impl(
&self,
prefix: Option<ModPath>,
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index 431a7f66f4..bd17fce37b 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -1,12 +1,18 @@
//! AST -> `ItemTree` lowering code.
-use std::collections::hash_map::Entry;
+use std::{cell::OnceCell, collections::hash_map::Entry};
-use hir_expand::{mod_path::path, name::AsName, span_map::SpanMapRef, HirFileId};
+use hir_expand::{
+ mod_path::path,
+ name::AsName,
+ span_map::{SpanMap, SpanMapRef},
+ HirFileId,
+};
use intern::{sym, Symbol};
use la_arena::Arena;
use rustc_hash::FxHashMap;
use span::{AstIdMap, SyntaxContextId};
+use stdx::thin_vec::ThinVec;
use syntax::{
ast::{self, HasModuleItem, HasName, HasTypeBounds, IsString},
AstNode,
@@ -18,14 +24,19 @@ use crate::{
generics::{GenericParams, GenericParamsCollector, TypeParamData, TypeParamProvenance},
item_tree::{
AssocItem, AttrOwner, Const, Either, Enum, ExternBlock, ExternCrate, Field, FieldParent,
- FieldsShape, FileItemTreeId, FnFlags, Function, GenericArgs, GenericModItem, Idx, Impl,
- ImportAlias, Interned, ItemTree, ItemTreeData, Macro2, MacroCall, MacroRules, Mod, ModItem,
+ FieldsShape, FileItemTreeId, FnFlags, Function, GenericArgs, GenericItemSourceMapBuilder,
+ GenericModItem, Idx, Impl, ImportAlias, Interned, ItemTree, ItemTreeData,
+ ItemTreeSourceMaps, ItemTreeSourceMapsBuilder, Macro2, MacroCall, MacroRules, Mod, ModItem,
ModKind, ModPath, Mutability, Name, Param, Path, Range, RawAttrs, RawIdx, RawVisibilityId,
Static, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind,
Variant,
},
+ lower::LowerCtx,
path::AssociatedTypeBinding,
- type_ref::{LifetimeRef, TraitBoundModifier, TraitRef, TypeBound, TypeRef},
+ type_ref::{
+ LifetimeRef, RefType, TraitBoundModifier, TraitRef, TypeBound, TypeRef, TypeRefId,
+ TypesMap, TypesSourceMap,
+ },
visibility::RawVisibility,
LocalLifetimeParamId, LocalTypeOrConstParamId,
};
@@ -40,7 +51,9 @@ pub(super) struct Ctx<'a> {
source_ast_id_map: Arc<AstIdMap>,
generic_param_attr_buffer:
FxHashMap<Either<LocalTypeOrConstParamId, LocalLifetimeParamId>, RawAttrs>,
- body_ctx: crate::lower::LowerCtx<'a>,
+ span_map: OnceCell<SpanMap>,
+ file: HirFileId,
+ source_maps: ItemTreeSourceMapsBuilder,
}
impl<'a> Ctx<'a> {
@@ -50,22 +63,49 @@ impl<'a> Ctx<'a> {
tree: ItemTree::default(),
generic_param_attr_buffer: FxHashMap::default(),
source_ast_id_map: db.ast_id_map(file),
- body_ctx: crate::lower::LowerCtx::new(db, file),
+ file,
+ span_map: OnceCell::new(),
+ source_maps: ItemTreeSourceMapsBuilder::default(),
}
}
pub(super) fn span_map(&self) -> SpanMapRef<'_> {
- self.body_ctx.span_map()
+ self.span_map.get_or_init(|| self.db.span_map(self.file)).as_ref()
+ }
+
+ fn body_ctx<'b, 'c>(
+ &self,
+ types_map: &'b mut TypesMap,
+ types_source_map: &'b mut TypesSourceMap,
+ ) -> LowerCtx<'c>
+ where
+ 'a: 'c,
+ 'b: 'c,
+ {
+ // FIXME: This seems a bit wasteful that if `LowerCtx` will initialize the span map we won't benefit.
+ LowerCtx::with_span_map_cell(
+ self.db,
+ self.file,
+ self.span_map.clone(),
+ types_map,
+ types_source_map,
+ )
}
- pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree {
+ pub(super) fn lower_module_items(
+ mut self,
+ item_owner: &dyn HasModuleItem,
+ ) -> (ItemTree, ItemTreeSourceMaps) {
self.tree.top_level =
item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect();
assert!(self.generic_param_attr_buffer.is_empty());
- self.tree
+ (self.tree, self.source_maps.build())
}
- pub(super) fn lower_macro_stmts(mut self, stmts: ast::MacroStmts) -> ItemTree {
+ pub(super) fn lower_macro_stmts(
+ mut self,
+ stmts: ast::MacroStmts,
+ ) -> (ItemTree, ItemTreeSourceMaps) {
self.tree.top_level = stmts
.statements()
.filter_map(|stmt| {
@@ -96,10 +136,10 @@ impl<'a> Ctx<'a> {
}
assert!(self.generic_param_attr_buffer.is_empty());
- self.tree
+ (self.tree, self.source_maps.build())
}
- pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree {
+ pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> (ItemTree, ItemTreeSourceMaps) {
self.tree
.attrs
.insert(AttrOwner::TopLevel, RawAttrs::new(self.db.upcast(), block, self.span_map()));
@@ -125,7 +165,7 @@ impl<'a> Ctx<'a> {
}
assert!(self.generic_param_attr_buffer.is_empty());
- self.tree
+ (self.tree, self.source_maps.build())
}
fn data(&mut self) -> &mut ItemTreeData {
@@ -144,7 +184,7 @@ impl<'a> Ctx<'a> {
ast::Item::Module(ast) => self.lower_module(ast)?.into(),
ast::Item::Trait(ast) => self.lower_trait(ast)?.into(),
ast::Item::TraitAlias(ast) => self.lower_trait_alias(ast)?.into(),
- ast::Item::Impl(ast) => self.lower_impl(ast)?.into(),
+ ast::Item::Impl(ast) => self.lower_impl(ast).into(),
ast::Item::Use(ast) => self.lower_use(ast)?.into(),
ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?.into(),
ast::Item::MacroCall(ast) => self.lower_macro_call(ast)?.into(),
@@ -159,12 +199,14 @@ impl<'a> Ctx<'a> {
}
fn add_attrs(&mut self, item: AttrOwner, attrs: RawAttrs) {
- match self.tree.attrs.entry(item) {
- Entry::Occupied(mut entry) => {
- *entry.get_mut() = entry.get().merge(attrs);
- }
- Entry::Vacant(entry) => {
- entry.insert(attrs);
+ if !attrs.is_empty() {
+ match self.tree.attrs.entry(item) {
+ Entry::Occupied(mut entry) => {
+ *entry.get_mut() = entry.get().merge(attrs);
+ }
+ Entry::Vacant(entry) => {
+ entry.insert(attrs);
+ }
}
}
}
@@ -190,13 +232,31 @@ impl<'a> Ctx<'a> {
}
fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<FileItemTreeId<Struct>> {
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
let visibility = self.lower_visibility(strukt);
let name = strukt.name()?.as_name();
let ast_id = self.source_ast_id_map.ast_id(strukt);
- let (fields, kind, attrs) = self.lower_fields(&strukt.kind());
- let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt);
- let res = Struct { name, visibility, generic_params, fields, shape: kind, ast_id };
+ let (fields, kind, attrs) = self.lower_fields(&strukt.kind(), &body_ctx);
+ let (generic_params, generics_source_map) =
+ self.lower_generic_params(HasImplicitSelf::No, strukt);
+ types_map.shrink_to_fit();
+ types_source_map.shrink_to_fit();
+ let res = Struct {
+ name,
+ visibility,
+ generic_params,
+ fields,
+ shape: kind,
+ ast_id,
+ types_map: Arc::new(types_map),
+ };
let id = id(self.data().structs.alloc(res));
+ self.source_maps.structs.push(GenericItemSourceMapBuilder {
+ item: types_source_map,
+ generics: generics_source_map,
+ });
for (idx, attr) in attrs {
self.add_attrs(
AttrOwner::Field(
@@ -213,6 +273,7 @@ impl<'a> Ctx<'a> {
fn lower_fields(
&mut self,
strukt_kind: &ast::StructKind,
+ body_ctx: &LowerCtx<'_>,
) -> (Box<[Field]>, FieldsShape, Vec<(usize, RawAttrs)>) {
match strukt_kind {
ast::StructKind::Record(it) => {
@@ -220,7 +281,7 @@ impl<'a> Ctx<'a> {
let mut attrs = vec![];
for (i, field) in it.fields().enumerate() {
- let data = self.lower_record_field(&field);
+ let data = self.lower_record_field(&field, body_ctx);
fields.push(data);
let attr = RawAttrs::new(self.db.upcast(), &field, self.span_map());
if !attr.is_empty() {
@@ -234,7 +295,7 @@ impl<'a> Ctx<'a> {
let mut attrs = vec![];
for (i, field) in it.fields().enumerate() {
- let data = self.lower_tuple_field(i, &field);
+ let data = self.lower_tuple_field(i, &field, body_ctx);
fields.push(data);
let attr = RawAttrs::new(self.db.upcast(), &field, self.span_map());
if !attr.is_empty() {
@@ -247,35 +308,59 @@ impl<'a> Ctx<'a> {
}
}
- fn lower_record_field(&mut self, field: &ast::RecordField) -> Field {
+ fn lower_record_field(&mut self, field: &ast::RecordField, body_ctx: &LowerCtx<'_>) -> Field {
let name = match field.name() {
Some(name) => name.as_name(),
None => Name::missing(),
};
let visibility = self.lower_visibility(field);
- let type_ref = self.lower_type_ref_opt(field.ty());
+ let type_ref = TypeRef::from_ast_opt(body_ctx, field.ty());
Field { name, type_ref, visibility }
}
- fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleField) -> Field {
+ fn lower_tuple_field(
+ &mut self,
+ idx: usize,
+ field: &ast::TupleField,
+ body_ctx: &LowerCtx<'_>,
+ ) -> Field {
let name = Name::new_tuple_field(idx);
let visibility = self.lower_visibility(field);
- let type_ref = self.lower_type_ref_opt(field.ty());
+ let type_ref = TypeRef::from_ast_opt(body_ctx, field.ty());
Field { name, type_ref, visibility }
}
fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> {
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
let visibility = self.lower_visibility(union);
let name = union.name()?.as_name();
let ast_id = self.source_ast_id_map.ast_id(union);
let (fields, _, attrs) = match union.record_field_list() {
- Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)),
+ Some(record_field_list) => {
+ self.lower_fields(&StructKind::Record(record_field_list), &body_ctx)
+ }
None => (Box::default(), FieldsShape::Record, Vec::default()),
};
- let generic_params = self.lower_generic_params(HasImplicitSelf::No, union);
- let res = Union { name, visibility, generic_params, fields, ast_id };
+ let (generic_params, generics_source_map) =
+ self.lower_generic_params(HasImplicitSelf::No, union);
+ types_map.shrink_to_fit();
+ types_source_map.shrink_to_fit();
+ let res = Union {
+ name,
+ visibility,
+ generic_params,
+ fields,
+ ast_id,
+ types_map: Arc::new(types_map),
+ };
let id = id(self.data().unions.alloc(res));
+ self.source_maps.unions.push(GenericItemSourceMapBuilder {
+ item: types_source_map,
+ generics: generics_source_map,
+ });
for (idx, attr) in attrs {
self.add_attrs(
AttrOwner::Field(
@@ -299,9 +384,11 @@ impl<'a> Ctx<'a> {
FileItemTreeId(self.next_variant_idx())..FileItemTreeId(self.next_variant_idx())
}
};
- let generic_params = self.lower_generic_params(HasImplicitSelf::No, enum_);
+ let (generic_params, generics_source_map) =
+ self.lower_generic_params(HasImplicitSelf::No, enum_);
let res = Enum { name, visibility, generic_params, variants, ast_id };
let id = id(self.data().enums.alloc(res));
+ self.source_maps.enum_generics.push(generics_source_map);
self.write_generic_params_attributes(id.into());
Some(id)
}
@@ -320,14 +407,20 @@ impl<'a> Ctx<'a> {
}
fn lower_variant(&mut self, variant: &ast::Variant) -> Idx<Variant> {
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
let name = match variant.name() {
Some(name) => name.as_name(),
None => Name::missing(),
};
- let (fields, kind, attrs) = self.lower_fields(&variant.kind());
+ let (fields, kind, attrs) = self.lower_fields(&variant.kind(), &body_ctx);
let ast_id = self.source_ast_id_map.ast_id(variant);
- let res = Variant { name, fields, shape: kind, ast_id };
+ types_map.shrink_to_fit();
+ types_source_map.shrink_to_fit();
+ let res = Variant { name, fields, shape: kind, ast_id, types_map: Arc::new(types_map) };
let id = self.data().variants.alloc(res);
+ self.source_maps.variants.push(types_source_map);
for (idx, attr) in attrs {
self.add_attrs(
AttrOwner::Field(
@@ -341,6 +434,10 @@ impl<'a> Ctx<'a> {
}
fn lower_function(&mut self, func: &ast::Fn) -> Option<FileItemTreeId<Function>> {
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
+
let visibility = self.lower_visibility(func);
let name = func.name()?.as_name();
@@ -360,27 +457,31 @@ impl<'a> Ctx<'a> {
RawAttrs::new(self.db.upcast(), &self_param, self.span_map()),
);
let self_type = match self_param.ty() {
- Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
+ Some(type_ref) => TypeRef::from_ast(&body_ctx, type_ref),
None => {
- let self_type =
- TypeRef::Path(Name::new_symbol_root(sym::Self_.clone()).into());
+ let self_type = body_ctx.alloc_type_ref_desugared(TypeRef::Path(
+ Name::new_symbol_root(sym::Self_.clone()).into(),
+ ));
match self_param.kind() {
ast::SelfParamKind::Owned => self_type,
- ast::SelfParamKind::Ref => TypeRef::Reference(
- Box::new(self_type),
- self_param.lifetime().as_ref().map(LifetimeRef::new),
- Mutability::Shared,
+ ast::SelfParamKind::Ref => body_ctx.alloc_type_ref_desugared(
+ TypeRef::Reference(Box::new(RefType {
+ ty: self_type,
+ lifetime: self_param.lifetime().as_ref().map(LifetimeRef::new),
+ mutability: Mutability::Shared,
+ })),
),
- ast::SelfParamKind::MutRef => TypeRef::Reference(
- Box::new(self_type),
- self_param.lifetime().as_ref().map(LifetimeRef::new),
- Mutability::Mut,
+ ast::SelfParamKind::MutRef => body_ctx.alloc_type_ref_desugared(
+ TypeRef::Reference(Box::new(RefType {
+ ty: self_type,
+ lifetime: self_param.lifetime().as_ref().map(LifetimeRef::new),
+ mutability: Mutability::Mut,
+ })),
),
}
}
};
- let type_ref = Interned::new(self_type);
- params.push(Param { type_ref: Some(type_ref) });
+ params.push(Param { type_ref: Some(self_type) });
has_self_param = true;
}
for param in param_list.params() {
@@ -391,9 +492,8 @@ impl<'a> Ctx<'a> {
Param { type_ref: None }
}
None => {
- let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
- let ty = Interned::new(type_ref);
- Param { type_ref: Some(ty) }
+ let type_ref = TypeRef::from_ast_opt(&body_ctx, param.ty());
+ Param { type_ref: Some(type_ref) }
}
};
params.push(param);
@@ -402,17 +502,17 @@ impl<'a> Ctx<'a> {
let ret_type = match func.ret_type() {
Some(rt) => match rt.ty() {
- Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
- None if rt.thin_arrow_token().is_some() => TypeRef::Error,
- None => TypeRef::unit(),
+ Some(type_ref) => TypeRef::from_ast(&body_ctx, type_ref),
+ None if rt.thin_arrow_token().is_some() => body_ctx.alloc_error_type(),
+ None => body_ctx.alloc_type_ref_desugared(TypeRef::unit()),
},
- None => TypeRef::unit(),
+ None => body_ctx.alloc_type_ref_desugared(TypeRef::unit()),
};
let ret_type = if func.async_token().is_some() {
let future_impl = desugar_future_path(ret_type);
- let ty_bound = Interned::new(TypeBound::Path(future_impl, TraitBoundModifier::None));
- TypeRef::ImplTrait(vec![ty_bound])
+ let ty_bound = TypeBound::Path(future_impl, TraitBoundModifier::None);
+ body_ctx.alloc_type_ref_desugared(TypeRef::ImplTrait(ThinVec::from_iter([ty_bound])))
} else {
ret_type
};
@@ -447,18 +547,27 @@ impl<'a> Ctx<'a> {
flags |= FnFlags::IS_VARARGS;
}
+ types_map.shrink_to_fit();
+ types_source_map.shrink_to_fit();
+ let (generic_params, generics_source_map) =
+ self.lower_generic_params(HasImplicitSelf::No, func);
let res = Function {
name,
visibility,
- explicit_generic_params: self.lower_generic_params(HasImplicitSelf::No, func),
+ explicit_generic_params: generic_params,
abi,
params: params.into_boxed_slice(),
- ret_type: Interned::new(ret_type),
+ ret_type,
ast_id,
+ types_map: Arc::new(types_map),
flags,
};
let id = id(self.data().functions.alloc(res));
+ self.source_maps.functions.push(GenericItemSourceMapBuilder {
+ item: types_source_map,
+ generics: generics_source_map,
+ });
for (idx, attr) in attrs {
self.add_attrs(AttrOwner::Param(id, Idx::from_raw(RawIdx::from_u32(idx as u32))), attr);
}
@@ -470,37 +579,82 @@ impl<'a> Ctx<'a> {
&mut self,
type_alias: &ast::TypeAlias,
) -> Option<FileItemTreeId<TypeAlias>> {
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
let name = type_alias.name()?.as_name();
- let type_ref = type_alias.ty().map(|it| self.lower_type_ref(&it));
+ let type_ref = type_alias.ty().map(|it| TypeRef::from_ast(&body_ctx, it));
let visibility = self.lower_visibility(type_alias);
- let bounds = self.lower_type_bounds(type_alias);
+ let bounds = self.lower_type_bounds(type_alias, &body_ctx);
let ast_id = self.source_ast_id_map.ast_id(type_alias);
- let generic_params = self.lower_generic_params(HasImplicitSelf::No, type_alias);
- let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id };
+ let (generic_params, generics_source_map) =
+ self.lower_generic_params(HasImplicitSelf::No, type_alias);
+ types_map.shrink_to_fit();
+ types_source_map.shrink_to_fit();
+ let res = TypeAlias {
+ name,
+ visibility,
+ bounds,
+ generic_params,
+ type_ref,
+ ast_id,
+ types_map: Arc::new(types_map),
+ };
let id = id(self.data().type_aliases.alloc(res));
+ self.source_maps.type_aliases.push(GenericItemSourceMapBuilder {
+ item: types_source_map,
+ generics: generics_source_map,
+ });
self.write_generic_params_attributes(id.into());
Some(id)
}
fn lower_static(&mut self, static_: &ast::Static) -> Option<FileItemTreeId<Static>> {
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
let name = static_.name()?.as_name();
- let type_ref = self.lower_type_ref_opt(static_.ty());
+ let type_ref = TypeRef::from_ast_opt(&body_ctx, static_.ty());
let visibility = self.lower_visibility(static_);
let mutable = static_.mut_token().is_some();
let has_safe_kw = static_.safe_token().is_some();
let has_unsafe_kw = static_.unsafe_token().is_some();
let ast_id = self.source_ast_id_map.ast_id(static_);
- let res =
- Static { name, visibility, mutable, type_ref, ast_id, has_safe_kw, has_unsafe_kw };
+ types_map.shrink_to_fit();
+ types_source_map.shrink_to_fit();
+ let res = Static {
+ name,
+ visibility,
+ mutable,
+ type_ref,
+ ast_id,
+ has_safe_kw,
+ has_unsafe_kw,
+ types_map: Arc::new(types_map),
+ };
+ self.source_maps.statics.push(types_source_map);
Some(id(self.data().statics.alloc(res)))
}
fn lower_const(&mut self, konst: &ast::Const) -> FileItemTreeId<Const> {
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
let name = konst.name().map(|it| it.as_name());
- let type_ref = self.lower_type_ref_opt(konst.ty());
+ let type_ref = TypeRef::from_ast_opt(&body_ctx, konst.ty());
let visibility = self.lower_visibility(konst);
let ast_id = self.source_ast_id_map.ast_id(konst);
- let res = Const { name, visibility, type_ref, ast_id, has_body: konst.body().is_some() };
+ types_map.shrink_to_fit();
+ types_source_map.shrink_to_fit();
+ let res = Const {
+ name,
+ visibility,
+ type_ref,
+ ast_id,
+ has_body: konst.body().is_some(),
+ types_map: Arc::new(types_map),
+ };
+ self.source_maps.consts.push(types_source_map);
id(self.data().consts.alloc(res))
}
@@ -539,10 +693,11 @@ impl<'a> Ctx<'a> {
.filter_map(|item_node| self.lower_assoc_item(&item_node))
.collect();
- let generic_params =
+ let (generic_params, generics_source_map) =
self.lower_generic_params(HasImplicitSelf::Yes(trait_def.type_bound_list()), trait_def);
let def = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id };
let id = id(self.data().traits.alloc(def));
+ self.source_maps.trait_generics.push(generics_source_map);
self.write_generic_params_attributes(id.into());
Some(id)
}
@@ -554,24 +709,29 @@ impl<'a> Ctx<'a> {
let name = trait_alias_def.name()?.as_name();
let visibility = self.lower_visibility(trait_alias_def);
let ast_id = self.source_ast_id_map.ast_id(trait_alias_def);
- let generic_params = self.lower_generic_params(
+ let (generic_params, generics_source_map) = self.lower_generic_params(
HasImplicitSelf::Yes(trait_alias_def.type_bound_list()),
trait_alias_def,
);
let alias = TraitAlias { name, visibility, generic_params, ast_id };
let id = id(self.data().trait_aliases.alloc(alias));
+ self.source_maps.trait_alias_generics.push(generics_source_map);
self.write_generic_params_attributes(id.into());
Some(id)
}
- fn lower_impl(&mut self, impl_def: &ast::Impl) -> Option<FileItemTreeId<Impl>> {
+ fn lower_impl(&mut self, impl_def: &ast::Impl) -> FileItemTreeId<Impl> {
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
+
let ast_id = self.source_ast_id_map.ast_id(impl_def);
// FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl
// as if it was an non-trait impl. Ideally we want to create a unique missing ref that only
// equals itself.
- let self_ty = self.lower_type_ref(&impl_def.self_ty()?);
- let target_trait = impl_def.trait_().and_then(|tr| self.lower_trait_ref(&tr));
+ let self_ty = TypeRef::from_ast_opt(&body_ctx, impl_def.self_ty());
+ let target_trait = impl_def.trait_().and_then(|tr| TraitRef::from_ast(&body_ctx, tr));
let is_negative = impl_def.excl_token().is_some();
let is_unsafe = impl_def.unsafe_token().is_some();
@@ -584,12 +744,27 @@ impl<'a> Ctx<'a> {
.collect();
// Note that trait impls don't get implicit `Self` unlike traits, because here they are a
// type alias rather than a type parameter, so this is handled by the resolver.
- let generic_params = self.lower_generic_params(HasImplicitSelf::No, impl_def);
- let res =
- Impl { generic_params, target_trait, self_ty, is_negative, is_unsafe, items, ast_id };
+ let (generic_params, generics_source_map) =
+ self.lower_generic_params(HasImplicitSelf::No, impl_def);
+ types_map.shrink_to_fit();
+ types_source_map.shrink_to_fit();
+ let res = Impl {
+ generic_params,
+ target_trait,
+ self_ty,
+ is_negative,
+ is_unsafe,
+ items,
+ ast_id,
+ types_map: Arc::new(types_map),
+ };
let id = id(self.data().impls.alloc(res));
+ self.source_maps.impls.push(GenericItemSourceMapBuilder {
+ item: types_source_map,
+ generics: generics_source_map,
+ });
self.write_generic_params_attributes(id.into());
- Some(id)
+ id
}
fn lower_use(&mut self, use_item: &ast::Use) -> Option<FileItemTreeId<Use>> {
@@ -692,14 +867,17 @@ impl<'a> Ctx<'a> {
&mut self,
has_implicit_self: HasImplicitSelf,
node: &dyn ast::HasGenericParams,
- ) -> Interned<GenericParams> {
+ ) -> (Arc<GenericParams>, TypesSourceMap) {
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let body_ctx = self.body_ctx(&mut types_map, &mut types_source_map);
debug_assert!(self.generic_param_attr_buffer.is_empty(),);
let add_param_attrs = |item: Either<LocalTypeOrConstParamId, LocalLifetimeParamId>,
param| {
- let attrs = RawAttrs::new(self.db.upcast(), &param, self.body_ctx.span_map());
+ let attrs = RawAttrs::new(self.db.upcast(), &param, body_ctx.span_map());
debug_assert!(self.generic_param_attr_buffer.insert(item, attrs).is_none());
};
- self.body_ctx.take_impl_traits_bounds();
+ body_ctx.take_impl_traits_bounds();
let mut generics = GenericParamsCollector::default();
if let HasImplicitSelf::Yes(bounds) = has_implicit_self {
@@ -715,23 +893,29 @@ impl<'a> Ctx<'a> {
// add super traits as bounds on Self
// i.e., `trait Foo: Bar` is equivalent to `trait Foo where Self: Bar`
generics.fill_bounds(
- &self.body_ctx,
+ &body_ctx,
bounds,
- Either::Left(TypeRef::Path(Name::new_symbol_root(sym::Self_.clone()).into())),
+ Either::Left(body_ctx.alloc_type_ref_desugared(TypeRef::Path(
+ Name::new_symbol_root(sym::Self_.clone()).into(),
+ ))),
);
}
- generics.fill(&self.body_ctx, node, add_param_attrs);
+ generics.fill(&body_ctx, node, add_param_attrs);
- Interned::new(generics.finish())
+ let generics = generics.finish(types_map, &mut types_source_map);
+ (generics, types_source_map)
}
- fn lower_type_bounds(&mut self, node: &dyn ast::HasTypeBounds) -> Box<[Interned<TypeBound>]> {
+ fn lower_type_bounds(
+ &mut self,
+ node: &dyn ast::HasTypeBounds,
+ body_ctx: &LowerCtx<'_>,
+ ) -> Box<[TypeBound]> {
match node.type_bound_list() {
- Some(bound_list) => bound_list
- .bounds()
- .map(|it| Interned::new(TypeBound::from_ast(&self.body_ctx, it)))
- .collect(),
+ Some(bound_list) => {
+ bound_list.bounds().map(|it| TypeBound::from_ast(body_ctx, it)).collect()
+ }
None => Box::default(),
}
}
@@ -743,23 +927,6 @@ impl<'a> Ctx<'a> {
self.data().vis.alloc(vis)
}
- fn lower_trait_ref(&mut self, trait_ref: &ast::Type) -> Option<Interned<TraitRef>> {
- let trait_ref = TraitRef::from_ast(&self.body_ctx, trait_ref.clone())?;
- Some(Interned::new(trait_ref))
- }
-
- fn lower_type_ref(&mut self, type_ref: &ast::Type) -> Interned<TypeRef> {
- let tyref = TypeRef::from_ast(&self.body_ctx, type_ref.clone());
- Interned::new(tyref)
- }
-
- fn lower_type_ref_opt(&mut self, type_ref: Option<ast::Type>) -> Interned<TypeRef> {
- match type_ref.map(|ty| self.lower_type_ref(&ty)) {
- Some(it) => it,
- None => Interned::new(TypeRef::Error),
- }
- }
-
fn next_variant_idx(&self) -> Idx<Variant> {
Idx::from_raw(RawIdx::from(
self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32),
@@ -767,7 +934,7 @@ impl<'a> Ctx<'a> {
}
}
-fn desugar_future_path(orig: TypeRef) -> Path {
+fn desugar_future_path(orig: TypeRefId) -> Path {
let path = path![core::future::Future];
let mut generic_args: Vec<_> =
std::iter::repeat(None).take(path.segments().len() - 1).collect();
@@ -777,10 +944,7 @@ fn desugar_future_path(orig: TypeRef) -> Path {
type_ref: Some(orig),
bounds: Box::default(),
};
- generic_args.push(Some(Interned::new(GenericArgs {
- bindings: Box::new([binding]),
- ..GenericArgs::empty()
- })));
+ generic_args.push(Some(GenericArgs { bindings: Box::new([binding]), ..GenericArgs::empty() }));
Path::from_known_path(path, generic_args)
}
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index 9dce28b2e4..b6816a1f96 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -10,11 +10,12 @@ use crate::{
item_tree::{
AttrOwner, Const, DefDatabase, Enum, ExternBlock, ExternCrate, Field, FieldParent,
FieldsShape, FileItemTreeId, FnFlags, Function, GenericModItem, GenericParams, Impl,
- Interned, ItemTree, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, Param, Path,
- RawAttrs, RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias, TypeBound,
- TypeRef, Union, Use, UseTree, UseTreeKind, Variant,
+ ItemTree, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, Param, Path, RawAttrs,
+ RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias, TypeBound, Union, Use,
+ UseTree, UseTreeKind, Variant,
},
pretty::{print_path, print_type_bounds, print_type_ref},
+ type_ref::{TypeRefId, TypesMap},
visibility::RawVisibility,
};
@@ -121,7 +122,13 @@ impl Printer<'_> {
};
}
- fn print_fields(&mut self, parent: FieldParent, kind: FieldsShape, fields: &[Field]) {
+ fn print_fields(
+ &mut self,
+ parent: FieldParent,
+ kind: FieldsShape,
+ fields: &[Field],
+ map: &TypesMap,
+ ) {
let edition = self.edition;
match kind {
FieldsShape::Record => {
@@ -135,7 +142,7 @@ impl Printer<'_> {
);
this.print_visibility(*visibility);
w!(this, "{}: ", name.display(self.db.upcast(), edition));
- this.print_type_ref(type_ref);
+ this.print_type_ref(*type_ref, map);
wln!(this, ",");
}
});
@@ -151,7 +158,7 @@ impl Printer<'_> {
);
this.print_visibility(*visibility);
w!(this, "{}: ", name.display(self.db.upcast(), edition));
- this.print_type_ref(type_ref);
+ this.print_type_ref(*type_ref, map);
wln!(this, ",");
}
});
@@ -167,20 +174,21 @@ impl Printer<'_> {
kind: FieldsShape,
fields: &[Field],
params: &GenericParams,
+ map: &TypesMap,
) {
match kind {
FieldsShape::Record => {
if self.print_where_clause(params) {
wln!(self);
}
- self.print_fields(parent, kind, fields);
+ self.print_fields(parent, kind, fields, map);
}
FieldsShape::Unit => {
self.print_where_clause(params);
- self.print_fields(parent, kind, fields);
+ self.print_fields(parent, kind, fields, map);
}
FieldsShape::Tuple => {
- self.print_fields(parent, kind, fields);
+ self.print_fields(parent, kind, fields, map);
self.print_where_clause(params);
}
}
@@ -262,6 +270,7 @@ impl Printer<'_> {
params,
ret_type,
ast_id,
+ types_map,
flags,
} = &self.tree[it];
self.print_ast_id(ast_id.erase());
@@ -298,7 +307,7 @@ impl Printer<'_> {
w!(this, "self: ");
}
if let Some(type_ref) = type_ref {
- this.print_type_ref(type_ref);
+ this.print_type_ref(*type_ref, types_map);
} else {
wln!(this, "...");
}
@@ -307,7 +316,7 @@ impl Printer<'_> {
});
}
w!(self, ") -> ");
- self.print_type_ref(ret_type);
+ self.print_type_ref(*ret_type, types_map);
self.print_where_clause(explicit_generic_params);
if flags.contains(FnFlags::HAS_BODY) {
wln!(self, " {{ ... }}");
@@ -316,8 +325,15 @@ impl Printer<'_> {
}
}
ModItem::Struct(it) => {
- let Struct { visibility, name, fields, shape: kind, generic_params, ast_id } =
- &self.tree[it];
+ let Struct {
+ visibility,
+ name,
+ fields,
+ shape: kind,
+ generic_params,
+ ast_id,
+ types_map,
+ } = &self.tree[it];
self.print_ast_id(ast_id.erase());
self.print_visibility(*visibility);
w!(self, "struct {}", name.display(self.db.upcast(), self.edition));
@@ -327,6 +343,7 @@ impl Printer<'_> {
*kind,
fields,
generic_params,
+ types_map,
);
if matches!(kind, FieldsShape::Record) {
wln!(self);
@@ -335,7 +352,8 @@ impl Printer<'_> {
}
}
ModItem::Union(it) => {
- let Union { name, visibility, fields, generic_params, ast_id } = &self.tree[it];
+ let Union { name, visibility, fields, generic_params, ast_id, types_map } =
+ &self.tree[it];
self.print_ast_id(ast_id.erase());
self.print_visibility(*visibility);
w!(self, "union {}", name.display(self.db.upcast(), self.edition));
@@ -345,6 +363,7 @@ impl Printer<'_> {
FieldsShape::Record,
fields,
generic_params,
+ types_map,
);
wln!(self);
}
@@ -358,18 +377,20 @@ impl Printer<'_> {
let edition = self.edition;
self.indented(|this| {
for variant in FileItemTreeId::range_iter(variants.clone()) {
- let Variant { name, fields, shape: kind, ast_id } = &this.tree[variant];
+ let Variant { name, fields, shape: kind, ast_id, types_map } =
+ &this.tree[variant];
this.print_ast_id(ast_id.erase());
this.print_attrs_of(variant, "\n");
w!(this, "{}", name.display(self.db.upcast(), edition));
- this.print_fields(FieldParent::Variant(variant), *kind, fields);
+ this.print_fields(FieldParent::Variant(variant), *kind, fields, types_map);
wln!(this, ",");
}
});
wln!(self, "}}");
}
ModItem::Const(it) => {
- let Const { name, visibility, type_ref, ast_id, has_body: _ } = &self.tree[it];
+ let Const { name, visibility, type_ref, ast_id, has_body: _, types_map } =
+ &self.tree[it];
self.print_ast_id(ast_id.erase());
self.print_visibility(*visibility);
w!(self, "const ");
@@ -378,7 +399,7 @@ impl Printer<'_> {
None => w!(self, "_"),
}
w!(self, ": ");
- self.print_type_ref(type_ref);
+ self.print_type_ref(*type_ref, types_map);
wln!(self, " = _;");
}
ModItem::Static(it) => {
@@ -390,6 +411,7 @@ impl Printer<'_> {
ast_id,
has_safe_kw,
has_unsafe_kw,
+ types_map,
} = &self.tree[it];
self.print_ast_id(ast_id.erase());
self.print_visibility(*visibility);
@@ -404,7 +426,7 @@ impl Printer<'_> {
w!(self, "mut ");
}
w!(self, "{}: ", name.display(self.db.upcast(), self.edition));
- self.print_type_ref(type_ref);
+ self.print_type_ref(*type_ref, types_map);
w!(self, " = _;");
wln!(self);
}
@@ -449,6 +471,7 @@ impl Printer<'_> {
items,
generic_params,
ast_id,
+ types_map,
} = &self.tree[it];
self.print_ast_id(ast_id.erase());
if *is_unsafe {
@@ -461,10 +484,10 @@ impl Printer<'_> {
w!(self, "!");
}
if let Some(tr) = target_trait {
- self.print_path(&tr.path);
+ self.print_path(&tr.path, types_map);
w!(self, " for ");
}
- self.print_type_ref(self_ty);
+ self.print_type_ref(*self_ty, types_map);
self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
for item in &**items {
@@ -474,19 +497,26 @@ impl Printer<'_> {
wln!(self, "}}");
}
ModItem::TypeAlias(it) => {
- let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id } =
- &self.tree[it];
+ let TypeAlias {
+ name,
+ visibility,
+ bounds,
+ type_ref,
+ generic_params,
+ ast_id,
+ types_map,
+ } = &self.tree[it];
self.print_ast_id(ast_id.erase());
self.print_visibility(*visibility);
w!(self, "type {}", name.display(self.db.upcast(), self.edition));
self.print_generic_params(generic_params, it.into());
if !bounds.is_empty() {
w!(self, ": ");
- self.print_type_bounds(bounds);
+ self.print_type_bounds(bounds, types_map);
}
if let Some(ty) = type_ref {
w!(self, " = ");
- self.print_type_ref(ty);
+ self.print_type_ref(*ty, types_map);
}
self.print_where_clause(generic_params);
w!(self, ";");
@@ -543,19 +573,19 @@ impl Printer<'_> {
self.blank();
}
- fn print_type_ref(&mut self, type_ref: &TypeRef) {
+ fn print_type_ref(&mut self, type_ref: TypeRefId, map: &TypesMap) {
let edition = self.edition;
- print_type_ref(self.db, type_ref, self, edition).unwrap();
+ print_type_ref(self.db, type_ref, map, self, edition).unwrap();
}
- fn print_type_bounds(&mut self, bounds: &[Interned<TypeBound>]) {
+ fn print_type_bounds(&mut self, bounds: &[TypeBound], map: &TypesMap) {
let edition = self.edition;
- print_type_bounds(self.db, bounds, self, edition).unwrap();
+ print_type_bounds(self.db, bounds, map, self, edition).unwrap();
}
- fn print_path(&mut self, path: &Path) {
+ fn print_path(&mut self, path: &Path, map: &TypesMap) {
let edition = self.edition;
- print_path(self.db, path, self, edition).unwrap();
+ print_path(self.db, path, map, self, edition).unwrap();
}
fn print_generic_params(&mut self, params: &GenericParams, parent: GenericModItem) {
@@ -586,7 +616,7 @@ impl Printer<'_> {
},
TypeOrConstParamData::ConstParamData(konst) => {
w!(self, "const {}: ", konst.name.display(self.db.upcast(), self.edition));
- self.print_type_ref(&konst.ty);
+ self.print_type_ref(konst.ty, &params.types_map);
}
}
}
@@ -640,14 +670,16 @@ impl Printer<'_> {
};
match target {
- WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
+ WherePredicateTypeTarget::TypeRef(ty) => {
+ this.print_type_ref(*ty, &params.types_map)
+ }
WherePredicateTypeTarget::TypeOrConstParam(id) => match params[*id].name() {
Some(name) => w!(this, "{}", name.display(self.db.upcast(), edition)),
None => w!(this, "_anon_{}", id.into_raw()),
},
}
w!(this, ": ");
- this.print_type_bounds(std::slice::from_ref(bound));
+ this.print_type_bounds(std::slice::from_ref(bound), &params.types_map);
}
});
true
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 157c9ef080..f6ed826f04 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -1531,11 +1531,3 @@ fn macro_call_as_call_id_with_eager(
pub struct UnresolvedMacro {
pub path: hir_expand::mod_path::ModPath,
}
-
-intern::impl_internable!(
- crate::type_ref::TypeRef,
- crate::type_ref::TraitRef,
- crate::type_ref::TypeBound,
- crate::path::GenericArgs,
- generics::GenericParams,
-);
diff --git a/crates/hir-def/src/lower.rs b/crates/hir-def/src/lower.rs
index e4786a1dd4..df5847929c 100644
--- a/crates/hir-def/src/lower.rs
+++ b/crates/hir-def/src/lower.rs
@@ -5,43 +5,53 @@ use hir_expand::{
span_map::{SpanMap, SpanMapRef},
AstId, HirFileId, InFile,
};
-use intern::Interned;
use span::{AstIdMap, AstIdNode};
+use stdx::thin_vec::ThinVec;
use syntax::ast;
use triomphe::Arc;
-use crate::{db::DefDatabase, path::Path, type_ref::TypeBound};
+use crate::{
+ db::DefDatabase,
+ path::Path,
+ type_ref::{TypeBound, TypePtr, TypeRef, TypeRefId, TypesMap, TypesSourceMap},
+};
pub struct LowerCtx<'a> {
pub db: &'a dyn DefDatabase,
file_id: HirFileId,
span_map: OnceCell<SpanMap>,
ast_id_map: OnceCell<Arc<AstIdMap>>,
- impl_trait_bounds: RefCell<Vec<Vec<Interned<TypeBound>>>>,
+ impl_trait_bounds: RefCell<Vec<ThinVec<TypeBound>>>,
// Prevent nested impl traits like `impl Foo<impl Bar>`.
outer_impl_trait: RefCell<bool>,
+ types_map: RefCell<(&'a mut TypesMap, &'a mut TypesSourceMap)>,
}
-pub(crate) struct OuterImplTraitGuard<'a> {
- ctx: &'a LowerCtx<'a>,
+pub(crate) struct OuterImplTraitGuard<'a, 'b> {
+ ctx: &'a LowerCtx<'b>,
old: bool,
}
-impl<'a> OuterImplTraitGuard<'a> {
- fn new(ctx: &'a LowerCtx<'a>, impl_trait: bool) -> Self {
+impl<'a, 'b> OuterImplTraitGuard<'a, 'b> {
+ fn new(ctx: &'a LowerCtx<'b>, impl_trait: bool) -> Self {
let old = ctx.outer_impl_trait.replace(impl_trait);
Self { ctx, old }
}
}
-impl<'a> Drop for OuterImplTraitGuard<'a> {
+impl Drop for OuterImplTraitGuard<'_, '_> {
fn drop(&mut self) {
self.ctx.outer_impl_trait.replace(self.old);
}
}
impl<'a> LowerCtx<'a> {
- pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
+ pub fn new(
+ db: &'a dyn DefDatabase,
+ file_id: HirFileId,
+ types_map: &'a mut TypesMap,
+ types_source_map: &'a mut TypesSourceMap,
+ ) -> Self {
LowerCtx {
db,
file_id,
@@ -49,6 +59,7 @@ impl<'a> LowerCtx<'a> {
ast_id_map: OnceCell::new(),
impl_trait_bounds: RefCell::new(Vec::new()),
outer_impl_trait: RefCell::default(),
+ types_map: RefCell::new((types_map, types_source_map)),
}
}
@@ -56,6 +67,8 @@ impl<'a> LowerCtx<'a> {
db: &'a dyn DefDatabase,
file_id: HirFileId,
span_map: OnceCell<SpanMap>,
+ types_map: &'a mut TypesMap,
+ types_source_map: &'a mut TypesSourceMap,
) -> Self {
LowerCtx {
db,
@@ -64,6 +77,7 @@ impl<'a> LowerCtx<'a> {
ast_id_map: OnceCell::new(),
impl_trait_bounds: RefCell::new(Vec::new()),
outer_impl_trait: RefCell::default(),
+ types_map: RefCell::new((types_map, types_source_map)),
}
}
@@ -82,11 +96,11 @@ impl<'a> LowerCtx<'a> {
)
}
- pub fn update_impl_traits_bounds(&self, bounds: Vec<Interned<TypeBound>>) {
+ pub fn update_impl_traits_bounds(&self, bounds: ThinVec<TypeBound>) {
self.impl_trait_bounds.borrow_mut().push(bounds);
}
- pub fn take_impl_traits_bounds(&self) -> Vec<Vec<Interned<TypeBound>>> {
+ pub fn take_impl_traits_bounds(&self) -> Vec<ThinVec<TypeBound>> {
self.impl_trait_bounds.take()
}
@@ -94,7 +108,32 @@ impl<'a> LowerCtx<'a> {
*self.outer_impl_trait.borrow()
}
- pub(crate) fn outer_impl_trait_scope(&'a self, impl_trait: bool) -> OuterImplTraitGuard<'a> {
+ pub(crate) fn outer_impl_trait_scope<'b>(
+ &'b self,
+ impl_trait: bool,
+ ) -> OuterImplTraitGuard<'b, 'a> {
OuterImplTraitGuard::new(self, impl_trait)
}
+
+ pub(crate) fn alloc_type_ref(&self, type_ref: TypeRef, node: TypePtr) -> TypeRefId {
+ let mut types_map = self.types_map.borrow_mut();
+ let (types_map, types_source_map) = &mut *types_map;
+ let id = types_map.types.alloc(type_ref);
+ types_source_map.types_map_back.insert(id, InFile::new(self.file_id, node));
+ id
+ }
+
+ pub(crate) fn alloc_type_ref_desugared(&self, type_ref: TypeRef) -> TypeRefId {
+ self.types_map.borrow_mut().0.types.alloc(type_ref)
+ }
+
+ pub(crate) fn alloc_error_type(&self) -> TypeRefId {
+ self.types_map.borrow_mut().0.types.alloc(TypeRef::Error)
+ }
+
+ // FIXME: If we alloc while holding this, well... Bad Things will happen. Need to change this
+ // to use proper mutability instead of interior mutability.
+ pub(crate) fn types_map(&self) -> std::cell::Ref<'_, TypesMap> {
+ std::cell::Ref::map(self.types_map.borrow(), |it| &*it.0)
+ }
}
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index 450a15bd66..d5b94f0ae4 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -122,7 +122,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
let mut expn_text = String::new();
if let Some(err) = exp.err {
- format_to!(expn_text, "/* error: {} */", err.render_to_string(&db).0);
+ format_to!(expn_text, "/* error: {} */", err.render_to_string(&db).message);
}
let (parse, token_map) = exp.value;
if expect_errors {
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 22b9c2b4e3..a37e3c70e2 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -31,7 +31,7 @@ use crate::{
item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
item_tree::{
self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId,
- ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
+ ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, UseTreeKind,
},
macro_call_as_call_id, macro_call_as_call_id_with_eager,
nameres::{
@@ -985,12 +985,8 @@ impl DefCollector<'_> {
for (name, res) in resolutions {
match name {
Some(name) => {
- changed |= self.push_res_and_update_glob_vis(
- module_id,
- name,
- res.with_visibility(vis),
- import,
- );
+ changed |=
+ self.push_res_and_update_glob_vis(module_id, name, *res, vis, import);
}
None => {
let tr = match res.take_types() {
@@ -1043,10 +1039,11 @@ impl DefCollector<'_> {
.collect::<Vec<_>>();
for (glob_importing_module, glob_import_vis, use_) in glob_imports {
+ let vis = glob_import_vis.min(vis, &self.def_map).unwrap_or(glob_import_vis);
self.update_recursive(
glob_importing_module,
resolutions,
- glob_import_vis,
+ vis,
Some(ImportType::Glob(use_)),
depth + 1,
);
@@ -1058,8 +1055,44 @@ impl DefCollector<'_> {
module_id: LocalModuleId,
name: &Name,
mut defs: PerNs,
+ vis: Visibility,
def_import_type: Option<ImportType>,
) -> bool {
+ // `extern crate crate_name` things can be re-exported as `pub use crate_name`.
+ // But they cannot be re-exported as `pub use self::crate_name`, `pub use crate::crate_name`
+ // or `pub use ::crate_name`.
+ //
+ // This has been historically allowed, but may be not allowed in future
+ // https://github.com/rust-lang/rust/issues/127909
+ if let Some((_, v, it)) = defs.types.as_mut() {
+ let is_extern_crate_reimport_without_prefix = || {
+ let Some(ImportOrExternCrate::ExternCrate(_)) = it else {
+ return false;
+ };
+ let Some(ImportType::Import(id)) = def_import_type else {
+ return false;
+ };
+ let use_id = id.import.lookup(self.db).id;
+ let item_tree = use_id.item_tree(self.db);
+ let use_kind = item_tree[use_id.value].use_tree.kind();
+ let UseTreeKind::Single { path, .. } = use_kind else {
+ return false;
+ };
+ path.segments().len() < 2
+ };
+ if is_extern_crate_reimport_without_prefix() {
+ *v = vis;
+ } else {
+ *v = v.min(vis, &self.def_map).unwrap_or(vis);
+ }
+ }
+ if let Some((_, v, _)) = defs.values.as_mut() {
+ *v = v.min(vis, &self.def_map).unwrap_or(vis);
+ }
+ if let Some((_, v, _)) = defs.macros.as_mut() {
+ *v = v.min(vis, &self.def_map).unwrap_or(vis);
+ }
+
let mut changed = false;
if let Some(ImportType::Glob(_)) = def_import_type {
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index 75cab137f7..29379d0074 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -10,6 +10,7 @@
//!
//! `ReachedFixedPoint` signals about this.
+use either::Either;
use hir_expand::{name::Name, Lookup};
use span::Edition;
use triomphe::Arc;
@@ -150,17 +151,8 @@ impl DefMap {
let mut arc;
let mut current_map = self;
- loop {
- let new = current_map.resolve_path_fp_with_macro_single(
- db,
- mode,
- original_module,
- path,
- shadow,
- expected_macro_subns,
- );
- // Merge `new` into `result`.
+ let mut merge = |new: ResolvePathResult| {
result.resolved_def = result.resolved_def.or(new.resolved_def);
if result.reached_fixedpoint == ReachedFixedPoint::No {
result.reached_fixedpoint = new.reached_fixedpoint;
@@ -171,7 +163,9 @@ impl DefMap {
(Some(old), Some(new)) => Some(old.max(new)),
(None, new) => new,
};
+ };
+ loop {
match current_map.block {
Some(block) if original_module == Self::ROOT => {
// Block modules "inherit" names from its parent module.
@@ -180,8 +174,38 @@ impl DefMap {
current_map = &arc;
}
// Proper (non-block) modules, including those in block `DefMap`s, don't.
- _ => return result,
+ _ => {
+ if original_module != Self::ROOT && current_map.block.is_some() {
+ // A module inside a block. Do not resolve items declared in upper blocks, but we do need to get
+ // the prelude items (which are not inserted into blocks because they can be overridden there).
+ original_module = Self::ROOT;
+ arc = db.crate_def_map(self.krate);
+ current_map = &arc;
+
+ let new = current_map.resolve_path_fp_in_all_preludes(
+ db,
+ mode,
+ original_module,
+ path,
+ shadow,
+ );
+ merge(new);
+ }
+
+ return result;
+ }
}
+
+ let new = current_map.resolve_path_fp_with_macro_single(
+ db,
+ mode,
+ original_module,
+ path,
+ shadow,
+ expected_macro_subns,
+ );
+
+ merge(new);
}
}
@@ -195,7 +219,7 @@ impl DefMap {
expected_macro_subns: Option<MacroSubNs>,
) -> ResolvePathResult {
let mut segments = path.segments().iter().enumerate();
- let mut curr_per_ns = match path.kind {
+ let curr_per_ns = match path.kind {
PathKind::DollarCrate(krate) => {
if krate == self.krate {
cov_mark::hit!(macro_dollar_crate_self);
@@ -296,25 +320,96 @@ impl DefMap {
PerNs::types(module.into(), Visibility::Public, None)
}
- PathKind::Abs => {
- // 2018-style absolute path -- only extern prelude
- let segment = match segments.next() {
- Some((_, segment)) => segment,
+ PathKind::Abs => match self.resolve_path_abs(&mut segments, path) {
+ Either::Left(it) => it,
+ Either::Right(reached_fixed_point) => {
+ return ResolvePathResult::empty(reached_fixed_point)
+ }
+ },
+ };
+
+ self.resolve_remaining_segments(segments, curr_per_ns, path, db, shadow, original_module)
+ }
+
+ /// Resolves a path only in the preludes, without accounting for item scopes.
+ pub(super) fn resolve_path_fp_in_all_preludes(
+ &self,
+ db: &dyn DefDatabase,
+ mode: ResolveMode,
+ original_module: LocalModuleId,
+ path: &ModPath,
+ shadow: BuiltinShadowMode,
+ ) -> ResolvePathResult {
+ let mut segments = path.segments().iter().enumerate();
+ let curr_per_ns = match path.kind {
+ // plain import or absolute path in 2015: crate-relative with
+ // fallback to extern prelude (with the simplification in
+ // rust-lang/rust#57745)
+ // FIXME there must be a nicer way to write this condition
+ PathKind::Plain | PathKind::Abs
+ if self.data.edition == Edition::Edition2015
+ && (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
+ {
+ let (_, segment) = match segments.next() {
+ Some((idx, segment)) => (idx, segment),
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
};
- if let Some(&(def, extern_crate)) = self.data.extern_prelude.get(segment) {
- tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
- PerNs::types(
- def.into(),
- Visibility::Public,
- extern_crate.map(ImportOrExternCrate::ExternCrate),
- )
- } else {
- return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
+ tracing::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
+ self.resolve_name_in_extern_prelude(segment)
+ }
+ PathKind::Plain => {
+ let (_, segment) = match segments.next() {
+ Some((idx, segment)) => (idx, segment),
+ None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
+ };
+ tracing::debug!("resolving {:?} in module", segment);
+ self.resolve_name_in_all_preludes(db, segment)
+ }
+ PathKind::Abs => match self.resolve_path_abs(&mut segments, path) {
+ Either::Left(it) => it,
+ Either::Right(reached_fixed_point) => {
+ return ResolvePathResult::empty(reached_fixed_point)
}
+ },
+ PathKind::DollarCrate(_) | PathKind::Crate | PathKind::Super(_) => {
+ return ResolvePathResult::empty(ReachedFixedPoint::Yes)
}
};
+ self.resolve_remaining_segments(segments, curr_per_ns, path, db, shadow, original_module)
+ }
+
+ /// 2018-style absolute path -- only extern prelude
+ fn resolve_path_abs<'a>(
+ &self,
+ segments: &mut impl Iterator<Item = (usize, &'a Name)>,
+ path: &ModPath,
+ ) -> Either<PerNs, ReachedFixedPoint> {
+ let segment = match segments.next() {
+ Some((_, segment)) => segment,
+ None => return Either::Right(ReachedFixedPoint::Yes),
+ };
+ if let Some(&(def, extern_crate)) = self.data.extern_prelude.get(segment) {
+ tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
+ Either::Left(PerNs::types(
+ def.into(),
+ Visibility::Public,
+ extern_crate.map(ImportOrExternCrate::ExternCrate),
+ ))
+ } else {
+ Either::Right(ReachedFixedPoint::No) // extern crate declarations can add to the extern prelude
+ }
+ }
+
+ fn resolve_remaining_segments<'a>(
+ &self,
+ segments: impl Iterator<Item = (usize, &'a Name)>,
+ mut curr_per_ns: PerNs,
+ path: &ModPath,
+ db: &dyn DefDatabase,
+ shadow: BuiltinShadowMode,
+ original_module: LocalModuleId,
+ ) -> ResolvePathResult {
for (i, segment) in segments {
let (curr, vis, imp) = match curr_per_ns.take_types_full() {
Some(r) => r,
@@ -475,24 +570,9 @@ impl DefMap {
// they might been shadowed by local names.
return PerNs::none();
}
- self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
- PerNs::types(
- it.into(),
- Visibility::Public,
- extern_crate.map(ImportOrExternCrate::ExternCrate),
- )
- })
- };
- let macro_use_prelude = || {
- self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, _extern_crate)| {
- PerNs::macros(
- it,
- Visibility::Public,
- // FIXME?
- None, // extern_crate.map(ImportOrExternCrate::ExternCrate),
- )
- })
+ self.resolve_name_in_extern_prelude(name)
};
+ let macro_use_prelude = || self.resolve_in_macro_use_prelude(name);
let prelude = || {
if self.block.is_some() && module == DefMap::ROOT {
return PerNs::none();
@@ -507,6 +587,38 @@ impl DefMap {
.or_else(prelude)
}
+ fn resolve_name_in_all_preludes(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
+ // Resolve in:
+ // - extern prelude / macro_use prelude
+ // - std prelude
+ let extern_prelude = self.resolve_name_in_extern_prelude(name);
+ let macro_use_prelude = || self.resolve_in_macro_use_prelude(name);
+ let prelude = || self.resolve_in_prelude(db, name);
+
+ extern_prelude.or_else(macro_use_prelude).or_else(prelude)
+ }
+
+ fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs {
+ self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
+ PerNs::types(
+ it.into(),
+ Visibility::Public,
+ extern_crate.map(ImportOrExternCrate::ExternCrate),
+ )
+ })
+ }
+
+ fn resolve_in_macro_use_prelude(&self, name: &Name) -> PerNs {
+ self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, _extern_crate)| {
+ PerNs::macros(
+ it,
+ Visibility::Public,
+ // FIXME?
+ None, // extern_crate.map(ImportOrExternCrate::ExternCrate),
+ )
+ })
+ }
+
fn resolve_name_in_crate_root_or_extern_prelude(
&self,
db: &dyn DefDatabase,
@@ -525,16 +637,7 @@ impl DefMap {
// Don't resolve extern prelude in pseudo-module of a block.
return PerNs::none();
}
- self.data.extern_prelude.get(name).copied().map_or(
- PerNs::none(),
- |(it, extern_crate)| {
- PerNs::types(
- it.into(),
- Visibility::Public,
- extern_crate.map(ImportOrExternCrate::ExternCrate),
- )
- },
- )
+ self.resolve_name_in_extern_prelude(name)
};
from_crate_root.or_else(from_extern_prelude)
diff --git a/crates/hir-def/src/nameres/tests.rs b/crates/hir-def/src/nameres/tests.rs
index 7b02a89e5d..e1e30e5cec 100644
--- a/crates/hir-def/src/nameres/tests.rs
+++ b/crates/hir-def/src/nameres/tests.rs
@@ -386,6 +386,52 @@ pub struct Arc;
}
#[test]
+fn extern_crate_reexport() {
+ check(
+ r#"
+//- /main.rs crate:main deps:importer
+use importer::*;
+use importer::extern_crate1::exported::*;
+use importer::allowed_reexport::*;
+use importer::extern_crate2::*;
+use importer::not_allowed_reexport1;
+use importer::not_allowed_reexport2;
+
+//- /importer.rs crate:importer deps:extern_crate1,extern_crate2
+extern crate extern_crate1;
+extern crate extern_crate2;
+
+pub use extern_crate1;
+pub use extern_crate1 as allowed_reexport;
+
+pub use ::extern_crate;
+pub use self::extern_crate as not_allowed_reexport1;
+pub use crate::extern_crate as not_allowed_reexport2;
+
+//- /extern_crate1.rs crate:extern_crate1
+pub mod exported {
+ pub struct PublicItem;
+ struct PrivateItem;
+}
+
+pub struct Exported;
+
+//- /extern_crate2.rs crate:extern_crate2
+pub struct NotExported;
+"#,
+ expect![[r#"
+ crate
+ Exported: t v
+ PublicItem: t v
+ allowed_reexport: t
+ exported: t
+ not_allowed_reexport1: _
+ not_allowed_reexport2: _
+ "#]],
+ );
+}
+
+#[test]
fn extern_crate_rename_2015_edition() {
check(
r#"
diff --git a/crates/hir-def/src/nameres/tests/globs.rs b/crates/hir-def/src/nameres/tests/globs.rs
index a2696055ca..543ab41cd5 100644
--- a/crates/hir-def/src/nameres/tests/globs.rs
+++ b/crates/hir-def/src/nameres/tests/globs.rs
@@ -412,3 +412,42 @@ use reexport::*;
"#]],
);
}
+
+#[test]
+fn regression_18308() {
+ check(
+ r#"
+use outer::*;
+
+mod outer {
+ mod inner_superglob {
+ pub use super::*;
+ }
+
+ // The importing order matters!
+ pub use inner_superglob::*;
+ use super::glob_target::*;
+}
+
+mod glob_target {
+ pub struct ShouldBePrivate;
+}
+"#,
+ expect![[r#"
+ crate
+ glob_target: t
+ outer: t
+
+ crate::glob_target
+ ShouldBePrivate: t v
+
+ crate::outer
+ ShouldBePrivate: t v
+ inner_superglob: t
+
+ crate::outer::inner_superglob
+ ShouldBePrivate: t v
+ inner_superglob: t
+ "#]],
+ );
+}
diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs
index d319831867..d920c10826 100644
--- a/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/crates/hir-def/src/nameres/tests/incremental.rs
@@ -253,7 +253,8 @@ m!(Z);
let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
assert_eq!(module_data.scope.resolutions().count(), 4);
});
- let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
+ let n_recalculated_item_trees =
+ events.iter().filter(|it| it.contains("item_tree(")).count();
assert_eq!(n_recalculated_item_trees, 6);
let n_reparsed_macros =
events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
@@ -308,7 +309,7 @@ pub type Ty = ();
let events = db.log_executed(|| {
db.file_item_tree(pos.file_id.into());
});
- let n_calculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
+ let n_calculated_item_trees = events.iter().filter(|it| it.contains("item_tree(")).count();
assert_eq!(n_calculated_item_trees, 1);
let n_parsed_files = events.iter().filter(|it| it.contains("parse(")).count();
assert_eq!(n_parsed_files, 1);
diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs
index 077863c0c9..dc6947c5b5 100644
--- a/crates/hir-def/src/path.rs
+++ b/crates/hir-def/src/path.rs
@@ -9,11 +9,12 @@ use std::{
use crate::{
lang_item::LangItemTarget,
lower::LowerCtx,
- type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
+ type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRefId},
};
use hir_expand::name::Name;
use intern::Interned;
use span::Edition;
+use stdx::thin_vec::thin_vec_with_header_struct;
use syntax::ast;
pub use hir_expand::mod_path::{path, ModPath, PathKind};
@@ -47,20 +48,33 @@ impl Display for ImportAliasDisplay<'_> {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Path {
- /// A normal path
- Normal {
- /// Type based path like `<T>::foo`.
- /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
- type_anchor: Option<Interned<TypeRef>>,
- mod_path: Interned<ModPath>,
- /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
- generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
- },
+ /// `BarePath` is used when the path has neither generics nor type anchor, since the vast majority of paths
+ /// are in this category, and splitting `Path` this way allows it to be more thin. When the path has either generics
+ /// or type anchor, it is `Path::Normal` with the generics filled with `None` even if there are none (practically
+ /// this is not a problem since many more paths have generics than a type anchor).
+ BarePath(Interned<ModPath>),
+ /// `Path::Normal` may have empty generics and type anchor (but generic args will be filled with `None`).
+ Normal(NormalPath),
/// A link to a lang item. It is used in desugaring of things like `it?`. We can show these
/// links via a normal path since they might be private and not accessible in the usage place.
LangItem(LangItemTarget, Option<Name>),
}
+// This type is being used a lot, make sure it doesn't grow unintentionally.
+#[cfg(target_arch = "x86_64")]
+const _: () = {
+ assert!(size_of::<Path>() == 16);
+ assert!(size_of::<Option<Path>>() == 16);
+};
+
+thin_vec_with_header_struct! {
+ pub new(pub(crate)) struct NormalPath, NormalPathHeader {
+ pub generic_args: [Option<GenericArgs>],
+ pub type_anchor: Option<TypeRefId>,
+ pub mod_path: Interned<ModPath>; ref,
+ }
+}
+
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
/// also includes bindings of associated types, like in `Iterator<Item = Foo>`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -86,20 +100,20 @@ pub struct AssociatedTypeBinding {
pub name: Name,
/// The generic arguments to the associated type. e.g. For `Trait<Assoc<'a, T> = &'a T>`, this
/// would be `['a, T]`.
- pub args: Option<Interned<GenericArgs>>,
+ pub args: Option<GenericArgs>,
/// The type bound to this associated type (in `Item = T`, this would be the
/// `T`). This can be `None` if there are bounds instead.
- pub type_ref: Option<TypeRef>,
+ pub type_ref: Option<TypeRefId>,
/// Bounds for the associated type, like in `Iterator<Item:
/// SomeOtherTrait>`. (This is the unstable `associated_type_bounds`
/// feature.)
- pub bounds: Box<[Interned<TypeBound>]>,
+ pub bounds: Box<[TypeBound]>,
}
/// A single generic argument.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum GenericArg {
- Type(TypeRef),
+ Type(TypeRefId),
Lifetime(LifetimeRef),
Const(ConstRef),
}
@@ -112,50 +126,49 @@ impl Path {
}
/// Converts a known mod path to `Path`.
- pub fn from_known_path(
- path: ModPath,
- generic_args: impl Into<Box<[Option<Interned<GenericArgs>>]>>,
- ) -> Path {
- let generic_args = generic_args.into();
- assert_eq!(path.len(), generic_args.len());
- Path::Normal {
- type_anchor: None,
- mod_path: Interned::new(path),
- generic_args: Some(generic_args),
- }
+ pub fn from_known_path(path: ModPath, generic_args: Vec<Option<GenericArgs>>) -> Path {
+ Path::Normal(NormalPath::new(None, Interned::new(path), generic_args))
}
/// Converts a known mod path to `Path`.
pub fn from_known_path_with_no_generic(path: ModPath) -> Path {
- Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None }
+ Path::BarePath(Interned::new(path))
}
+ #[inline]
pub fn kind(&self) -> &PathKind {
match self {
- Path::Normal { mod_path, .. } => &mod_path.kind,
+ Path::BarePath(mod_path) => &mod_path.kind,
+ Path::Normal(path) => &path.mod_path().kind,
Path::LangItem(..) => &PathKind::Abs,
}
}
- pub fn type_anchor(&self) -> Option<&TypeRef> {
+ #[inline]
+ pub fn type_anchor(&self) -> Option<TypeRefId> {
match self {
- Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
- Path::LangItem(..) => None,
+ Path::Normal(path) => path.type_anchor(),
+ Path::LangItem(..) | Path::BarePath(_) => None,
+ }
+ }
+
+ #[inline]
+ pub fn generic_args(&self) -> Option<&[Option<GenericArgs>]> {
+ match self {
+ Path::Normal(path) => Some(path.generic_args()),
+ Path::LangItem(..) | Path::BarePath(_) => None,
}
}
pub fn segments(&self) -> PathSegments<'_> {
match self {
- Path::Normal { mod_path, generic_args, .. } => {
- let s = PathSegments {
- segments: mod_path.segments(),
- generic_args: generic_args.as_deref(),
- };
- if let Some(generic_args) = s.generic_args {
- assert_eq!(s.segments.len(), generic_args.len());
- }
- s
+ Path::BarePath(mod_path) => {
+ PathSegments { segments: mod_path.segments(), generic_args: None }
}
+ Path::Normal(path) => PathSegments {
+ segments: path.mod_path().segments(),
+ generic_args: Some(path.generic_args()),
+ },
Path::LangItem(_, seg) => PathSegments {
segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
generic_args: None,
@@ -165,34 +178,55 @@ impl Path {
pub fn mod_path(&self) -> Option<&ModPath> {
match self {
- Path::Normal { mod_path, .. } => Some(mod_path),
+ Path::BarePath(mod_path) => Some(mod_path),
+ Path::Normal(path) => Some(path.mod_path()),
Path::LangItem(..) => None,
}
}
pub fn qualifier(&self) -> Option<Path> {
- let Path::Normal { mod_path, generic_args, type_anchor } = self else {
- return None;
- };
- if mod_path.is_ident() {
- return None;
+ match self {
+ Path::BarePath(mod_path) => {
+ if mod_path.is_ident() {
+ return None;
+ }
+ Some(Path::BarePath(Interned::new(ModPath::from_segments(
+ mod_path.kind,
+ mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
+ ))))
+ }
+ Path::Normal(path) => {
+ let mod_path = path.mod_path();
+ if mod_path.is_ident() {
+ return None;
+ }
+ let type_anchor = path.type_anchor();
+ let generic_args = path.generic_args();
+ let qualifier_mod_path = Interned::new(ModPath::from_segments(
+ mod_path.kind,
+ mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
+ ));
+ let qualifier_generic_args = &generic_args[..generic_args.len() - 1];
+ Some(Path::Normal(NormalPath::new(
+ type_anchor,
+ qualifier_mod_path,
+ qualifier_generic_args.iter().cloned(),
+ )))
+ }
+ Path::LangItem(..) => None,
}
- let res = Path::Normal {
- type_anchor: type_anchor.clone(),
- mod_path: Interned::new(ModPath::from_segments(
- mod_path.kind,
- mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
- )),
- generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
- };
- Some(res)
}
pub fn is_self_type(&self) -> bool {
- let Path::Normal { mod_path, generic_args, type_anchor } = self else {
- return false;
- };
- type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self()
+ match self {
+ Path::BarePath(mod_path) => mod_path.is_Self(),
+ Path::Normal(path) => {
+ path.type_anchor().is_none()
+ && path.mod_path().is_Self()
+ && path.generic_args().iter().all(|args| args.is_none())
+ }
+ Path::LangItem(..) => false,
+ }
}
}
@@ -204,7 +238,7 @@ pub struct PathSegment<'a> {
pub struct PathSegments<'a> {
segments: &'a [Name],
- generic_args: Option<&'a [Option<Interned<GenericArgs>>]>,
+ generic_args: Option<&'a [Option<GenericArgs>]>,
}
impl<'a> PathSegments<'a> {
@@ -224,7 +258,7 @@ impl<'a> PathSegments<'a> {
pub fn get(&self, idx: usize) -> Option<PathSegment<'a>> {
let res = PathSegment {
name: self.segments.get(idx)?,
- args_and_bindings: self.generic_args.and_then(|it| it.get(idx)?.as_deref()),
+ args_and_bindings: self.generic_args.and_then(|it| it.get(idx)?.as_ref()),
};
Some(res)
}
@@ -244,7 +278,7 @@ impl<'a> PathSegments<'a> {
self.segments
.iter()
.zip(self.generic_args.into_iter().flatten().chain(iter::repeat(&None)))
- .map(|(name, args)| PathSegment { name, args_and_bindings: args.as_deref() })
+ .map(|(name, args)| PathSegment { name, args_and_bindings: args.as_ref() })
}
}
@@ -268,16 +302,6 @@ impl GenericArgs {
impl From<Name> for Path {
fn from(name: Name) -> Path {
- Path::Normal {
- type_anchor: None,
- mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
- generic_args: None,
- }
- }
-}
-
-impl From<Name> for Box<Path> {
- fn from(name: Name) -> Box<Path> {
- Box::new(Path::from(name))
+ Path::BarePath(Interned::new(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 70918a9358..c328b9c6ce 100644
--- a/crates/hir-def/src/path/lower.rs
+++ b/crates/hir-def/src/path/lower.rs
@@ -2,13 +2,14 @@
use std::iter;
-use crate::{lower::LowerCtx, type_ref::ConstRef};
+use crate::{lower::LowerCtx, path::NormalPath, type_ref::ConstRef};
use hir_expand::{
mod_path::resolve_crate_root,
name::{AsName, Name},
};
use intern::{sym, Interned};
+use stdx::thin_vec::EmptyOptimizedThinVec;
use syntax::ast::{self, AstNode, HasGenericArgs, HasTypeBounds};
use crate::{
@@ -51,8 +52,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path
segment.param_list(),
segment.ret_type(),
)
- })
- .map(Interned::new);
+ });
if args.is_some() {
generic_args.resize(segments.len(), None);
generic_args.push(args);
@@ -70,16 +70,14 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path
match trait_ref {
// <T>::foo
None => {
- type_anchor = Some(Interned::new(self_type));
+ type_anchor = Some(self_type);
kind = PathKind::Plain;
}
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
Some(trait_ref) => {
- let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
- Path::from_src(ctx, trait_ref.path()?)?
- else {
- return None;
- };
+ let path = Path::from_src(ctx, trait_ref.path()?)?;
+ let mod_path = path.mod_path()?;
+ let path_generic_args = path.generic_args();
let num_segments = mod_path.segments().len();
kind = mod_path.kind;
@@ -95,7 +93,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path
// Insert the type reference (T in the above example) as Self parameter for the trait
let last_segment = generic_args.get_mut(segments.len() - num_segments)?;
- *last_segment = Some(Interned::new(match last_segment.take() {
+ *last_segment = Some(match last_segment.take() {
Some(it) => GenericArgs {
args: iter::once(self_type)
.chain(it.args.iter().cloned())
@@ -110,7 +108,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path
has_self_type: true,
..GenericArgs::empty()
},
- }));
+ });
}
}
}
@@ -137,7 +135,7 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path
};
}
segments.reverse();
- if !generic_args.is_empty() {
+ if !generic_args.is_empty() || type_anchor.is_some() {
generic_args.resize(segments.len(), None);
generic_args.reverse();
}
@@ -166,11 +164,11 @@ pub(super) fn lower_path(ctx: &LowerCtx<'_>, mut path: ast::Path) -> Option<Path
}
let mod_path = Interned::new(ModPath::from_segments(kind, segments));
- return Some(Path::Normal {
- type_anchor,
- mod_path,
- generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },
- });
+ if type_anchor.is_none() && generic_args.is_empty() {
+ return Some(Path::BarePath(mod_path));
+ } else {
+ return Some(Path::Normal(NormalPath::new(type_anchor, mod_path, generic_args)));
+ }
fn qualifier(path: &ast::Path) -> Option<ast::Path> {
if let Some(q) = path.qualifier() {
@@ -194,11 +192,13 @@ pub(super) fn lower_generic_args(
match generic_arg {
ast::GenericArg::TypeArg(type_arg) => {
let type_ref = TypeRef::from_ast_opt(lower_ctx, type_arg.ty());
- type_ref.walk(&mut |tr| {
+ let types_map = lower_ctx.types_map();
+ TypeRef::walk(type_ref, &types_map, &mut |tr| {
if let TypeRef::ImplTrait(bounds) = tr {
lower_ctx.update_impl_traits_bounds(bounds.clone());
}
});
+ drop(types_map);
args.push(GenericArg::Type(type_ref));
}
ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
@@ -212,20 +212,19 @@ pub(super) fn lower_generic_args(
let name = name_ref.as_name();
let args = assoc_type_arg
.generic_arg_list()
- .and_then(|args| lower_generic_args(lower_ctx, args))
- .map(Interned::new);
+ .and_then(|args| lower_generic_args(lower_ctx, args));
let type_ref = assoc_type_arg.ty().map(|it| TypeRef::from_ast(lower_ctx, it));
- let type_ref = type_ref.inspect(|tr| {
- tr.walk(&mut |tr| {
+ let type_ref = type_ref.inspect(|&tr| {
+ let types_map = lower_ctx.types_map();
+ TypeRef::walk(tr, &types_map, &mut |tr| {
if let TypeRef::ImplTrait(bounds) = tr {
lower_ctx.update_impl_traits_bounds(bounds.clone());
}
});
+ drop(types_map);
});
let bounds = if let Some(l) = assoc_type_arg.type_bound_list() {
- l.bounds()
- .map(|it| Interned::new(TypeBound::from_ast(lower_ctx, it)))
- .collect()
+ l.bounds().map(|it| TypeBound::from_ast(lower_ctx, it)).collect()
} else {
Box::default()
};
@@ -269,7 +268,9 @@ fn lower_generic_args_from_fn_path(
let type_ref = TypeRef::from_ast_opt(ctx, param.ty());
param_types.push(type_ref);
}
- let args = Box::new([GenericArg::Type(TypeRef::Tuple(param_types))]);
+ let args = Box::new([GenericArg::Type(
+ ctx.alloc_type_ref_desugared(TypeRef::Tuple(EmptyOptimizedThinVec::from_iter(param_types))),
+ )]);
let bindings = if let Some(ret_type) = ret_type {
let type_ref = TypeRef::from_ast_opt(ctx, ret_type.ty());
Box::new([AssociatedTypeBinding {
@@ -280,7 +281,7 @@ fn lower_generic_args_from_fn_path(
}])
} else {
// -> ()
- let type_ref = TypeRef::Tuple(Vec::new());
+ let type_ref = ctx.alloc_type_ref_desugared(TypeRef::unit());
Box::new([AssociatedTypeBinding {
name: Name::new_symbol_root(sym::Output.clone()),
args: None,
diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs
index d5ef17a91f..9ceb82d5fd 100644
--- a/crates/hir-def/src/pretty.rs
+++ b/crates/hir-def/src/pretty.rs
@@ -1,9 +1,11 @@
//! Display and pretty printing routines.
-use std::fmt::{self, Write};
+use std::{
+ fmt::{self, Write},
+ mem,
+};
use hir_expand::mod_path::PathKind;
-use intern::Interned;
use itertools::Itertools;
use span::Edition;
@@ -11,12 +13,15 @@ use crate::{
db::DefDatabase,
lang_item::LangItemTarget,
path::{GenericArg, GenericArgs, Path},
- type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
+ type_ref::{
+ Mutability, TraitBoundModifier, TypeBound, TypeRef, TypeRefId, TypesMap, UseArgRef,
+ },
};
pub(crate) fn print_path(
db: &dyn DefDatabase,
path: &Path,
+ map: &TypesMap,
buf: &mut dyn Write,
edition: Edition,
) -> fmt::Result {
@@ -58,7 +63,7 @@ pub(crate) fn print_path(
match path.type_anchor() {
Some(anchor) => {
write!(buf, "<")?;
- print_type_ref(db, anchor, buf, edition)?;
+ print_type_ref(db, anchor, map, buf, edition)?;
write!(buf, ">::")?;
}
None => match path.kind() {
@@ -87,7 +92,7 @@ pub(crate) fn print_path(
write!(buf, "{}", segment.name.display(db.upcast(), edition))?;
if let Some(generics) = segment.args_and_bindings {
write!(buf, "::<")?;
- print_generic_args(db, generics, buf, edition)?;
+ print_generic_args(db, generics, map, buf, edition)?;
write!(buf, ">")?;
}
@@ -99,6 +104,7 @@ pub(crate) fn print_path(
pub(crate) fn print_generic_args(
db: &dyn DefDatabase,
generics: &GenericArgs,
+ map: &TypesMap,
buf: &mut dyn Write,
edition: Edition,
) -> fmt::Result {
@@ -106,7 +112,7 @@ pub(crate) fn print_generic_args(
let args = if generics.has_self_type {
let (self_ty, args) = generics.args.split_first().unwrap();
write!(buf, "Self=")?;
- print_generic_arg(db, self_ty, buf, edition)?;
+ print_generic_arg(db, self_ty, map, buf, edition)?;
first = false;
args
} else {
@@ -117,7 +123,7 @@ pub(crate) fn print_generic_args(
write!(buf, ", ")?;
}
first = false;
- print_generic_arg(db, arg, buf, edition)?;
+ print_generic_arg(db, arg, map, buf, edition)?;
}
for binding in generics.bindings.iter() {
if !first {
@@ -127,11 +133,11 @@ pub(crate) fn print_generic_args(
write!(buf, "{}", binding.name.display(db.upcast(), edition))?;
if !binding.bounds.is_empty() {
write!(buf, ": ")?;
- print_type_bounds(db, &binding.bounds, buf, edition)?;
+ print_type_bounds(db, &binding.bounds, map, buf, edition)?;
}
- if let Some(ty) = &binding.type_ref {
+ if let Some(ty) = binding.type_ref {
write!(buf, " = ")?;
- print_type_ref(db, ty, buf, edition)?;
+ print_type_ref(db, ty, map, buf, edition)?;
}
}
Ok(())
@@ -140,11 +146,12 @@ pub(crate) fn print_generic_args(
pub(crate) fn print_generic_arg(
db: &dyn DefDatabase,
arg: &GenericArg,
+ map: &TypesMap,
buf: &mut dyn Write,
edition: Edition,
) -> fmt::Result {
match arg {
- GenericArg::Type(ty) => print_type_ref(db, ty, buf, edition),
+ GenericArg::Type(ty) => print_type_ref(db, *ty, map, buf, edition),
GenericArg::Const(c) => write!(buf, "{}", c.display(db.upcast(), edition)),
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast(), edition)),
}
@@ -152,12 +159,13 @@ pub(crate) fn print_generic_arg(
pub(crate) fn print_type_ref(
db: &dyn DefDatabase,
- type_ref: &TypeRef,
+ type_ref: TypeRefId,
+ map: &TypesMap,
buf: &mut dyn Write,
edition: Edition,
) -> fmt::Result {
// FIXME: deduplicate with `HirDisplay` impl
- match type_ref {
+ match &map[type_ref] {
TypeRef::Never => write!(buf, "!")?,
TypeRef::Placeholder => write!(buf, "_")?,
TypeRef::Tuple(fields) => {
@@ -166,48 +174,48 @@ pub(crate) fn print_type_ref(
if i != 0 {
write!(buf, ", ")?;
}
- print_type_ref(db, field, buf, edition)?;
+ print_type_ref(db, *field, map, buf, edition)?;
}
write!(buf, ")")?;
}
- TypeRef::Path(path) => print_path(db, path, buf, edition)?,
+ TypeRef::Path(path) => print_path(db, path, map, buf, edition)?,
TypeRef::RawPtr(pointee, mtbl) => {
let mtbl = match mtbl {
Mutability::Shared => "*const",
Mutability::Mut => "*mut",
};
write!(buf, "{mtbl} ")?;
- print_type_ref(db, pointee, buf, edition)?;
+ print_type_ref(db, *pointee, map, buf, edition)?;
}
- TypeRef::Reference(pointee, lt, mtbl) => {
- let mtbl = match mtbl {
+ TypeRef::Reference(ref_) => {
+ let mtbl = match ref_.mutability {
Mutability::Shared => "",
Mutability::Mut => "mut ",
};
write!(buf, "&")?;
- if let Some(lt) = lt {
+ if let Some(lt) = &ref_.lifetime {
write!(buf, "{} ", lt.name.display(db.upcast(), edition))?;
}
write!(buf, "{mtbl}")?;
- print_type_ref(db, pointee, buf, edition)?;
+ print_type_ref(db, ref_.ty, map, buf, edition)?;
}
- TypeRef::Array(elem, len) => {
+ TypeRef::Array(array) => {
write!(buf, "[")?;
- print_type_ref(db, elem, buf, edition)?;
- write!(buf, "; {}]", len.display(db.upcast(), edition))?;
+ print_type_ref(db, array.ty, map, buf, edition)?;
+ write!(buf, "; {}]", array.len.display(db.upcast(), edition))?;
}
TypeRef::Slice(elem) => {
write!(buf, "[")?;
- print_type_ref(db, elem, buf, edition)?;
+ print_type_ref(db, *elem, map, buf, edition)?;
write!(buf, "]")?;
}
- TypeRef::Fn(args_and_ret, varargs, is_unsafe, abi) => {
+ TypeRef::Fn(fn_) => {
let ((_, return_type), args) =
- args_and_ret.split_last().expect("TypeRef::Fn is missing return type");
- if *is_unsafe {
+ fn_.params().split_last().expect("TypeRef::Fn is missing return type");
+ if fn_.is_unsafe() {
write!(buf, "unsafe ")?;
}
- if let Some(abi) = abi {
+ if let Some(abi) = fn_.abi() {
buf.write_str("extern ")?;
buf.write_str(abi.as_str())?;
buf.write_char(' ')?;
@@ -217,16 +225,16 @@ pub(crate) fn print_type_ref(
if i != 0 {
write!(buf, ", ")?;
}
- print_type_ref(db, typeref, buf, edition)?;
+ print_type_ref(db, *typeref, map, buf, edition)?;
}
- if *varargs {
+ if fn_.is_varargs() {
if !args.is_empty() {
write!(buf, ", ")?;
}
write!(buf, "...")?;
}
write!(buf, ") -> ")?;
- print_type_ref(db, return_type, buf, edition)?;
+ print_type_ref(db, *return_type, map, buf, edition)?;
}
TypeRef::Macro(_ast_id) => {
write!(buf, "<macro>")?;
@@ -234,11 +242,11 @@ pub(crate) fn print_type_ref(
TypeRef::Error => write!(buf, "{{unknown}}")?,
TypeRef::ImplTrait(bounds) => {
write!(buf, "impl ")?;
- print_type_bounds(db, bounds, buf, edition)?;
+ print_type_bounds(db, bounds, map, buf, edition)?;
}
TypeRef::DynTrait(bounds) => {
write!(buf, "dyn ")?;
- print_type_bounds(db, bounds, buf, edition)?;
+ print_type_bounds(db, bounds, map, buf, edition)?;
}
}
@@ -247,7 +255,8 @@ pub(crate) fn print_type_ref(
pub(crate) fn print_type_bounds(
db: &dyn DefDatabase,
- bounds: &[Interned<TypeBound>],
+ bounds: &[TypeBound],
+ map: &TypesMap,
buf: &mut dyn Write,
edition: Edition,
) -> fmt::Result {
@@ -256,13 +265,13 @@ pub(crate) fn print_type_bounds(
write!(buf, " + ")?;
}
- match bound.as_ref() {
+ match bound {
TypeBound::Path(path, modifier) => {
match modifier {
TraitBoundModifier::None => (),
TraitBoundModifier::Maybe => write!(buf, "?")?,
}
- print_path(db, path, buf, edition)?;
+ print_path(db, path, map, buf, edition)?;
}
TypeBound::ForLifetime(lifetimes, path) => {
write!(
@@ -270,9 +279,25 @@ pub(crate) fn print_type_bounds(
"for<{}> ",
lifetimes.iter().map(|it| it.display(db.upcast(), edition)).format(", ")
)?;
- print_path(db, path, buf, edition)?;
+ print_path(db, path, map, buf, edition)?;
}
TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast(), edition))?,
+ TypeBound::Use(args) => {
+ write!(buf, "use<")?;
+ let mut first = true;
+ for arg in args {
+ if !mem::take(&mut first) {
+ write!(buf, ", ")?;
+ }
+ match arg {
+ UseArgRef::Name(it) => write!(buf, "{}", it.display(db.upcast(), edition))?,
+ UseArgRef::Lifetime(it) => {
+ write!(buf, "{}", it.name.display(db.upcast(), edition))?
+ }
+ }
+ }
+ write!(buf, ">")?
+ }
TypeBound::Error => write!(buf, "{{unknown}}")?,
}
}
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index f0f2210ec2..26655e40ca 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -3,14 +3,17 @@ use std::{fmt, iter, mem};
use base_db::CrateId;
use hir_expand::{name::Name, MacroDefId};
-use intern::{sym, Interned};
+use intern::sym;
use itertools::Itertools as _;
use rustc_hash::FxHashSet;
use smallvec::{smallvec, SmallVec};
use triomphe::Arc;
use crate::{
- body::scope::{ExprScopes, ScopeId},
+ body::{
+ scope::{ExprScopes, ScopeId},
+ HygieneId,
+ },
builtin_type::BuiltinType,
data::ExternCrateDeclData,
db::DefDatabase,
@@ -21,7 +24,7 @@ use crate::{
nameres::{DefMap, MacroSubNs},
path::{ModPath, Path, PathKind},
per_ns::PerNs,
- type_ref::LifetimeRef,
+ type_ref::{LifetimeRef, TypesMap},
visibility::{RawVisibility, Visibility},
AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId,
ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, HasModule,
@@ -73,13 +76,15 @@ enum Scope {
/// All the items and imported names of a module
BlockScope(ModuleItemMap),
/// Brings the generic parameters of an item into scope
- GenericParams { def: GenericDefId, params: Interned<GenericParams> },
+ GenericParams { def: GenericDefId, params: Arc<GenericParams> },
/// Brings `Self` in `impl` block into scope
ImplDefScope(ImplId),
/// Brings `Self` in enum, struct and union definitions into scope
AdtScope(AdtId),
/// Local bindings
ExprScope(ExprScope),
+ /// Macro definition inside bodies that affects all paths after it in the same block.
+ MacroDefScope(Box<MacroDefId>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -162,7 +167,8 @@ impl Resolver {
path: &Path,
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
let path = match path {
- Path::Normal { mod_path, .. } => mod_path,
+ Path::BarePath(mod_path) => mod_path,
+ Path::Normal(it) => it.mod_path(),
Path::LangItem(l, seg) => {
let type_ns = match *l {
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
@@ -188,7 +194,7 @@ impl Resolver {
for scope in self.scopes() {
match scope {
- Scope::ExprScope(_) => continue,
+ Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue,
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_type_by_name(first_name, *def) {
return Some((TypeNs::GenericParam(id), remaining_idx(), None));
@@ -257,9 +263,11 @@ impl Resolver {
&self,
db: &dyn DefDatabase,
path: &Path,
+ mut hygiene_id: HygieneId,
) -> Option<ResolveValueResult> {
let path = match path {
- Path::Normal { mod_path, .. } => mod_path,
+ Path::BarePath(mod_path) => mod_path,
+ Path::Normal(it) => it.mod_path(),
Path::LangItem(l, None) => {
return Some(ResolveValueResult::ValueNs(
match *l {
@@ -300,14 +308,22 @@ impl Resolver {
}
if n_segments <= 1 {
+ let mut hygiene_info = if !hygiene_id.is_root() {
+ let ctx = db.lookup_intern_syntax_context(hygiene_id.0);
+ ctx.outer_expn.map(|expansion| {
+ let expansion = db.lookup_intern_macro_call(expansion);
+ (ctx.parent, expansion.def)
+ })
+ } else {
+ None
+ };
for scope in self.scopes() {
match scope {
Scope::ExprScope(scope) => {
- let entry = scope
- .expr_scopes
- .entries(scope.scope_id)
- .iter()
- .find(|entry| entry.name() == first_name);
+ let entry =
+ scope.expr_scopes.entries(scope.scope_id).iter().find(|entry| {
+ entry.name() == first_name && entry.hygiene() == hygiene_id
+ });
if let Some(e) = entry {
return Some(ResolveValueResult::ValueNs(
@@ -316,6 +332,21 @@ impl Resolver {
));
}
}
+ Scope::MacroDefScope(macro_id) => {
+ if let Some((parent_ctx, label_macro_id)) = hygiene_info {
+ if label_macro_id == **macro_id {
+ // A macro is allowed to refer to variables from before its declaration.
+ // Therefore, if we got to the rib of its declaration, give up its hygiene
+ // and use its parent expansion.
+ let parent_ctx = db.lookup_intern_syntax_context(parent_ctx);
+ hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent);
+ hygiene_info = parent_ctx.outer_expn.map(|expansion| {
+ let expansion = db.lookup_intern_macro_call(expansion);
+ (parent_ctx.parent, expansion.def)
+ });
+ }
+ }
+ }
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_const_by_name(first_name, *def) {
let val = ValueNs::GenericParam(id);
@@ -342,7 +373,7 @@ impl Resolver {
} else {
for scope in self.scopes() {
match scope {
- Scope::ExprScope(_) => continue,
+ Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue,
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_type_by_name(first_name, *def) {
let ty = TypeNs::GenericParam(id);
@@ -393,8 +424,9 @@ impl Resolver {
&self,
db: &dyn DefDatabase,
path: &Path,
+ hygiene: HygieneId,
) -> Option<ValueNs> {
- match self.resolve_path_in_value_ns(db, path)? {
+ match self.resolve_path_in_value_ns(db, path, hygiene)? {
ResolveValueResult::ValueNs(it, _) => Some(it),
ResolveValueResult::Partial(..) => None,
}
@@ -590,13 +622,15 @@ impl Resolver {
pub fn where_predicates_in_scope(
&self,
- ) -> impl Iterator<Item = (&crate::generics::WherePredicate, &GenericDefId)> {
+ ) -> impl Iterator<Item = (&crate::generics::WherePredicate, (&GenericDefId, &TypesMap))> {
self.scopes()
.filter_map(|scope| match scope {
Scope::GenericParams { params, def } => Some((params, def)),
_ => None,
})
- .flat_map(|(params, def)| params.where_predicates().zip(iter::repeat(def)))
+ .flat_map(|(params, def)| {
+ params.where_predicates().zip(iter::repeat((def, &params.types_map)))
+ })
}
pub fn generic_def(&self) -> Option<GenericDefId> {
@@ -606,13 +640,20 @@ impl Resolver {
})
}
- pub fn generic_params(&self) -> Option<&Interned<GenericParams>> {
+ pub fn generic_params(&self) -> Option<&Arc<GenericParams>> {
self.scopes().find_map(|scope| match scope {
Scope::GenericParams { params, .. } => Some(params),
_ => None,
})
}
+ pub fn all_generic_params(&self) -> impl Iterator<Item = (&GenericParams, &GenericDefId)> {
+ self.scopes().filter_map(|scope| match scope {
+ Scope::GenericParams { params, def } => Some((&**params, def)),
+ _ => None,
+ })
+ }
+
pub fn body_owner(&self) -> Option<DefWithBodyId> {
self.scopes().find_map(|scope| match scope {
Scope::ExprScope(it) => Some(it.owner),
@@ -622,7 +663,7 @@ impl Resolver {
pub fn type_owner(&self) -> Option<TypeOwnerId> {
self.scopes().find_map(|scope| match scope {
- Scope::BlockScope(_) => None,
+ Scope::BlockScope(_) | Scope::MacroDefScope(_) => None,
&Scope::GenericParams { def, .. } => Some(def.into()),
&Scope::ImplDefScope(id) => Some(id.into()),
&Scope::AdtScope(adt) => Some(adt.into()),
@@ -653,6 +694,9 @@ impl Resolver {
expr_scopes: &Arc<ExprScopes>,
scope_id: ScopeId,
) {
+ if let Some(macro_id) = expr_scopes.macro_def(scope_id) {
+ resolver.scopes.push(Scope::MacroDefScope(macro_id.clone()));
+ }
resolver.scopes.push(Scope::ExprScope(ExprScope {
owner,
expr_scopes: expr_scopes.clone(),
@@ -670,7 +714,7 @@ impl Resolver {
}
let start = self.scopes.len();
- let innermost_scope = self.scopes().next();
+ let innermost_scope = self.scopes().find(|scope| !matches!(scope, Scope::MacroDefScope(_)));
match innermost_scope {
Some(&Scope::ExprScope(ExprScope { scope_id, ref expr_scopes, owner })) => {
let expr_scopes = expr_scopes.clone();
@@ -794,6 +838,7 @@ impl Scope {
acc.add_local(e.name(), e.binding());
});
}
+ Scope::MacroDefScope(_) => {}
}
}
}
@@ -833,6 +878,9 @@ fn resolver_for_scope_(
// already traverses all parents, so this is O(n²). I think we could only store the
// innermost module scope instead?
}
+ if let Some(macro_id) = scopes.macro_def(scope) {
+ r = r.push_scope(Scope::MacroDefScope(macro_id.clone()));
+ }
r = r.push_expr_scope(owner, Arc::clone(&scopes), scope);
}
@@ -1006,12 +1054,12 @@ impl HasResolver for ModuleId {
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
let mut def_map = self.def_map(db);
let mut module_id = self.local_id;
- let mut modules: SmallVec<[_; 1]> = smallvec![];
if !self.is_block_module() {
return Resolver { scopes: vec![], module_scope: ModuleItemMap { def_map, module_id } };
}
+ let mut modules: SmallVec<[_; 1]> = smallvec![];
while let Some(parent) = def_map.parent() {
let block_def_map = mem::replace(&mut def_map, parent.def_map(db));
modules.push(block_def_map);
diff --git a/crates/hir-def/src/test_db.rs b/crates/hir-def/src/test_db.rs
index 4db21eb46b..0c36c88fb0 100644
--- a/crates/hir-def/src/test_db.rs
+++ b/crates/hir-def/src/test_db.rs
@@ -198,7 +198,10 @@ impl TestDB {
.filter_map(|node| {
let block = ast::BlockExpr::cast(node)?;
let expr = ast::Expr::from(block);
- let expr_id = source_map.node_expr(InFile::new(position.file_id.into(), &expr))?;
+ let expr_id = source_map
+ .node_expr(InFile::new(position.file_id.into(), &expr))?
+ .as_expr()
+ .unwrap();
let scope = scopes.scope_for(expr_id).unwrap();
Some(scope)
});
diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs
index 3aeb88047a..4edb683592 100644
--- a/crates/hir-def/src/visibility.rs
+++ b/crates/hir-def/src/visibility.rs
@@ -191,6 +191,11 @@ impl Visibility {
return None;
}
+ let def_block = def_map.block_id();
+ if (mod_a.containing_block(), mod_b.containing_block()) != (def_block, def_block) {
+ return None;
+ }
+
let mut a_ancestors =
iter::successors(Some(mod_a.local_id), |&m| def_map[m].parent);
let mut b_ancestors =
@@ -210,6 +215,43 @@ impl Visibility {
}
}
}
+
+ /// Returns the least permissive visibility of `self` and `other`.
+ ///
+ /// If there is no subset relation between `self` and `other`, returns `None` (ie. they're only
+ /// visible in unrelated modules).
+ pub(crate) fn min(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
+ match (self, other) {
+ (vis, Visibility::Public) | (Visibility::Public, vis) => Some(vis),
+ (Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => {
+ if mod_a.krate != mod_b.krate {
+ return None;
+ }
+
+ let def_block = def_map.block_id();
+ if (mod_a.containing_block(), mod_b.containing_block()) != (def_block, def_block) {
+ return None;
+ }
+
+ let mut a_ancestors =
+ iter::successors(Some(mod_a.local_id), |&m| def_map[m].parent);
+ let mut b_ancestors =
+ iter::successors(Some(mod_b.local_id), |&m| def_map[m].parent);
+
+ if a_ancestors.any(|m| m == mod_b.local_id) {
+ // B is above A
+ return Some(Visibility::Module(mod_a, expl_b));
+ }
+
+ if b_ancestors.any(|m| m == mod_a.local_id) {
+ // A is above B
+ return Some(Visibility::Module(mod_b, expl_a));
+ }
+
+ None
+ }
+ }
+ }
}
/// Whether the item was imported through an explicit `pub(crate) use` or just a `use` without
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index 79cfeb4cf1..12df3cf218 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -26,6 +26,7 @@ use crate::{
/// Syntactical attributes, without filtering of `cfg_attr`s.
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct RawAttrs {
+ // FIXME: This can become `Box<[Attr]>` if https://internals.rust-lang.org/t/layout-of-dst-box/21728?u=chrefr is accepted.
entries: Option<ThinArc<(), Attr>>,
}
@@ -169,6 +170,10 @@ impl RawAttrs {
};
RawAttrs { entries }
}
+
+ pub fn is_empty(&self) -> bool {
+ self.entries.is_none()
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs
index a819118915..f48de807c2 100644
--- a/crates/hir-expand/src/hygiene.rs
+++ b/crates/hir-expand/src/hygiene.rs
@@ -227,7 +227,7 @@ pub(crate) fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String {
&'a SyntaxContextData,
);
- impl<'a> std::fmt::Debug for SyntaxContextDebug<'a> {
+ impl std::fmt::Debug for SyntaxContextDebug<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fancy_debug(self.2, self.1, self.0, f)
}
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 5d5f72490d..7d2f556406 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -165,40 +165,73 @@ pub enum ExpandErrorKind {
}
impl ExpandError {
- pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> (String, bool) {
+ pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> RenderedExpandError {
self.inner.0.render_to_string(db)
}
}
+pub struct RenderedExpandError {
+ pub message: String,
+ pub error: bool,
+ pub kind: &'static str,
+}
+
+impl RenderedExpandError {
+ const GENERAL_KIND: &str = "macro-error";
+}
+
impl ExpandErrorKind {
- pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> (String, bool) {
+ pub fn render_to_string(&self, db: &dyn ExpandDatabase) -> RenderedExpandError {
match self {
- ExpandErrorKind::ProcMacroAttrExpansionDisabled => {
- ("procedural attribute macro expansion is disabled".to_owned(), false)
- }
- ExpandErrorKind::MacroDisabled => {
- ("proc-macro is explicitly disabled".to_owned(), false)
- }
+ ExpandErrorKind::ProcMacroAttrExpansionDisabled => RenderedExpandError {
+ message: "procedural attribute macro expansion is disabled".to_owned(),
+ error: false,
+ kind: "proc-macros-disabled",
+ },
+ ExpandErrorKind::MacroDisabled => RenderedExpandError {
+ message: "proc-macro is explicitly disabled".to_owned(),
+ error: false,
+ kind: "proc-macro-disabled",
+ },
&ExpandErrorKind::MissingProcMacroExpander(def_crate) => {
match db.proc_macros().get_error_for_crate(def_crate) {
- Some((e, hard_err)) => (e.to_owned(), hard_err),
- None => (
- format!(
- "internal error: proc-macro map is missing error entry for crate {def_crate:?}"
- ),
- true,
- ),
+ Some((e, hard_err)) => RenderedExpandError {
+ message: e.to_owned(),
+ error: hard_err,
+ kind: RenderedExpandError::GENERAL_KIND,
+ },
+ None => RenderedExpandError {
+ message: format!("internal error: proc-macro map is missing error entry for crate {def_crate:?}"),
+ error: true,
+ kind: RenderedExpandError::GENERAL_KIND,
+ },
}
}
- ExpandErrorKind::MacroDefinition => {
- ("macro definition has parse errors".to_owned(), true)
- }
- ExpandErrorKind::Mbe(e) => (e.to_string(), true),
- ExpandErrorKind::RecursionOverflow => {
- ("overflow expanding the original macro".to_owned(), true)
- }
- ExpandErrorKind::Other(e) => ((**e).to_owned(), true),
- ExpandErrorKind::ProcMacroPanic(e) => (format!("proc-macro panicked: {e}"), true),
+ ExpandErrorKind::MacroDefinition => RenderedExpandError {
+ message: "macro definition has parse errors".to_owned(),
+ error: true,
+ kind: RenderedExpandError::GENERAL_KIND,
+ },
+ ExpandErrorKind::Mbe(e) => RenderedExpandError {
+ message: e.to_string(),
+ error: true,
+ kind: RenderedExpandError::GENERAL_KIND,
+ },
+ ExpandErrorKind::RecursionOverflow => RenderedExpandError {
+ message: "overflow expanding the original macro".to_owned(),
+ error: true,
+ kind: RenderedExpandError::GENERAL_KIND,
+ },
+ ExpandErrorKind::Other(e) => RenderedExpandError {
+ message: (**e).to_owned(),
+ error: true,
+ kind: RenderedExpandError::GENERAL_KIND,
+ },
+ ExpandErrorKind::ProcMacroPanic(e) => RenderedExpandError {
+ message: format!("proc-macro panicked: {e}"),
+ error: true,
+ kind: RenderedExpandError::GENERAL_KIND,
+ },
}
}
}
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 54313904a7..267d545833 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -18,6 +18,8 @@ use syntax::utils::is_raw_identifier;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Name {
symbol: Symbol,
+ // If you are making this carry actual hygiene, beware that the special handling for variables and labels
+ // in bodies can go.
ctx: (),
}
diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs
index 7a3846df40..2b5342314a 100644
--- a/crates/hir-ty/src/autoderef.rs
+++ b/crates/hir-ty/src/autoderef.rs
@@ -114,7 +114,7 @@ impl<'table, 'db> Autoderef<'table, 'db, usize> {
}
#[allow(private_bounds)]
-impl<'table, 'db, T: TrackAutoderefSteps> Autoderef<'table, 'db, T> {
+impl<T: TrackAutoderefSteps> Autoderef<'_, '_, T> {
pub(crate) fn step_count(&self) -> usize {
self.steps.len()
}
diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs
index f7bacbd49b..4bc78afacc 100644
--- a/crates/hir-ty/src/chalk_db.rs
+++ b/crates/hir-ty/src/chalk_db.rs
@@ -521,7 +521,7 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
}
}
-impl<'a> ChalkContext<'a> {
+impl ChalkContext<'_> {
fn edition(&self) -> Edition {
self.db.crate_graph()[self.krate].edition
}
@@ -615,8 +615,9 @@ pub(crate) fn associated_ty_data_query(
let type_alias_data = db.type_alias_data(type_alias);
let generic_params = generics(db.upcast(), type_alias.into());
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
- let ctx = crate::TyLoweringContext::new(db, &resolver, type_alias.into())
- .with_type_param_mode(crate::lower::ParamLoweringMode::Variable);
+ let ctx =
+ crate::TyLoweringContext::new(db, &resolver, &type_alias_data.types_map, type_alias.into())
+ .with_type_param_mode(crate::lower::ParamLoweringMode::Variable);
let trait_subst = TyBuilder::subst_for_def(db, trait_, None)
.fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, generic_params.len_self())
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index e41058aac2..091cfcd465 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -3,7 +3,7 @@
use base_db::{ra_salsa::Cycle, CrateId};
use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
use hir_def::{
- body::Body,
+ body::{Body, HygieneId},
hir::{Expr, ExprId},
path::Path,
resolver::{Resolver, ValueNs},
@@ -11,7 +11,7 @@ use hir_def::{
ConstBlockLoc, EnumVariantId, GeneralConstId, StaticId,
};
use hir_expand::Lookup;
-use stdx::{never, IsNoneOr};
+use stdx::never;
use triomphe::Arc;
use crate::{
@@ -80,7 +80,7 @@ pub(crate) fn path_to_const<'g>(
debruijn: DebruijnIndex,
expected_ty: Ty,
) -> Option<Const> {
- match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) {
+ match resolver.resolve_path_in_value_ns_fully(db.upcast(), path, HygieneId::ROOT) {
Some(ValueNs::GenericParam(p)) => {
let ty = db.const_param_ty(p);
let value = match mode {
@@ -287,7 +287,7 @@ pub(crate) fn const_eval_discriminant_variant(
}
let repr = db.enum_data(loc.parent).repr;
- let is_signed = IsNoneOr::is_none_or(repr.and_then(|repr| repr.int), |int| int.is_signed());
+ let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed());
let mir_body = db.monomorphized_mir_body(
def,
@@ -319,7 +319,7 @@ pub(crate) fn eval_to_const(
return true;
}
let mut r = false;
- body[expr].walk_child_exprs(|idx| r |= has_closure(body, idx));
+ body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx));
r
}
if has_closure(ctx.body, expr) {
diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs
index 7f6b7e392b..c9ab0acc08 100644
--- a/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -309,7 +309,7 @@ impl<'a> DeclValidator<'a> {
/// Check incorrect names for struct fields.
fn validate_struct_fields(&mut self, struct_id: StructId) {
let data = self.db.struct_data(struct_id);
- let VariantData::Record(fields) = data.variant_data.as_ref() else {
+ let VariantData::Record { fields, .. } = data.variant_data.as_ref() else {
return;
};
let edition = self.edition(struct_id);
@@ -469,7 +469,7 @@ impl<'a> DeclValidator<'a> {
/// Check incorrect names for fields of enum variant.
fn validate_enum_variant_fields(&mut self, variant_id: EnumVariantId) {
let variant_data = self.db.enum_variant_data(variant_id);
- let VariantData::Record(fields) = variant_data.variant_data.as_ref() else {
+ let VariantData::Record { fields, .. } = variant_data.variant_data.as_ref() else {
return;
};
let edition = self.edition(variant_id);
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index f8b5c7d0ce..92404e3a10 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -289,10 +289,12 @@ impl ExprValidator {
match &self.body[scrutinee_expr] {
Expr::UnaryOp { op: UnaryOp::Deref, .. } => false,
Expr::Path(path) => {
- let value_or_partial = self
- .owner
- .resolver(db.upcast())
- .resolve_path_in_value_ns_fully(db.upcast(), path);
+ let value_or_partial =
+ self.owner.resolver(db.upcast()).resolve_path_in_value_ns_fully(
+ db.upcast(),
+ path,
+ self.body.expr_path_hygiene(scrutinee_expr),
+ );
value_or_partial.map_or(true, |v| !matches!(v, ValueNs::StaticId(_)))
}
Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) {
@@ -546,10 +548,7 @@ pub fn record_literal_missing_fields(
expr: &Expr,
) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> {
let (fields, exhaustive) = match expr {
- Expr::RecordLit { fields, spread, ellipsis, is_assignee_expr, .. } => {
- let exhaustive = if *is_assignee_expr { !*ellipsis } else { spread.is_none() };
- (fields, exhaustive)
- }
+ Expr::RecordLit { fields, spread, .. } => (fields, spread.is_none()),
_ => return None,
};
diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs
index 4bc07bc9ec..c5d8c95661 100644
--- a/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/crates/hir-ty/src/diagnostics/match_check.rs
@@ -341,7 +341,7 @@ impl HirDisplay for Pat {
};
let variant_data = variant.variant_data(f.db.upcast());
- if let VariantData::Record(rec_fields) = &*variant_data {
+ if let VariantData::Record { fields: rec_fields, .. } = &*variant_data {
write!(f, " {{ ")?;
let mut printed = 0;
diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index 1066a28c3f..58de19ba81 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -519,7 +519,7 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
}
}
-impl<'db> fmt::Debug for MatchCheckCtx<'db> {
+impl fmt::Debug for MatchCheckCtx<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MatchCheckCtx").finish()
}
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index bcfc37c867..c7f7fb7ad3 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -3,7 +3,7 @@
use hir_def::{
body::Body,
- hir::{Expr, ExprId, UnaryOp},
+ hir::{Expr, ExprId, ExprOrPatId, Pat, UnaryOp},
resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
type_ref::Rawness,
DefWithBodyId,
@@ -16,7 +16,7 @@ use crate::{
/// Returns `(unsafe_exprs, fn_is_unsafe)`.
///
/// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`.
-pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> (Vec<ExprId>, bool) {
+pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> (Vec<ExprOrPatId>, bool) {
let _p = tracing::info_span!("missing_unsafe").entered();
let mut res = Vec::new();
@@ -32,7 +32,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> (Vec<ExprId>,
let infer = db.infer(def);
unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| {
if !expr.inside_unsafe_block {
- res.push(expr.expr);
+ res.push(expr.node);
}
});
@@ -40,7 +40,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> (Vec<ExprId>,
}
pub struct UnsafeExpr {
- pub expr: ExprId,
+ pub node: ExprOrPatId,
pub inside_unsafe_block: bool,
}
@@ -75,26 +75,29 @@ fn walk_unsafe(
inside_unsafe_block: bool,
unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
) {
+ let mut mark_unsafe_path = |path, node| {
+ let g = resolver.update_to_inner_scope(db.upcast(), def, current);
+ let hygiene = body.expr_or_pat_path_hygiene(node);
+ let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path, hygiene);
+ if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
+ let static_data = db.static_data(id);
+ if static_data.mutable || (static_data.is_extern && !static_data.has_safe_kw) {
+ unsafe_expr_cb(UnsafeExpr { node, inside_unsafe_block });
+ }
+ }
+ resolver.reset_to_guard(g);
+ };
+
let expr = &body.exprs[current];
match expr {
&Expr::Call { callee, .. } => {
if let Some(func) = infer[callee].as_fn_def(db) {
if is_fn_unsafe_to_call(db, func) {
- unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
- }
- }
- }
- Expr::Path(path) => {
- let g = resolver.update_to_inner_scope(db.upcast(), def, current);
- let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
- if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
- let static_data = db.static_data(id);
- if static_data.mutable || (static_data.is_extern && !static_data.has_safe_kw) {
- unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
+ unsafe_expr_cb(UnsafeExpr { node: current.into(), inside_unsafe_block });
}
}
- resolver.reset_to_guard(g);
}
+ Expr::Path(path) => mark_unsafe_path(path, current.into()),
Expr::Ref { expr, rawness: Rawness::RawPtr, mutability: _ } => {
if let Expr::Path(_) = body.exprs[*expr] {
// Do not report unsafe for `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)`,
@@ -108,23 +111,30 @@ fn walk_unsafe(
.map(|(func, _)| is_fn_unsafe_to_call(db, func))
.unwrap_or(false)
{
- unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
+ unsafe_expr_cb(UnsafeExpr { node: current.into(), inside_unsafe_block });
}
}
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
if let TyKind::Raw(..) = &infer[*expr].kind(Interner) {
- unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
+ unsafe_expr_cb(UnsafeExpr { node: current.into(), inside_unsafe_block });
}
}
Expr::Unsafe { .. } => {
- return expr.walk_child_exprs(|child| {
+ return body.walk_child_exprs(current, |child| {
walk_unsafe(db, infer, body, resolver, def, child, true, unsafe_expr_cb);
});
}
+ &Expr::Assignment { target, value: _ } => {
+ body.walk_pats(target, &mut |pat| {
+ if let Pat::Path(path) = &body[pat] {
+ mark_unsafe_path(path, pat.into());
+ }
+ });
+ }
_ => {}
}
- expr.walk_child_exprs(|child| {
+ body.walk_child_exprs(current, |child| {
walk_unsafe(db, infer, body, resolver, def, child, inside_unsafe_block, unsafe_expr_cb);
});
}
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 10f5bcdad8..277dabe9aa 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -19,7 +19,9 @@ use hir_def::{
lang_item::{LangItem, LangItemTarget},
nameres::DefMap,
path::{Path, PathKind},
- type_ref::{TraitBoundModifier, TypeBound, TypeRef},
+ type_ref::{
+ TraitBoundModifier, TypeBound, TypeRef, TypeRefId, TypesMap, TypesSourceMap, UseArgRef,
+ },
visibility::Visibility,
GenericDefId, HasModule, ImportPathConfig, ItemContainerId, LocalFieldId, Lookup, ModuleDefId,
ModuleId, TraitId,
@@ -806,7 +808,7 @@ fn render_variant_after_name(
memory_map: &MemoryMap,
) -> Result<(), HirDisplayError> {
match data {
- VariantData::Record(fields) | VariantData::Tuple(fields) => {
+ VariantData::Record { fields, .. } | VariantData::Tuple { fields, .. } => {
let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| {
let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize();
let ty = field_types[id].clone().substitute(Interner, subst);
@@ -817,7 +819,7 @@ fn render_variant_after_name(
render_const_scalar(f, &b[offset..offset + size], memory_map, &ty)
};
let mut it = fields.iter();
- if matches!(data, VariantData::Record(_)) {
+ if matches!(data, VariantData::Record { .. }) {
write!(f, " {{")?;
if let Some((id, data)) = it.next() {
write!(f, " {}: ", data.name.display(f.db.upcast(), f.edition()))?;
@@ -1897,100 +1899,150 @@ pub fn write_visibility(
}
}
-impl HirDisplay for TypeRef {
+pub trait HirDisplayWithTypesMap {
+ fn hir_fmt(
+ &self,
+ f: &mut HirFormatter<'_>,
+ types_map: &TypesMap,
+ ) -> Result<(), HirDisplayError>;
+}
+
+impl<T: ?Sized + HirDisplayWithTypesMap> HirDisplayWithTypesMap for &'_ T {
+ fn hir_fmt(
+ &self,
+ f: &mut HirFormatter<'_>,
+ types_map: &TypesMap,
+ ) -> Result<(), HirDisplayError> {
+ T::hir_fmt(&**self, f, types_map)
+ }
+}
+
+pub fn hir_display_with_types_map<'a, T: HirDisplayWithTypesMap + 'a>(
+ value: T,
+ types_map: &'a TypesMap,
+) -> impl HirDisplay + 'a {
+ TypesMapAdapter(value, types_map)
+}
+
+struct TypesMapAdapter<'a, T>(T, &'a TypesMap);
+
+impl<'a, T> TypesMapAdapter<'a, T> {
+ fn wrap(types_map: &'a TypesMap) -> impl Fn(T) -> TypesMapAdapter<'a, T> {
+ move |value| TypesMapAdapter(value, types_map)
+ }
+}
+
+impl<T: HirDisplayWithTypesMap> HirDisplay for TypesMapAdapter<'_, T> {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
- match self {
+ T::hir_fmt(&self.0, f, self.1)
+ }
+}
+
+impl HirDisplayWithTypesMap for TypeRefId {
+ fn hir_fmt(
+ &self,
+ f: &mut HirFormatter<'_>,
+ types_map: &TypesMap,
+ ) -> Result<(), HirDisplayError> {
+ match &types_map[*self] {
TypeRef::Never => write!(f, "!")?,
TypeRef::Placeholder => write!(f, "_")?,
TypeRef::Tuple(elems) => {
write!(f, "(")?;
- f.write_joined(elems, ", ")?;
+ f.write_joined(elems.iter().map(TypesMapAdapter::wrap(types_map)), ", ")?;
if elems.len() == 1 {
write!(f, ",")?;
}
write!(f, ")")?;
}
- TypeRef::Path(path) => path.hir_fmt(f)?,
+ TypeRef::Path(path) => path.hir_fmt(f, types_map)?,
TypeRef::RawPtr(inner, mutability) => {
let mutability = match mutability {
hir_def::type_ref::Mutability::Shared => "*const ",
hir_def::type_ref::Mutability::Mut => "*mut ",
};
write!(f, "{mutability}")?;
- inner.hir_fmt(f)?;
+ inner.hir_fmt(f, types_map)?;
}
- TypeRef::Reference(inner, lifetime, mutability) => {
- let mutability = match mutability {
+ TypeRef::Reference(ref_) => {
+ let mutability = match ref_.mutability {
hir_def::type_ref::Mutability::Shared => "",
hir_def::type_ref::Mutability::Mut => "mut ",
};
write!(f, "&")?;
- if let Some(lifetime) = lifetime {
+ if let Some(lifetime) = &ref_.lifetime {
write!(f, "{} ", lifetime.name.display(f.db.upcast(), f.edition()))?;
}
write!(f, "{mutability}")?;
- inner.hir_fmt(f)?;
+ ref_.ty.hir_fmt(f, types_map)?;
}
- TypeRef::Array(inner, len) => {
+ TypeRef::Array(array) => {
write!(f, "[")?;
- inner.hir_fmt(f)?;
- write!(f, "; {}]", len.display(f.db.upcast(), f.edition()))?;
+ array.ty.hir_fmt(f, types_map)?;
+ write!(f, "; {}]", array.len.display(f.db.upcast(), f.edition()))?;
}
TypeRef::Slice(inner) => {
write!(f, "[")?;
- inner.hir_fmt(f)?;
+ inner.hir_fmt(f, types_map)?;
write!(f, "]")?;
}
- &TypeRef::Fn(ref parameters, is_varargs, is_unsafe, ref abi) => {
- if is_unsafe {
+ TypeRef::Fn(fn_) => {
+ if fn_.is_unsafe() {
write!(f, "unsafe ")?;
}
- if let Some(abi) = abi {
+ if let Some(abi) = fn_.abi() {
f.write_str("extern \"")?;
f.write_str(abi.as_str())?;
f.write_str("\" ")?;
}
write!(f, "fn(")?;
- if let Some(((_, return_type), function_parameters)) = parameters.split_last() {
+ if let Some(((_, return_type), function_parameters)) = fn_.params().split_last() {
for index in 0..function_parameters.len() {
let (param_name, param_type) = &function_parameters[index];
if let Some(name) = param_name {
write!(f, "{}: ", name.display(f.db.upcast(), f.edition()))?;
}
- param_type.hir_fmt(f)?;
+ param_type.hir_fmt(f, types_map)?;
if index != function_parameters.len() - 1 {
write!(f, ", ")?;
}
}
- if is_varargs {
- write!(f, "{}...", if parameters.len() == 1 { "" } else { ", " })?;
+ if fn_.is_varargs() {
+ write!(f, "{}...", if fn_.params().len() == 1 { "" } else { ", " })?;
}
write!(f, ")")?;
- match &return_type {
+ match &types_map[*return_type] {
TypeRef::Tuple(tup) if tup.is_empty() => {}
_ => {
write!(f, " -> ")?;
- return_type.hir_fmt(f)?;
+ return_type.hir_fmt(f, types_map)?;
}
}
}
}
TypeRef::ImplTrait(bounds) => {
write!(f, "impl ")?;
- f.write_joined(bounds, " + ")?;
+ f.write_joined(bounds.iter().map(TypesMapAdapter::wrap(types_map)), " + ")?;
}
TypeRef::DynTrait(bounds) => {
write!(f, "dyn ")?;
- f.write_joined(bounds, " + ")?;
+ f.write_joined(bounds.iter().map(TypesMapAdapter::wrap(types_map)), " + ")?;
}
TypeRef::Macro(macro_call) => {
- let ctx = hir_def::lower::LowerCtx::new(f.db.upcast(), macro_call.file_id);
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let ctx = hir_def::lower::LowerCtx::new(
+ f.db.upcast(),
+ macro_call.file_id,
+ &mut types_map,
+ &mut types_source_map,
+ );
let macro_call = macro_call.to_node(f.db.upcast());
match macro_call.path() {
Some(path) => match Path::from_src(&ctx, path) {
- Some(path) => path.hir_fmt(f)?,
+ Some(path) => path.hir_fmt(f, &types_map)?,
None => write!(f, "{{macro}}")?,
},
None => write!(f, "{{macro}}")?,
@@ -2003,15 +2055,19 @@ impl HirDisplay for TypeRef {
}
}
-impl HirDisplay for TypeBound {
- fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+impl HirDisplayWithTypesMap for TypeBound {
+ fn hir_fmt(
+ &self,
+ f: &mut HirFormatter<'_>,
+ types_map: &TypesMap,
+ ) -> Result<(), HirDisplayError> {
match self {
TypeBound::Path(path, modifier) => {
match modifier {
TraitBoundModifier::None => (),
TraitBoundModifier::Maybe => write!(f, "?")?,
}
- path.hir_fmt(f)
+ path.hir_fmt(f, types_map)
}
TypeBound::Lifetime(lifetime) => {
write!(f, "{}", lifetime.name.display(f.db.upcast(), f.edition()))
@@ -2023,19 +2079,36 @@ impl HirDisplay for TypeBound {
"for<{}> ",
lifetimes.iter().map(|it| it.display(f.db.upcast(), edition)).format(", ")
)?;
- path.hir_fmt(f)
+ path.hir_fmt(f, types_map)
+ }
+ TypeBound::Use(args) => {
+ let edition = f.edition();
+ write!(
+ f,
+ "use<{}> ",
+ args.iter()
+ .map(|it| match it {
+ UseArgRef::Lifetime(lt) => lt.name.display(f.db.upcast(), edition),
+ UseArgRef::Name(n) => n.display(f.db.upcast(), edition),
+ })
+ .format(", ")
+ )
}
TypeBound::Error => write!(f, "{{error}}"),
}
}
}
-impl HirDisplay for Path {
- fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+impl HirDisplayWithTypesMap for Path {
+ fn hir_fmt(
+ &self,
+ f: &mut HirFormatter<'_>,
+ types_map: &TypesMap,
+ ) -> Result<(), HirDisplayError> {
match (self.type_anchor(), self.kind()) {
(Some(anchor), _) => {
write!(f, "<")?;
- anchor.hir_fmt(f)?;
+ anchor.hir_fmt(f, types_map)?;
write!(f, ">")?;
}
(_, PathKind::Plain) => {}
@@ -2078,7 +2151,7 @@ impl HirDisplay for Path {
});
if let Some(ty) = trait_self_ty {
write!(f, "<")?;
- ty.hir_fmt(f)?;
+ ty.hir_fmt(f, types_map)?;
write!(f, " as ")?;
// Now format the path of the trait...
}
@@ -2094,21 +2167,26 @@ impl HirDisplay for Path {
if generic_args.desugared_from_fn {
// First argument will be a tuple, which already includes the parentheses.
// If the tuple only contains 1 item, write it manually to avoid the trailing `,`.
- if let hir_def::path::GenericArg::Type(TypeRef::Tuple(v)) =
- &generic_args.args[0]
- {
+ let tuple = match generic_args.args[0] {
+ hir_def::path::GenericArg::Type(ty) => match &types_map[ty] {
+ TypeRef::Tuple(it) => Some(it),
+ _ => None,
+ },
+ _ => None,
+ };
+ if let Some(v) = tuple {
if v.len() == 1 {
write!(f, "(")?;
- v[0].hir_fmt(f)?;
+ v[0].hir_fmt(f, types_map)?;
write!(f, ")")?;
} else {
- generic_args.args[0].hir_fmt(f)?;
+ generic_args.args[0].hir_fmt(f, types_map)?;
}
}
- if let Some(ret) = &generic_args.bindings[0].type_ref {
- if !matches!(ret, TypeRef::Tuple(v) if v.is_empty()) {
+ if let Some(ret) = generic_args.bindings[0].type_ref {
+ if !matches!(&types_map[ret], TypeRef::Tuple(v) if v.is_empty()) {
write!(f, " -> ")?;
- ret.hir_fmt(f)?;
+ ret.hir_fmt(f, types_map)?;
}
}
return Ok(());
@@ -2123,7 +2201,7 @@ impl HirDisplay for Path {
} else {
write!(f, ", ")?;
}
- arg.hir_fmt(f)?;
+ arg.hir_fmt(f, types_map)?;
}
for binding in generic_args.bindings.iter() {
if first {
@@ -2136,11 +2214,14 @@ impl HirDisplay for Path {
match &binding.type_ref {
Some(ty) => {
write!(f, " = ")?;
- ty.hir_fmt(f)?
+ ty.hir_fmt(f, types_map)?
}
None => {
write!(f, ": ")?;
- f.write_joined(binding.bounds.iter(), " + ")?;
+ f.write_joined(
+ binding.bounds.iter().map(TypesMapAdapter::wrap(types_map)),
+ " + ",
+ )?;
}
}
}
@@ -2162,10 +2243,14 @@ impl HirDisplay for Path {
}
}
-impl HirDisplay for hir_def::path::GenericArg {
- fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+impl HirDisplayWithTypesMap for hir_def::path::GenericArg {
+ fn hir_fmt(
+ &self,
+ f: &mut HirFormatter<'_>,
+ types_map: &TypesMap,
+ ) -> Result<(), HirDisplayError> {
match self {
- hir_def::path::GenericArg::Type(ty) => ty.hir_fmt(f),
+ hir_def::path::GenericArg::Type(ty) => ty.hir_fmt(f, types_map),
hir_def::path::GenericArg::Const(c) => {
write!(f, "{}", c.display(f.db.upcast(), f.edition()))
}
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs
index e0d1758210..3d21785a70 100644
--- a/crates/hir-ty/src/dyn_compatibility.rs
+++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -266,7 +266,7 @@ fn contains_illegal_self_type_reference<T: TypeVisitable<Interner>>(
trait_self_param_idx: usize,
allow_self_projection: AllowSelfProjection,
}
- impl<'a> TypeVisitor<Interner> for IllegalSelfTypeVisitor<'a> {
+ impl TypeVisitor<Interner> for IllegalSelfTypeVisitor<'_> {
type BreakTy = ();
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs
index 89ca707c2e..c094bc3951 100644
--- a/crates/hir-ty/src/generics.rs
+++ b/crates/hir-ty/src/generics.rs
@@ -16,12 +16,13 @@ use hir_def::{
GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData,
TypeParamProvenance,
},
+ type_ref::TypesMap,
ConstParamId, GenericDefId, GenericParamId, ItemContainerId, LifetimeParamId,
LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
};
-use intern::Interned;
use itertools::chain;
use stdx::TupleExt;
+use triomphe::Arc;
use crate::{db::HirDatabase, lt_to_placeholder_idx, to_placeholder_idx, Interner, Substitution};
@@ -34,7 +35,7 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
#[derive(Clone, Debug)]
pub(crate) struct Generics {
def: GenericDefId,
- params: Interned<GenericParams>,
+ params: Arc<GenericParams>,
parent_generics: Option<Box<Generics>>,
has_trait_self_param: bool,
}
@@ -85,6 +86,18 @@ impl Generics {
self.iter_self().chain(self.iter_parent())
}
+ pub(crate) fn iter_with_types_map(
+ &self,
+ ) -> impl Iterator<Item = ((GenericParamId, GenericParamDataRef<'_>), &TypesMap)> + '_ {
+ self.iter_self().zip(std::iter::repeat(&self.params.types_map)).chain(
+ self.iter_parent().zip(
+ self.parent_generics()
+ .into_iter()
+ .flat_map(|it| std::iter::repeat(&it.params.types_map)),
+ ),
+ )
+ }
+
/// Iterate over the params without parent params.
pub(crate) fn iter_self(
&self,
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 88334b492d..3685ed5696 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -33,7 +33,7 @@ use chalk_ir::{
};
use either::Either;
use hir_def::{
- body::Body,
+ body::{Body, HygieneId},
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
data::{ConstData, StaticData},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
@@ -41,7 +41,7 @@ use hir_def::{
layout::Integer,
path::{ModPath, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
- type_ref::{LifetimeRef, TypeRef},
+ type_ref::{LifetimeRef, TypeRefId, TypesMap},
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, ImplId, ItemContainerId, Lookup,
TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
};
@@ -228,7 +228,7 @@ pub enum InferenceDiagnostic {
id: ExprOrPatId,
},
UnresolvedIdent {
- expr: ExprId,
+ id: ExprOrPatId,
},
// FIXME: This should be emitted in body lowering
BreakOutsideOfLoop {
@@ -482,12 +482,27 @@ impl InferenceResult {
pub fn variant_resolution_for_pat(&self, id: PatId) -> Option<VariantId> {
self.variant_resolutions.get(&id.into()).copied()
}
+ pub fn variant_resolution_for_expr_or_pat(&self, id: ExprOrPatId) -> Option<VariantId> {
+ match id {
+ ExprOrPatId::ExprId(id) => self.variant_resolution_for_expr(id),
+ ExprOrPatId::PatId(id) => self.variant_resolution_for_pat(id),
+ }
+ }
pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<(AssocItemId, Substitution)> {
self.assoc_resolutions.get(&id.into()).cloned()
}
pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<(AssocItemId, Substitution)> {
self.assoc_resolutions.get(&id.into()).cloned()
}
+ pub fn assoc_resolutions_for_expr_or_pat(
+ &self,
+ id: ExprOrPatId,
+ ) -> Option<(AssocItemId, Substitution)> {
+ match id {
+ ExprOrPatId::ExprId(id) => self.assoc_resolutions_for_expr(id),
+ ExprOrPatId::PatId(id) => self.assoc_resolutions_for_pat(id),
+ }
+ }
pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
self.type_mismatches.get(&expr.into())
}
@@ -506,6 +521,12 @@ impl InferenceResult {
pub fn closure_info(&self, closure: &ClosureId) -> &(Vec<CapturedItem>, FnTrait) {
self.closure_info.get(closure).unwrap()
}
+ pub fn type_of_expr_or_pat(&self, id: ExprOrPatId) -> Option<&Ty> {
+ match id {
+ ExprOrPatId::ExprId(id) => self.type_of_expr.get(id),
+ ExprOrPatId::PatId(id) => self.type_of_pat.get(id),
+ }
+ }
}
impl Index<ExprId> for InferenceResult {
@@ -524,6 +545,14 @@ impl Index<PatId> for InferenceResult {
}
}
+impl Index<ExprOrPatId> for InferenceResult {
+ type Output = Ty;
+
+ fn index(&self, id: ExprOrPatId) -> &Ty {
+ self.type_of_expr_or_pat(id).unwrap_or(&self.standard_types.unknown)
+ }
+}
+
impl Index<BindingId> for InferenceResult {
type Output = Ty;
@@ -561,6 +590,9 @@ pub(crate) struct InferenceContext<'a> {
diverges: Diverges,
breakables: Vec<BreakableContext>,
+ /// Whether we are inside the pattern of a destructuring assignment.
+ inside_assignment: bool,
+
deferred_cast_checks: Vec<CastCheck>,
// fields related to closure capture
@@ -656,6 +688,7 @@ impl<'a> InferenceContext<'a> {
current_closure: None,
deferred_closures: FxHashMap::default(),
closure_dependencies: FxHashMap::default(),
+ inside_assignment: false,
}
}
@@ -825,7 +858,7 @@ impl<'a> InferenceContext<'a> {
}
fn collect_const(&mut self, data: &ConstData) {
- let return_ty = self.make_ty(&data.type_ref);
+ let return_ty = self.make_ty(data.type_ref, &data.types_map);
// Constants might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty));
@@ -834,7 +867,7 @@ impl<'a> InferenceContext<'a> {
}
fn collect_static(&mut self, data: &StaticData) {
- let return_ty = self.make_ty(&data.type_ref);
+ let return_ty = self.make_ty(data.type_ref, &data.types_map);
// Statics might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty));
@@ -844,11 +877,11 @@ impl<'a> InferenceContext<'a> {
fn collect_fn(&mut self, func: FunctionId) {
let data = self.db.function_data(func);
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into())
- .with_type_param_mode(ParamLoweringMode::Placeholder)
- .with_impl_trait_mode(ImplTraitLoweringMode::Param);
- let mut param_tys =
- data.params.iter().map(|type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
+ let mut param_tys = self.with_ty_lowering(&data.types_map, |ctx| {
+ ctx.type_param_mode(ParamLoweringMode::Placeholder)
+ .impl_trait_mode(ImplTraitLoweringMode::Param);
+ data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>()
+ });
// Check if function contains a va_list, if it does then we append it to the parameter types
// that are collected from the function data
if data.is_varargs() {
@@ -883,12 +916,13 @@ impl<'a> InferenceContext<'a> {
tait_candidates.insert(ty);
}
}
- let return_ty = &*data.ret_type;
+ let return_ty = data.ret_type;
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into())
- .with_type_param_mode(ParamLoweringMode::Placeholder)
- .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
- let return_ty = ctx.lower_ty(return_ty);
+ let return_ty = self.with_ty_lowering(&data.types_map, |ctx| {
+ ctx.type_param_mode(ParamLoweringMode::Placeholder)
+ .impl_trait_mode(ImplTraitLoweringMode::Opaque)
+ .lower_ty(return_ty)
+ });
let return_ty = self.insert_type_vars(return_ty);
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
@@ -1022,7 +1056,7 @@ impl<'a> InferenceContext<'a> {
non_assocs: FxHashMap<OpaqueTyId, Ty>,
}
- impl<'a, 'b> TypeVisitor<Interner> for TypeAliasImplTraitCollector<'a, 'b> {
+ impl TypeVisitor<Interner> for TypeAliasImplTraitCollector<'_, '_> {
type BreakTy = ();
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
@@ -1192,20 +1226,43 @@ impl<'a> InferenceContext<'a> {
self.result.diagnostics.push(diagnostic);
}
- fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
- let ty = ctx.lower_ty(type_ref);
+ fn with_ty_lowering<R>(
+ &self,
+ types_map: &TypesMap,
+ f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
+ ) -> R {
+ let mut ctx = crate::lower::TyLoweringContext::new(
+ self.db,
+ &self.resolver,
+ types_map,
+ self.owner.into(),
+ );
+ f(&mut ctx)
+ }
+
+ fn with_body_ty_lowering<R>(
+ &self,
+ f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
+ ) -> R {
+ self.with_ty_lowering(&self.body.types, f)
+ }
+
+ fn make_ty(&mut self, type_ref: TypeRefId, types_map: &TypesMap) -> Ty {
+ let ty = self.with_ty_lowering(types_map, |ctx| ctx.lower_ty(type_ref));
let ty = self.insert_type_vars(ty);
self.normalize_associated_types_in(ty)
}
+ fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty {
+ self.make_ty(type_ref, &self.body.types)
+ }
+
fn err_ty(&self) -> Ty {
self.result.standard_types.unknown.clone()
}
fn make_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime {
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
- let lt = ctx.lower_lifetime(lifetime_ref);
+ let lt = self.with_ty_lowering(TypesMap::EMPTY, |ctx| ctx.lower_lifetime(lifetime_ref));
self.insert_type_vars(lt)
}
@@ -1363,9 +1420,14 @@ impl<'a> InferenceContext<'a> {
Some(path) => path,
None => return (self.err_ty(), None),
};
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
+ let ctx = crate::lower::TyLoweringContext::new(
+ self.db,
+ &self.resolver,
+ &self.body.types,
+ self.owner.into(),
+ );
let (resolution, unresolved) = if value_ns {
- match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
+ match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, HygieneId::ROOT) {
Some(ResolveValueResult::ValueNs(value, _)) => match value {
ValueNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index e9825cf099..5a251683b9 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -11,11 +11,12 @@ use either::Either;
use hir_def::{
data::adt::VariantData,
hir::{
- Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement,
- UnaryOp,
+ Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId,
+ Statement, UnaryOp,
},
lang_item::LangItem,
- resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
+ path::Path,
+ resolver::ValueNs,
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
};
use hir_expand::name::Name;
@@ -282,11 +283,11 @@ impl CapturedItem {
ProjectionElem::Deref => {}
ProjectionElem::Field(Either::Left(f)) => {
match &*f.parent.variant_data(db.upcast()) {
- VariantData::Record(fields) => {
+ VariantData::Record { fields, .. } => {
result.push('_');
result.push_str(fields[f.local_id].name.as_str())
}
- VariantData::Tuple(fields) => {
+ VariantData::Tuple { fields, .. } => {
let index = fields.iter().position(|it| it.0 == f.local_id);
if let Some(index) = index {
format_to!(result, "_{index}");
@@ -324,12 +325,12 @@ impl CapturedItem {
ProjectionElem::Field(Either::Left(f)) => {
let variant_data = f.parent.variant_data(db.upcast());
match &*variant_data {
- VariantData::Record(fields) => format_to!(
+ VariantData::Record { fields, .. } => format_to!(
result,
".{}",
fields[f.local_id].name.display(db.upcast(), edition)
),
- VariantData::Tuple(fields) => format_to!(
+ VariantData::Tuple { fields, .. } => format_to!(
result,
".{}",
fields.iter().position(|it| it.0 == f.local_id).unwrap_or_default()
@@ -382,8 +383,10 @@ impl CapturedItem {
}
let variant_data = f.parent.variant_data(db.upcast());
let field = match &*variant_data {
- VariantData::Record(fields) => fields[f.local_id].name.as_str().to_owned(),
- VariantData::Tuple(fields) => fields
+ VariantData::Record { fields, .. } => {
+ fields[f.local_id].name.as_str().to_owned()
+ }
+ VariantData::Tuple { fields, .. } => fields
.iter()
.position(|it| it.0 == f.local_id)
.unwrap_or_default()
@@ -508,18 +511,39 @@ impl InferenceContext<'_> {
apply_adjusts_to_place(&mut self.current_capture_span_stack, r, adjustments)
}
+ /// Pushes the span into `current_capture_span_stack`, *without clearing it first*.
+ fn path_place(&mut self, path: &Path, id: ExprOrPatId) -> Option<HirPlace> {
+ if path.type_anchor().is_some() {
+ return None;
+ }
+ let hygiene = self.body.expr_or_pat_path_hygiene(id);
+ let result = self
+ .resolver
+ .resolve_path_in_value_ns_fully(self.db.upcast(), path, hygiene)
+ .and_then(|result| match result {
+ ValueNs::LocalBinding(binding) => {
+ let mir_span = match id {
+ ExprOrPatId::ExprId(id) => MirSpan::ExprId(id),
+ ExprOrPatId::PatId(id) => MirSpan::PatId(id),
+ };
+ self.current_capture_span_stack.push(mir_span);
+ Some(HirPlace { local: binding, projections: Vec::new() })
+ }
+ _ => None,
+ });
+ result
+ }
+
/// Changes `current_capture_span_stack` to contain the stack of spans for this expr.
fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
self.current_capture_span_stack.clear();
match &self.body[tgt_expr] {
Expr::Path(p) => {
- let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
- if let Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(b), _)) =
- resolver.resolve_path_in_value_ns(self.db.upcast(), p)
- {
- self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
- return Some(HirPlace { local: b, projections: vec![] });
- }
+ let resolver_guard =
+ self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
+ let result = self.path_place(p, tgt_expr.into());
+ self.resolver.reset_to_guard(resolver_guard);
+ return result;
}
Expr::Field { expr, name: _ } => {
let mut place = self.place_of_expr(*expr)?;
@@ -590,6 +614,16 @@ impl InferenceContext<'_> {
}
}
+ fn mutate_path_pat(&mut self, path: &Path, id: PatId) {
+ if let Some(place) = self.path_place(path, id.into()) {
+ self.add_capture(
+ place,
+ CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }),
+ );
+ self.current_capture_span_stack.pop(); // Remove the pattern span.
+ }
+ }
+
fn mutate_expr(&mut self, expr: ExprId, place: Option<HirPlace>) {
if let Some(place) = place {
self.add_capture(
@@ -715,14 +749,14 @@ impl InferenceContext<'_> {
Statement::Expr { expr, has_semi: _ } => {
self.consume_expr(*expr);
}
- Statement::Item => (),
+ Statement::Item(_) => (),
}
}
if let Some(tail) = tail {
self.consume_expr(*tail);
}
}
- Expr::Call { callee, args, is_assignee_expr: _ } => {
+ Expr::Call { callee, args } => {
self.consume_expr(*callee);
self.consume_exprs(args.iter().copied());
}
@@ -838,7 +872,7 @@ impl InferenceContext<'_> {
self.consume_expr(expr);
}
}
- Expr::Index { base, index, is_assignee_expr: _ } => {
+ Expr::Index { base, index } => {
self.select_from_expr(*base);
self.consume_expr(*index);
}
@@ -862,10 +896,30 @@ impl InferenceContext<'_> {
}));
self.current_captures = cc;
}
- Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ })
- | Expr::Tuple { exprs, is_assignee_expr: _ } => {
+ Expr::Array(Array::ElementList { elements: exprs }) | Expr::Tuple { exprs } => {
self.consume_exprs(exprs.iter().copied())
}
+ &Expr::Assignment { target, value } => {
+ self.walk_expr(value);
+ let resolver_guard =
+ self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
+ match self.place_of_expr(value) {
+ Some(rhs_place) => {
+ self.inside_assignment = true;
+ self.consume_with_pat(rhs_place, target);
+ self.inside_assignment = false;
+ }
+ None => self.body.walk_pats(target, &mut |pat| match &self.body[pat] {
+ Pat::Path(path) => self.mutate_path_pat(path, pat),
+ &Pat::Expr(expr) => {
+ let place = self.place_of_expr(expr);
+ self.mutate_expr(expr, place);
+ }
+ _ => {}
+ }),
+ }
+ self.resolver.reset_to_guard(resolver_guard);
+ }
Expr::Missing
| Expr::Continue { .. }
@@ -903,6 +957,7 @@ impl InferenceContext<'_> {
| Pat::Missing
| Pat::Wild
| Pat::Tuple { .. }
+ | Pat::Expr(_)
| Pat::Or(_) => (),
Pat::TupleStruct { .. } | Pat::Record { .. } => {
if let Some(variant) = self.result.variant_resolution_for_pat(p) {
@@ -1122,11 +1177,15 @@ impl InferenceContext<'_> {
}
}
}
- Pat::Range { .. }
- | Pat::Slice { .. }
- | Pat::ConstBlock(_)
- | Pat::Path(_)
- | Pat::Lit(_) => self.consume_place(place),
+ Pat::Range { .. } | Pat::Slice { .. } | Pat::ConstBlock(_) | Pat::Lit(_) => {
+ self.consume_place(place)
+ }
+ Pat::Path(path) => {
+ if self.inside_assignment {
+ self.mutate_path_pat(path, tgt_pat);
+ }
+ self.consume_place(place);
+ }
&Pat::Bind { id, subpat: _ } => {
let mode = self.result.binding_modes[tgt_pat];
let capture_kind = match mode {
@@ -1180,6 +1239,15 @@ impl InferenceContext<'_> {
self.current_capture_span_stack.pop();
}
Pat::Box { .. } => (), // not supported
+ &Pat::Expr(expr) => {
+ self.consume_place(place);
+ let pat_capture_span_stack = mem::take(&mut self.current_capture_span_stack);
+ let old_inside_assignment = mem::replace(&mut self.inside_assignment, false);
+ let lhs_place = self.place_of_expr(expr);
+ self.mutate_expr(expr, lhs_place);
+ self.inside_assignment = old_inside_assignment;
+ self.current_capture_span_stack = pat_capture_span_stack;
+ }
}
}
self.current_capture_span_stack
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 657e4d7796..32b4ea2f28 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -9,8 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
use either::Either;
use hir_def::{
hir::{
- ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId,
- Literal, Pat, PatId, Statement, UnaryOp,
+ ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, ExprOrPatId,
+ LabelId, Literal, Pat, PatId, Statement, UnaryOp,
},
lang_item::{LangItem, LangItemTarget},
path::{GenericArg, GenericArgs, Path},
@@ -188,6 +188,9 @@ impl InferenceContext<'_> {
| Pat::ConstBlock(_)
| Pat::Record { .. }
| Pat::Missing => true,
+ Pat::Expr(_) => unreachable!(
+ "we don't call pat_guaranteed_to_constitute_read_for_never() with assignments"
+ ),
}
}
@@ -195,10 +198,14 @@ impl InferenceContext<'_> {
match &self.body[expr] {
// Lang item paths cannot currently be local variables or statics.
Expr::Path(Path::LangItem(_, _)) => false,
- Expr::Path(Path::Normal { type_anchor: Some(_), .. }) => false,
+ Expr::Path(Path::Normal(path)) => path.type_anchor().is_none(),
Expr::Path(path) => self
.resolver
- .resolve_path_in_value_ns_fully(self.db.upcast(), path)
+ .resolve_path_in_value_ns_fully(
+ self.db.upcast(),
+ path,
+ self.body.expr_path_hygiene(expr),
+ )
.map_or(true, |res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))),
Expr::Underscore => true,
Expr::UnaryOp { op: UnaryOp::Deref, .. } => true,
@@ -223,6 +230,7 @@ impl InferenceContext<'_> {
| Expr::Const(..)
| Expr::UnaryOp { .. }
| Expr::BinaryOp { .. }
+ | Expr::Assignment { .. }
| Expr::Yield { .. }
| Expr::Cast { .. }
| Expr::Async { .. }
@@ -374,7 +382,7 @@ impl InferenceContext<'_> {
// collect explicitly written argument types
for arg_type in arg_types.iter() {
let arg_ty = match arg_type {
- Some(type_ref) => self.make_ty(type_ref),
+ Some(type_ref) => self.make_body_ty(*type_ref),
None => self.table.new_type_var(),
};
sig_tys.push(arg_ty);
@@ -382,7 +390,7 @@ impl InferenceContext<'_> {
// add return type
let ret_ty = match ret_type {
- Some(type_ref) => self.make_ty(type_ref),
+ Some(type_ref) => self.make_body_ty(*type_ref),
None => self.table.new_type_var(),
};
if let ClosureKind::Async = closure_kind {
@@ -609,23 +617,7 @@ impl InferenceContext<'_> {
coerce.complete(self)
}
}
- Expr::Path(p) => {
- let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
- let ty = match self.infer_path(p, tgt_expr.into()) {
- Some(ty) => ty,
- None => {
- if matches!(p, Path::Normal { mod_path, .. } if mod_path.is_ident() || mod_path.is_self())
- {
- self.push_diagnostic(InferenceDiagnostic::UnresolvedIdent {
- expr: tgt_expr,
- });
- }
- self.err_ty()
- }
- };
- self.resolver.reset_to_guard(g);
- ty
- }
+ Expr::Path(p) => self.infer_expr_path(p, tgt_expr.into(), tgt_expr),
&Expr::Continue { label } => {
if find_continuable(&mut self.breakables, label).is_none() {
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
@@ -794,7 +786,7 @@ impl InferenceContext<'_> {
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
}
Expr::Cast { expr, type_ref } => {
- let cast_ty = self.make_ty(type_ref);
+ let cast_ty = self.make_body_ty(*type_ref);
let expr_ty = self.infer_expr(
*expr,
&Expectation::Castable(cast_ty.clone()),
@@ -892,36 +884,6 @@ impl InferenceContext<'_> {
}
}
Expr::BinaryOp { lhs, rhs, op } => match op {
- Some(BinaryOp::Assignment { op: None }) => {
- let lhs = *lhs;
- let is_ordinary = match &self.body[lhs] {
- Expr::Array(_)
- | Expr::RecordLit { .. }
- | Expr::Tuple { .. }
- | Expr::Underscore => false,
- Expr::Call { callee, .. } => !matches!(&self.body[*callee], Expr::Path(_)),
- _ => true,
- };
-
- // In ordinary (non-destructuring) assignments, the type of
- // `lhs` must be inferred first so that the ADT fields
- // instantiations in RHS can be coerced to it. Note that this
- // cannot happen in destructuring assignments because of how
- // they are desugared.
- if is_ordinary {
- // LHS of assignment doesn't constitute reads.
- let lhs_ty = self.infer_expr(lhs, &Expectation::none(), ExprIsRead::No);
- self.infer_expr_coerce(
- *rhs,
- &Expectation::has_type(lhs_ty),
- ExprIsRead::No,
- );
- } else {
- let rhs_ty = self.infer_expr(*rhs, &Expectation::none(), ExprIsRead::Yes);
- self.infer_assignee_expr(lhs, &rhs_ty);
- }
- self.result.standard_types.unit.clone()
- }
Some(BinaryOp::LogicOp(_)) => {
let bool_ty = self.result.standard_types.bool_.clone();
self.infer_expr_coerce(
@@ -942,6 +904,35 @@ impl InferenceContext<'_> {
Some(op) => self.infer_overloadable_binop(*lhs, *op, *rhs, tgt_expr),
_ => self.err_ty(),
},
+ &Expr::Assignment { target, value } => {
+ // In ordinary (non-destructuring) assignments, the type of
+ // `lhs` must be inferred first so that the ADT fields
+ // instantiations in RHS can be coerced to it. Note that this
+ // cannot happen in destructuring assignments because of how
+ // they are desugared.
+ let lhs_ty = match &self.body[target] {
+ // LHS of assignment doesn't constitute reads.
+ &Pat::Expr(expr) => {
+ Some(self.infer_expr(expr, &Expectation::none(), ExprIsRead::No))
+ }
+ Pat::Path(path) => Some(self.infer_expr_path(path, target.into(), tgt_expr)),
+ _ => None,
+ };
+
+ if let Some(lhs_ty) = lhs_ty {
+ self.write_pat_ty(target, lhs_ty.clone());
+ self.infer_expr_coerce(value, &Expectation::has_type(lhs_ty), ExprIsRead::No);
+ } else {
+ let rhs_ty = self.infer_expr(value, &Expectation::none(), ExprIsRead::Yes);
+ let resolver_guard =
+ self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr);
+ self.inside_assignment = true;
+ self.infer_top_pat(target, &rhs_ty);
+ self.inside_assignment = false;
+ self.resolver.reset_to_guard(resolver_guard);
+ }
+ self.result.standard_types.unit.clone()
+ }
Expr::Range { lhs, rhs, range_type } => {
let lhs_ty =
lhs.map(|e| self.infer_expr_inner(e, &Expectation::none(), ExprIsRead::Yes));
@@ -981,7 +972,7 @@ impl InferenceContext<'_> {
(RangeOp::Inclusive, _, None) => self.err_ty(),
}
}
- Expr::Index { base, index, is_assignee_expr } => {
+ Expr::Index { base, index } => {
let base_ty = self.infer_expr_inner(*base, &Expectation::none(), ExprIsRead::Yes);
let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes);
@@ -1017,23 +1008,11 @@ impl InferenceContext<'_> {
self.write_method_resolution(tgt_expr, func, subst);
}
let assoc = self.resolve_ops_index_output();
- let res = self.resolve_associated_type_with_params(
+ self.resolve_associated_type_with_params(
self_ty.clone(),
assoc,
&[index_ty.clone().cast(Interner)],
- );
-
- if *is_assignee_expr {
- if let Some(index_trait) = self.resolve_lang_trait(LangItem::IndexMut) {
- let trait_ref = TyBuilder::trait_ref(self.db, index_trait)
- .push(self_ty)
- .fill(|_| index_ty.clone().cast(Interner))
- .build();
- self.push_obligation(trait_ref.cast(Interner));
- }
- }
-
- res
+ )
} else {
self.err_ty()
}
@@ -1151,9 +1130,7 @@ impl InferenceContext<'_> {
},
},
Expr::Underscore => {
- // Underscore expressions may only appear in assignee expressions,
- // which are handled by `infer_assignee_expr()`.
- // Any other underscore expression is an error, we render a specialized diagnostic
+ // Underscore expression is an error, we render a specialized diagnostic
// to let the user know what type is expected though.
let expected = expected.to_option(&mut self.table).unwrap_or_else(|| self.err_ty());
self.push_diagnostic(InferenceDiagnostic::TypedHole {
@@ -1232,6 +1209,22 @@ impl InferenceContext<'_> {
ty
}
+ fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty {
+ let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, scope_id);
+ let ty = match self.infer_path(path, id) {
+ Some(ty) => ty,
+ None => {
+ if path.mod_path().is_some_and(|mod_path| mod_path.is_ident() || mod_path.is_self())
+ {
+ self.push_diagnostic(InferenceDiagnostic::UnresolvedIdent { id });
+ }
+ self.err_ty()
+ }
+ };
+ self.resolver.reset_to_guard(g);
+ ty
+ }
+
fn infer_async_block(
&mut self,
tgt_expr: ExprId,
@@ -1482,107 +1475,6 @@ impl InferenceContext<'_> {
}
}
- pub(super) fn infer_assignee_expr(&mut self, lhs: ExprId, rhs_ty: &Ty) -> Ty {
- let is_rest_expr = |expr| {
- matches!(
- &self.body[expr],
- Expr::Range { lhs: None, rhs: None, range_type: RangeOp::Exclusive },
- )
- };
-
- let rhs_ty = self.resolve_ty_shallow(rhs_ty);
-
- let ty = match &self.body[lhs] {
- Expr::Tuple { exprs, .. } => {
- // We don't consider multiple ellipses. This is analogous to
- // `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
- let ellipsis = exprs.iter().position(|e| is_rest_expr(*e)).map(|it| it as u32);
- let exprs: Vec<_> = exprs.iter().filter(|e| !is_rest_expr(**e)).copied().collect();
-
- self.infer_tuple_pat_like(&rhs_ty, (), ellipsis, &exprs)
- }
- Expr::Call { callee, args, .. } => {
- // Tuple structs
- let path = match &self.body[*callee] {
- Expr::Path(path) => Some(path),
- _ => None,
- };
-
- // We don't consider multiple ellipses. This is analogous to
- // `hir_def::body::lower::ExprCollector::collect_tuple_pat()`.
- let ellipsis = args.iter().position(|e| is_rest_expr(*e)).map(|it| it as u32);
- let args: Vec<_> = args.iter().filter(|e| !is_rest_expr(**e)).copied().collect();
-
- self.infer_tuple_struct_pat_like(path, &rhs_ty, (), lhs, ellipsis, &args)
- }
- Expr::Array(Array::ElementList { elements, .. }) => {
- let elem_ty = match rhs_ty.kind(Interner) {
- TyKind::Array(st, _) => st.clone(),
- _ => self.err_ty(),
- };
-
- // There's no need to handle `..` as it cannot be bound.
- let sub_exprs = elements.iter().filter(|e| !is_rest_expr(**e));
-
- for e in sub_exprs {
- self.infer_assignee_expr(*e, &elem_ty);
- }
-
- match rhs_ty.kind(Interner) {
- TyKind::Array(_, _) => rhs_ty.clone(),
- // Even when `rhs_ty` is not an array type, this assignee
- // expression is inferred to be an array (of unknown element
- // type and length). This should not be just an error type,
- // because we are to compute the unifiability of this type and
- // `rhs_ty` in the end of this function to issue type mismatches.
- _ => TyKind::Array(
- self.err_ty(),
- crate::consteval::usize_const(self.db, None, self.resolver.krate()),
- )
- .intern(Interner),
- }
- }
- Expr::RecordLit { path, fields, .. } => {
- let subs = fields.iter().map(|f| (f.name.clone(), f.expr));
-
- self.infer_record_pat_like(path.as_deref(), &rhs_ty, (), lhs, subs)
- }
- Expr::Underscore => rhs_ty.clone(),
- _ => {
- // `lhs` is a place expression, a unit struct, or an enum variant.
- // LHS of assignment doesn't constitute reads.
- let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none(), ExprIsRead::No);
-
- // This is the only branch where this function may coerce any type.
- // We are returning early to avoid the unifiability check below.
- let lhs_ty = self.insert_type_vars_shallow(lhs_ty);
- let ty = match self.coerce(None, &rhs_ty, &lhs_ty, CoerceNever::Yes) {
- Ok(ty) => ty,
- Err(_) => {
- self.result.type_mismatches.insert(
- lhs.into(),
- TypeMismatch { expected: rhs_ty.clone(), actual: lhs_ty.clone() },
- );
- // `rhs_ty` is returned so no further type mismatches are
- // reported because of this mismatch.
- rhs_ty
- }
- };
- self.write_expr_ty(lhs, ty.clone());
- return ty;
- }
- };
-
- let ty = self.insert_type_vars_shallow(ty);
- if !self.unify(&ty, &rhs_ty) {
- self.result
- .type_mismatches
- .insert(lhs.into(), TypeMismatch { expected: rhs_ty.clone(), actual: ty.clone() });
- }
- self.write_expr_ty(lhs, ty.clone());
- ty
- }
-
fn infer_overloadable_binop(
&mut self,
lhs: ExprId,
@@ -1706,7 +1598,7 @@ impl InferenceContext<'_> {
Statement::Let { pat, type_ref, initializer, else_branch } => {
let decl_ty = type_ref
.as_ref()
- .map(|tr| this.make_ty(tr))
+ .map(|&tr| this.make_body_ty(tr))
.unwrap_or_else(|| this.table.new_type_var());
let ty = if let Some(expr) = initializer {
@@ -1764,7 +1656,7 @@ impl InferenceContext<'_> {
);
}
}
- Statement::Item => (),
+ Statement::Item(_) => (),
}
}
@@ -2249,7 +2141,8 @@ impl InferenceContext<'_> {
kind_id,
args.next().unwrap(), // `peek()` is `Some(_)`, so guaranteed no panic
self,
- |this, type_ref| this.make_ty(type_ref),
+ &self.body.types,
+ |this, type_ref| this.make_body_ty(type_ref),
|this, c, ty| {
const_or_path_to_chalk(
this.db,
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
index 6a0daee6ea..d74a383f44 100644
--- a/crates/hir-ty/src/infer/mutability.rs
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -4,7 +4,8 @@
use chalk_ir::{cast::Cast, Mutability};
use hir_def::{
hir::{
- Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp,
+ Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement,
+ UnaryOp,
},
lang_item::LangItem,
};
@@ -88,7 +89,7 @@ impl InferenceContext<'_> {
Statement::Expr { expr, has_semi: _ } => {
self.infer_mut_expr(*expr, Mutability::Not);
}
- Statement::Item => (),
+ Statement::Item(_) => (),
}
}
if let Some(tail) = tail {
@@ -96,7 +97,7 @@ impl InferenceContext<'_> {
}
}
Expr::MethodCall { receiver: it, method_name: _, args, generic_args: _ }
- | Expr::Call { callee: it, args, is_assignee_expr: _ } => {
+ | Expr::Call { callee: it, args } => {
self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*it)));
}
Expr::Match { expr, arms } => {
@@ -120,10 +121,10 @@ impl InferenceContext<'_> {
Expr::Become { expr } => {
self.infer_mut_expr(*expr, Mutability::Not);
}
- Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => {
+ Expr::RecordLit { path: _, fields, spread } => {
self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread))
}
- &Expr::Index { base, index, is_assignee_expr } => {
+ &Expr::Index { base, index } => {
if mutability == Mutability::Mut {
if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
if let Some(index_trait) = self
@@ -148,11 +149,8 @@ impl InferenceContext<'_> {
target,
}) = base_adjustments
{
- // For assignee exprs `IndexMut` obligations are already applied
- if !is_assignee_expr {
- if let TyKind::Ref(_, _, ty) = target.kind(Interner) {
- base_ty = Some(ty.clone());
- }
+ if let TyKind::Ref(_, _, ty) = target.kind(Interner) {
+ base_ty = Some(ty.clone());
}
*mutability = Mutability::Mut;
}
@@ -233,6 +231,14 @@ impl InferenceContext<'_> {
self.infer_mut_expr(*lhs, Mutability::Mut);
self.infer_mut_expr(*rhs, Mutability::Not);
}
+ &Expr::Assignment { target, value } => {
+ self.body.walk_pats(target, &mut |pat| match self.body[pat] {
+ Pat::Expr(expr) => self.infer_mut_expr(expr, Mutability::Mut),
+ Pat::ConstBlock(block) => self.infer_mut_expr(block, Mutability::Not),
+ _ => {}
+ });
+ self.infer_mut_expr(value, Mutability::Not);
+ }
Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs })
| Expr::BinaryOp { lhs, rhs, op: _ }
| Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => {
@@ -242,8 +248,7 @@ impl InferenceContext<'_> {
Expr::Closure { body, .. } => {
self.infer_mut_expr(*body, Mutability::Not);
}
- Expr::Tuple { exprs, is_assignee_expr: _ }
- | Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) => {
+ Expr::Tuple { exprs } | Expr::Array(Array::ElementList { elements: exprs }) => {
self.infer_mut_not_expr_iter(exprs.iter().copied());
}
// These don't need any action, as they don't have sub expressions
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index fee6755408..50e761196e 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -4,7 +4,7 @@ use std::iter::repeat_with;
use hir_def::{
body::Body,
- hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
+ hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId},
path::Path,
};
use hir_expand::name::Name;
@@ -12,63 +12,28 @@ use stdx::TupleExt;
use crate::{
consteval::{try_const_usize, usize_const},
- infer::{expr::ExprIsRead, BindingMode, Expectation, InferenceContext, TypeMismatch},
+ infer::{
+ coerce::CoerceNever, expr::ExprIsRead, BindingMode, Expectation, InferenceContext,
+ TypeMismatch,
+ },
lower::lower_to_chalk_mutability,
primitive::UintTy,
static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty,
TyBuilder, TyExt, TyKind,
};
-/// Used to generalize patterns and assignee expressions.
-pub(super) trait PatLike: Into<ExprOrPatId> + Copy {
- type BindingMode: Copy;
-
- fn infer(
- this: &mut InferenceContext<'_>,
- id: Self,
- expected_ty: &Ty,
- default_bm: Self::BindingMode,
- ) -> Ty;
-}
-
-impl PatLike for ExprId {
- type BindingMode = ();
-
- fn infer(
- this: &mut InferenceContext<'_>,
- id: Self,
- expected_ty: &Ty,
- (): Self::BindingMode,
- ) -> Ty {
- this.infer_assignee_expr(id, expected_ty)
- }
-}
-
-impl PatLike for PatId {
- type BindingMode = BindingMode;
-
- fn infer(
- this: &mut InferenceContext<'_>,
- id: Self,
- expected_ty: &Ty,
- default_bm: Self::BindingMode,
- ) -> Ty {
- this.infer_pat(id, expected_ty, default_bm)
- }
-}
-
impl InferenceContext<'_> {
/// Infers type for tuple struct pattern or its corresponding assignee expression.
///
/// Ellipses found in the original pattern or expression must be filtered out.
- pub(super) fn infer_tuple_struct_pat_like<T: PatLike>(
+ pub(super) fn infer_tuple_struct_pat_like(
&mut self,
path: Option<&Path>,
expected: &Ty,
- default_bm: T::BindingMode,
- id: T,
+ default_bm: BindingMode,
+ id: PatId,
ellipsis: Option<u32>,
- subs: &[T],
+ subs: &[PatId],
) -> Ty {
let (ty, def) = self.resolve_variant(path, true);
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
@@ -127,13 +92,13 @@ impl InferenceContext<'_> {
}
};
- T::infer(self, subpat, &expected_ty, default_bm);
+ self.infer_pat(subpat, &expected_ty, default_bm);
}
}
None => {
let err_ty = self.err_ty();
for &inner in subs {
- T::infer(self, inner, &err_ty, default_bm);
+ self.infer_pat(inner, &err_ty, default_bm);
}
}
}
@@ -142,13 +107,13 @@ impl InferenceContext<'_> {
}
/// Infers type for record pattern or its corresponding assignee expression.
- pub(super) fn infer_record_pat_like<T: PatLike>(
+ pub(super) fn infer_record_pat_like(
&mut self,
path: Option<&Path>,
expected: &Ty,
- default_bm: T::BindingMode,
- id: T,
- subs: impl ExactSizeIterator<Item = (Name, T)>,
+ default_bm: BindingMode,
+ id: PatId,
+ subs: impl ExactSizeIterator<Item = (Name, PatId)>,
) -> Ty {
let (ty, def) = self.resolve_variant(path, false);
if let Some(variant) = def {
@@ -197,13 +162,13 @@ impl InferenceContext<'_> {
}
};
- T::infer(self, inner, &expected_ty, default_bm);
+ self.infer_pat(inner, &expected_ty, default_bm);
}
}
None => {
let err_ty = self.err_ty();
for (_, inner) in subs {
- T::infer(self, inner, &err_ty, default_bm);
+ self.infer_pat(inner, &err_ty, default_bm);
}
}
}
@@ -214,12 +179,12 @@ impl InferenceContext<'_> {
/// Infers type for tuple pattern or its corresponding assignee expression.
///
/// Ellipses found in the original pattern or expression must be filtered out.
- pub(super) fn infer_tuple_pat_like<T: PatLike>(
+ pub(super) fn infer_tuple_pat_like(
&mut self,
expected: &Ty,
- default_bm: T::BindingMode,
+ default_bm: BindingMode,
ellipsis: Option<u32>,
- subs: &[T],
+ subs: &[PatId],
) -> Ty {
let expected = self.resolve_ty_shallow(expected);
let expectations = match expected.as_tuple() {
@@ -244,18 +209,20 @@ impl InferenceContext<'_> {
// Process pre
for (ty, pat) in inner_tys.iter_mut().zip(pre) {
- *ty = T::infer(self, *pat, ty, default_bm);
+ *ty = self.infer_pat(*pat, ty, default_bm);
}
// Process post
for (ty, pat) in inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) {
- *ty = T::infer(self, *pat, ty, default_bm);
+ *ty = self.infer_pat(*pat, ty, default_bm);
}
TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
.intern(Interner)
}
+ /// The resolver needs to be updated to the surrounding expression when inside assignment
+ /// (because there, `Pat::Path` can refer to a variable).
pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty) {
self.infer_pat(pat, expected, BindingMode::default());
}
@@ -263,7 +230,14 @@ impl InferenceContext<'_> {
fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty {
let mut expected = self.resolve_ty_shallow(expected);
- if self.is_non_ref_pat(self.body, pat) {
+ if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment {
+ cov_mark::hit!(match_ergonomics_ref);
+ // When you encounter a `&pat` pattern, reset to Move.
+ // This is so that `w` is by value: `let (_, &w) = &(1, &2);`
+ // Destructuring assignments also reset the binding mode and
+ // don't do match ergonomics.
+ default_bm = BindingMode::Move;
+ } else if self.is_non_ref_pat(self.body, pat) {
let mut pat_adjustments = Vec::new();
while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
pat_adjustments.push(expected.clone());
@@ -279,11 +253,6 @@ impl InferenceContext<'_> {
pat_adjustments.shrink_to_fit();
self.result.pat_adjustments.insert(pat, pat_adjustments);
}
- } else if let Pat::Ref { .. } = &self.body[pat] {
- cov_mark::hit!(match_ergonomics_ref);
- // When you encounter a `&pat` pattern, reset to Move.
- // This is so that `w` is by value: `let (_, &w) = &(1, &2);`
- default_bm = BindingMode::Move;
}
// Lose mutability.
@@ -320,8 +289,34 @@ impl InferenceContext<'_> {
self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs)
}
Pat::Path(path) => {
- // FIXME update resolver for the surrounding expression
- self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty())
+ let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty());
+ let ty_inserted_vars = self.insert_type_vars_shallow(ty.clone());
+ match self.table.coerce(&expected, &ty_inserted_vars, CoerceNever::Yes) {
+ Ok((adjustments, coerced_ty)) => {
+ if !adjustments.is_empty() {
+ self.result
+ .pat_adjustments
+ .entry(pat)
+ .or_default()
+ .extend(adjustments.into_iter().map(|adjust| adjust.target));
+ }
+ self.write_pat_ty(pat, coerced_ty);
+ return self.pat_ty_after_adjustment(pat);
+ }
+ Err(_) => {
+ self.result.type_mismatches.insert(
+ pat.into(),
+ TypeMismatch {
+ expected: expected.clone(),
+ actual: ty_inserted_vars.clone(),
+ },
+ );
+ self.write_pat_ty(pat, ty);
+ // We return `expected` to prevent cascading errors. I guess an alternative is to
+ // not emit type mismatches for error types and emit an error type here.
+ return expected;
+ }
+ }
}
Pat::Bind { id, subpat } => {
return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected);
@@ -361,7 +356,40 @@ impl InferenceContext<'_> {
None => self.err_ty(),
},
Pat::ConstBlock(expr) => {
- self.infer_expr(*expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes)
+ let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false);
+ let result = self.infer_expr(
+ *expr,
+ &Expectation::has_type(expected.clone()),
+ ExprIsRead::Yes,
+ );
+ self.inside_assignment = old_inside_assign;
+ result
+ }
+ Pat::Expr(expr) => {
+ let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false);
+ // LHS of assignment doesn't constitute reads.
+ let result = self.infer_expr_coerce(
+ *expr,
+ &Expectation::has_type(expected.clone()),
+ ExprIsRead::No,
+ );
+ // We are returning early to avoid the unifiability check below.
+ let lhs_ty = self.insert_type_vars_shallow(result);
+ let ty = match self.coerce(None, &expected, &lhs_ty, CoerceNever::Yes) {
+ Ok(ty) => ty,
+ Err(_) => {
+ self.result.type_mismatches.insert(
+ pat.into(),
+ TypeMismatch { expected: expected.clone(), actual: lhs_ty.clone() },
+ );
+ // `rhs_ty` is returned so no further type mismatches are
+ // reported because of this mismatch.
+ expected
+ }
+ };
+ self.write_pat_ty(pat, ty.clone());
+ self.inside_assignment = old_inside_assign;
+ return ty;
}
Pat::Missing => self.err_ty(),
};
@@ -517,9 +545,12 @@ impl InferenceContext<'_> {
body[*expr],
Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..))
),
- Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => {
- false
- }
+ Pat::Wild
+ | Pat::Bind { .. }
+ | Pat::Ref { .. }
+ | Pat::Box { .. }
+ | Pat::Missing
+ | Pat::Expr(_) => false,
}
}
}
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index e4841c7b15..442daa9f9e 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -94,8 +94,7 @@ impl InferenceContext<'_> {
return Some(ValuePathResolution::NonGeneric(ty));
};
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
- let substs = ctx.substs_from_path(path, value_def, true);
+ let substs = self.with_body_ty_lowering(|ctx| ctx.substs_from_path(path, value_def, true));
let substs = substs.as_slice(Interner);
if let ValueNs::EnumVariantId(_) = value {
@@ -152,8 +151,12 @@ impl InferenceContext<'_> {
let last = path.segments().last()?;
// Don't use `self.make_ty()` here as we need `orig_ns`.
- let ctx =
- crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
+ let ctx = crate::lower::TyLoweringContext::new(
+ self.db,
+ &self.resolver,
+ &self.body.types,
+ self.owner.into(),
+ );
let (ty, orig_ns) = ctx.lower_ty_ext(type_ref);
let ty = self.table.insert_type_vars(ty);
let ty = self.table.normalize_associated_types_in(ty);
@@ -164,9 +167,10 @@ impl InferenceContext<'_> {
let ty = self.table.normalize_associated_types_in(ty);
self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
} else {
+ let hygiene = self.body.expr_or_pat_path_hygiene(id);
// FIXME: report error, unresolved first path segment
let value_or_partial =
- self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
+ self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, hygiene)?;
match value_or_partial {
ResolveValueResult::ValueNs(it, _) => (it, None),
@@ -218,7 +222,7 @@ impl InferenceContext<'_> {
let _d;
let (resolved_segment, remaining_segments) = match path {
- Path::Normal { .. } => {
+ Path::Normal { .. } | Path::BarePath(_) => {
assert!(remaining_index < path.segments().len());
(
path.segments().get(remaining_index - 1).unwrap(),
@@ -242,17 +246,10 @@ impl InferenceContext<'_> {
(TypeNs::TraitId(trait_), true) => {
let segment =
remaining_segments.last().expect("there should be at least one segment here");
- let ctx = crate::lower::TyLoweringContext::new(
- self.db,
- &self.resolver,
- self.owner.into(),
- );
- let trait_ref = ctx.lower_trait_ref_from_resolved_path(
- trait_,
- resolved_segment,
- self.table.new_type_var(),
- );
-
+ let self_ty = self.table.new_type_var();
+ let trait_ref = self.with_body_ty_lowering(|ctx| {
+ ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, self_ty)
+ });
self.resolve_trait_assoc_item(trait_ref, segment, id)
}
(def, _) => {
@@ -262,17 +259,14 @@ impl InferenceContext<'_> {
// as Iterator>::Item::default`)
let remaining_segments_for_ty =
remaining_segments.take(remaining_segments.len() - 1);
- let ctx = crate::lower::TyLoweringContext::new(
- self.db,
- &self.resolver,
- self.owner.into(),
- );
- let (ty, _) = ctx.lower_partly_resolved_path(
- def,
- resolved_segment,
- remaining_segments_for_ty,
- true,
- );
+ let (ty, _) = self.with_body_ty_lowering(|ctx| {
+ ctx.lower_partly_resolved_path(
+ def,
+ resolved_segment,
+ remaining_segments_for_ty,
+ true,
+ )
+ });
if ty.is_unknown() {
return None;
}
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index c7ed68448b..e3a92e52f6 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -34,6 +34,7 @@ use hir_def::{
resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
type_ref::{
ConstRef, LifetimeRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef,
+ TypeRefId, TypesMap, TypesSourceMap,
},
AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, ItemContainerId,
@@ -41,7 +42,6 @@ use hir_def::{
TypeOwnerId, UnionId, VariantId,
};
use hir_expand::{name::Name, ExpandResult};
-use intern::Interned;
use la_arena::{Arena, ArenaMap};
use rustc_hash::FxHashSet;
use rustc_pattern_analysis::Captures;
@@ -122,6 +122,11 @@ pub struct TyLoweringContext<'a> {
pub db: &'a dyn HirDatabase,
resolver: &'a Resolver,
generics: OnceCell<Option<Generics>>,
+ types_map: &'a TypesMap,
+ /// If this is set, that means we're in a context of a freshly expanded macro, and that means
+ /// we should not use `TypeRefId` in diagnostics because the caller won't have the `TypesMap`,
+ /// instead we need to put `TypeSource` from the source map.
+ types_source_map: Option<&'a TypesSourceMap>,
in_binders: DebruijnIndex,
// FIXME: Should not be an `Option` but `Resolver` currently does not return owners in all cases
// where expected
@@ -138,13 +143,20 @@ pub struct TyLoweringContext<'a> {
}
impl<'a> TyLoweringContext<'a> {
- pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver, owner: TypeOwnerId) -> Self {
- Self::new_maybe_unowned(db, resolver, Some(owner))
+ pub fn new(
+ db: &'a dyn HirDatabase,
+ resolver: &'a Resolver,
+ types_map: &'a TypesMap,
+ owner: TypeOwnerId,
+ ) -> Self {
+ Self::new_maybe_unowned(db, resolver, types_map, None, Some(owner))
}
pub fn new_maybe_unowned(
db: &'a dyn HirDatabase,
resolver: &'a Resolver,
+ types_map: &'a TypesMap,
+ types_source_map: Option<&'a TypesSourceMap>,
owner: Option<TypeOwnerId>,
) -> Self {
let impl_trait_mode = ImplTraitLoweringState::Disallowed;
@@ -154,6 +166,8 @@ impl<'a> TyLoweringContext<'a> {
db,
resolver,
generics: OnceCell::new(),
+ types_map,
+ types_source_map,
owner,
in_binders,
impl_trait_mode,
@@ -201,6 +215,16 @@ impl<'a> TyLoweringContext<'a> {
pub fn with_type_param_mode(self, type_param_mode: ParamLoweringMode) -> Self {
Self { type_param_mode, ..self }
}
+
+ pub fn impl_trait_mode(&mut self, impl_trait_mode: ImplTraitLoweringMode) -> &mut Self {
+ self.impl_trait_mode = ImplTraitLoweringState::new(impl_trait_mode);
+ self
+ }
+
+ pub fn type_param_mode(&mut self, type_param_mode: ParamLoweringMode) -> &mut Self {
+ self.type_param_mode = type_param_mode;
+ self
+ }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -230,7 +254,7 @@ pub enum ParamLoweringMode {
}
impl<'a> TyLoweringContext<'a> {
- pub fn lower_ty(&self, type_ref: &TypeRef) -> Ty {
+ pub fn lower_ty(&self, type_ref: TypeRefId) -> Ty {
self.lower_ty_ext(type_ref).0
}
@@ -254,12 +278,13 @@ impl<'a> TyLoweringContext<'a> {
.as_ref()
}
- pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
+ pub fn lower_ty_ext(&self, type_ref_id: TypeRefId) -> (Ty, Option<TypeNs>) {
let mut res = None;
+ let type_ref = &self.types_map[type_ref_id];
let ty = match type_ref {
TypeRef::Never => TyKind::Never.intern(Interner),
TypeRef::Tuple(inner) => {
- let inner_tys = inner.iter().map(|tr| self.lower_ty(tr));
+ let inner_tys = inner.iter().map(|&tr| self.lower_ty(tr));
TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys))
.intern(Interner)
}
@@ -268,38 +293,43 @@ impl<'a> TyLoweringContext<'a> {
res = res_;
ty
}
- TypeRef::RawPtr(inner, mutability) => {
+ &TypeRef::RawPtr(inner, mutability) => {
let inner_ty = self.lower_ty(inner);
- TyKind::Raw(lower_to_chalk_mutability(*mutability), inner_ty).intern(Interner)
+ TyKind::Raw(lower_to_chalk_mutability(mutability), inner_ty).intern(Interner)
}
- TypeRef::Array(inner, len) => {
- let inner_ty = self.lower_ty(inner);
- let const_len = self.lower_const(len, TyBuilder::usize());
+ TypeRef::Array(array) => {
+ let inner_ty = self.lower_ty(array.ty);
+ let const_len = self.lower_const(&array.len, TyBuilder::usize());
TyKind::Array(inner_ty, const_len).intern(Interner)
}
- TypeRef::Slice(inner) => {
+ &TypeRef::Slice(inner) => {
let inner_ty = self.lower_ty(inner);
TyKind::Slice(inner_ty).intern(Interner)
}
- TypeRef::Reference(inner, lifetime, mutability) => {
- let inner_ty = self.lower_ty(inner);
+ TypeRef::Reference(ref_) => {
+ let inner_ty = self.lower_ty(ref_.ty);
// FIXME: It should infer the eldided lifetimes instead of stubbing with static
- let lifetime =
- lifetime.as_ref().map_or_else(error_lifetime, |lr| self.lower_lifetime(lr));
- TyKind::Ref(lower_to_chalk_mutability(*mutability), lifetime, inner_ty)
+ let lifetime = ref_
+ .lifetime
+ .as_ref()
+ .map_or_else(error_lifetime, |lr| self.lower_lifetime(lr));
+ TyKind::Ref(lower_to_chalk_mutability(ref_.mutability), lifetime, inner_ty)
.intern(Interner)
}
TypeRef::Placeholder => TyKind::Error.intern(Interner),
- &TypeRef::Fn(ref params, variadic, is_unsafe, ref abi) => {
+ TypeRef::Fn(fn_) => {
let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
- Substitution::from_iter(Interner, params.iter().map(|(_, tr)| ctx.lower_ty(tr)))
+ Substitution::from_iter(
+ Interner,
+ fn_.params().iter().map(|&(_, tr)| ctx.lower_ty(tr)),
+ )
});
TyKind::Function(FnPointer {
num_binders: 0, // FIXME lower `for<'a> fn()` correctly
sig: FnSig {
- abi: abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol),
- safety: if is_unsafe { Safety::Unsafe } else { Safety::Safe },
- variadic,
+ abi: fn_.abi().as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol),
+ safety: if fn_.is_unsafe() { Safety::Unsafe } else { Safety::Safe },
+ variadic: fn_.is_varargs(),
},
substitution: FnSubst(substs),
})
@@ -351,8 +381,8 @@ impl<'a> TyLoweringContext<'a> {
ImplTraitLoweringState::Param(counter) => {
let idx = counter.get();
// Count the number of `impl Trait` things that appear within our bounds.
- // Since t hose have been emitted as implicit type args already.
- counter.set(idx + count_impl_traits(type_ref) as u16);
+ // Since those have been emitted as implicit type args already.
+ counter.set(idx + self.count_impl_traits(type_ref_id) as u16);
let kind = self
.generics()
.expect("param impl trait lowering must be in a generic def")
@@ -376,7 +406,7 @@ impl<'a> TyLoweringContext<'a> {
let idx = counter.get();
// Count the number of `impl Trait` things that appear within our bounds.
// Since t hose have been emitted as implicit type args already.
- counter.set(idx + count_impl_traits(type_ref) as u16);
+ counter.set(idx + self.count_impl_traits(type_ref_id) as u16);
let kind = self
.generics()
.expect("variable impl trait lowering must be in a generic def")
@@ -432,12 +462,40 @@ impl<'a> TyLoweringContext<'a> {
match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call, resolver)
{
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
- let ctx = expander.ctx(self.db.upcast());
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+
+ let ctx = expander.ctx(
+ self.db.upcast(),
+ &mut types_map,
+ &mut types_source_map,
+ );
// FIXME: Report syntax errors in expansion here
let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
drop(expander);
- let ty = self.lower_ty(&type_ref);
+
+ // FIXME: That may be better served by mutating `self` then restoring, but this requires
+ // making it `&mut self`.
+ let inner_ctx = TyLoweringContext {
+ db: self.db,
+ resolver: self.resolver,
+ generics: self.generics.clone(),
+ types_map: &types_map,
+ types_source_map: Some(&types_source_map),
+ in_binders: self.in_binders,
+ owner: self.owner,
+ type_param_mode: self.type_param_mode,
+ impl_trait_mode: self.impl_trait_mode.take(),
+ expander: RefCell::new(self.expander.take()),
+ unsized_types: RefCell::new(self.unsized_types.take()),
+ };
+
+ let ty = inner_ctx.lower_ty(type_ref);
+
+ self.impl_trait_mode.swap(&inner_ctx.impl_trait_mode);
+ *self.expander.borrow_mut() = inner_ctx.expander.into_inner();
+ *self.unsized_types.borrow_mut() = inner_ctx.unsized_types.into_inner();
self.expander.borrow_mut().as_mut().unwrap().exit(mark);
Some(ty)
@@ -463,7 +521,8 @@ impl<'a> TyLoweringContext<'a> {
/// This is only for `generic_predicates_for_param`, where we can't just
/// lower the self types of the predicates since that could lead to cycles.
/// So we just check here if the `type_ref` resolves to a generic param, and which.
- fn lower_ty_only_param(&self, type_ref: &TypeRef) -> Option<TypeOrConstParamId> {
+ fn lower_ty_only_param(&self, type_ref: TypeRefId) -> Option<TypeOrConstParamId> {
+ let type_ref = &self.types_map[type_ref];
let path = match type_ref {
TypeRef::Path(path) => path,
_ => return None,
@@ -663,7 +722,7 @@ impl<'a> TyLoweringContext<'a> {
if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() {
// trait object type without dyn
let bound = TypeBound::Path(path.clone(), TraitBoundModifier::None);
- let ty = self.lower_dyn_trait(&[Interned::new(bound)]);
+ let ty = self.lower_dyn_trait(&[bound]);
return (ty, None);
}
@@ -864,7 +923,7 @@ impl<'a> TyLoweringContext<'a> {
assert!(matches!(id, GenericParamId::TypeParamId(_)));
had_explicit_args = true;
if let GenericArg::Type(ty) = &args[0] {
- substs.push(self.lower_ty(ty).cast(Interner));
+ substs.push(self.lower_ty(*ty).cast(Interner));
}
}
} else {
@@ -901,6 +960,7 @@ impl<'a> TyLoweringContext<'a> {
id,
arg,
&mut (),
+ self.types_map,
|_, type_ref| self.lower_ty(type_ref),
|_, const_ref, ty| self.lower_const(const_ref, ty),
|_, lifetime_ref| self.lower_lifetime(lifetime_ref),
@@ -998,7 +1058,7 @@ impl<'a> TyLoweringContext<'a> {
WherePredicate::ForLifetime { target, bound, .. }
| WherePredicate::TypeBound { target, bound } => {
let self_ty = match target {
- WherePredicateTypeTarget::TypeRef(type_ref) => self.lower_ty(type_ref),
+ WherePredicateTypeTarget::TypeRef(type_ref) => self.lower_ty(*type_ref),
&WherePredicateTypeTarget::TypeOrConstParam(local_id) => {
let param_id = hir_def::TypeOrConstParamId { parent: def, local_id };
match self.type_param_mode {
@@ -1029,12 +1089,12 @@ impl<'a> TyLoweringContext<'a> {
pub(crate) fn lower_type_bound(
&'a self,
- bound: &'a Interned<TypeBound>,
+ bound: &'a TypeBound,
self_ty: Ty,
ignore_bindings: bool,
) -> impl Iterator<Item = QuantifiedWhereClause> + 'a {
let mut trait_ref = None;
- let clause = match bound.as_ref() {
+ let clause = match bound {
TypeBound::Path(path, TraitBoundModifier::None) => {
trait_ref = self.lower_trait_ref_from_path(path, self_ty);
trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
@@ -1067,7 +1127,7 @@ impl<'a> TyLoweringContext<'a> {
lifetime,
})))
}
- TypeBound::Error => None,
+ TypeBound::Use(_) | TypeBound::Error => None,
};
clause.into_iter().chain(
trait_ref
@@ -1079,14 +1139,15 @@ impl<'a> TyLoweringContext<'a> {
fn assoc_type_bindings_from_type_bound(
&'a self,
- bound: &'a Interned<TypeBound>,
+ bound: &'a TypeBound,
trait_ref: TraitRef,
) -> impl Iterator<Item = QuantifiedWhereClause> + 'a {
- let last_segment = match bound.as_ref() {
+ let last_segment = match bound {
TypeBound::Path(path, TraitBoundModifier::None) | TypeBound::ForLifetime(_, path) => {
path.segments().last()
}
TypeBound::Path(_, TraitBoundModifier::Maybe)
+ | TypeBound::Use(_)
| TypeBound::Error
| TypeBound::Lifetime(_) => None,
};
@@ -1110,7 +1171,7 @@ impl<'a> TyLoweringContext<'a> {
// this point (`super_trait_ref.substitution`).
let substitution = self.substs_from_path_segment(
// FIXME: This is hack. We shouldn't really build `PathSegment` directly.
- PathSegment { name: &binding.name, args_and_bindings: binding.args.as_deref() },
+ PathSegment { name: &binding.name, args_and_bindings: binding.args.as_ref() },
Some(associated_ty.into()),
false, // this is not relevant
Some(super_trait_ref.self_type_parameter(Interner)),
@@ -1130,8 +1191,8 @@ impl<'a> TyLoweringContext<'a> {
let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
);
- if let Some(type_ref) = &binding.type_ref {
- match (type_ref, &self.impl_trait_mode) {
+ if let Some(type_ref) = binding.type_ref {
+ match (&self.types_map[type_ref], &self.impl_trait_mode) {
(TypeRef::ImplTrait(_), ImplTraitLoweringState::Disallowed) => (),
(
_,
@@ -1178,6 +1239,8 @@ impl<'a> TyLoweringContext<'a> {
let mut ext = TyLoweringContext::new_maybe_unowned(
self.db,
self.resolver,
+ self.types_map,
+ self.types_source_map,
self.owner,
)
.with_type_param_mode(self.type_param_mode);
@@ -1215,7 +1278,7 @@ impl<'a> TyLoweringContext<'a> {
})
}
- fn lower_dyn_trait(&self, bounds: &[Interned<TypeBound>]) -> Ty {
+ fn lower_dyn_trait(&self, bounds: &[TypeBound]) -> Ty {
let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
// INVARIANT: The principal trait bound, if present, must come first. Others may be in any
// order but should be in the same order for the same set but possibly different order of
@@ -1313,7 +1376,7 @@ impl<'a> TyLoweringContext<'a> {
}
}
- fn lower_impl_trait(&self, bounds: &[Interned<TypeBound>], krate: CrateId) -> ImplTrait {
+ fn lower_impl_trait(&self, bounds: &[TypeBound], krate: CrateId) -> ImplTrait {
cov_mark::hit!(lower_rpit);
let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
@@ -1365,6 +1428,17 @@ impl<'a> TyLoweringContext<'a> {
None => error_lifetime(),
}
}
+
+ // FIXME: This does not handle macros!
+ fn count_impl_traits(&self, type_ref: TypeRefId) -> usize {
+ let mut count = 0;
+ TypeRef::walk(type_ref, self.types_map, &mut |type_ref| {
+ if matches!(type_ref, TypeRef::ImplTrait(_)) {
+ count += 1;
+ }
+ });
+ count
+ }
}
/// Build the signature of a callable item (function, struct or enum variant).
@@ -1385,17 +1459,6 @@ pub fn associated_type_shorthand_candidates<R>(
named_associated_type_shorthand_candidates(db, def, res, None, |name, _, id| cb(name, id))
}
-// FIXME: This does not handle macros!
-fn count_impl_traits(type_ref: &TypeRef) -> usize {
- let mut count = 0;
- type_ref.walk(&mut |type_ref| {
- if matches!(type_ref, TypeRef::ImplTrait(_)) {
- count += 1;
- }
- });
- count
-}
-
fn named_associated_type_shorthand_candidates<R>(
db: &dyn HirDatabase,
// If the type parameter is defined in an impl and we're in a method, there
@@ -1499,10 +1562,10 @@ pub(crate) fn field_types_query(
};
let generics = generics(db.upcast(), def);
let mut res = ArenaMap::default();
- let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ let ctx = TyLoweringContext::new(db, &resolver, var_data.types_map(), def.into())
.with_type_param_mode(ParamLoweringMode::Variable);
for (field_id, field_data) in var_data.fields().iter() {
- res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref)));
+ res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(field_data.type_ref)));
}
Arc::new(res)
}
@@ -1522,38 +1585,38 @@ pub(crate) fn generic_predicates_for_param_query(
assoc_name: Option<Name>,
) -> GenericPredicates {
let resolver = def.resolver(db.upcast());
- let ctx = if let GenericDefId::FunctionId(_) = def {
- TyLoweringContext::new(db, &resolver, def.into())
+ let mut ctx = if let GenericDefId::FunctionId(_) = def {
+ TyLoweringContext::new(db, &resolver, TypesMap::EMPTY, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Variable)
.with_type_param_mode(ParamLoweringMode::Variable)
} else {
- TyLoweringContext::new(db, &resolver, def.into())
+ TyLoweringContext::new(db, &resolver, TypesMap::EMPTY, def.into())
.with_type_param_mode(ParamLoweringMode::Variable)
};
let generics = generics(db.upcast(), def);
// we have to filter out all other predicates *first*, before attempting to lower them
- let predicate = |(pred, &def): &(&_, _)| match pred {
+ let predicate = |pred: &_, def: &_, ctx: &TyLoweringContext<'_>| match pred {
WherePredicate::ForLifetime { target, bound, .. }
| WherePredicate::TypeBound { target, bound, .. } => {
let invalid_target = match target {
WherePredicateTypeTarget::TypeRef(type_ref) => {
- ctx.lower_ty_only_param(type_ref) != Some(param_id)
+ ctx.lower_ty_only_param(*type_ref) != Some(param_id)
}
&WherePredicateTypeTarget::TypeOrConstParam(local_id) => {
- let target_id = TypeOrConstParamId { parent: def, local_id };
+ let target_id = TypeOrConstParamId { parent: *def, local_id };
target_id != param_id
}
};
if invalid_target {
// If this is filtered out without lowering, `?Sized` is not gathered into `ctx.unsized_types`
- if let TypeBound::Path(_, TraitBoundModifier::Maybe) = &**bound {
- ctx.lower_where_predicate(pred, &def, true).for_each(drop);
+ if let TypeBound::Path(_, TraitBoundModifier::Maybe) = bound {
+ ctx.lower_where_predicate(pred, def, true).for_each(drop);
}
return false;
}
- match &**bound {
+ match bound {
TypeBound::ForLifetime(_, path) | TypeBound::Path(path, _) => {
// Only lower the bound if the trait could possibly define the associated
// type we're looking for.
@@ -1571,18 +1634,20 @@ pub(crate) fn generic_predicates_for_param_query(
})
})
}
- TypeBound::Lifetime(_) | TypeBound::Error => false,
+ TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false,
}
}
WherePredicate::Lifetime { .. } => false,
};
- let mut predicates: Vec<_> = resolver
- .where_predicates_in_scope()
- .filter(predicate)
- .flat_map(|(pred, def)| {
- ctx.lower_where_predicate(pred, def, true).map(|p| make_binders(db, &generics, p))
- })
- .collect();
+ let mut predicates = Vec::new();
+ for (params, def) in resolver.all_generic_params() {
+ ctx.types_map = &params.types_map;
+ predicates.extend(
+ params.where_predicates().filter(|pred| predicate(pred, def, &ctx)).flat_map(|pred| {
+ ctx.lower_where_predicate(pred, def, true).map(|p| make_binders(db, &generics, p))
+ }),
+ );
+ }
let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
if !subst.is_empty(Interner) {
@@ -1629,23 +1694,27 @@ pub(crate) fn trait_environment_query(
def: GenericDefId,
) -> Arc<TraitEnvironment> {
let resolver = def.resolver(db.upcast());
- let ctx = if let GenericDefId::FunctionId(_) = def {
- TyLoweringContext::new(db, &resolver, def.into())
+ let mut ctx = if let GenericDefId::FunctionId(_) = def {
+ TyLoweringContext::new(db, &resolver, TypesMap::EMPTY, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Param)
.with_type_param_mode(ParamLoweringMode::Placeholder)
} else {
- TyLoweringContext::new(db, &resolver, def.into())
+ TyLoweringContext::new(db, &resolver, TypesMap::EMPTY, def.into())
.with_type_param_mode(ParamLoweringMode::Placeholder)
};
let mut traits_in_scope = Vec::new();
let mut clauses = Vec::new();
- for (pred, def) in resolver.where_predicates_in_scope() {
- for pred in ctx.lower_where_predicate(pred, def, false) {
- if let WhereClause::Implemented(tr) = &pred.skip_binders() {
- traits_in_scope.push((tr.self_type_parameter(Interner).clone(), tr.hir_trait_id()));
+ for (params, def) in resolver.all_generic_params() {
+ ctx.types_map = &params.types_map;
+ for pred in params.where_predicates() {
+ for pred in ctx.lower_where_predicate(pred, def, false) {
+ if let WhereClause::Implemented(tr) = pred.skip_binders() {
+ traits_in_scope
+ .push((tr.self_type_parameter(Interner).clone(), tr.hir_trait_id()));
+ }
+ let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(Interner);
+ clauses.push(program_clause.into_from_env_clause(Interner));
}
- let program_clause: chalk_ir::ProgramClause<Interner> = pred.cast(Interner);
- clauses.push(program_clause.into_from_env_clause(Interner));
}
}
@@ -1724,18 +1793,20 @@ where
}
_ => (ImplTraitLoweringMode::Disallowed, ParamLoweringMode::Variable),
};
- let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ let mut ctx = TyLoweringContext::new(db, &resolver, TypesMap::EMPTY, def.into())
.with_impl_trait_mode(impl_trait_lowering)
.with_type_param_mode(param_lowering);
let generics = generics(db.upcast(), def);
- let mut predicates = resolver
- .where_predicates_in_scope()
- .filter(|(pred, def)| filter(pred, def))
- .flat_map(|(pred, def)| {
- ctx.lower_where_predicate(pred, def, false).map(|p| make_binders(db, &generics, p))
- })
- .collect::<Vec<_>>();
+ let mut predicates = Vec::new();
+ for (params, def) in resolver.all_generic_params() {
+ ctx.types_map = &params.types_map;
+ predicates.extend(params.where_predicates().filter(|pred| filter(pred, def)).flat_map(
+ |pred| {
+ ctx.lower_where_predicate(pred, def, false).map(|p| make_binders(db, &generics, p))
+ },
+ ));
+ }
if generics.len() > 0 {
let subst = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
@@ -1811,18 +1882,19 @@ pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) ->
let resolver = def.resolver(db.upcast());
let parent_start_idx = generic_params.len_self();
- let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ let mut ctx = TyLoweringContext::new(db, &resolver, TypesMap::EMPTY, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Disallowed)
.with_type_param_mode(ParamLoweringMode::Variable);
- GenericDefaults(Some(Arc::from_iter(generic_params.iter().enumerate().map(
- |(idx, (id, p))| {
+ GenericDefaults(Some(Arc::from_iter(generic_params.iter_with_types_map().enumerate().map(
+ |(idx, ((id, p), types_map))| {
+ ctx.types_map = types_map;
match p {
GenericParamDataRef::TypeParamData(p) => {
let ty = p.default.as_ref().map_or(TyKind::Error.intern(Interner), |ty| {
// Each default can only refer to previous parameters.
// Type variable default referring to parameter coming
// after it is forbidden (FIXME: report diagnostic)
- fallback_bound_vars(ctx.lower_ty(ty), idx, parent_start_idx)
+ fallback_bound_vars(ctx.lower_ty(*ty), idx, parent_start_idx)
});
crate::make_binders(db, &generic_params, ty.cast(Interner))
}
@@ -1834,7 +1906,7 @@ pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) ->
let mut val = p.default.as_ref().map_or_else(
|| unknown_const_as_generic(db.const_param_ty(id)),
|c| {
- let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
+ let c = ctx.lower_const(c, ctx.lower_ty(p.ty));
c.cast(Interner)
},
);
@@ -1874,14 +1946,14 @@ pub(crate) fn generic_defaults_recover(
fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
let data = db.function_data(def);
let resolver = def.resolver(db.upcast());
- let ctx_params = TyLoweringContext::new(db, &resolver, def.into())
+ let ctx_params = TyLoweringContext::new(db, &resolver, &data.types_map, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Variable)
.with_type_param_mode(ParamLoweringMode::Variable);
- let params = data.params.iter().map(|tr| ctx_params.lower_ty(tr));
- let ctx_ret = TyLoweringContext::new(db, &resolver, def.into())
+ let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr));
+ let ctx_ret = TyLoweringContext::new(db, &resolver, &data.types_map, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
- let ret = ctx_ret.lower_ty(&data.ret_type);
+ let ret = ctx_ret.lower_ty(data.ret_type);
let generics = generics(db.upcast(), def.into());
let sig = CallableSig::from_params_and_return(
params,
@@ -1910,28 +1982,33 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
let data = db.const_data(def);
let generics = generics(db.upcast(), def.into());
let resolver = def.resolver(db.upcast());
- let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ let ctx = TyLoweringContext::new(db, &resolver, &data.types_map, def.into())
.with_type_param_mode(ParamLoweringMode::Variable);
- make_binders(db, &generics, ctx.lower_ty(&data.type_ref))
+ make_binders(db, &generics, ctx.lower_ty(data.type_ref))
}
/// Build the declared type of a static.
fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders<Ty> {
let data = db.static_data(def);
let resolver = def.resolver(db.upcast());
- let ctx = TyLoweringContext::new(db, &resolver, def.into());
+ let ctx = TyLoweringContext::new(db, &resolver, &data.types_map, def.into());
- Binders::empty(Interner, ctx.lower_ty(&data.type_ref))
+ Binders::empty(Interner, ctx.lower_ty(data.type_ref))
}
fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnSig {
let struct_data = db.struct_data(def);
let fields = struct_data.variant_data.fields();
let resolver = def.resolver(db.upcast());
- let ctx = TyLoweringContext::new(db, &resolver, AdtId::from(def).into())
- .with_type_param_mode(ParamLoweringMode::Variable);
- let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref));
+ let ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ struct_data.variant_data.types_map(),
+ AdtId::from(def).into(),
+ )
+ .with_type_param_mode(ParamLoweringMode::Variable);
+ let params = fields.iter().map(|(_, field)| ctx.lower_ty(field.type_ref));
let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
Binders::new(
binders,
@@ -1961,9 +2038,14 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
let var_data = db.enum_variant_data(def);
let fields = var_data.variant_data.fields();
let resolver = def.resolver(db.upcast());
- let ctx = TyLoweringContext::new(db, &resolver, DefWithBodyId::VariantId(def).into())
- .with_type_param_mode(ParamLoweringMode::Variable);
- let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref));
+ let ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ var_data.variant_data.types_map(),
+ DefWithBodyId::VariantId(def).into(),
+ )
+ .with_type_param_mode(ParamLoweringMode::Variable);
+ let params = fields.iter().map(|(_, field)| ctx.lower_ty(field.type_ref));
let (ret, binders) =
type_for_adt(db, def.lookup(db.upcast()).parent.into()).into_value_and_skipped_binders();
Binders::new(
@@ -2004,15 +2086,17 @@ fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
let generics = generics(db.upcast(), t.into());
let resolver = t.resolver(db.upcast());
- let ctx = TyLoweringContext::new(db, &resolver, t.into())
+ let type_alias_data = db.type_alias_data(t);
+ let ctx = TyLoweringContext::new(db, &resolver, &type_alias_data.types_map, t.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
- let type_alias_data = db.type_alias_data(t);
let inner = if type_alias_data.is_extern {
TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner)
} else {
- let type_ref = &type_alias_data.type_ref;
- ctx.lower_ty(type_ref.as_deref().unwrap_or(&TypeRef::Error))
+ type_alias_data
+ .type_ref
+ .map(|type_ref| ctx.lower_ty(type_ref))
+ .unwrap_or_else(|| TyKind::Error.intern(Interner))
};
make_binders(db, &generics, inner)
}
@@ -2085,9 +2169,9 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde
let impl_data = db.impl_data(impl_id);
let resolver = impl_id.resolver(db.upcast());
let generics = generics(db.upcast(), impl_id.into());
- let ctx = TyLoweringContext::new(db, &resolver, impl_id.into())
+ let ctx = TyLoweringContext::new(db, &resolver, &impl_data.types_map, impl_id.into())
.with_type_param_mode(ParamLoweringMode::Variable);
- make_binders(db, &generics, ctx.lower_ty(&impl_data.self_ty))
+ make_binders(db, &generics, ctx.lower_ty(impl_data.self_ty))
}
// returns None if def is a type arg
@@ -2095,13 +2179,13 @@ pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> T
let parent_data = db.generic_params(def.parent());
let data = &parent_data[def.local_id()];
let resolver = def.parent().resolver(db.upcast());
- let ctx = TyLoweringContext::new(db, &resolver, def.parent().into());
+ let ctx = TyLoweringContext::new(db, &resolver, &parent_data.types_map, def.parent().into());
match data {
TypeOrConstParamData::TypeParamData(_) => {
never!();
Ty::new(Interner, TyKind::Error)
}
- TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(&d.ty),
+ TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(d.ty),
}
}
@@ -2117,7 +2201,7 @@ pub(crate) fn impl_self_ty_recover(
pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> {
let impl_data = db.impl_data(impl_id);
let resolver = impl_id.resolver(db.upcast());
- let ctx = TyLoweringContext::new(db, &resolver, impl_id.into())
+ let ctx = TyLoweringContext::new(db, &resolver, &impl_data.types_map, impl_id.into())
.with_type_param_mode(ParamLoweringMode::Variable);
let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders();
let target_trait = impl_data.target_trait.as_ref()?;
@@ -2131,10 +2215,10 @@ pub(crate) fn return_type_impl_traits(
// FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
let data = db.function_data(def);
let resolver = def.resolver(db.upcast());
- let ctx_ret = TyLoweringContext::new(db, &resolver, def.into())
+ let ctx_ret = TyLoweringContext::new(db, &resolver, &data.types_map, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
- let _ret = ctx_ret.lower_ty(&data.ret_type);
+ let _ret = ctx_ret.lower_ty(data.ret_type);
let generics = generics(db.upcast(), def.into());
let return_type_impl_traits = ImplTraits {
impl_traits: match ctx_ret.impl_trait_mode {
@@ -2155,10 +2239,10 @@ pub(crate) fn type_alias_impl_traits(
) -> Option<Arc<Binders<ImplTraits>>> {
let data = db.type_alias_data(def);
let resolver = def.resolver(db.upcast());
- let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ let ctx = TyLoweringContext::new(db, &resolver, &data.types_map, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
- if let Some(type_ref) = &data.type_ref {
+ if let Some(type_ref) = data.type_ref {
let _ty = ctx.lower_ty(type_ref);
}
let type_alias_impl_traits = ImplTraits {
@@ -2190,7 +2274,8 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
kind_id: GenericParamId,
arg: &'a GenericArg,
this: &mut T,
- for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a,
+ types_map: &TypesMap,
+ for_type: impl FnOnce(&mut T, TypeRefId) -> Ty + 'a,
for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a,
for_lifetime: impl FnOnce(&mut T, &LifetimeRef) -> Lifetime + 'a,
) -> crate::GenericArg {
@@ -2203,7 +2288,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime,
};
match (arg, kind) {
- (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, type_ref).cast(Interner),
+ (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, *type_ref).cast(Interner),
(GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner),
(GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => {
for_lifetime(this, lifetime_ref).cast(Interner)
@@ -2214,7 +2299,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
// We want to recover simple idents, which parser detects them
// as types. Maybe here is not the best place to do it, but
// it works.
- if let TypeRef::Path(p) = t {
+ if let TypeRef::Path(p) = &types_map[*t] {
if let Some(p) = p.mod_path() {
if p.kind == PathKind::Plain {
if let [n] = p.segments() {
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index 8e815aabf2..59c583afb2 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -879,7 +879,8 @@ pub enum Rvalue {
///
/// **Needs clarification**: Are there weird additional semantics here related to the runtime
/// nature of this operation?
- //ThreadLocalRef(DefId),
+ // ThreadLocalRef(DefId),
+ ThreadLocalRef(std::convert::Infallible),
/// Creates a pointer with the indicated mutability to the place.
///
@@ -888,7 +889,8 @@ pub enum Rvalue {
///
/// Like with references, the semantics of this operation are heavily dependent on the aliasing
/// model.
- //AddressOf(Mutability, Place),
+ // AddressOf(Mutability, Place),
+ AddressOf(std::convert::Infallible),
/// Yields the length of the place, as a `usize`.
///
@@ -906,19 +908,21 @@ pub enum Rvalue {
Cast(CastKind, Operand, Ty),
// FIXME link to `pointer::offset` when it hits stable.
- // /// * `Offset` has the same semantics as `pointer::offset`, except that the second
- // /// parameter may be a `usize` as well.
- // /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
- // /// raw pointers, or function pointers and return a `bool`. The types of the operands must be
- // /// matching, up to the usual caveat of the lifetimes in function pointers.
- // /// * Left and right shift operations accept signed or unsigned integers not necessarily of the
- // /// same type and return a value of the same type as their LHS. Like in Rust, the RHS is
- // /// truncated as needed.
- // /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
- // /// types and return a value of that type.
- // /// * The remaining operations accept signed integers, unsigned integers, or floats with
- // /// matching types and return a value of that type.
+ /// * `Offset` has the same semantics as `pointer::offset`, except that the second
+ /// parameter may be a `usize` as well.
+ /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats,
+ /// raw pointers, or function pointers and return a `bool`. The types of the operands must be
+ /// matching, up to the usual caveat of the lifetimes in function pointers.
+ /// * Left and right shift operations accept signed or unsigned integers not necessarily of the
+ /// same type and return a value of the same type as their LHS. Like in Rust, the RHS is
+ /// truncated as needed.
+ /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching
+ /// types and return a value of that type.
+ /// * The remaining operations accept signed integers, unsigned integers, or floats with
+ /// matching types and return a value of that type.
//BinaryOp(BinOp, Box<(Operand, Operand)>),
+ BinaryOp(std::convert::Infallible),
+
/// Same as `BinaryOp`, but yields `(T, bool)` with a `bool` indicating an error condition.
///
/// When overflow checking is disabled and we are generating run-time code, the error condition
@@ -937,6 +941,7 @@ pub enum Rvalue {
/// Computes a value as described by the operation.
//NullaryOp(NullOp, Ty),
+ NullaryOp(std::convert::Infallible),
/// Exactly like `BinaryOp`, but less operands.
///
@@ -1095,6 +1100,10 @@ impl MirBody {
for_operand(op, &mut f, &mut self.projection_store);
}
}
+ Rvalue::ThreadLocalRef(n)
+ | Rvalue::AddressOf(n)
+ | Rvalue::BinaryOp(n)
+ | Rvalue::NullaryOp(n) => match *n {},
}
}
StatementKind::FakeRead(p) | StatementKind::Deinit(p) => {
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs
index 9830fa1ca7..9c86d3b59f 100644
--- a/crates/hir-ty/src/mir/borrowck.rs
+++ b/crates/hir-ty/src/mir/borrowck.rs
@@ -167,6 +167,10 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
for_operand(op, statement.span);
}
}
+ Rvalue::ThreadLocalRef(n)
+ | Rvalue::AddressOf(n)
+ | Rvalue::BinaryOp(n)
+ | Rvalue::NullaryOp(n) => match *n {},
},
StatementKind::FakeRead(_)
| StatementKind::Deinit(_)
@@ -253,6 +257,10 @@ fn partially_moved(db: &dyn HirDatabase, body: &MirBody) -> Vec<PartiallyMoved>
for_operand(op, statement.span);
}
}
+ Rvalue::ThreadLocalRef(n)
+ | Rvalue::AddressOf(n)
+ | Rvalue::BinaryOp(n)
+ | Rvalue::NullaryOp(n) => match *n {},
},
StatementKind::FakeRead(_)
| StatementKind::Deinit(_)
@@ -548,6 +556,10 @@ fn mutability_of_locals(
}
}
Rvalue::ShallowInitBox(_, _) | Rvalue::ShallowInitBoxWithAlloc(_) => (),
+ Rvalue::ThreadLocalRef(n)
+ | Rvalue::AddressOf(n)
+ | Rvalue::BinaryOp(n)
+ | Rvalue::NullaryOp(n) => match *n {},
}
if let Rvalue::Ref(
BorrowKind::Mut {
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 0d42617d18..e73b9dc27d 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -6,6 +6,7 @@ use base_db::CrateId;
use chalk_ir::{cast::Cast, Mutability};
use either::Either;
use hir_def::{
+ body::HygieneId,
builtin_type::BuiltinType,
data::adt::{StructFlags, VariantData},
lang_item::LangItem,
@@ -1628,6 +1629,10 @@ impl Evaluator<'_> {
}
CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"),
},
+ Rvalue::ThreadLocalRef(n)
+ | Rvalue::AddressOf(n)
+ | Rvalue::BinaryOp(n)
+ | Rvalue::NullaryOp(n) => match *n {},
})
}
@@ -2703,17 +2708,15 @@ impl Evaluator<'_> {
TyKind::Function(_) => {
self.exec_fn_pointer(func_data, destination, &args[1..], locals, target_bb, span)
}
- TyKind::Closure(closure, subst) => {
- return self.exec_closure(
- *closure,
- func_data,
- &Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()),
- destination,
- &args[1..],
- locals,
- span,
- );
- }
+ TyKind::Closure(closure, subst) => self.exec_closure(
+ *closure,
+ func_data,
+ &Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()),
+ destination,
+ &args[1..],
+ locals,
+ span,
+ ),
_ => {
// try to execute the manual impl of `FnTrait` for structs (nightly feature used in std)
let arg0 = func;
@@ -2846,7 +2849,8 @@ impl Evaluator<'_> {
}
let layout = self.layout_adt(id.0, subst.clone())?;
match data.variant_data.as_ref() {
- VariantData::Record(fields) | VariantData::Tuple(fields) => {
+ VariantData::Record { fields, .. }
+ | VariantData::Tuple { fields, .. } => {
let field_types = self.db.field_types(s.into());
for (field, _) in fields.iter() {
let offset = layout
@@ -2951,6 +2955,7 @@ pub fn render_const_using_debug_impl(
let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully(
db.upcast(),
&hir_def::path::Path::from_known_path_with_no_generic(path![std::fmt::format]),
+ HygieneId::ROOT,
) else {
not_supported!("std::fmt::format not found");
};
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 16994cdd0c..c4e0640051 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -5,7 +5,7 @@ use std::{fmt::Write, iter, mem};
use base_db::ra_salsa::Cycle;
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
use hir_def::{
- body::Body,
+ body::{Body, HygieneId},
data::adt::{StructKind, VariantData},
hir::{
ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
@@ -13,7 +13,8 @@ use hir_def::{
},
lang_item::{LangItem, LangItemTarget},
path::Path,
- resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs},
+ resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
+ type_ref::TypesMap,
AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId,
Lookup, TraitId, TupleId, TypeOrConstParamId,
};
@@ -28,7 +29,7 @@ use triomphe::Arc;
use crate::{
consteval::ConstEvalError,
db::{HirDatabase, InternedClosure},
- display::HirDisplay,
+ display::{hir_display_with_types_map, HirDisplay},
error_lifetime,
generics::generics,
infer::{cast::CastTy, unify::InferenceTable, CaptureKind, CapturedItem, TypeMismatch},
@@ -76,6 +77,7 @@ struct MirLowerCtx<'a> {
db: &'a dyn HirDatabase,
body: &'a Body,
infer: &'a InferenceResult,
+ resolver: Resolver,
drop_scopes: Vec<DropScope>,
}
@@ -246,8 +248,15 @@ impl From<LayoutError> for MirLowerError {
}
impl MirLowerError {
- fn unresolved_path(db: &dyn HirDatabase, p: &Path, edition: Edition) -> Self {
- Self::UnresolvedName(p.display(db, edition).to_string())
+ fn unresolved_path(
+ db: &dyn HirDatabase,
+ p: &Path,
+ edition: Edition,
+ types_map: &TypesMap,
+ ) -> Self {
+ Self::UnresolvedName(
+ hir_display_with_types_map(p, types_map).display(db, edition).to_string(),
+ )
}
}
@@ -278,6 +287,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
owner,
closures: vec![],
};
+ let resolver = owner.resolver(db.upcast());
MirLowerCtx {
result: mir,
@@ -285,6 +295,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
infer,
body,
owner,
+ resolver,
current_loop_blocks: None,
labeled_loop_blocks: Default::default(),
discr_temp: None,
@@ -410,43 +421,54 @@ impl<'ctx> MirLowerCtx<'ctx> {
Err(MirLowerError::IncompleteExpr)
}
Expr::Path(p) => {
- let pr =
- if let Some((assoc, subst)) = self.infer.assoc_resolutions_for_expr(expr_id) {
- match assoc {
- hir_def::AssocItemId::ConstId(c) => {
- self.lower_const(
- c.into(),
- current,
- place,
- subst,
- expr_id.into(),
- self.expr_ty_without_adjust(expr_id),
- )?;
- return Ok(Some(current));
- }
- hir_def::AssocItemId::FunctionId(_) => {
- // FnDefs are zero sized, no action is needed.
- return Ok(Some(current));
- }
- hir_def::AssocItemId::TypeAliasId(_) => {
- // FIXME: If it is unreachable, use proper error instead of `not_supported`.
- not_supported!("associated functions and types")
- }
+ let pr = if let Some((assoc, subst)) =
+ self.infer.assoc_resolutions_for_expr(expr_id)
+ {
+ match assoc {
+ hir_def::AssocItemId::ConstId(c) => {
+ self.lower_const(
+ c.into(),
+ current,
+ place,
+ subst,
+ expr_id.into(),
+ self.expr_ty_without_adjust(expr_id),
+ )?;
+ return Ok(Some(current));
}
- } else if let Some(variant) = self.infer.variant_resolution_for_expr(expr_id) {
- match variant {
- VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
- VariantId::StructId(s) => ValueNs::StructId(s),
- VariantId::UnionId(_) => implementation_error!("Union variant as path"),
+ hir_def::AssocItemId::FunctionId(_) => {
+ // FnDefs are zero sized, no action is needed.
+ return Ok(Some(current));
}
- } else {
- let unresolved_name =
- || MirLowerError::unresolved_path(self.db, p, self.edition());
- let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
- resolver
- .resolve_path_in_value_ns_fully(self.db.upcast(), p)
- .ok_or_else(unresolved_name)?
- };
+ hir_def::AssocItemId::TypeAliasId(_) => {
+ // FIXME: If it is unreachable, use proper error instead of `not_supported`.
+ not_supported!("associated functions and types")
+ }
+ }
+ } else if let Some(variant) = self.infer.variant_resolution_for_expr(expr_id) {
+ match variant {
+ VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
+ VariantId::StructId(s) => ValueNs::StructId(s),
+ VariantId::UnionId(_) => implementation_error!("Union variant as path"),
+ }
+ } else {
+ let resolver_guard =
+ self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
+ let hygiene = self.body.expr_path_hygiene(expr_id);
+ let result = self
+ .resolver
+ .resolve_path_in_value_ns_fully(self.db.upcast(), p, hygiene)
+ .ok_or_else(|| {
+ MirLowerError::unresolved_path(
+ self.db,
+ p,
+ self.edition(),
+ &self.body.types,
+ )
+ })?;
+ self.resolver.reset_to_guard(resolver_guard);
+ result
+ };
match pr {
ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => {
let Some((temp, current)) =
@@ -553,8 +575,11 @@ impl<'ctx> MirLowerCtx<'ctx> {
return Ok(None);
};
self.push_fake_read(current, cond_place, expr_id.into());
+ let resolver_guard =
+ self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
let (then_target, else_target) =
self.pattern_match(current, None, cond_place, *pat)?;
+ self.resolver.reset_to_guard(resolver_guard);
self.write_bytes_to_place(
then_target,
place,
@@ -688,6 +713,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
};
self.push_fake_read(current, cond_place, expr_id.into());
let mut end = None;
+ let resolver_guard =
+ self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
for MatchArm { pat, guard, expr } in arms.iter() {
let (then, mut otherwise) =
self.pattern_match(current, None, cond_place, *pat)?;
@@ -721,6 +748,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
}
}
}
+ self.resolver.reset_to_guard(resolver_guard);
if self.is_unterminated(current) {
self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into());
}
@@ -795,7 +823,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
}
Expr::Become { .. } => not_supported!("tail-calls"),
Expr::Yield { .. } => not_supported!("yield"),
- Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => {
+ Expr::RecordLit { fields, path, spread } => {
let spread_place = match spread {
&Some(it) => {
let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else {
@@ -809,7 +837,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
let variant_id =
self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path {
Some(p) => MirLowerError::UnresolvedName(
- p.display(self.db, self.edition()).to_string(),
+ hir_display_with_types_map(&**p, &self.body.types)
+ .display(self.db, self.edition())
+ .to_string(),
),
None => MirLowerError::RecordLiteralWithoutPath,
})?;
@@ -1010,35 +1040,28 @@ impl<'ctx> MirLowerCtx<'ctx> {
);
}
}
- if let hir_def::hir::BinaryOp::Assignment { op } = op {
- if let Some(op) = op {
- // last adjustment is `&mut` which we don't want it.
- let adjusts = self
- .infer
- .expr_adjustments
- .get(lhs)
- .and_then(|it| it.split_last())
- .map(|it| it.1)
- .ok_or(MirLowerError::TypeError(
- "adjustment of binary op was missing",
- ))?;
- let Some((lhs_place, current)) =
- self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?
- else {
- return Ok(None);
- };
- let Some((rhs_op, current)) =
- self.lower_expr_to_some_operand(*rhs, current)?
- else {
- return Ok(None);
- };
- let r_value =
- Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place), rhs_op);
- self.push_assignment(current, lhs_place, r_value, expr_id.into());
- return Ok(Some(current));
- } else {
- return self.lower_assignment(current, *lhs, *rhs, expr_id.into());
- }
+ if let hir_def::hir::BinaryOp::Assignment { op: Some(op) } = op {
+ // last adjustment is `&mut` which we don't want it.
+ let adjusts = self
+ .infer
+ .expr_adjustments
+ .get(lhs)
+ .and_then(|it| it.split_last())
+ .map(|it| it.1)
+ .ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?;
+ let Some((lhs_place, current)) =
+ self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?
+ else {
+ return Ok(None);
+ };
+ let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)?
+ else {
+ return Ok(None);
+ };
+ let r_value =
+ Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place), rhs_op);
+ self.push_assignment(current, lhs_place, r_value, expr_id.into());
+ return Ok(Some(current));
}
let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)?
else {
@@ -1097,6 +1120,18 @@ impl<'ctx> MirLowerCtx<'ctx> {
);
Ok(Some(current))
}
+ &Expr::Assignment { target, value } => {
+ let Some((value, mut current)) = self.lower_expr_as_place(current, value, true)?
+ else {
+ return Ok(None);
+ };
+ self.push_fake_read(current, value, expr_id.into());
+ let resolver_guard =
+ self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
+ current = self.pattern_match_assignment(current, value, target)?;
+ self.resolver.reset_to_guard(resolver_guard);
+ Ok(Some(current))
+ }
&Expr::Range { lhs, rhs, range_type: _ } => {
let ty = self.expr_ty_without_adjust(expr_id);
let Some((adt, subst)) = ty.as_adt() else {
@@ -1213,7 +1248,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
);
Ok(Some(current))
}
- Expr::Tuple { exprs, is_assignee_expr: _ } => {
+ Expr::Tuple { exprs } => {
let Some(values) = exprs
.iter()
.map(|it| {
@@ -1291,73 +1326,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
}
}
- fn lower_destructing_assignment(
- &mut self,
- mut current: BasicBlockId,
- lhs: ExprId,
- rhs: Place,
- span: MirSpan,
- ) -> Result<Option<BasicBlockId>> {
- match &self.body.exprs[lhs] {
- Expr::Tuple { exprs, is_assignee_expr: _ } => {
- for (i, expr) in exprs.iter().enumerate() {
- let rhs = rhs.project(
- ProjectionElem::Field(Either::Right(TupleFieldId {
- tuple: TupleId(!0), // Dummy this as its unused
- index: i as u32,
- })),
- &mut self.result.projection_store,
- );
- let Some(c) = self.lower_destructing_assignment(current, *expr, rhs, span)?
- else {
- return Ok(None);
- };
- current = c;
- }
- Ok(Some(current))
- }
- Expr::Underscore => Ok(Some(current)),
- _ => {
- let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)?
- else {
- return Ok(None);
- };
- self.push_assignment(current, lhs_place, Operand::Copy(rhs).into(), span);
- Ok(Some(current))
- }
- }
- }
-
- fn lower_assignment(
- &mut self,
- current: BasicBlockId,
- lhs: ExprId,
- rhs: ExprId,
- span: MirSpan,
- ) -> Result<Option<BasicBlockId>> {
- let Some((rhs_op, current)) = self.lower_expr_to_some_operand(rhs, current)? else {
- return Ok(None);
- };
- if matches!(&self.body.exprs[lhs], Expr::Underscore) {
- self.push_fake_read_for_operand(current, rhs_op, span);
- return Ok(Some(current));
- }
- if matches!(
- &self.body.exprs[lhs],
- Expr::Tuple { .. } | Expr::RecordLit { .. } | Expr::Call { .. }
- ) {
- let temp = self.temp(self.expr_ty_after_adjustments(rhs), current, rhs.into())?;
- let temp = Place::from(temp);
- self.push_assignment(current, temp, rhs_op.into(), span);
- return self.lower_destructing_assignment(current, lhs, temp, span);
- }
- let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)? else {
- return Ok(None);
- };
- self.push_assignment(current, lhs_place, rhs_op.into(), span);
- Ok(Some(current))
- }
-
fn placeholder_subst(&mut self) -> Substitution {
match self.owner.as_generic_def_id(self.db.upcast()) {
Some(it) => TyBuilder::placeholder_subst(self.db, it),
@@ -1406,10 +1374,10 @@ impl<'ctx> MirLowerCtx<'ctx> {
};
let edition = self.edition();
let unresolved_name =
- || MirLowerError::unresolved_path(self.db, c.as_ref(), edition);
- let resolver = self.owner.resolver(self.db.upcast());
- let pr = resolver
- .resolve_path_in_value_ns(self.db.upcast(), c.as_ref())
+ || MirLowerError::unresolved_path(self.db, c, edition, &self.body.types);
+ let pr = self
+ .resolver
+ .resolve_path_in_value_ns(self.db.upcast(), c, HygieneId::ROOT)
.ok_or_else(unresolved_name)?;
match pr {
ResolveValueResult::ValueNs(v, _) => {
@@ -1632,12 +1600,6 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.push_statement(block, StatementKind::FakeRead(p).with_span(span));
}
- fn push_fake_read_for_operand(&mut self, block: BasicBlockId, operand: Operand, span: MirSpan) {
- if let Operand::Move(p) | Operand::Copy(p) = operand {
- self.push_fake_read(block, p, span);
- }
- }
-
fn push_assignment(
&mut self,
block: BasicBlockId,
@@ -1791,8 +1753,16 @@ impl<'ctx> MirLowerCtx<'ctx> {
};
current = c;
self.push_fake_read(current, init_place, span);
+ // Using the initializer for the resolver scope is good enough for us, as it cannot create new declarations
+ // and has all declarations of the `let`.
+ let resolver_guard = self.resolver.update_to_inner_scope(
+ self.db.upcast(),
+ self.owner,
+ *expr_id,
+ );
(current, else_block) =
self.pattern_match(current, None, init_place, *pat)?;
+ self.resolver.reset_to_guard(resolver_guard);
match (else_block, else_branch) {
(None, _) => (),
(Some(else_block), None) => {
@@ -1828,7 +1798,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.push_fake_read(c, p, expr.into());
current = scope2.pop_and_drop(self, c, expr.into());
}
- hir_def::hir::Statement::Item => (),
+ hir_def::hir::Statement::Item(_) => (),
}
}
if let Some(tail) = tail {
@@ -2066,11 +2036,13 @@ pub fn mir_body_for_closure_query(
let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else {
implementation_error!("closure has not callable sig");
};
+ let resolver_guard = ctx.resolver.update_to_inner_scope(db.upcast(), owner, expr);
let current = ctx.lower_params_and_bindings(
args.iter().zip(sig.params().iter()).map(|(it, y)| (*it, y.clone())),
None,
|_| true,
)?;
+ ctx.resolver.reset_to_guard(resolver_guard);
if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
let current = ctx.pop_drop_scope_assert_finished(current, root.into())?;
ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index 424ee1160c..420f2aaff4 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -135,8 +135,13 @@ impl MirLowerCtx<'_> {
};
match &self.body.exprs[expr_id] {
Expr::Path(p) => {
- let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
- let Some(pr) = resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p) else {
+ let resolver_guard =
+ self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id);
+ let hygiene = self.body.expr_path_hygiene(expr_id);
+ let resolved =
+ self.resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p, hygiene);
+ self.resolver.reset_to_guard(resolver_guard);
+ let Some(pr) = resolved else {
return try_rvalue(self);
};
match pr {
@@ -216,7 +221,7 @@ impl MirLowerCtx<'_> {
self.push_field_projection(&mut r, expr_id)?;
Ok(Some((r, current)))
}
- Expr::Index { base, index, is_assignee_expr: _ } => {
+ Expr::Index { base, index } => {
let base_ty = self.expr_ty_after_adjustments(*base);
let index_ty = self.expr_ty_after_adjustments(*index);
if index_ty != TyBuilder::usize()
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index b1c0d1f2b3..2ffea34c85 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -1,6 +1,6 @@
//! MIR lowering for patterns
-use hir_def::{hir::LiteralOrConst, resolver::HasResolver, AssocItemId};
+use hir_def::{hir::LiteralOrConst, AssocItemId};
use crate::{
mir::{
@@ -46,6 +46,8 @@ enum MatchingMode {
Check,
/// Assume that this pattern matches, fill bindings
Bind,
+ /// Assume that this pattern matches, assign to existing variables.
+ Assign,
}
impl MirLowerCtx<'_> {
@@ -82,6 +84,17 @@ impl MirLowerCtx<'_> {
Ok((current, current_else))
}
+ pub(super) fn pattern_match_assignment(
+ &mut self,
+ current: BasicBlockId,
+ value: Place,
+ pattern: PatId,
+ ) -> Result<BasicBlockId> {
+ let (current, _) =
+ self.pattern_match_inner(current, None, value, pattern, MatchingMode::Assign)?;
+ Ok(current)
+ }
+
pub(super) fn match_self_param(
&mut self,
id: BindingId,
@@ -155,14 +168,8 @@ impl MirLowerCtx<'_> {
*pat,
MatchingMode::Check,
)?;
- if mode == MatchingMode::Bind {
- (next, _) = self.pattern_match_inner(
- next,
- None,
- cond_place,
- *pat,
- MatchingMode::Bind,
- )?;
+ if mode != MatchingMode::Check {
+ (next, _) = self.pattern_match_inner(next, None, cond_place, *pat, mode)?;
}
self.set_goto(next, then_target, pattern.into());
match next_else {
@@ -176,11 +183,11 @@ impl MirLowerCtx<'_> {
}
}
if !finished {
- if mode == MatchingMode::Bind {
- self.set_terminator(current, TerminatorKind::Unreachable, pattern.into());
- } else {
+ if mode == MatchingMode::Check {
let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
self.set_goto(current, ce, pattern.into());
+ } else {
+ self.set_terminator(current, TerminatorKind::Unreachable, pattern.into());
}
}
(then_target, current_else)
@@ -300,7 +307,7 @@ impl MirLowerCtx<'_> {
self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
}
if let &Some(slice) = slice {
- if mode == MatchingMode::Bind {
+ if mode != MatchingMode::Check {
if let Pat::Bind { id, subpat: _ } = self.body[slice] {
let next_place = cond_place.project(
ProjectionElem::Subslice {
@@ -342,17 +349,36 @@ impl MirLowerCtx<'_> {
mode,
)?,
None => {
- // The path is not a variant, so it is a const
+ let unresolved_name = || {
+ MirLowerError::unresolved_path(self.db, p, self.edition(), &self.body.types)
+ };
+ let hygiene = self.body.pat_path_hygiene(pattern);
+ let pr = self
+ .resolver
+ .resolve_path_in_value_ns(self.db.upcast(), p, hygiene)
+ .ok_or_else(unresolved_name)?;
+
+ if let (
+ MatchingMode::Assign,
+ ResolveValueResult::ValueNs(ValueNs::LocalBinding(binding), _),
+ ) = (mode, &pr)
+ {
+ let local = self.binding_local(*binding)?;
+ self.push_match_assignment(
+ current,
+ local,
+ BindingMode::Move,
+ cond_place,
+ pattern.into(),
+ );
+ return Ok((current, current_else));
+ }
+
+ // The path is not a variant or a local, so it is a const
if mode != MatchingMode::Check {
// A const don't bind anything. Only needs check.
return Ok((current, current_else));
}
- let unresolved_name =
- || MirLowerError::unresolved_path(self.db, p, self.edition());
- let resolver = self.owner.resolver(self.db.upcast());
- let pr = resolver
- .resolve_path_in_value_ns(self.db.upcast(), p)
- .ok_or_else(unresolved_name)?;
let (c, subst) = 'b: {
if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern) {
if let AssocItemId::ConstId(c) = x.0 {
@@ -415,7 +441,7 @@ impl MirLowerCtx<'_> {
(current, current_else) =
self.pattern_match_inner(current, current_else, cond_place, *subpat, mode)?
}
- if mode == MatchingMode::Bind {
+ if mode != MatchingMode::Check {
let mode = self.infer.binding_modes[pattern];
self.pattern_match_binding(
*id,
@@ -448,6 +474,23 @@ impl MirLowerCtx<'_> {
cond_place.project(ProjectionElem::Deref, &mut self.result.projection_store);
self.pattern_match_inner(current, current_else, cond_place, *pat, mode)?
}
+ &Pat::Expr(expr) => {
+ stdx::always!(
+ mode == MatchingMode::Assign,
+ "Pat::Expr can only come in destructuring assignments"
+ );
+ let Some((lhs_place, current)) = self.lower_expr_as_place(current, expr, false)?
+ else {
+ return Ok((current, current_else));
+ };
+ self.push_assignment(
+ current,
+ lhs_place,
+ Operand::Copy(cond_place).into(),
+ expr.into(),
+ );
+ (current, current_else)
+ }
Pat::Box { .. } => not_supported!("box pattern"),
Pat::ConstBlock(_) => not_supported!("const block pattern"),
})
@@ -464,6 +507,18 @@ impl MirLowerCtx<'_> {
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
let target_place = self.binding_local(id)?;
self.push_storage_live(id, current)?;
+ self.push_match_assignment(current, target_place, mode, cond_place, span);
+ Ok((current, current_else))
+ }
+
+ fn push_match_assignment(
+ &mut self,
+ current: BasicBlockId,
+ target_place: LocalId,
+ mode: BindingMode,
+ cond_place: Place,
+ span: MirSpan,
+ ) {
self.push_assignment(
current,
target_place.into(),
@@ -476,7 +531,6 @@ impl MirLowerCtx<'_> {
},
span,
);
- Ok((current, current_else))
}
fn pattern_match_const(
diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs
index 4c6bc376e2..92132fa047 100644
--- a/crates/hir-ty/src/mir/monomorphization.rs
+++ b/crates/hir-ty/src/mir/monomorphization.rs
@@ -258,6 +258,10 @@ impl Filler<'_> {
| Rvalue::UnaryOp(_, _)
| Rvalue::Discriminant(_)
| Rvalue::CopyForDeref(_) => (),
+ Rvalue::ThreadLocalRef(n)
+ | Rvalue::AddressOf(n)
+ | Rvalue::BinaryOp(n)
+ | Rvalue::NullaryOp(n) => match *n {},
},
StatementKind::Deinit(_)
| StatementKind::FakeRead(_)
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index df56071aa9..06765a104c 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -459,6 +459,10 @@ impl<'a> MirPrettyCtx<'a> {
self.place(p);
w!(self, ")");
}
+ Rvalue::ThreadLocalRef(n)
+ | Rvalue::AddressOf(n)
+ | Rvalue::BinaryOp(n)
+ | Rvalue::NullaryOp(n) => match *n {},
}
}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index a8170b6060..5f0f341f39 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -3418,11 +3418,11 @@ struct TS(usize);
fn main() {
let x;
[x,] = &[1,];
- //^^^^expected &'? [i32; 1], got [{unknown}; _]
+ //^^^^expected &'? [i32; 1], got [{unknown}]
let x;
[(x,),] = &[(1,),];
- //^^^^^^^expected &'? [(i32,); 1], got [{unknown}; _]
+ //^^^^^^^expected &'? [(i32,); 1], got [{unknown}]
let x;
((x,),) = &((1,),);
@@ -3720,3 +3720,85 @@ fn test() -> bool {
"#]],
);
}
+
+#[test]
+fn macro_semitransparent_hygiene() {
+ check_types(
+ r#"
+macro_rules! m {
+ () => { let bar: i32; };
+}
+fn foo() {
+ let bar: bool;
+ m!();
+ bar;
+ // ^^^ bool
+}
+ "#,
+ );
+}
+
+#[test]
+fn macro_expansion_can_refer_variables_defined_before_macro_definition() {
+ check_types(
+ r#"
+fn foo() {
+ let v: i32 = 0;
+ macro_rules! m {
+ () => { v };
+ }
+ let v: bool = true;
+ m!();
+ // ^^^^ i32
+}
+ "#,
+ );
+}
+
+#[test]
+fn macro_rules_shadowing_works_with_hygiene() {
+ check_types(
+ r#"
+fn foo() {
+ let v: bool;
+ macro_rules! m { () => { v } }
+ m!();
+ // ^^^^ bool
+
+ let v: char;
+ macro_rules! m { () => { v } }
+ m!();
+ // ^^^^ char
+
+ {
+ let v: u8;
+ macro_rules! m { () => { v } }
+ m!();
+ // ^^^^ u8
+
+ let v: i8;
+ macro_rules! m { () => { v } }
+ m!();
+ // ^^^^ i8
+
+ let v: i16;
+ macro_rules! m { () => { v } }
+ m!();
+ // ^^^^ i16
+
+ {
+ let v: u32;
+ macro_rules! m { () => { v } }
+ m!();
+ // ^^^^ u32
+
+ let v: u64;
+ macro_rules! m { () => { v } }
+ m!();
+ // ^^^^ u64
+ }
+ }
+}
+ "#,
+ );
+}
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 620bba2d75..0a436ff2b4 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -123,7 +123,7 @@ pub(super) struct ClauseElaborator<'a> {
seen: FxHashSet<WhereClause>,
}
-impl<'a> ClauseElaborator<'a> {
+impl ClauseElaborator<'_> {
fn extend_deduped(&mut self, clauses: impl IntoIterator<Item = WhereClause>) {
self.stack.extend(clauses.into_iter().filter(|c| self.seen.insert(c.clone())))
}
@@ -163,10 +163,12 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(Tra
WherePredicate::ForLifetime { target, bound, .. }
| WherePredicate::TypeBound { target, bound } => {
let is_trait = match target {
- WherePredicateTypeTarget::TypeRef(type_ref) => match &**type_ref {
- TypeRef::Path(p) => p.is_self_type(),
- _ => false,
- },
+ WherePredicateTypeTarget::TypeRef(type_ref) => {
+ match &generic_params.types_map[*type_ref] {
+ TypeRef::Path(p) => p.is_self_type(),
+ _ => false,
+ }
+ }
WherePredicateTypeTarget::TypeOrConstParam(local_id) => {
Some(*local_id) == trait_self
}
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs
index cb5f5b06ae..22760c41aa 100644
--- a/crates/hir/src/db.rs
+++ b/crates/hir/src/db.rs
@@ -4,35 +4,43 @@
//!
//! But we need this for at least LRU caching at the query level.
pub use hir_def::db::{
- AttrsQuery, BlockDefMapQuery, BlockItemTreeQuery, BodyQuery, BodyWithSourceMapQuery,
- ConstDataQuery, ConstVisibilityQuery, CrateDefMapQuery, CrateLangItemsQuery,
- CrateNotableTraitsQuery, CrateSupportsNoStdQuery, DefDatabase, DefDatabaseStorage,
- EnumDataQuery, EnumVariantDataWithDiagnosticsQuery, ExprScopesQuery, ExternCrateDeclDataQuery,
- FieldVisibilitiesQuery, FieldsAttrsQuery, FieldsAttrsSourceMapQuery, FileItemTreeQuery,
- FunctionDataQuery, FunctionVisibilityQuery, GenericParamsQuery, ImplDataWithDiagnosticsQuery,
- ImportMapQuery, InternAnonymousConstQuery, InternBlockQuery, InternConstQuery, InternDatabase,
- InternDatabaseStorage, InternEnumQuery, InternExternBlockQuery, InternExternCrateQuery,
- InternFunctionQuery, InternImplQuery, InternInTypeConstQuery, InternMacro2Query,
- InternMacroRulesQuery, InternProcMacroQuery, InternStaticQuery, InternStructQuery,
- InternTraitAliasQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery,
- InternUseQuery, LangItemQuery, Macro2DataQuery, MacroRulesDataQuery, ProcMacroDataQuery,
- StaticDataQuery, StructDataWithDiagnosticsQuery, TraitAliasDataQuery,
- TraitDataWithDiagnosticsQuery, TypeAliasDataQuery, UnionDataWithDiagnosticsQuery,
+ AttrsQuery, BlockDefMapQuery, BlockItemTreeQuery, BlockItemTreeWithSourceMapQuery, BodyQuery,
+ BodyWithSourceMapQuery, ConstDataQuery, ConstVisibilityQuery, CrateDefMapQuery,
+ CrateLangItemsQuery, CrateNotableTraitsQuery, CrateSupportsNoStdQuery, DefDatabase,
+ DefDatabaseStorage, EnumDataQuery, EnumVariantDataWithDiagnosticsQuery,
+ ExpandProcAttrMacrosQuery, ExprScopesQuery, ExternCrateDeclDataQuery, FieldVisibilitiesQuery,
+ FieldsAttrsQuery, FieldsAttrsSourceMapQuery, FileItemTreeQuery, FileItemTreeWithSourceMapQuery,
+ FunctionDataQuery, FunctionVisibilityQuery, GenericParamsQuery,
+ GenericParamsWithSourceMapQuery, ImplDataWithDiagnosticsQuery, ImportMapQuery,
+ IncludeMacroInvocQuery, InternAnonymousConstQuery, InternBlockQuery, InternConstQuery,
+ InternDatabase, InternDatabaseStorage, InternEnumQuery, InternExternBlockQuery,
+ InternExternCrateQuery, InternFunctionQuery, InternImplQuery, InternInTypeConstQuery,
+ InternMacro2Query, InternMacroRulesQuery, InternProcMacroQuery, InternStaticQuery,
+ InternStructQuery, InternTraitAliasQuery, InternTraitQuery, InternTypeAliasQuery,
+ InternUnionQuery, InternUseQuery, LangItemQuery, Macro2DataQuery, MacroDefQuery,
+ MacroRulesDataQuery, NotableTraitsInDepsQuery, ProcMacroDataQuery, StaticDataQuery,
+ StructDataWithDiagnosticsQuery, TraitAliasDataQuery, TraitDataWithDiagnosticsQuery,
+ TypeAliasDataQuery, UnionDataWithDiagnosticsQuery,
};
pub use hir_expand::db::{
AstIdMapQuery, DeclMacroExpanderQuery, ExpandDatabase, ExpandDatabaseStorage,
ExpandProcMacroQuery, InternMacroCallQuery, InternSyntaxContextQuery, MacroArgQuery,
- ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery, ProcMacrosQuery, RealSpanMapQuery,
+ ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery, ProcMacroSpanQuery, ProcMacrosQuery,
+ RealSpanMapQuery,
};
pub use hir_ty::db::{
AdtDatumQuery, AdtVarianceQuery, AssociatedTyDataQuery, AssociatedTyValueQuery, BorrowckQuery,
CallableItemSignatureQuery, ConstEvalDiscriminantQuery, ConstEvalQuery, ConstEvalStaticQuery,
- ConstParamTyQuery, FieldTypesQuery, FnDefDatumQuery, FnDefVarianceQuery, GenericDefaultsQuery,
- GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, HirDatabaseStorage,
- ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, IncoherentInherentImplCratesQuery,
+ ConstParamTyQuery, DynCompatibilityOfTraitQuery, FieldTypesQuery, FnDefDatumQuery,
+ FnDefVarianceQuery, GenericDefaultsQuery, GenericPredicatesForParamQuery,
+ GenericPredicatesQuery, GenericPredicatesWithoutParentQuery, HirDatabase, HirDatabaseStorage,
+ ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, IncoherentInherentImplCratesQuery, InferQuery,
InherentImplsInBlockQuery, InherentImplsInCrateQuery, InternCallableDefQuery,
InternClosureQuery, InternCoroutineQuery, InternImplTraitIdQuery, InternLifetimeParamIdQuery,
- InternTypeOrConstParamIdQuery, LayoutOfAdtQuery, MirBodyQuery, ProgramClausesForChalkEnvQuery,
- ReturnTypeImplTraitsQuery, TargetDataLayoutQuery, TraitDatumQuery, TraitEnvironmentQuery,
- TraitImplsInBlockQuery, TraitImplsInCrateQuery, TraitImplsInDepsQuery, TyQuery, ValueTyQuery,
+ InternTypeOrConstParamIdQuery, LayoutOfAdtQuery, LayoutOfTyQuery, LookupImplMethodQuery,
+ MirBodyForClosureQuery, MirBodyQuery, MonomorphizedMirBodyForClosureQuery,
+ MonomorphizedMirBodyQuery, ProgramClausesForChalkEnvQuery, ReturnTypeImplTraitsQuery,
+ TargetDataLayoutQuery, TraitDatumQuery, TraitEnvironmentQuery, TraitImplsInBlockQuery,
+ TraitImplsInCrateQuery, TraitImplsInDepsQuery, TraitSolveQuery, TyQuery,
+ TypeAliasImplTraitsQuery, ValueTyQuery,
};
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 0b3cdb2f37..8297acde85 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -165,6 +165,7 @@ pub struct MacroError {
pub precise_location: Option<TextRange>,
pub message: String,
pub error: bool,
+ pub kind: &'static str,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -246,7 +247,7 @@ pub struct UnresolvedAssocItem {
#[derive(Debug)]
pub struct UnresolvedIdent {
- pub expr: InFile<AstPtr<ast::Expr>>,
+ pub expr_or_pat: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
}
#[derive(Debug)]
@@ -257,7 +258,7 @@ pub struct PrivateField {
#[derive(Debug)]
pub struct MissingUnsafe {
- pub expr: InFile<AstPtr<ast::Expr>>,
+ pub expr: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
/// If true, the diagnostics is an `unsafe_op_in_unsafe_fn` lint instead of a hard error.
pub only_lint: bool,
}
@@ -398,56 +399,46 @@ impl AnyDiagnostic {
.map(|idx| variant_data.fields()[idx].name.clone())
.collect();
- match record {
- Either::Left(record_expr) => match source_map.expr_syntax(record_expr) {
- Ok(source_ptr) => {
- let root = source_ptr.file_syntax(db.upcast());
- if let ast::Expr::RecordExpr(record_expr) =
- source_ptr.value.to_node(&root)
- {
- if record_expr.record_expr_field_list().is_some() {
- let field_list_parent_path =
- record_expr.path().map(|path| AstPtr::new(&path));
- return Some(
- MissingFields {
- file: source_ptr.file_id,
- field_list_parent: AstPtr::new(&Either::Left(
- record_expr,
- )),
- field_list_parent_path,
- missed_fields,
- }
- .into(),
- );
+ let record = match record {
+ Either::Left(record_expr) => {
+ source_map.expr_syntax(record_expr).ok()?.map(AstPtr::wrap_left)
+ }
+ Either::Right(record_pat) => source_map.pat_syntax(record_pat).ok()?,
+ };
+ let file = record.file_id;
+ let root = record.file_syntax(db.upcast());
+ match record.value.to_node(&root) {
+ Either::Left(ast::Expr::RecordExpr(record_expr)) => {
+ if record_expr.record_expr_field_list().is_some() {
+ let field_list_parent_path =
+ record_expr.path().map(|path| AstPtr::new(&path));
+ return Some(
+ MissingFields {
+ file,
+ field_list_parent: AstPtr::new(&Either::Left(record_expr)),
+ field_list_parent_path,
+ missed_fields,
}
- }
+ .into(),
+ );
}
- Err(SyntheticSyntax) => (),
- },
- Either::Right(record_pat) => match source_map.pat_syntax(record_pat) {
- Ok(source_ptr) => {
- if let Some(ptr) = source_ptr.value.cast::<ast::RecordPat>() {
- let root = source_ptr.file_syntax(db.upcast());
- let record_pat = ptr.to_node(&root);
- if record_pat.record_pat_field_list().is_some() {
- let field_list_parent_path =
- record_pat.path().map(|path| AstPtr::new(&path));
- return Some(
- MissingFields {
- file: source_ptr.file_id,
- field_list_parent: AstPtr::new(&Either::Right(
- record_pat,
- )),
- field_list_parent_path,
- missed_fields,
- }
- .into(),
- );
+ }
+ Either::Right(ast::Pat::RecordPat(record_pat)) => {
+ if record_pat.record_pat_field_list().is_some() {
+ let field_list_parent_path =
+ record_pat.path().map(|path| AstPtr::new(&path));
+ return Some(
+ MissingFields {
+ file,
+ field_list_parent: AstPtr::new(&Either::Right(record_pat)),
+ field_list_parent_path,
+ missed_fields,
}
- }
+ .into(),
+ );
}
- Err(SyntheticSyntax) => (),
- },
+ }
+ _ => {}
}
}
BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { method_call_expr } => {
@@ -541,15 +532,17 @@ impl AnyDiagnostic {
let pat_syntax = |pat| {
source_map.pat_syntax(pat).inspect_err(|_| tracing::error!("synthetic syntax")).ok()
};
+ let expr_or_pat_syntax = |id| match id {
+ ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(|it| it.map(AstPtr::wrap_left)),
+ ExprOrPatId::PatId(pat) => pat_syntax(pat),
+ };
Some(match d {
&InferenceDiagnostic::NoSuchField { field: expr, private, variant } => {
let expr_or_pat = match expr {
ExprOrPatId::ExprId(expr) => {
source_map.field_syntax(expr).map(AstPtr::wrap_left)
}
- ExprOrPatId::PatId(pat) => {
- source_map.pat_field_syntax(pat).map(AstPtr::wrap_right)
- }
+ ExprOrPatId::PatId(pat) => source_map.pat_field_syntax(pat),
};
NoSuchField { field: expr_or_pat, private, variant }.into()
}
@@ -562,10 +555,7 @@ impl AnyDiagnostic {
PrivateField { expr, field }.into()
}
&InferenceDiagnostic::PrivateAssocItem { id, item } => {
- let expr_or_pat = match id {
- ExprOrPatId::ExprId(expr) => expr_syntax(expr)?.map(AstPtr::wrap_left),
- ExprOrPatId::PatId(pat) => pat_syntax(pat)?.map(AstPtr::wrap_right),
- };
+ let expr_or_pat = expr_or_pat_syntax(id)?;
let item = item.into();
PrivateAssocItem { expr_or_pat, item }.into()
}
@@ -609,15 +599,12 @@ impl AnyDiagnostic {
.into()
}
&InferenceDiagnostic::UnresolvedAssocItem { id } => {
- let expr_or_pat = match id {
- ExprOrPatId::ExprId(expr) => expr_syntax(expr)?.map(AstPtr::wrap_left),
- ExprOrPatId::PatId(pat) => pat_syntax(pat)?.map(AstPtr::wrap_right),
- };
+ let expr_or_pat = expr_or_pat_syntax(id)?;
UnresolvedAssocItem { expr_or_pat }.into()
}
- &InferenceDiagnostic::UnresolvedIdent { expr } => {
- let expr = expr_syntax(expr)?;
- UnresolvedIdent { expr }.into()
+ &InferenceDiagnostic::UnresolvedIdent { id } => {
+ let expr_or_pat = expr_or_pat_syntax(id)?;
+ UnresolvedIdent { expr_or_pat }.into()
}
&InferenceDiagnostic::BreakOutsideOfLoop { expr, is_break, bad_value_break } => {
let expr = expr_syntax(expr)?;
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index c2b2fbef75..9275f45d88 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -12,12 +12,11 @@ use hir_def::{
};
use hir_ty::{
display::{
- write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
- HirFormatter, SizedByDefault,
+ hir_display_with_types_map, write_bounds_like_dyn_trait_with_prefix, write_visibility,
+ HirDisplay, HirDisplayError, HirDisplayWithTypesMap, HirFormatter, SizedByDefault,
},
AliasEq, AliasTy, Interner, ProjectionTyExt, TraitRefExt, TyKind, WhereClause,
};
-use intern::Interned;
use itertools::Itertools;
use crate::{
@@ -113,7 +112,7 @@ impl HirDisplay for Function {
f.write_str(&pat_str)?;
f.write_str(": ")?;
- type_ref.hir_fmt(f)?;
+ type_ref.hir_fmt(f, &data.types_map)?;
}
if data.is_varargs() {
@@ -129,28 +128,30 @@ impl HirDisplay for Function {
// Use ugly pattern match to strip the Future trait.
// Better way?
let ret_type = if !data.is_async() {
- &data.ret_type
+ Some(data.ret_type)
} else {
- match &*data.ret_type {
- TypeRef::ImplTrait(bounds) => match bounds[0].as_ref() {
- TypeBound::Path(path, _) => {
- path.segments().iter().last().unwrap().args_and_bindings.unwrap().bindings
+ match &data.types_map[data.ret_type] {
+ TypeRef::ImplTrait(bounds) => match &bounds[0] {
+ TypeBound::Path(path, _) => Some(
+ *path.segments().iter().last().unwrap().args_and_bindings.unwrap().bindings
[0]
.type_ref
.as_ref()
- .unwrap()
- }
- _ => &TypeRef::Error,
+ .unwrap(),
+ ),
+ _ => None,
},
- _ => &TypeRef::Error,
+ _ => None,
}
};
- match ret_type {
- TypeRef::Tuple(tup) if tup.is_empty() => {}
- ty => {
- f.write_str(" -> ")?;
- ty.hir_fmt(f)?;
+ if let Some(ret_type) = ret_type {
+ match &data.types_map[ret_type] {
+ TypeRef::Tuple(tup) if tup.is_empty() => {}
+ _ => {
+ f.write_str(" -> ")?;
+ ret_type.hir_fmt(f, &data.types_map)?;
+ }
}
}
@@ -192,23 +193,23 @@ fn write_impl_header(impl_: &Impl, f: &mut HirFormatter<'_>) -> Result<(), HirDi
impl HirDisplay for SelfParam {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
let data = f.db.function_data(self.func);
- let param = data.params.first().unwrap();
- match &**param {
+ let param = *data.params.first().unwrap();
+ match &data.types_map[param] {
TypeRef::Path(p) if p.is_self_type() => f.write_str("self"),
- TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) =>
+ TypeRef::Reference(ref_) if matches!(&data.types_map[ref_.ty], TypeRef::Path(p) if p.is_self_type()) =>
{
f.write_char('&')?;
- if let Some(lifetime) = lifetime {
+ if let Some(lifetime) = &ref_.lifetime {
write!(f, "{} ", lifetime.name.display(f.db.upcast(), f.edition()))?;
}
- if let hir_def::type_ref::Mutability::Mut = mut_ {
+ if let hir_def::type_ref::Mutability::Mut = ref_.mutability {
f.write_str("mut ")?;
}
f.write_str("self")
}
- ty => {
+ _ => {
f.write_str("self: ")?;
- ty.hir_fmt(f)
+ param.hir_fmt(f, &data.types_map)
}
}
}
@@ -393,7 +394,7 @@ impl HirDisplay for Variant {
let data = self.variant_data(f.db);
match &*data {
VariantData::Unit => {}
- VariantData::Tuple(fields) => {
+ VariantData::Tuple { fields, types_map } => {
f.write_char('(')?;
let mut first = true;
for (_, field) in fields.iter() {
@@ -403,11 +404,11 @@ impl HirDisplay for Variant {
f.write_str(", ")?;
}
// Enum variant fields must be pub.
- field.type_ref.hir_fmt(f)?;
+ field.type_ref.hir_fmt(f, types_map)?;
}
f.write_char(')')?;
}
- VariantData::Record(_) => {
+ VariantData::Record { .. } => {
if let Some(limit) = f.entity_limit {
write_fields(&self.fields(f.db), false, limit, true, f)?;
}
@@ -579,13 +580,13 @@ fn write_generic_params(
write!(f, "{}", name.display(f.db.upcast(), f.edition()))?;
if let Some(default) = &ty.default {
f.write_str(" = ")?;
- default.hir_fmt(f)?;
+ default.hir_fmt(f, &params.types_map)?;
}
}
TypeOrConstParamData::ConstParamData(c) => {
delim(f)?;
write!(f, "const {}: ", name.display(f.db.upcast(), f.edition()))?;
- c.ty.hir_fmt(f)?;
+ c.ty.hir_fmt(f, &params.types_map)?;
if let Some(default) = &c.default {
f.write_str(" = ")?;
@@ -615,7 +616,7 @@ fn write_where_clause(
Ok(true)
}
-fn has_disaplayable_predicates(params: &Interned<GenericParams>) -> bool {
+fn has_disaplayable_predicates(params: &GenericParams) -> bool {
params.where_predicates().any(|pred| {
!matches!(
pred,
@@ -626,21 +627,20 @@ fn has_disaplayable_predicates(params: &Interned<GenericParams>) -> bool {
}
fn write_where_predicates(
- params: &Interned<GenericParams>,
+ params: &GenericParams,
f: &mut HirFormatter<'_>,
) -> Result<(), HirDisplayError> {
use WherePredicate::*;
// unnamed type targets are displayed inline with the argument itself, e.g. `f: impl Y`.
- let is_unnamed_type_target =
- |params: &Interned<GenericParams>, target: &WherePredicateTypeTarget| {
- matches!(target,
- WherePredicateTypeTarget::TypeOrConstParam(id) if params[*id].name().is_none()
- )
- };
+ let is_unnamed_type_target = |params: &GenericParams, target: &WherePredicateTypeTarget| {
+ matches!(target,
+ WherePredicateTypeTarget::TypeOrConstParam(id) if params[*id].name().is_none()
+ )
+ };
let write_target = |target: &WherePredicateTypeTarget, f: &mut HirFormatter<'_>| match target {
- WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f),
+ WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f, &params.types_map),
WherePredicateTypeTarget::TypeOrConstParam(id) => match params[*id].name() {
Some(name) => write!(f, "{}", name.display(f.db.upcast(), f.edition())),
None => f.write_str("{unnamed}"),
@@ -668,7 +668,7 @@ fn write_where_predicates(
TypeBound { target, bound } => {
write_target(target, f)?;
f.write_str(": ")?;
- bound.hir_fmt(f)?;
+ bound.hir_fmt(f, &params.types_map)?;
}
Lifetime { target, bound } => {
let target = target.name.display(f.db.upcast(), f.edition());
@@ -681,14 +681,16 @@ fn write_where_predicates(
write!(f, "for<{lifetimes}> ")?;
write_target(target, f)?;
f.write_str(": ")?;
- bound.hir_fmt(f)?;
+ bound.hir_fmt(f, &params.types_map)?;
}
}
while let Some(nxt) = iter.next_if(|nxt| check_same_target(pred, nxt)) {
f.write_str(" + ")?;
match nxt {
- TypeBound { bound, .. } | ForLifetime { bound, .. } => bound.hir_fmt(f)?,
+ TypeBound { bound, .. } | ForLifetime { bound, .. } => {
+ bound.hir_fmt(f, &params.types_map)?
+ }
Lifetime { bound, .. } => {
write!(f, "{}", bound.name.display(f.db.upcast(), f.edition()))?
}
@@ -716,7 +718,7 @@ impl HirDisplay for Const {
Some(name) => write!(f, "{}: ", name.display(f.db.upcast(), f.edition()))?,
None => f.write_str("_: ")?,
}
- data.type_ref.hir_fmt(f)?;
+ data.type_ref.hir_fmt(f, &data.types_map)?;
Ok(())
}
}
@@ -730,7 +732,7 @@ impl HirDisplay for Static {
f.write_str("mut ")?;
}
write!(f, "{}: ", data.name.display(f.db.upcast(), f.edition()))?;
- data.type_ref.hir_fmt(f)?;
+ data.type_ref.hir_fmt(f, &data.types_map)?;
Ok(())
}
}
@@ -813,11 +815,14 @@ impl HirDisplay for TypeAlias {
write_generic_params(def_id, f)?;
if !data.bounds.is_empty() {
f.write_str(": ")?;
- f.write_joined(data.bounds.iter(), " + ")?;
+ f.write_joined(
+ data.bounds.iter().map(|bound| hir_display_with_types_map(bound, &data.types_map)),
+ " + ",
+ )?;
}
- if let Some(ty) = &data.type_ref {
+ if let Some(ty) = data.type_ref {
f.write_str(" = ")?;
- ty.hir_fmt(f)?;
+ ty.hir_fmt(f, &data.types_map)?;
}
write_where_clause(def_id, f)?;
Ok(())
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 30e023e1a4..88eb3b127e 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -58,7 +58,8 @@ use hir_def::{
TypeOrConstParamId, TypeParamId, UnionId,
};
use hir_expand::{
- attrs::collect_attrs, proc_macro::ProcMacroKind, AstId, MacroCallKind, ValueResult,
+ attrs::collect_attrs, proc_macro::ProcMacroKind, AstId, MacroCallKind, RenderedExpandError,
+ ValueResult,
};
use hir_ty::{
all_super_traits, autoderef, check_orphan_rules,
@@ -838,7 +839,7 @@ fn macro_call_diagnostics(
let file_id = loc.kind.file_id();
let node =
InFile::new(file_id, db.ast_id_map(file_id).get_erased(loc.kind.erased_ast_id()));
- let (message, error) = err.render_to_string(db.upcast());
+ let RenderedExpandError { message, error, kind } = err.render_to_string(db.upcast());
let precise_location = if err.span().anchor.file_id == file_id {
Some(
err.span().range
@@ -850,7 +851,7 @@ fn macro_call_diagnostics(
} else {
None
};
- acc.push(MacroError { node, precise_location, message, error }.into());
+ acc.push(MacroError { node, precise_location, message, error, kind }.into());
}
if !parse_errors.is_empty() {
@@ -916,13 +917,14 @@ fn emit_def_diagnostic_(
DefDiagnosticKind::MacroError { ast, path, err } => {
let item = ast.to_ptr(db.upcast());
- let (message, error) = err.render_to_string(db.upcast());
+ let RenderedExpandError { message, error, kind } = err.render_to_string(db.upcast());
acc.push(
MacroError {
node: InFile::new(ast.file_id, item.syntax_node_ptr()),
precise_location: None,
message: format!("{}: {message}", path.display(db.upcast(), edition)),
error,
+ kind,
}
.into(),
)
@@ -1811,7 +1813,8 @@ impl DefWithBody {
InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into()
}
BodyDiagnostic::MacroError { node, err } => {
- let (message, error) = err.render_to_string(db.upcast());
+ let RenderedExpandError { message, error, kind } =
+ err.render_to_string(db.upcast());
let precise_location = if err.span().anchor.file_id == node.file_id {
Some(
@@ -1829,6 +1832,7 @@ impl DefWithBody {
precise_location,
message,
error,
+ kind,
}
.into()
}
@@ -1885,7 +1889,7 @@ impl DefWithBody {
let (unafe_exprs, only_lint) = hir_ty::diagnostics::missing_unsafe(db, self.into());
for expr in unafe_exprs {
- match source_map.expr_syntax(expr) {
+ match source_map.expr_or_pat_syntax(expr) {
Ok(expr) => acc.push(MissingUnsafe { expr, only_lint }.into()),
Err(SyntheticSyntax) => {
// FIXME: Here and elsewhere in this file, the `expr` was
@@ -2420,8 +2424,8 @@ impl SelfParam {
func_data
.params
.first()
- .map(|param| match &**param {
- TypeRef::Reference(.., mutability) => match mutability {
+ .map(|&param| match &func_data.types_map[param] {
+ TypeRef::Reference(ref_) => match ref_.mutability {
hir_def::type_ref::Mutability::Shared => Access::Shared,
hir_def::type_ref::Mutability::Mut => Access::Exclusive,
},
@@ -2747,10 +2751,6 @@ impl TypeAlias {
Module { id: self.id.module(db.upcast()) }
}
- pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> {
- db.type_alias_data(self.id).type_ref.as_deref().cloned()
- }
-
pub fn ty(self, db: &dyn HirDatabase) -> Type {
Type::from_def(db, self.id)
}
@@ -3481,7 +3481,7 @@ impl Local {
LocalSource {
local: self,
source: src.map(|ast| match ast.to_node(&root) {
- ast::Pat::IdentPat(it) => Either::Left(it),
+ Either::Right(ast::Pat::IdentPat(it)) => Either::Left(it),
_ => unreachable!("local with non ident-pattern"),
}),
}
@@ -3510,7 +3510,7 @@ impl Local {
LocalSource {
local: self,
source: src.map(|ast| match ast.to_node(&root) {
- ast::Pat::IdentPat(it) => Either::Left(it),
+ Either::Right(ast::Pat::IdentPat(it)) => Either::Left(it),
_ => unreachable!("local with non ident-pattern"),
}),
}
@@ -4235,10 +4235,7 @@ impl CaptureUsages {
}
mir::MirSpan::PatId(pat) => {
if let Ok(pat) = source_map.pat_syntax(pat) {
- result.push(CaptureUsageSource {
- is_ref,
- source: pat.map(AstPtr::wrap_right),
- });
+ result.push(CaptureUsageSource { is_ref, source: pat });
}
}
mir::MirSpan::BindingId(binding) => result.extend(
@@ -4246,10 +4243,7 @@ impl CaptureUsages {
.patterns_for_binding(binding)
.iter()
.filter_map(|&pat| source_map.pat_syntax(pat).ok())
- .map(|pat| CaptureUsageSource {
- is_ref,
- source: pat.map(AstPtr::wrap_right),
- }),
+ .map(|pat| CaptureUsageSource { is_ref, source: pat }),
),
mir::MirSpan::SelfParam | mir::MirSpan::Unknown => {
unreachable!("invalid capture usage span")
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 3eac33ce99..feb9a344d8 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -11,13 +11,13 @@ use std::{
use either::Either;
use hir_def::{
- hir::Expr,
+ hir::{Expr, ExprOrPatId},
lower::LowerCtx,
nameres::{MacroSubNs, ModuleOrigin},
path::ModPath,
resolver::{self, HasResolver, Resolver, TypeNs},
- type_ref::Mutability,
- AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
+ type_ref::{Mutability, TypesMap, TypesSourceMap},
+ AsMacroCall, DefWithBodyId, FunctionId, MacroId, StructId, TraitId, VariantId,
};
use hir_expand::{
attrs::collect_attrs,
@@ -45,7 +45,7 @@ use syntax::{
use crate::{
db::HirDatabase,
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
- source_analyzer::{resolve_hir_path, SourceAnalyzer},
+ source_analyzer::{name_hygiene, resolve_hir_path, SourceAnalyzer},
Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile,
InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name,
@@ -154,7 +154,7 @@ impl<'db, DB> ops::Deref for Semantics<'db, DB> {
}
}
-impl<'db, DB: HirDatabase> Semantics<'db, DB> {
+impl<DB: HirDatabase> Semantics<'_, DB> {
pub fn new(db: &DB) -> Semantics<'_, DB> {
let impl_ = SemanticsImpl::new(db);
Semantics { db, imp: impl_ }
@@ -203,6 +203,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.descend_node_at_offset(node, offset).filter_map(|mut it| it.find_map(N::cast))
}
+ pub fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<Struct> {
+ self.imp.resolve_range_pat(range_pat).map(Struct::from)
+ }
+
+ pub fn resolve_range_expr(&self, range_expr: &ast::RangeExpr) -> Option<Struct> {
+ self.imp.resolve_range_expr(range_expr).map(Struct::from)
+ }
+
pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
self.imp.resolve_await_to_poll(await_expr).map(Function::from)
}
@@ -928,16 +936,7 @@ impl<'db> SemanticsImpl<'db> {
}
}
- let (file_id, tokens) = stack.first()?;
- // make sure we pick the token in the expanded include if we encountered an include,
- // otherwise we'll get the wrong semantics
- let sa =
- tokens.first()?.0.parent().and_then(|parent| {
- self.analyze_impl(InFile::new(*file_id, &parent), None, false)
- })?;
-
let mut m_cache = self.macro_call_cache.borrow_mut();
- let def_map = sa.resolver.def_map();
// Filters out all tokens that contain the given range (usually the macro call), any such
// token is redundant as the corresponding macro call has already been processed
@@ -946,6 +945,10 @@ impl<'db> SemanticsImpl<'db> {
};
while let Some((expansion, ref mut tokens)) = stack.pop() {
+ // Reverse the tokens so we prefer first tokens (to accommodate for popping from the
+ // back)
+ // alternatively we could pop from the front but that would shift the content on every pop
+ tokens.reverse();
while let Some((token, ctx)) = tokens.pop() {
let was_not_remapped = (|| {
// First expand into attribute invocations
@@ -1016,8 +1019,16 @@ impl<'db> SemanticsImpl<'db> {
) {
call.as_macro_file()
} else {
- // FIXME: This is wrong, the SourceAnalyzer might be invalid here
- sa.expand(self.db, mcall.as_ref())?
+ token
+ .parent()
+ .and_then(|parent| {
+ self.analyze_impl(
+ InFile::new(expansion, &parent),
+ None,
+ false,
+ )
+ })?
+ .expand(self.db, mcall.as_ref())?
};
m_cache.insert(mcall, it);
it
@@ -1087,9 +1098,16 @@ impl<'db> SemanticsImpl<'db> {
attr.path().and_then(|it| it.as_single_name_ref())?.as_name();
// Not an attribute, nor a derive, so it's either an intert attribute or a derive helper
// Try to resolve to a derive helper and downmap
+ let resolver = &token
+ .parent()
+ .and_then(|parent| {
+ self.analyze_impl(InFile::new(expansion, &parent), None, false)
+ })?
+ .resolver;
let id = self.db.ast_id_map(expansion).ast_id(&adt);
- let helpers =
- def_map.derive_helpers_in_scope(InFile::new(expansion, id))?;
+ let helpers = resolver
+ .def_map()
+ .derive_helpers_in_scope(InFile::new(expansion, id))?;
if !helpers.is_empty() {
let text_range = attr.syntax().text_range();
@@ -1251,19 +1269,28 @@ impl<'db> SemanticsImpl<'db> {
pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
let analyze = self.analyze(ty.syntax())?;
- let ctx = LowerCtx::new(self.db.upcast(), analyze.file_id);
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let ctx =
+ LowerCtx::new(self.db.upcast(), analyze.file_id, &mut types_map, &mut types_source_map);
+ let type_ref = crate::TypeRef::from_ast(&ctx, ty.clone());
let ty = hir_ty::TyLoweringContext::new_maybe_unowned(
self.db,
&analyze.resolver,
+ &types_map,
+ None,
analyze.resolver.type_owner(),
)
- .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone()));
+ .lower_ty(type_ref);
Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
}
pub fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> {
let analyze = self.analyze(path.syntax())?;
- let ctx = LowerCtx::new(self.db.upcast(), analyze.file_id);
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let ctx =
+ LowerCtx::new(self.db.upcast(), analyze.file_id, &mut types_map, &mut types_source_map);
let hir_path = Path::from_src(&ctx, path.clone())?;
match analyze.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), &hir_path)? {
TypeNs::TraitId(id) => Some(Trait { id }),
@@ -1363,6 +1390,14 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
}
+ fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {
+ self.analyze(range_pat.syntax())?.resolve_range_pat(self.db, range_pat)
+ }
+
+ fn resolve_range_expr(&self, range_expr: &ast::RangeExpr) -> Option<StructId> {
+ self.analyze(range_expr.syntax())?.resolve_range_expr(self.db, range_expr)
+ }
+
fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<FunctionId> {
self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr)
}
@@ -1761,7 +1796,9 @@ impl<'db> SemanticsImpl<'db> {
}
if let Some(parent) = ast::Expr::cast(parent.clone()) {
- if let Some(expr_id) = source_map.node_expr(InFile { file_id, value: &parent }) {
+ if let Some(ExprOrPatId::ExprId(expr_id)) =
+ source_map.node_expr(InFile { file_id, value: &parent })
+ {
if let Expr::Unsafe { .. } = body[expr_id] {
break true;
}
@@ -1934,10 +1971,19 @@ impl SemanticsScope<'_> {
/// Resolve a path as-if it was written at the given scope. This is
/// necessary a heuristic, as it doesn't take hygiene into account.
- pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
- let ctx = LowerCtx::new(self.db.upcast(), self.file_id);
- let path = Path::from_src(&ctx, path.clone())?;
- resolve_hir_path(self.db, &self.resolver, &path)
+ pub fn speculative_resolve(&self, ast_path: &ast::Path) -> Option<PathResolution> {
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let ctx =
+ LowerCtx::new(self.db.upcast(), self.file_id, &mut types_map, &mut types_source_map);
+ let path = Path::from_src(&ctx, ast_path.clone())?;
+ resolve_hir_path(
+ self.db,
+ &self.resolver,
+ &path,
+ name_hygiene(self.db, InFile::new(self.file_id, ast_path.syntax())),
+ &types_map,
+ )
}
/// Iterates over associated types that may be specified after the given path (using
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 389778b44e..5357e824d0 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -328,7 +328,7 @@ impl SourceToDefCtx<'_, '_> {
.position(|it| it == *src.value)?;
let container = self.find_pat_or_label_container(src.syntax_ref())?;
let (_, source_map) = self.db.body_with_source_map(container);
- let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?;
+ let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?.as_expr()?;
Some(InlineAsmOperand { owner: container, expr, index })
}
@@ -372,7 +372,8 @@ impl SourceToDefCtx<'_, '_> {
let break_or_continue = ast::Expr::cast(src.value.syntax().parent()?)?;
let container = self.find_pat_or_label_container(src.syntax_ref())?;
let (body, source_map) = self.db.body_with_source_map(container);
- let break_or_continue = source_map.node_expr(src.with_value(&break_or_continue))?;
+ let break_or_continue =
+ source_map.node_expr(src.with_value(&break_or_continue))?.as_expr()?;
let (Expr::Break { label, .. } | Expr::Continue { label }) = body[break_or_continue] else {
return None;
};
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 3da67ae23f..8d6e228e14 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -7,21 +7,26 @@
//! purely for "IDE needs".
use std::iter::{self, once};
+use crate::{
+ db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
+ BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static,
+ Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant,
+};
use either::Either;
use hir_def::{
body::{
scope::{ExprScopes, ScopeId},
- Body, BodySourceMap,
+ Body, BodySourceMap, HygieneId,
},
- hir::{BindingId, ExprId, Pat, PatId},
+ hir::{BindingId, ExprId, ExprOrPatId, Pat, PatId},
lang_item::LangItem,
lower::LowerCtx,
nameres::MacroSubNs,
path::{ModPath, Path, PathKind},
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
- type_ref::Mutability,
+ type_ref::{Mutability, TypesMap, TypesSourceMap},
AsMacroCall, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, ItemContainerId,
- LocalFieldId, Lookup, ModuleDefId, TraitId, VariantId,
+ LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId,
};
use hir_expand::{
mod_path::path,
@@ -40,18 +45,13 @@ use hir_ty::{
use intern::sym;
use itertools::Itertools;
use smallvec::SmallVec;
+use syntax::ast::{RangeItem, RangeOp};
use syntax::{
ast::{self, AstNode},
SyntaxKind, SyntaxNode, TextRange, TextSize,
};
use triomphe::Arc;
-use crate::{
- db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
- BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static,
- Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant,
-};
-
/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
/// original source files. It should not be used inside the HIR itself.
#[derive(Debug)]
@@ -120,7 +120,7 @@ impl SourceAnalyzer {
self.def.as_ref().map(|(_, body, _)| &**body)
}
- fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprId> {
+ fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprOrPatId> {
let src = match expr {
ast::Expr::MacroExpr(expr) => {
self.expand_expr(db, InFile::new(self.file_id, expr.macro_call()?))?.into()
@@ -174,7 +174,9 @@ impl SourceAnalyzer {
db: &dyn HirDatabase,
expr: &ast::Expr,
) -> Option<&[Adjustment]> {
- let expr_id = self.expr_id(db, expr)?;
+ // It is safe to omit destructuring assignments here because they have no adjustments (neither
+ // expressions nor patterns).
+ let expr_id = self.expr_id(db, expr)?.as_expr()?;
let infer = self.infer.as_ref()?;
infer.expr_adjustments.get(&expr_id).map(|v| &**v)
}
@@ -186,9 +188,9 @@ impl SourceAnalyzer {
) -> Option<(Type, Option<Type>)> {
let expr_id = self.expr_id(db, expr)?;
let infer = self.infer.as_ref()?;
- let coerced = infer
- .expr_adjustments
- .get(&expr_id)
+ let coerced = expr_id
+ .as_expr()
+ .and_then(|expr_id| infer.expr_adjustments.get(&expr_id))
.and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.clone()));
let ty = infer[expr_id].clone();
let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
@@ -268,7 +270,7 @@ impl SourceAnalyzer {
db: &dyn HirDatabase,
call: &ast::MethodCallExpr,
) -> Option<Callable> {
- let expr_id = self.expr_id(db, &call.clone().into())?;
+ let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?;
let (func, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
let ty = db.value_ty(func.into())?.substitute(Interner, &substs);
let ty = Type::new_with_resolver(db, &self.resolver, ty);
@@ -282,7 +284,7 @@ impl SourceAnalyzer {
db: &dyn HirDatabase,
call: &ast::MethodCallExpr,
) -> Option<Function> {
- let expr_id = self.expr_id(db, &call.clone().into())?;
+ let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?;
let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into())
@@ -293,7 +295,7 @@ impl SourceAnalyzer {
db: &dyn HirDatabase,
call: &ast::MethodCallExpr,
) -> Option<Either<Function, Field>> {
- let expr_id = self.expr_id(db, &call.clone().into())?;
+ let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?;
let inference_result = self.infer.as_ref()?;
match inference_result.method_resolution(expr_id) {
Some((f_in_trait, substs)) => Some(Either::Left(
@@ -322,7 +324,7 @@ impl SourceAnalyzer {
field: &ast::FieldExpr,
) -> Option<Either<Field, TupleField>> {
let &(def, ..) = self.def.as_ref()?;
- let expr_id = self.expr_id(db, &field.clone().into())?;
+ let expr_id = self.expr_id(db, &field.clone().into())?.as_expr()?;
self.infer.as_ref()?.field_resolution(expr_id).map(|it| {
it.map_either(Into::into, |f| TupleField { owner: def, tuple: f.tuple, index: f.index })
})
@@ -334,7 +336,7 @@ impl SourceAnalyzer {
field: &ast::FieldExpr,
) -> Option<Either<Either<Field, TupleField>, Function>> {
let &(def, ..) = self.def.as_ref()?;
- let expr_id = self.expr_id(db, &field.clone().into())?;
+ let expr_id = self.expr_id(db, &field.clone().into())?.as_expr()?;
let inference_result = self.infer.as_ref()?;
match inference_result.field_resolution(expr_id) {
Some(field) => Some(Either::Left(field.map_either(Into::into, |f| TupleField {
@@ -348,6 +350,45 @@ impl SourceAnalyzer {
}
}
+ pub(crate) fn resolve_range_pat(
+ &self,
+ db: &dyn HirDatabase,
+ range_pat: &ast::RangePat,
+ ) -> Option<StructId> {
+ let path: ModPath = match (range_pat.op_kind()?, range_pat.start(), range_pat.end()) {
+ (RangeOp::Exclusive, None, Some(_)) => path![core::ops::RangeTo],
+ (RangeOp::Exclusive, Some(_), None) => path![core::ops::RangeFrom],
+ (RangeOp::Exclusive, Some(_), Some(_)) => path![core::ops::Range],
+ (RangeOp::Inclusive, None, Some(_)) => path![core::ops::RangeToInclusive],
+ (RangeOp::Inclusive, Some(_), Some(_)) => path![core::ops::RangeInclusive],
+
+ (RangeOp::Exclusive, None, None) => return None,
+ (RangeOp::Inclusive, None, None) => return None,
+ (RangeOp::Inclusive, Some(_), None) => return None,
+ };
+ self.resolver.resolve_known_struct(db.upcast(), &path)
+ }
+
+ pub(crate) fn resolve_range_expr(
+ &self,
+ db: &dyn HirDatabase,
+ range_expr: &ast::RangeExpr,
+ ) -> Option<StructId> {
+ let path: ModPath = match (range_expr.op_kind()?, range_expr.start(), range_expr.end()) {
+ (RangeOp::Exclusive, None, None) => path![core::ops::RangeFull],
+ (RangeOp::Exclusive, None, Some(_)) => path![core::ops::RangeTo],
+ (RangeOp::Exclusive, Some(_), None) => path![core::ops::RangeFrom],
+ (RangeOp::Exclusive, Some(_), Some(_)) => path![core::ops::Range],
+ (RangeOp::Inclusive, None, Some(_)) => path![core::ops::RangeToInclusive],
+ (RangeOp::Inclusive, Some(_), Some(_)) => path![core::ops::RangeInclusive],
+
+ // [E0586] inclusive ranges must be bounded at the end
+ (RangeOp::Inclusive, None, None) => return None,
+ (RangeOp::Inclusive, Some(_), None) => return None,
+ };
+ self.resolver.resolve_known_struct(db.upcast(), &path)
+ }
+
pub(crate) fn resolve_await_to_poll(
&self,
db: &dyn HirDatabase,
@@ -403,7 +444,7 @@ impl SourceAnalyzer {
self.infer
.as_ref()
.and_then(|infer| {
- let expr = self.expr_id(db, &prefix_expr.clone().into())?;
+ let expr = self.expr_id(db, &prefix_expr.clone().into())?.as_expr()?;
let (func, _) = infer.method_resolution(expr)?;
let (deref_mut_trait, deref_mut) = self.lang_trait_fn(
db,
@@ -449,7 +490,7 @@ impl SourceAnalyzer {
.infer
.as_ref()
.and_then(|infer| {
- let expr = self.expr_id(db, &index_expr.clone().into())?;
+ let expr = self.expr_id(db, &index_expr.clone().into())?.as_expr()?;
let (func, _) = infer.method_resolution(expr)?;
let (index_mut_trait, index_mut_fn) = self.lang_trait_fn(
db,
@@ -521,7 +562,8 @@ impl SourceAnalyzer {
let expr = ast::Expr::from(record_expr);
let expr_id = self.body_source_map()?.node_expr(InFile::new(self.file_id, &expr))?;
- let local_name = field.field_name()?.as_name();
+ let ast_name = field.field_name()?;
+ let local_name = ast_name.as_name();
let local = if field.name_ref().is_some() {
None
} else {
@@ -530,15 +572,19 @@ impl SourceAnalyzer {
PathKind::Plain,
once(local_name.clone()),
));
- match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
+ match self.resolver.resolve_path_in_value_ns_fully(
+ db.upcast(),
+ &path,
+ name_hygiene(db, InFile::new(self.file_id, ast_name.syntax())),
+ ) {
Some(ValueNs::LocalBinding(binding_id)) => {
Some(Local { binding_id, parent: self.resolver.body_owner()? })
}
_ => None,
}
};
- let (_, subst) = self.infer.as_ref()?.type_of_expr.get(expr_id)?.as_adt()?;
- let variant = self.infer.as_ref()?.variant_resolution_for_expr(expr_id)?;
+ let (_, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?;
+ let variant = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?;
let variant_data = variant.variant_data(db.upcast());
let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? };
let field_ty =
@@ -568,7 +614,10 @@ impl SourceAnalyzer {
db: &dyn HirDatabase,
macro_call: InFile<&ast::MacroCall>,
) -> Option<Macro> {
- let ctx = LowerCtx::new(db.upcast(), macro_call.file_id);
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let ctx =
+ LowerCtx::new(db.upcast(), macro_call.file_id, &mut types_map, &mut types_source_map);
let path = macro_call.value.path().and_then(|ast| Path::from_src(&ctx, ast))?;
self.resolver
.resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Bang))
@@ -586,7 +635,7 @@ impl SourceAnalyzer {
Pat::Path(path) => path,
_ => return None,
};
- let res = resolve_hir_path(db, &self.resolver, path)?;
+ let res = resolve_hir_path(db, &self.resolver, path, HygieneId::ROOT, TypesMap::EMPTY)?;
match res {
PathResolution::Def(def) => Some(def),
_ => None,
@@ -606,10 +655,10 @@ impl SourceAnalyzer {
let infer = self.infer.as_deref()?;
if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) {
let expr_id = self.expr_id(db, &path_expr.into())?;
- if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr(expr_id) {
+ if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_id) {
let assoc = match assoc {
AssocItemId::FunctionId(f_in_trait) => {
- match infer.type_of_expr.get(expr_id) {
+ match infer.type_of_expr_or_pat(expr_id) {
None => assoc,
Some(func_ty) => {
if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) {
@@ -634,7 +683,7 @@ impl SourceAnalyzer {
return Some(PathResolution::Def(AssocItem::from(assoc).into()));
}
if let Some(VariantId::EnumVariantId(variant)) =
- infer.variant_resolution_for_expr(expr_id)
+ infer.variant_resolution_for_expr_or_pat(expr_id)
{
return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
}
@@ -658,7 +707,7 @@ impl SourceAnalyzer {
} else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) {
let expr_id = self.expr_id(db, &rec_lit.into())?;
if let Some(VariantId::EnumVariantId(variant)) =
- infer.variant_resolution_for_expr(expr_id)
+ infer.variant_resolution_for_expr_or_pat(expr_id)
{
return Some(PathResolution::Def(ModuleDef::Variant(variant.into())));
}
@@ -680,14 +729,16 @@ impl SourceAnalyzer {
return resolved;
}
- let ctx = LowerCtx::new(db.upcast(), self.file_id);
+ let (mut types_map, mut types_source_map) =
+ (TypesMap::default(), TypesSourceMap::default());
+ let ctx = LowerCtx::new(db.upcast(), self.file_id, &mut types_map, &mut types_source_map);
let hir_path = Path::from_src(&ctx, path.clone())?;
// Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are
// trying to resolve foo::bar.
if let Some(use_tree) = parent().and_then(ast::UseTree::cast) {
if use_tree.coloncolon_token().is_some() {
- return resolve_hir_path_qualifier(db, &self.resolver, &hir_path);
+ return resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map);
}
}
@@ -704,7 +755,7 @@ impl SourceAnalyzer {
// Case where path is a qualifier of another path, e.g. foo::bar::Baz where we are
// trying to resolve foo::bar.
if path.parent_path().is_some() {
- return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path) {
+ return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) {
None if meta_path.is_some() => {
path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| {
ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text())
@@ -775,9 +826,16 @@ impl SourceAnalyzer {
};
}
if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) {
- resolve_hir_path_qualifier(db, &self.resolver, &hir_path)
+ resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map)
} else {
- resolve_hir_path_(db, &self.resolver, &hir_path, prefer_value_ns)
+ resolve_hir_path_(
+ db,
+ &self.resolver,
+ &hir_path,
+ prefer_value_ns,
+ name_hygiene(db, InFile::new(self.file_id, path.syntax())),
+ &types_map,
+ )
}
}
@@ -790,10 +848,16 @@ impl SourceAnalyzer {
let infer = self.infer.as_ref()?;
let expr_id = self.expr_id(db, &literal.clone().into())?;
- let substs = infer.type_of_expr[expr_id].as_adt()?.1;
+ let substs = infer[expr_id].as_adt()?.1;
- let (variant, missing_fields, _exhaustive) =
- record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?;
+ let (variant, missing_fields, _exhaustive) = match expr_id {
+ ExprOrPatId::ExprId(expr_id) => {
+ record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?
+ }
+ ExprOrPatId::PatId(pat_id) => {
+ record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?
+ }
+ };
let res = self.missing_fields(db, substs, variant, missing_fields);
Some(res)
}
@@ -856,7 +920,7 @@ impl SourceAnalyzer {
) -> Option<VariantId> {
let infer = self.infer.as_ref()?;
let expr_id = self.expr_id(db, &record_lit.into())?;
- infer.variant_resolution_for_expr(expr_id)
+ infer.variant_resolution_for_expr_or_pat(expr_id)
}
pub(crate) fn is_unsafe_macro_call_expr(
@@ -867,14 +931,24 @@ impl SourceAnalyzer {
if let (Some((def, body, sm)), Some(infer)) = (&self.def, &self.infer) {
if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr) {
let mut is_unsafe = false;
- unsafe_expressions(
- db,
- infer,
- *def,
- body,
- expanded_expr,
- &mut |UnsafeExpr { inside_unsafe_block, .. }| is_unsafe |= !inside_unsafe_block,
- );
+ let mut walk_expr = |expr_id| {
+ unsafe_expressions(
+ db,
+ infer,
+ *def,
+ body,
+ expr_id,
+ &mut |UnsafeExpr { inside_unsafe_block, .. }| {
+ is_unsafe |= !inside_unsafe_block
+ },
+ )
+ };
+ match expanded_expr {
+ ExprOrPatId::ExprId(expanded_expr) => walk_expr(expanded_expr),
+ ExprOrPatId::PatId(expanded_pat) => {
+ body.walk_exprs_in_pat(expanded_pat, &mut walk_expr)
+ }
+ }
return is_unsafe;
}
}
@@ -887,7 +961,7 @@ impl SourceAnalyzer {
format_args: InFile<&ast::FormatArgsExpr>,
offset: TextSize,
) -> Option<(TextRange, Option<PathResolution>)> {
- let implicits = self.body_source_map()?.implicit_format_args(format_args)?;
+ let (hygiene, implicits) = self.body_source_map()?.implicit_format_args(format_args)?;
implicits.iter().find(|(range, _)| range.contains_inclusive(offset)).map(|(range, name)| {
(
*range,
@@ -899,6 +973,7 @@ impl SourceAnalyzer {
PathKind::Plain,
Some(name.clone()),
)),
+ hygiene,
),
)
})
@@ -925,22 +1000,22 @@ impl SourceAnalyzer {
db: &'a dyn HirDatabase,
format_args: InFile<&ast::FormatArgsExpr>,
) -> Option<impl Iterator<Item = (TextRange, Option<PathResolution>)> + 'a> {
- Some(self.body_source_map()?.implicit_format_args(format_args)?.iter().map(
- move |(range, name)| {
- (
- *range,
- resolve_hir_value_path(
- db,
- &self.resolver,
- self.resolver.body_owner(),
- &Path::from_known_path_with_no_generic(ModPath::from_segments(
- PathKind::Plain,
- Some(name.clone()),
- )),
- ),
- )
- },
- ))
+ let (hygiene, names) = self.body_source_map()?.implicit_format_args(format_args)?;
+ Some(names.iter().map(move |(range, name)| {
+ (
+ *range,
+ resolve_hir_value_path(
+ db,
+ &self.resolver,
+ self.resolver.body_owner(),
+ &Path::from_known_path_with_no_generic(ModPath::from_segments(
+ PathKind::Plain,
+ Some(name.clone()),
+ )),
+ hygiene,
+ ),
+ )
+ }))
}
pub(crate) fn as_asm_parts(
@@ -991,7 +1066,7 @@ impl SourceAnalyzer {
}
fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
- self.infer.as_ref()?.type_of_expr.get(self.expr_id(db, expr)?)
+ self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?)
}
}
@@ -1004,7 +1079,7 @@ fn scope_for(
node.ancestors_with_macros(db.upcast())
.take_while(|it| !ast::Item::can_cast(it.kind()) || ast::MacroCall::can_cast(it.kind()))
.filter_map(|it| it.map(ast::Expr::cast).transpose())
- .filter_map(|it| source_map.node_expr(it.as_ref()))
+ .filter_map(|it| source_map.node_expr(it.as_ref())?.as_expr())
.find_map(|it| scopes.scope_for(it))
}
@@ -1086,8 +1161,10 @@ pub(crate) fn resolve_hir_path(
db: &dyn HirDatabase,
resolver: &Resolver,
path: &Path,
+ hygiene: HygieneId,
+ types_map: &TypesMap,
) -> Option<PathResolution> {
- resolve_hir_path_(db, resolver, path, false)
+ resolve_hir_path_(db, resolver, path, false, hygiene, types_map)
}
#[inline]
@@ -1107,13 +1184,20 @@ fn resolve_hir_path_(
resolver: &Resolver,
path: &Path,
prefer_value_ns: bool,
+ hygiene: HygieneId,
+ types_map: &TypesMap,
) -> Option<PathResolution> {
let types = || {
let (ty, unresolved) = match path.type_anchor() {
Some(type_ref) => {
- let (_, res) =
- TyLoweringContext::new_maybe_unowned(db, resolver, resolver.type_owner())
- .lower_ty_ext(type_ref);
+ let (_, res) = TyLoweringContext::new_maybe_unowned(
+ db,
+ resolver,
+ types_map,
+ None,
+ resolver.type_owner(),
+ )
+ .lower_ty_ext(type_ref);
res.map(|ty_ns| (ty_ns, path.segments().first()))
}
None => {
@@ -1172,7 +1256,7 @@ fn resolve_hir_path_(
};
let body_owner = resolver.body_owner();
- let values = || resolve_hir_value_path(db, resolver, body_owner, path);
+ let values = || resolve_hir_value_path(db, resolver, body_owner, path, hygiene);
let items = || {
resolver
@@ -1197,8 +1281,9 @@ fn resolve_hir_value_path(
resolver: &Resolver,
body_owner: Option<DefWithBodyId>,
path: &Path,
+ hygiene: HygieneId,
) -> Option<PathResolution> {
- resolver.resolve_path_in_value_ns_fully(db.upcast(), path).and_then(|val| {
+ resolver.resolve_path_in_value_ns_fully(db.upcast(), path, hygiene).and_then(|val| {
let res = match val {
ValueNs::LocalBinding(binding_id) => {
let var = Local { parent: body_owner?, binding_id };
@@ -1233,13 +1318,19 @@ fn resolve_hir_path_qualifier(
db: &dyn HirDatabase,
resolver: &Resolver,
path: &Path,
+ types_map: &TypesMap,
) -> Option<PathResolution> {
(|| {
let (ty, unresolved) = match path.type_anchor() {
Some(type_ref) => {
- let (_, res) =
- TyLoweringContext::new_maybe_unowned(db, resolver, resolver.type_owner())
- .lower_ty_ext(type_ref);
+ let (_, res) = TyLoweringContext::new_maybe_unowned(
+ db,
+ resolver,
+ types_map,
+ None,
+ resolver.type_owner(),
+ )
+ .lower_ty_ext(type_ref);
res.map(|ty_ns| (ty_ns, path.segments().first()))
}
None => {
@@ -1303,3 +1394,13 @@ fn resolve_hir_path_qualifier(
.map(|it| PathResolution::Def(it.into()))
})
}
+
+pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> HygieneId {
+ let Some(macro_file) = name.file_id.macro_file() else {
+ return HygieneId::ROOT;
+ };
+ let span_map = db.expansion_span_map(macro_file);
+ let ctx = span_map.span_at(name.value.text_range().start()).ctx;
+ let ctx = db.lookup_intern_syntax_context(ctx);
+ HygieneId::new(ctx.opaque_and_semitransparent)
+}
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs
index cabb7e3db3..f8416f86bf 100644
--- a/crates/hir/src/symbols.rs
+++ b/crates/hir/src/symbols.rs
@@ -8,7 +8,10 @@ use hir_def::{
TraitId,
};
use hir_expand::HirFileId;
-use hir_ty::{db::HirDatabase, display::HirDisplay};
+use hir_ty::{
+ db::HirDatabase,
+ display::{hir_display_with_types_map, HirDisplay},
+};
use span::Edition;
use syntax::{ast::HasName, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, ToSmolStr};
@@ -214,8 +217,11 @@ impl<'a> SymbolCollector<'a> {
fn collect_from_impl(&mut self, impl_id: ImplId) {
let impl_data = self.db.impl_data(impl_id);
- let impl_name =
- Some(SmolStr::new(impl_data.self_ty.display(self.db, self.edition).to_string()));
+ let impl_name = Some(
+ hir_display_with_types_map(impl_data.self_ty, &impl_data.types_map)
+ .display(self.db, self.edition)
+ .to_smolstr(),
+ );
self.with_container_name(impl_name, |s| {
for &assoc_item_id in impl_data.items.iter() {
s.push_assoc_item(assoc_item_id)
diff --git a/crates/ide-assists/Cargo.toml b/crates/ide-assists/Cargo.toml
index 2a14fbe1e0..ba21586871 100644
--- a/crates/ide-assists/Cargo.toml
+++ b/crates/ide-assists/Cargo.toml
@@ -23,7 +23,6 @@ tracing.workspace = true
# local deps
stdx.workspace = true
syntax.workspace = true
-text-edit.workspace = true
ide-db.workspace = true
hir.workspace = true
diff --git a/crates/ide-assists/src/handlers/bool_to_enum.rs b/crates/ide-assists/src/handlers/bool_to_enum.rs
index c035c59ffc..605fd14052 100644
--- a/crates/ide-assists/src/handlers/bool_to_enum.rs
+++ b/crates/ide-assists/src/handlers/bool_to_enum.rs
@@ -1,5 +1,6 @@
use either::Either;
use hir::ModuleDef;
+use ide_db::text_edit::TextRange;
use ide_db::{
assists::{AssistId, AssistKind},
defs::Definition,
@@ -19,7 +20,6 @@ use syntax::{
},
AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T,
};
-use text_edit::TextRange;
use crate::{
assist_context::{AssistContext, Assists},
diff --git a/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/crates/ide-assists/src/handlers/destructure_struct_binding.rs
index b229b750e8..22a1efdbea 100644
--- a/crates/ide-assists/src/handlers/destructure_struct_binding.rs
+++ b/crates/ide-assists/src/handlers/destructure_struct_binding.rs
@@ -1,4 +1,5 @@
use hir::{sym, HasVisibility};
+use ide_db::text_edit::TextRange;
use ide_db::{
assists::{AssistId, AssistKind},
defs::Definition,
@@ -8,7 +9,6 @@ use ide_db::{
};
use itertools::Itertools;
use syntax::{ast, ted, AstNode, Edition, SmolStr, SyntaxNode, ToSmolStr};
-use text_edit::TextRange;
use crate::{
assist_context::{AssistContext, Assists, SourceChangeBuilder},
diff --git a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
index 9ecfb83ed5..3f0d5cf152 100644
--- a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
+++ b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
@@ -1,3 +1,4 @@
+use ide_db::text_edit::TextRange;
use ide_db::{
assists::{AssistId, AssistKind},
defs::Definition,
@@ -8,7 +9,6 @@ use syntax::{
ast::{self, make, AstNode, FieldExpr, HasName, IdentPat},
ted,
};
-use text_edit::TextRange;
use crate::{
assist_context::{AssistContext, Assists, SourceChangeBuilder},
diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs
index 0e9c463e02..94274f6d17 100644
--- a/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -41,7 +41,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
acc.add(
- AssistId("remove_dbg", AssistKind::Refactor),
+ AssistId("remove_dbg", AssistKind::QuickFix),
"Remove dbg!()",
replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range))?,
|builder| {
diff --git a/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs
index c6f99d6874..0570b44778 100644
--- a/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -1,6 +1,7 @@
use std::collections::hash_map::Entry;
use hir::{FileRange, HirFileIdExt, InFile, InRealFile, Module, ModuleSource};
+use ide_db::text_edit::TextRange;
use ide_db::{
defs::Definition,
search::{FileReference, ReferenceCategory, SearchScope},
@@ -10,7 +11,6 @@ use syntax::{
ast::{self, Rename},
AstNode,
};
-use text_edit::TextRange;
use crate::{AssistContext, AssistId, AssistKind, Assists};
diff --git a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
index 8a6c2937d9..26fd887cc9 100644
--- a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
+++ b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
@@ -1,4 +1,5 @@
use hir::{FileRange, Semantics};
+use ide_db::text_edit::TextRange;
use ide_db::{
defs::Definition,
search::{SearchScope, UsageSearchResult},
@@ -11,7 +12,6 @@ use syntax::{
},
match_ast, ted, AstNode,
};
-use text_edit::TextRange;
use crate::{AssistContext, AssistId, AssistKind, Assists};
diff --git a/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
index db789cfa33..648bf358b4 100644
--- a/crates/ide-assists/src/handlers/unmerge_match_arm.rs
+++ b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
@@ -34,6 +34,9 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let pipe_token = ctx.find_token_syntax_at_offset(T![|])?;
let or_pat = ast::OrPat::cast(pipe_token.parent()?)?.clone_for_update();
+ if or_pat.leading_pipe().is_some_and(|it| it == pipe_token) {
+ return None;
+ }
let match_arm = ast::MatchArm::cast(or_pat.syntax().parent()?)?;
let match_arm_body = match_arm.expr()?;
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 22620816d5..8aaf5d6fff 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -301,6 +301,7 @@ mod handlers {
inline_call::inline_into_callers,
inline_const_as_literal::inline_const_as_literal,
inline_local_variable::inline_local_variable,
+ inline_macro::inline_macro,
inline_type_alias::inline_type_alias,
inline_type_alias::inline_type_alias_uses,
into_to_qualified_from::into_to_qualified_from,
@@ -326,6 +327,7 @@ mod handlers {
raw_string::add_hash,
raw_string::make_usual_string,
raw_string::remove_hash,
+ remove_dbg::remove_dbg,
remove_mut::remove_mut,
remove_unused_imports::remove_unused_imports,
remove_unused_param::remove_unused_param,
@@ -381,9 +383,6 @@ mod handlers {
generate_getter_or_setter::generate_setter,
generate_delegate_methods::generate_delegate_methods,
generate_deref::generate_deref,
- //
- remove_dbg::remove_dbg,
- inline_macro::inline_macro,
// Are you sure you want to add new assist here, and not to the
// sorted list above?
]
diff --git a/crates/ide-completion/Cargo.toml b/crates/ide-completion/Cargo.toml
index 614465b4d0..1bef82af5a 100644
--- a/crates/ide-completion/Cargo.toml
+++ b/crates/ide-completion/Cargo.toml
@@ -25,7 +25,6 @@ base-db.workspace = true
ide-db.workspace = true
stdx.workspace = true
syntax.workspace = true
-text-edit.workspace = true
# completions crate should depend only on the top-level `hir` package. if you need
# something from some `hir-xxx` subpackage, reexport the API via `hir`.
hir.workspace = true
diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 672e1796d1..c38a8ef29b 100644
--- a/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -32,6 +32,7 @@
//! ```
use hir::{db::ExpandDatabase, HasAttrs, MacroFileId, Name};
+use ide_db::text_edit::TextEdit;
use ide_db::{
documentation::HasDocs, path_transform::PathTransform,
syntax_helpers::prettify_macro_expansion, traits::get_missing_assoc_items, SymbolKind,
@@ -40,7 +41,6 @@ use syntax::{
ast::{self, edit_in_place::AttrsOwnerEdit, make, HasGenericArgs, HasTypeBounds},
format_smolstr, ted, AstNode, SmolStr, SyntaxElement, SyntaxKind, TextRange, ToSmolStr, T,
};
-use text_edit::TextEdit;
use crate::{
context::PathCompletionCtx, CompletionContext, CompletionItem, CompletionItemKind,
diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs
index 05e2892fdc..f12f011a6b 100644
--- a/crates/ide-completion/src/completions/mod_.rs
+++ b/crates/ide-completion/src/completions/mod_.rs
@@ -7,7 +7,6 @@ use ide_db::{
base_db::{SourceRootDatabase, VfsPath},
FxHashSet, RootDatabase, SymbolKind,
};
-use stdx::IsNoneOr;
use syntax::{ast, AstNode, SyntaxKind, ToSmolStr};
use crate::{context::CompletionContext, CompletionItem, Completions};
@@ -66,7 +65,7 @@ pub(crate) fn complete_mod(
.iter()
.filter(|&submodule_candidate_file| submodule_candidate_file != module_definition_file)
.filter(|&submodule_candidate_file| {
- IsNoneOr::is_none_or(module_declaration_file, |it| it != submodule_candidate_file)
+ module_declaration_file.is_none_or(|it| it != submodule_candidate_file)
})
.filter_map(|submodule_file| {
let submodule_path = source_root.path_for_file(&submodule_file)?;
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs
index d3579fd8cc..495f82da86 100644
--- a/crates/ide-completion/src/completions/postfix.rs
+++ b/crates/ide-completion/src/completions/postfix.rs
@@ -3,6 +3,7 @@
mod format_like;
use hir::ItemInNs;
+use ide_db::text_edit::TextEdit;
use ide_db::{
documentation::{Documentation, HasDocs},
imports::insert_use::ImportScope,
@@ -15,7 +16,6 @@ use syntax::{
SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR},
TextRange, TextSize,
};
-use text_edit::TextEdit;
use crate::{
completions::postfix::format_like::add_format_like_completions,
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 0e1302ff2e..efbee39a2d 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -20,7 +20,6 @@ use syntax::{
SyntaxKind::{self, *},
SyntaxToken, TextRange, TextSize, T,
};
-use text_edit::Indel;
use crate::{
context::analysis::{expand_and_analyze, AnalysisResult},
@@ -684,8 +683,7 @@ impl<'a> CompletionContext<'a> {
// actual completion.
let file_with_fake_ident = {
let parse = db.parse(file_id);
- let edit = Indel::insert(offset, COMPLETION_MARKER.to_owned());
- parse.reparse(&edit, file_id.edition()).tree()
+ parse.reparse(TextRange::empty(offset), COMPLETION_MARKER, file_id.edition()).tree()
};
// always pick the token to the immediate left of the cursor, as that is what we are actually
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 8c97ebd550..52f6bedaaa 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -3,6 +3,7 @@
use std::{fmt, mem};
use hir::Mutability;
+use ide_db::text_edit::TextEdit;
use ide_db::{
documentation::Documentation, imports::import_assets::LocatedImport, RootDatabase, SnippetCap,
SymbolKind,
@@ -11,7 +12,6 @@ use itertools::Itertools;
use smallvec::SmallVec;
use stdx::{impl_from, never};
use syntax::{format_smolstr, Edition, SmolStr, TextRange, TextSize};
-use text_edit::TextEdit;
use crate::{
context::{CompletionContext, PathCompletionCtx},
@@ -426,7 +426,7 @@ impl CompletionItem {
self.lookup.as_str()
}
- pub fn ref_match(&self) -> Option<(String, text_edit::Indel, CompletionRelevance)> {
+ pub fn ref_match(&self) -> Option<(String, ide_db::text_edit::Indel, CompletionRelevance)> {
// Relevance of the ref match should be the same as the original
// match, but with exact type match set because self.ref_match
// is only set if there is an exact type match.
@@ -436,7 +436,10 @@ impl CompletionItem {
self.ref_match.map(|(mutability, offset)| {
(
format!("&{}{}", mutability.as_keyword_for_ref(), self.label),
- text_edit::Indel::insert(offset, format!("&{}", mutability.as_keyword_for_ref())),
+ ide_db::text_edit::Indel::insert(
+ offset,
+ format!("&{}", mutability.as_keyword_for_ref()),
+ ),
relevance,
)
})
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index a78976d3fd..dfee01b187 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -10,16 +10,17 @@ mod snippet;
#[cfg(test)]
mod tests;
+use ide_db::text_edit::TextEdit;
use ide_db::{
helpers::mod_path_to_ast,
imports::{
import_assets::NameToImport,
insert_use::{self, ImportScope},
},
- items_locator, FilePosition, RootDatabase,
+ items_locator,
+ syntax_helpers::tree_diff::diff,
+ FilePosition, RootDatabase,
};
-use syntax::algo;
-use text_edit::TextEdit;
use crate::{
completions::Completions,
@@ -297,6 +298,6 @@ pub fn resolve_completion_edits(
}
});
- algo::diff(scope.as_syntax_node(), new_ast.as_syntax_node()).into_text_edit(&mut import_insert);
+ diff(scope.as_syntax_node(), new_ast.as_syntax_node()).into_text_edit(&mut import_insert);
Some(vec![import_insert.finish()])
}
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 4dd171142f..ec3c2fe355 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -11,6 +11,7 @@ pub(crate) mod union_literal;
pub(crate) mod variant;
use hir::{sym, AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type};
+use ide_db::text_edit::TextEdit;
use ide_db::{
documentation::{Documentation, HasDocs},
helpers::item_name,
@@ -18,7 +19,6 @@ use ide_db::{
RootDatabase, SnippetCap, SymbolKind,
};
use syntax::{ast, format_smolstr, AstNode, Edition, SmolStr, SyntaxKind, TextRange, ToSmolStr};
-use text_edit::TextEdit;
use crate::{
context::{DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext},
diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs
index 1bbe097cc6..45679355b4 100644
--- a/crates/ide-completion/src/tests/attribute.rs
+++ b/crates/ide-completion/src/tests/attribute.rs
@@ -663,6 +663,7 @@ mod cfg {
ba dbg
ba opt_level
ba test
+ ba true
"#]],
);
check(
@@ -674,6 +675,7 @@ mod cfg {
ba dbg
ba opt_level
ba test
+ ba true
"#]],
);
}
diff --git a/crates/ide-db/Cargo.toml b/crates/ide-db/Cargo.toml
index c078188d6d..17f0e69bde 100644
--- a/crates/ide-db/Cargo.toml
+++ b/crates/ide-db/Cargo.toml
@@ -35,7 +35,6 @@ parser.workspace = true
profile.workspace = true
stdx.workspace = true
syntax.workspace = true
-text-edit.workspace = true
span.workspace = true
# ide should depend only on the top-level `hir` package. if you need
# something from some `hir-xxx` subpackage, reexport the API via `hir`.
diff --git a/crates/ide-db/src/apply_change.rs b/crates/ide-db/src/apply_change.rs
index 7474d7bc54..35e3a8d9bf 100644
--- a/crates/ide-db/src/apply_change.rs
+++ b/crates/ide-db/src/apply_change.rs
@@ -100,16 +100,19 @@ impl RootDatabase {
hir::db::ConstEvalQuery
hir::db::ConstEvalStaticQuery
hir::db::ConstParamTyQuery
+ hir::db::DynCompatibilityOfTraitQuery
hir::db::FieldTypesQuery
hir::db::FnDefDatumQuery
hir::db::FnDefVarianceQuery
hir::db::GenericDefaultsQuery
hir::db::GenericPredicatesForParamQuery
hir::db::GenericPredicatesQuery
+ hir::db::GenericPredicatesWithoutParentQuery
hir::db::ImplDatumQuery
hir::db::ImplSelfTyQuery
hir::db::ImplTraitQuery
hir::db::IncoherentInherentImplCratesQuery
+ hir::db::InferQuery
hir::db::InherentImplsInBlockQuery
hir::db::InherentImplsInCrateQuery
hir::db::InternCallableDefQuery
@@ -119,7 +122,12 @@ impl RootDatabase {
hir::db::InternLifetimeParamIdQuery
hir::db::InternTypeOrConstParamIdQuery
hir::db::LayoutOfAdtQuery
+ hir::db::LayoutOfTyQuery
+ hir::db::LookupImplMethodQuery
+ hir::db::MirBodyForClosureQuery
hir::db::MirBodyQuery
+ hir::db::MonomorphizedMirBodyForClosureQuery
+ hir::db::MonomorphizedMirBodyQuery
hir::db::ProgramClausesForChalkEnvQuery
hir::db::ReturnTypeImplTraitsQuery
hir::db::TargetDataLayoutQuery
@@ -128,13 +136,16 @@ impl RootDatabase {
hir::db::TraitImplsInBlockQuery
hir::db::TraitImplsInCrateQuery
hir::db::TraitImplsInDepsQuery
+ hir::db::TraitSolveQuery
hir::db::TyQuery
+ hir::db::TypeAliasImplTraitsQuery
hir::db::ValueTyQuery
// DefDatabase
hir::db::AttrsQuery
hir::db::BlockDefMapQuery
hir::db::BlockItemTreeQuery
+ hir::db::BlockItemTreeWithSourceMapQuery
hir::db::BodyQuery
hir::db::BodyWithSourceMapQuery
hir::db::ConstDataQuery
@@ -145,17 +156,21 @@ impl RootDatabase {
hir::db::CrateSupportsNoStdQuery
hir::db::EnumDataQuery
hir::db::EnumVariantDataWithDiagnosticsQuery
+ hir::db::ExpandProcAttrMacrosQuery
hir::db::ExprScopesQuery
hir::db::ExternCrateDeclDataQuery
hir::db::FieldVisibilitiesQuery
hir::db::FieldsAttrsQuery
hir::db::FieldsAttrsSourceMapQuery
hir::db::FileItemTreeQuery
+ hir::db::FileItemTreeWithSourceMapQuery
hir::db::FunctionDataQuery
hir::db::FunctionVisibilityQuery
hir::db::GenericParamsQuery
+ hir::db::GenericParamsWithSourceMapQuery
hir::db::ImplDataWithDiagnosticsQuery
hir::db::ImportMapQuery
+ hir::db::IncludeMacroInvocQuery
hir::db::InternAnonymousConstQuery
hir::db::InternBlockQuery
hir::db::InternConstQuery
@@ -177,7 +192,9 @@ impl RootDatabase {
hir::db::InternUseQuery
hir::db::LangItemQuery
hir::db::Macro2DataQuery
+ hir::db::MacroDefQuery
hir::db::MacroRulesDataQuery
+ hir::db::NotableTraitsInDepsQuery
hir::db::ProcMacroDataQuery
hir::db::StaticDataQuery
hir::db::StructDataWithDiagnosticsQuery
@@ -212,6 +229,7 @@ impl RootDatabase {
hir::db::MacroArgQuery
hir::db::ParseMacroExpansionErrorQuery
hir::db::ParseMacroExpansionQuery
+ hir::db::ProcMacroSpanQuery
hir::db::ProcMacrosQuery
hir::db::RealSpanMapQuery
@@ -220,7 +238,9 @@ impl RootDatabase {
// SourceDatabase
base_db::ParseQuery
+ base_db::ParseErrorsQuery
base_db::CrateGraphQuery
+ base_db::CrateWorkspaceDataQuery
// SourceDatabaseExt
base_db::FileTextQuery
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index 099f26eba7..fdac4dd2ef 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -5,14 +5,17 @@
// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
+use crate::documentation::{Documentation, HasDocs};
+use crate::famous_defs::FamousDefs;
+use crate::RootDatabase;
use arrayvec::ArrayVec;
use either::Either;
use hir::{
Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro,
- Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait,
- TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
+ Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule,
+ Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
};
use span::Edition;
use stdx::{format_to, impl_from};
@@ -21,10 +24,6 @@ use syntax::{
match_ast, SyntaxKind, SyntaxNode, SyntaxToken,
};
-use crate::documentation::{Documentation, HasDocs};
-use crate::famous_defs::FamousDefs;
-use crate::RootDatabase;
-
// FIXME: a more precise name would probably be `Symbol`?
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub enum Definition {
@@ -179,7 +178,19 @@ impl Definition {
Definition::Static(it) => it.docs(db),
Definition::Trait(it) => it.docs(db),
Definition::TraitAlias(it) => it.docs(db),
- Definition::TypeAlias(it) => it.docs(db),
+ Definition::TypeAlias(it) => {
+ it.docs(db).or_else(|| {
+ // docs are missing, try to fall back to the docs of the aliased item.
+ let adt = it.ty(db).as_adt()?;
+ let docs = adt.docs(db)?;
+ let docs = format!(
+ "*This is the documentation for* `{}`\n\n{}",
+ adt.display(db, edition),
+ docs.as_str()
+ );
+ Some(Documentation::new(docs))
+ })
+ }
Definition::BuiltinType(it) => {
famous_defs.and_then(|fd| {
// std exposes prim_{} modules with docstrings on the root to document the builtins
@@ -319,6 +330,8 @@ impl IdentClass {
.map(IdentClass::NameClass)
.or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass))
},
+ ast::RangePat(range_pat) => OperatorClass::classify_range_pat(sema, &range_pat).map(IdentClass::Operator),
+ ast::RangeExpr(range_expr) => OperatorClass::classify_range_expr(sema, &range_expr).map(IdentClass::Operator),
ast::AwaitExpr(await_expr) => OperatorClass::classify_await(sema, &await_expr).map(IdentClass::Operator),
ast::BinExpr(bin_expr) => OperatorClass::classify_bin(sema, &bin_expr).map(IdentClass::Operator),
ast::IndexExpr(index_expr) => OperatorClass::classify_index(sema, &index_expr).map(IdentClass::Operator),
@@ -372,6 +385,9 @@ impl IdentClass {
| OperatorClass::Index(func)
| OperatorClass::Try(func),
) => res.push(Definition::Function(func)),
+ IdentClass::Operator(OperatorClass::Range(struct0)) => {
+ res.push(Definition::Adt(Adt::Struct(struct0)))
+ }
}
res
}
@@ -546,6 +562,7 @@ impl NameClass {
#[derive(Debug)]
pub enum OperatorClass {
+ Range(Struct),
Await(Function),
Prefix(Function),
Index(Function),
@@ -554,6 +571,20 @@ pub enum OperatorClass {
}
impl OperatorClass {
+ pub fn classify_range_pat(
+ sema: &Semantics<'_, RootDatabase>,
+ range_pat: &ast::RangePat,
+ ) -> Option<OperatorClass> {
+ sema.resolve_range_pat(range_pat).map(OperatorClass::Range)
+ }
+
+ pub fn classify_range_expr(
+ sema: &Semantics<'_, RootDatabase>,
+ range_expr: &ast::RangeExpr,
+ ) -> Option<OperatorClass> {
+ sema.resolve_range_expr(range_expr).map(OperatorClass::Range)
+ }
+
pub fn classify_await(
sema: &Semantics<'_, RootDatabase>,
await_expr: &ast::AwaitExpr,
diff --git a/crates/ide-db/src/documentation.rs b/crates/ide-db/src/documentation.rs
index 5e443badf9..b52a325790 100644
--- a/crates/ide-db/src/documentation.rs
+++ b/crates/ide-db/src/documentation.rs
@@ -5,11 +5,11 @@ use hir::{
resolve_doc_path_on, sym, AttrId, AttrSourceMap, AttrsWithOwner, HasAttrs, InFile,
};
use itertools::Itertools;
+use span::{TextRange, TextSize};
use syntax::{
ast::{self, IsString},
AstToken,
};
-use text_edit::{TextRange, TextSize};
/// Holds documentation
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index aed093f0eb..81260c3e08 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -19,6 +19,7 @@ pub mod rust_doc;
pub mod search;
pub mod source_change;
pub mod symbol_index;
+pub mod text_edit;
pub mod traits;
pub mod ty_filter;
pub mod use_trivial_constructor;
@@ -36,6 +37,7 @@ pub mod generated {
pub mod syntax_helpers {
pub mod format_string;
pub mod format_string_exprs;
+ pub mod tree_diff;
pub use hir::prettify_macro_expansion;
pub mod node_ext;
pub mod suggest_name;
@@ -293,3 +295,35 @@ impl SnippetCap {
}
}
}
+
+pub struct Ranker<'a> {
+ pub kind: parser::SyntaxKind,
+ pub text: &'a str,
+ pub ident_kind: bool,
+}
+
+impl<'a> Ranker<'a> {
+ pub const MAX_RANK: usize = 0b1110;
+
+ pub fn from_token(token: &'a syntax::SyntaxToken) -> Self {
+ let kind = token.kind();
+ Ranker { kind, text: token.text(), ident_kind: kind.is_any_identifier() }
+ }
+
+ /// A utility function that ranks a token again a given kind and text, returning a number that
+ /// represents how close the token is to the given kind and text.
+ pub fn rank_token(&self, tok: &syntax::SyntaxToken) -> usize {
+ let tok_kind = tok.kind();
+
+ let exact_same_kind = tok_kind == self.kind;
+ let both_idents = exact_same_kind || (tok_kind.is_any_identifier() && self.ident_kind);
+ let same_text = tok.text() == self.text;
+ // anything that mapped into a token tree has likely no semantic information
+ let no_tt_parent =
+ tok.parent().map_or(false, |it| it.kind() != parser::SyntaxKind::TOKEN_TREE);
+ (both_idents as usize)
+ | ((exact_same_kind as usize) << 1)
+ | ((same_text as usize) << 2)
+ | ((no_tt_parent as usize) << 3)
+ }
+}
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index f1404ed9f2..1d1679c3ff 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -22,6 +22,7 @@
//! Our current behavior is ¯\_(ツ)_/¯.
use std::fmt;
+use crate::text_edit::{TextEdit, TextEditBuilder};
use base_db::AnchoredPathBuf;
use either::Either;
use hir::{FieldSource, FileRange, HirFileIdExt, InFile, ModuleSource, Semantics};
@@ -32,7 +33,6 @@ use syntax::{
utils::is_raw_identifier,
AstNode, SyntaxKind, TextRange, T,
};
-use text_edit::{TextEdit, TextEditBuilder};
use crate::{
defs::Definition,
diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs
index 73073e92f7..27ff91dc19 100644
--- a/crates/ide-db/src/source_change.rs
+++ b/crates/ide-db/src/source_change.rs
@@ -5,7 +5,8 @@
use std::{collections::hash_map::Entry, iter, mem};
-use crate::{assists::Command, SnippetCap};
+use crate::text_edit::{TextEdit, TextEditBuilder};
+use crate::{assists::Command, syntax_helpers::tree_diff::diff, SnippetCap};
use base_db::AnchoredPathBuf;
use itertools::Itertools;
use nohash_hasher::IntMap;
@@ -13,11 +14,9 @@ use rustc_hash::FxHashMap;
use span::FileId;
use stdx::never;
use syntax::{
- algo,
syntax_editor::{SyntaxAnnotation, SyntaxEditor},
AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize,
};
-use text_edit::{TextEdit, TextEditBuilder};
#[derive(Default, Debug, Clone)]
pub struct SourceChange {
@@ -315,7 +314,7 @@ impl SourceChangeBuilder {
}
let mut edit = TextEdit::builder();
- algo::diff(edit_result.old_root(), edit_result.new_root()).into_text_edit(&mut edit);
+ diff(edit_result.old_root(), edit_result.new_root()).into_text_edit(&mut edit);
let edit = edit.finish();
let snippet_edit =
@@ -334,7 +333,7 @@ impl SourceChangeBuilder {
});
if let Some(tm) = self.mutated_tree.take() {
- algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit);
+ diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit);
}
let edit = mem::take(&mut self.edit).finish();
@@ -373,7 +372,7 @@ impl SourceChangeBuilder {
self.edit.replace(range, replace_with.into())
}
pub fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
- algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
+ diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
}
pub fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) {
let file_system_edit = FileSystemEdit::CreateFile { dst, initial_contents: content.into() };
diff --git a/crates/ide-db/src/syntax_helpers/tree_diff.rs b/crates/ide-db/src/syntax_helpers/tree_diff.rs
new file mode 100644
index 0000000000..02e24c4776
--- /dev/null
+++ b/crates/ide-db/src/syntax_helpers/tree_diff.rs
@@ -0,0 +1,559 @@
+//! Basic tree diffing functionality.
+use rustc_hash::FxHashMap;
+use syntax::{NodeOrToken, SyntaxElement, SyntaxNode};
+
+use crate::{text_edit::TextEditBuilder, FxIndexMap};
+
+#[derive(Debug, Hash, PartialEq, Eq)]
+enum TreeDiffInsertPos {
+ After(SyntaxElement),
+ AsFirstChild(SyntaxElement),
+}
+
+#[derive(Debug)]
+pub struct TreeDiff {
+ replacements: FxHashMap<SyntaxElement, SyntaxElement>,
+ deletions: Vec<SyntaxElement>,
+ // the vec as well as the indexmap are both here to preserve order
+ insertions: FxIndexMap<TreeDiffInsertPos, Vec<SyntaxElement>>,
+}
+
+impl TreeDiff {
+ pub fn into_text_edit(&self, builder: &mut TextEditBuilder) {
+ let _p = tracing::info_span!("into_text_edit").entered();
+
+ for (anchor, to) in &self.insertions {
+ let offset = match anchor {
+ TreeDiffInsertPos::After(it) => it.text_range().end(),
+ TreeDiffInsertPos::AsFirstChild(it) => it.text_range().start(),
+ };
+ to.iter().for_each(|to| builder.insert(offset, to.to_string()));
+ }
+ for (from, to) in &self.replacements {
+ builder.replace(from.text_range(), to.to_string());
+ }
+ for text_range in self.deletions.iter().map(SyntaxElement::text_range) {
+ builder.delete(text_range);
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.replacements.is_empty() && self.deletions.is_empty() && self.insertions.is_empty()
+ }
+}
+
+/// Finds a (potentially minimal) diff, which, applied to `from`, will result in `to`.
+///
+/// Specifically, returns a structure that consists of a replacements, insertions and deletions
+/// such that applying this map on `from` will result in `to`.
+///
+/// This function tries to find a fine-grained diff.
+pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
+ let _p = tracing::info_span!("diff").entered();
+
+ let mut diff = TreeDiff {
+ replacements: FxHashMap::default(),
+ insertions: FxIndexMap::default(),
+ deletions: Vec::new(),
+ };
+ let (from, to) = (from.clone().into(), to.clone().into());
+
+ if !syntax_element_eq(&from, &to) {
+ go(&mut diff, from, to);
+ }
+ return diff;
+
+ fn syntax_element_eq(lhs: &SyntaxElement, rhs: &SyntaxElement) -> bool {
+ lhs.kind() == rhs.kind()
+ && lhs.text_range().len() == rhs.text_range().len()
+ && match (&lhs, &rhs) {
+ (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => {
+ lhs == rhs || lhs.text() == rhs.text()
+ }
+ (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(),
+ _ => false,
+ }
+ }
+
+ // FIXME: this is horribly inefficient. I bet there's a cool algorithm to diff trees properly.
+ fn go(diff: &mut TreeDiff, lhs: SyntaxElement, rhs: SyntaxElement) {
+ let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) {
+ Some((lhs, rhs)) => (lhs, rhs),
+ _ => {
+ cov_mark::hit!(diff_node_token_replace);
+ diff.replacements.insert(lhs, rhs);
+ return;
+ }
+ };
+
+ let mut look_ahead_scratch = Vec::default();
+
+ let mut rhs_children = rhs.children_with_tokens();
+ let mut lhs_children = lhs.children_with_tokens();
+ let mut last_lhs = None;
+ loop {
+ let lhs_child = lhs_children.next();
+ match (lhs_child.clone(), rhs_children.next()) {
+ (None, None) => break,
+ (None, Some(element)) => {
+ let insert_pos = match last_lhs.clone() {
+ Some(prev) => {
+ cov_mark::hit!(diff_insert);
+ TreeDiffInsertPos::After(prev)
+ }
+ // first iteration, insert into out parent as the first child
+ None => {
+ cov_mark::hit!(diff_insert_as_first_child);
+ TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
+ }
+ };
+ diff.insertions.entry(insert_pos).or_default().push(element);
+ }
+ (Some(element), None) => {
+ cov_mark::hit!(diff_delete);
+ diff.deletions.push(element);
+ }
+ (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {}
+ (Some(lhs_ele), Some(rhs_ele)) => {
+ // nodes differ, look for lhs_ele in rhs, if its found we can mark everything up
+ // until that element as insertions. This is important to keep the diff minimal
+ // in regards to insertions that have been actually done, this is important for
+ // use insertions as we do not want to replace the entire module node.
+ look_ahead_scratch.push(rhs_ele.clone());
+ let mut rhs_children_clone = rhs_children.clone();
+ let mut insert = false;
+ for rhs_child in &mut rhs_children_clone {
+ if syntax_element_eq(&lhs_ele, &rhs_child) {
+ cov_mark::hit!(diff_insertions);
+ insert = true;
+ break;
+ }
+ look_ahead_scratch.push(rhs_child);
+ }
+ let drain = look_ahead_scratch.drain(..);
+ if insert {
+ let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) {
+ TreeDiffInsertPos::After(prev)
+ } else {
+ cov_mark::hit!(insert_first_child);
+ TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
+ };
+
+ diff.insertions.entry(insert_pos).or_default().extend(drain);
+ rhs_children = rhs_children_clone;
+ } else {
+ go(diff, lhs_ele, rhs_ele);
+ }
+ }
+ }
+ last_lhs = lhs_child.or(last_lhs);
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use expect_test::{expect, Expect};
+ use itertools::Itertools;
+ use parser::{Edition, SyntaxKind};
+ use syntax::{AstNode, SourceFile, SyntaxElement};
+
+ use crate::text_edit::TextEdit;
+
+ #[test]
+ fn replace_node_token() {
+ cov_mark::check!(diff_node_token_replace);
+ check_diff(
+ r#"use node;"#,
+ r#"ident"#,
+ expect![[r#"
+ insertions:
+
+
+
+ replacements:
+
+ Line 0: Token([email protected] "use") -> ident
+
+ deletions:
+
+ Line 1: " "
+ Line 1: node
+ Line 1: ;
+ "#]],
+ );
+ }
+
+ #[test]
+ fn replace_parent() {
+ cov_mark::check!(diff_insert_as_first_child);
+ check_diff(
+ r#""#,
+ r#"use foo::bar;"#,
+ expect![[r#"
+ insertions:
+
+ Line 0: AsFirstChild(Node([email protected]))
+ -> use foo::bar;
+
+ replacements:
+
+
+
+ deletions:
+
+
+ "#]],
+ );
+ }
+
+ #[test]
+ fn insert_last() {
+ cov_mark::check!(diff_insert);
+ check_diff(
+ r#"
+use foo;
+use bar;"#,
+ r#"
+use foo;
+use bar;
+use baz;"#,
+ expect![[r#"
+ insertions:
+
+ Line 2: After(Node([email protected]))
+ -> "\n"
+ -> use baz;
+
+ replacements:
+
+
+
+ deletions:
+
+
+ "#]],
+ );
+ }
+
+ #[test]
+ fn insert_middle() {
+ check_diff(
+ r#"
+use foo;
+use baz;"#,
+ r#"
+use foo;
+use bar;
+use baz;"#,
+ expect![[r#"
+ insertions:
+
+ Line 2: After(Token([email protected] "\n"))
+ -> use bar;
+ -> "\n"
+
+ replacements:
+
+
+
+ deletions:
+
+
+ "#]],
+ )
+ }
+
+ #[test]
+ fn insert_first() {
+ check_diff(
+ r#"
+use bar;
+use baz;"#,
+ r#"
+use foo;
+use bar;
+use baz;"#,
+ expect![[r#"
+ insertions:
+
+ Line 0: After(Token([email protected] "\n"))
+ -> use foo;
+ -> "\n"
+
+ replacements:
+
+
+
+ deletions:
+
+
+ "#]],
+ )
+ }
+
+ #[test]
+ fn first_child_insertion() {
+ cov_mark::check!(insert_first_child);
+ check_diff(
+ r#"fn main() {
+ stdi
+ }"#,
+ r#"use foo::bar;
+
+ fn main() {
+ stdi
+ }"#,
+ expect![[r#"
+ insertions:
+
+ Line 0: AsFirstChild(Node([email protected]))
+ -> use foo::bar;
+ -> "\n\n "
+
+ replacements:
+
+
+
+ deletions:
+
+
+ "#]],
+ );
+ }
+
+ #[test]
+ fn delete_last() {
+ cov_mark::check!(diff_delete);
+ check_diff(
+ r#"use foo;
+ use bar;"#,
+ r#"use foo;"#,
+ expect![[r#"
+ insertions:
+
+
+
+ replacements:
+
+
+
+ deletions:
+
+ Line 1: "\n "
+ Line 2: use bar;
+ "#]],
+ );
+ }
+
+ #[test]
+ fn delete_middle() {
+ cov_mark::check!(diff_insertions);
+ check_diff(
+ r#"
+use expect_test::{expect, Expect};
+use text_edit::TextEdit;
+
+use crate::AstNode;
+"#,
+ r#"
+use expect_test::{expect, Expect};
+
+use crate::AstNode;
+"#,
+ expect![[r#"
+ insertions:
+
+ Line 1: After(Node([email protected]))
+ -> "\n\n"
+ -> use crate::AstNode;
+
+ replacements:
+
+
+
+ deletions:
+
+ Line 2: use text_edit::TextEdit;
+ Line 3: "\n\n"
+ Line 4: use crate::AstNode;
+ Line 5: "\n"
+ "#]],
+ )
+ }
+
+ #[test]
+ fn delete_first() {
+ check_diff(
+ r#"
+use text_edit::TextEdit;
+
+use crate::AstNode;
+"#,
+ r#"
+use crate::AstNode;
+"#,
+ expect![[r#"
+ insertions:
+
+
+
+ replacements:
+
+ Line 2: Token([email protected] "text_edit") -> crate
+ Line 2: Token([email protected] "TextEdit") -> AstNode
+ Line 2: Token([email protected] "\n\n") -> "\n"
+
+ deletions:
+
+ Line 3: use crate::AstNode;
+ Line 4: "\n"
+ "#]],
+ )
+ }
+
+ #[test]
+ fn merge_use() {
+ check_diff(
+ r#"
+use std::{
+ fmt,
+ hash::BuildHasherDefault,
+ ops::{self, RangeInclusive},
+};
+"#,
+ r#"
+use std::fmt;
+use std::hash::BuildHasherDefault;
+use std::ops::{self, RangeInclusive};
+"#,
+ expect![[r#"
+ insertions:
+
+ Line 2: After(Node([email protected]))
+ -> ::
+ -> fmt
+ Line 6: After(Token([email protected] "\n"))
+ -> use std::hash::BuildHasherDefault;
+ -> "\n"
+ -> use std::ops::{self, RangeInclusive};
+ -> "\n"
+
+ replacements:
+
+ Line 2: Token([email protected] "std") -> std
+
+ deletions:
+
+ Line 2: ::
+ Line 2: {
+ fmt,
+ hash::BuildHasherDefault,
+ ops::{self, RangeInclusive},
+ }
+ "#]],
+ )
+ }
+
+ #[test]
+ fn early_return_assist() {
+ check_diff(
+ r#"
+fn main() {
+ if let Ok(x) = Err(92) {
+ foo(x);
+ }
+}
+ "#,
+ r#"
+fn main() {
+ let x = match Err(92) {
+ Ok(it) => it,
+ _ => return,
+ };
+ foo(x);
+}
+ "#,
+ expect![[r#"
+ insertions:
+
+ Line 3: After(Node([email protected]))
+ -> " "
+ -> match Err(92) {
+ Ok(it) => it,
+ _ => return,
+ }
+ -> ;
+ Line 3: After(Node([email protected]))
+ -> "\n "
+ -> foo(x);
+
+ replacements:
+
+ Line 3: Token([email protected] "if") -> let
+ Line 3: Token([email protected] "let") -> x
+ Line 3: Node([email protected]) -> =
+
+ deletions:
+
+ Line 3: " "
+ Line 3: Ok(x)
+ Line 3: " "
+ Line 3: =
+ Line 3: " "
+ Line 3: Err(92)
+ "#]],
+ )
+ }
+
+ fn check_diff(from: &str, to: &str, expected_diff: Expect) {
+ let from_node = SourceFile::parse(from, Edition::CURRENT).tree().syntax().clone();
+ let to_node = SourceFile::parse(to, Edition::CURRENT).tree().syntax().clone();
+ let diff = super::diff(&from_node, &to_node);
+
+ let line_number =
+ |syn: &SyntaxElement| from[..syn.text_range().start().into()].lines().count();
+
+ let fmt_syntax = |syn: &SyntaxElement| match syn.kind() {
+ SyntaxKind::WHITESPACE => format!("{:?}", syn.to_string()),
+ _ => format!("{syn}"),
+ };
+
+ let insertions =
+ diff.insertions.iter().format_with("\n", |(k, v), f| -> Result<(), std::fmt::Error> {
+ f(&format!(
+ "Line {}: {:?}\n-> {}",
+ line_number(match k {
+ super::TreeDiffInsertPos::After(syn) => syn,
+ super::TreeDiffInsertPos::AsFirstChild(syn) => syn,
+ }),
+ k,
+ v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v)))
+ ))
+ });
+
+ let replacements = diff
+ .replacements
+ .iter()
+ .sorted_by_key(|(syntax, _)| syntax.text_range().start())
+ .format_with("\n", |(k, v), f| {
+ f(&format!("Line {}: {k:?} -> {}", line_number(k), fmt_syntax(v)))
+ });
+
+ let deletions = diff
+ .deletions
+ .iter()
+ .format_with("\n", |v, f| f(&format!("Line {}: {}", line_number(v), fmt_syntax(v))));
+
+ let actual = format!(
+ "insertions:\n\n{insertions}\n\nreplacements:\n\n{replacements}\n\ndeletions:\n\n{deletions}\n"
+ );
+ expected_diff.assert_eq(&actual);
+
+ let mut from = from.to_owned();
+ let mut text_edit = TextEdit::builder();
+ diff.into_text_edit(&mut text_edit);
+ text_edit.finish().apply(&mut from);
+ assert_eq!(&*from, to, "diff did not turn `from` to `to`");
+ }
+}
diff --git a/crates/text-edit/src/lib.rs b/crates/ide-db/src/text_edit.rs
index 3efe0850d8..0c675f0619 100644
--- a/crates/text-edit/src/lib.rs
+++ b/crates/ide-db/src/text_edit.rs
@@ -5,8 +5,8 @@
//! rust-analyzer.
use itertools::Itertools;
+pub use span::{TextRange, TextSize};
use std::cmp::max;
-pub use text_size::{TextRange, TextSize};
/// `InsertDelete` -- a single "atomic" change to text
///
diff --git a/crates/ide-diagnostics/Cargo.toml b/crates/ide-diagnostics/Cargo.toml
index bf54f4ab32..281a08e542 100644
--- a/crates/ide-diagnostics/Cargo.toml
+++ b/crates/ide-diagnostics/Cargo.toml
@@ -22,7 +22,6 @@ tracing.workspace = true
# local deps
stdx.workspace = true
syntax.workspace = true
-text-edit.workspace = true
cfg.workspace = true
hir.workspace = true
ide-db.workspace = true
diff --git a/crates/ide-diagnostics/src/handlers/field_shorthand.rs b/crates/ide-diagnostics/src/handlers/field_shorthand.rs
index c7071d1ce4..876c2ccd49 100644
--- a/crates/ide-diagnostics/src/handlers/field_shorthand.rs
+++ b/crates/ide-diagnostics/src/handlers/field_shorthand.rs
@@ -1,9 +1,9 @@
//! Suggests shortening `Foo { field: field }` to `Foo { field }` in both
//! expressions and patterns.
+use ide_db::text_edit::TextEdit;
use ide_db::{source_change::SourceChange, EditionedFileId, FileRange};
use syntax::{ast, match_ast, AstNode, SyntaxNode};
-use text_edit::TextEdit;
use crate::{fix, Diagnostic, DiagnosticCode};
diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs
index 1f8f805a1e..4c0c685e55 100644
--- a/crates/ide-diagnostics/src/handlers/inactive_code.rs
+++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs
@@ -195,4 +195,20 @@ union FooBar {
"#,
);
}
+
+ #[test]
+ fn cfg_true_false() {
+ check(
+ r#"
+ #[cfg(false)] fn inactive() {}
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: false is disabled
+
+ #[cfg(true)] fn active() {}
+
+ #[cfg(any(not(true)), false)] fn inactive2() {}
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: true is enabled
+
+"#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
index ccb33fed10..dca889d1a8 100644
--- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
+++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
@@ -2,6 +2,7 @@
//! example.
use hir::{ImportPathConfig, PathResolution, Semantics};
+use ide_db::text_edit::TextEdit;
use ide_db::{
helpers::mod_path_to_ast,
imports::insert_use::{insert_use, ImportScope},
@@ -14,7 +15,6 @@ use syntax::{
ast::{self, make},
Edition, SyntaxKind, SyntaxNode,
};
-use text_edit::TextEdit;
use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsConfig, Severity};
diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs
index 6a976697c8..e177b72e4d 100644
--- a/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -3,14 +3,19 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
// Diagnostic: macro-error
//
// This diagnostic is shown for macro expansion errors.
+
+// Diagnostic: proc-macros-disabled
+//
+// This diagnostic is shown for proc macros where proc macros have been disabled.
+
+// Diagnostic: proc-macro-disabled
+//
+// This diagnostic is shown for proc macros that has been specifically disabled via `rust-analyzer.procMacro.ignored`.
pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
// Use more accurate position if available.
let display_range = ctx.resolve_precise_location(&d.node, d.precise_location);
Diagnostic::new(
- DiagnosticCode::Ra(
- "macro-error",
- if d.error { Severity::Error } else { Severity::WeakWarning },
- ),
+ DiagnosticCode::Ra(d.kind, if d.error { Severity::Error } else { Severity::WeakWarning }),
d.message.clone(),
display_range,
)
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 86c237f7b5..fd1044e51b 100644
--- a/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -5,15 +5,14 @@ use hir::{
};
use ide_db::{
assists::Assist, famous_defs::FamousDefs, imports::import_assets::item_for_path_search,
- source_change::SourceChange, use_trivial_constructor::use_trivial_constructor, FxHashMap,
+ source_change::SourceChange, syntax_helpers::tree_diff::diff, text_edit::TextEdit,
+ use_trivial_constructor::use_trivial_constructor, FxHashMap,
};
use stdx::format_to;
use syntax::{
- algo,
ast::{self, make},
AstNode, Edition, SyntaxNode, SyntaxNodePtr, ToSmolStr,
};
-use text_edit::TextEdit;
use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
@@ -77,7 +76,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
// FIXME: this also currently discards a lot of whitespace in the input... we really need a formatter here
builder.replace(old_range.range, new_syntax.to_string());
} else {
- algo::diff(old_syntax, new_syntax).into_text_edit(&mut builder);
+ diff(old_syntax, new_syntax).into_text_edit(&mut builder);
}
builder.finish()
};
@@ -308,22 +307,27 @@ struct T(S);
fn regular(a: S) {
let s;
S { s, .. } = a;
+ _ = s;
}
fn nested(a: S2) {
let s;
S2 { s: S { s, .. }, .. } = a;
+ _ = s;
}
fn in_tuple(a: (S,)) {
let s;
(S { s, .. },) = a;
+ _ = s;
}
fn in_array(a: [S;1]) {
let s;
[S { s, .. },] = a;
+ _ = s;
}
fn in_tuple_struct(a: T) {
let s;
T(S { s, .. }) = a;
+ _ = s;
}
",
);
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 44be53b87c..a630d3c7c3 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -1,9 +1,9 @@
use hir::db::ExpandDatabase;
use hir::HirFileIdExt;
+use ide_db::text_edit::TextEdit;
use ide_db::{assists::Assist, source_change::SourceChange};
use syntax::{ast, SyntaxNode};
use syntax::{match_ast, AstNode};
-use text_edit::TextEdit;
use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
@@ -32,7 +32,8 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option<Vec<Ass
}
let root = ctx.sema.db.parse_or_expand(d.expr.file_id);
- let expr = d.expr.value.to_node(&root);
+ let node = d.expr.value.to_node(&root);
+ let expr = node.syntax().ancestors().find_map(ast::Expr::cast)?;
let node_to_add_unsafe_block = pick_best_node_to_add_unsafe_block(&expr)?;
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 6fa0e7a5a8..1397979144 100644
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -1,6 +1,7 @@
+use hir::db::ExpandDatabase;
use ide_db::source_change::SourceChange;
-use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T};
-use text_edit::TextEdit;
+use ide_db::text_edit::TextEdit;
+use syntax::{ast, AstNode, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, T};
use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
@@ -8,18 +9,27 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is triggered on mutating an immutable variable.
pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option<Diagnostic> {
- if d.span.file_id.macro_file().is_some() {
- // FIXME: Our infra can't handle allow from within macro expansions rn
- return None;
- }
+ let root = ctx.sema.db.parse_or_expand(d.span.file_id);
+ let node = d.span.value.to_node(&root);
+ let mut span = d.span;
+ if let Some(parent) = node.parent() {
+ if ast::BinExpr::can_cast(parent.kind()) {
+ // In case of an assignment, the diagnostic is provided on the variable name.
+ // We want to expand it to include the whole assignment, but only when this
+ // is an ordinary assignment, not a destructuring assignment. So, the direct
+ // parent is an assignment expression.
+ span = d.span.with_value(SyntaxNodePtr::new(&parent));
+ }
+ };
+
let fixes = (|| {
if d.local.is_ref(ctx.sema.db) {
// There is no simple way to add `mut` to `ref x` and `ref mut x`
return None;
}
- let file_id = d.span.file_id.file_id()?;
+ let file_id = span.file_id.file_id()?;
let mut edit_builder = TextEdit::builder();
- let use_range = d.span.value.text_range();
+ let use_range = span.value.text_range();
for source in d.local.sources(ctx.sema.db) {
let Some(ast) = source.name() else { continue };
// FIXME: macros
@@ -33,6 +43,7 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option
use_range,
)])
})();
+
Some(
Diagnostic::new_with_syntax_node_ptr(
ctx,
@@ -42,7 +53,7 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option
"cannot mutate immutable variable `{}`",
d.local.name(ctx.sema.db).display(ctx.sema.db, ctx.edition)
),
- d.span,
+ span,
)
.with_fixes(fixes),
)
@@ -53,10 +64,6 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option
// This diagnostic is triggered when a mutable variable isn't actually mutated.
pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Option<Diagnostic> {
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
- if ast.file_id.macro_file().is_some() {
- // FIXME: Our infra can't handle allow from within macro expansions rn
- return None;
- }
let fixes = (|| {
let file_id = ast.file_id.file_id()?;
let mut edit_builder = TextEdit::builder();
@@ -937,7 +944,6 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
#[test]
fn closure() {
- // FIXME: Diagnostic spans are inconsistent inside and outside closure
check_diagnostics(
r#"
//- minicore: copy, fn
@@ -950,11 +956,11 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
fn f() {
let x = 5;
let closure1 = || { x = 2; };
- //^ 💡 error: cannot mutate immutable variable `x`
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
let _ = closure1();
//^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
let closure2 = || { x = x; };
- //^ 💡 error: cannot mutate immutable variable `x`
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
let closure3 = || {
let x = 2;
x = 5;
@@ -996,7 +1002,7 @@ fn f() {
|| {
let x = 2;
|| { || { x = 5; } }
- //^ 💡 error: cannot mutate immutable variable `x`
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
}
}
};
@@ -1283,4 +1289,19 @@ fn main() {
"#,
);
}
+
+ #[test]
+ fn destructuring_assignment_needs_mut() {
+ check_diagnostics(
+ r#"
+//- minicore: fn
+
+fn main() {
+ let mut var = 1;
+ let mut func = || (var,) = (2,);
+ func();
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs
index dfadef11fd..e5d871975b 100644
--- a/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -1,11 +1,11 @@
use either::Either;
use hir::{db::ExpandDatabase, HasSource, HirDisplay, HirFileIdExt, Semantics, VariantId};
+use ide_db::text_edit::TextEdit;
use ide_db::{source_change::SourceChange, EditionedFileId, RootDatabase};
use syntax::{
ast::{self, edit::IndentLevel, make},
AstNode,
};
-use text_edit::TextEdit;
use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext};
diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
index 62bc1f3d06..c8e3cff364 100644
--- a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
+++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
@@ -1,7 +1,7 @@
use hir::{db::ExpandDatabase, diagnostics::RemoveTrailingReturn, FileRange};
+use ide_db::text_edit::TextEdit;
use ide_db::{assists::Assist, source_change::SourceChange};
use syntax::{ast, AstNode};
-use text_edit::TextEdit;
use crate::{adjusted_display_range, fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
diff --git a/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs
index 448df1ca16..a46c48608f 100644
--- a/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs
+++ b/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs
@@ -1,4 +1,5 @@
use hir::{db::ExpandDatabase, diagnostics::RemoveUnnecessaryElse, HirFileIdExt};
+use ide_db::text_edit::TextEdit;
use ide_db::{assists::Assist, source_change::SourceChange};
use itertools::Itertools;
use syntax::{
@@ -8,7 +9,6 @@ use syntax::{
},
AstNode, SyntaxToken, TextRange,
};
-use text_edit::TextEdit;
use crate::{
adjusted_display_range, fix, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity,
diff --git a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index 1864720623..f481365f2a 100644
--- a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
+++ b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -1,10 +1,10 @@
use hir::{db::ExpandDatabase, HirFileIdExt, InFile};
use ide_db::source_change::SourceChange;
+use ide_db::text_edit::TextEdit;
use syntax::{
ast::{self, HasArgList},
AstNode, TextRange,
};
-use text_edit::TextEdit;
use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext};
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
index 3de51ca4a3..1363a8ff0d 100644
--- a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
+++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
@@ -1,11 +1,11 @@
use hir::{db::ExpandDatabase, HasSource, HirDisplay};
+use ide_db::text_edit::TextRange;
use ide_db::{
assists::{Assist, AssistId, AssistKind},
label::Label,
source_change::SourceChangeBuilder,
};
use syntax::ToSmolStr;
-use text_edit::TextRange;
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 90f88d6705..93fe9374a3 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1,5 +1,6 @@
use either::Either;
use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, HirFileIdExt, InFile, Type};
+use ide_db::text_edit::TextEdit;
use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
use syntax::{
ast::{
@@ -9,7 +10,6 @@ use syntax::{
},
AstNode, AstPtr, TextSize,
};
-use text_edit::TextEdit;
use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext};
diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs
index 6994a7ed14..3ad84f7bda 100644
--- a/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -3,13 +3,13 @@ use hir::{
term_search::{term_search, TermSearchConfig, TermSearchCtx},
ClosureStyle, HirDisplay, ImportPathConfig,
};
+use ide_db::text_edit::TextEdit;
use ide_db::{
assists::{Assist, AssistId, AssistKind, GroupLabel},
label::Label,
source_change::SourceChange,
};
use itertools::Itertools;
-use text_edit::TextEdit;
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs
index 6af36fb9e7..d16bfb8002 100644
--- a/crates/ide-diagnostics/src/handlers/undeclared_label.rs
+++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -107,4 +107,34 @@ async fn foo() {
"#,
);
}
+
+ #[test]
+ fn macro_expansion_can_refer_label_defined_before_macro_definition() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ 'bar: loop {
+ macro_rules! m {
+ () => { break 'bar };
+ }
+ m!();
+ }
+}
+"#,
+ );
+ check_diagnostics(
+ r#"
+fn foo() {
+ 'bar: loop {
+ macro_rules! m {
+ () => { break 'bar };
+ }
+ 'bar: loop {
+ m!();
+ }
+ }
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
index e0822fc5b3..13591dfb2e 100644
--- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs
+++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -3,6 +3,7 @@
use std::iter;
use hir::{db::DefDatabase, DefMap, InFile, ModuleSource};
+use ide_db::text_edit::TextEdit;
use ide_db::{
base_db::{FileLoader, SourceDatabase, SourceRootDatabase},
source_change::SourceChange,
@@ -13,7 +14,6 @@ use syntax::{
ast::{self, edit::IndentLevel, HasModuleItem, HasName},
AstNode, TextRange,
};
-use text_edit::TextEdit;
use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index 76d624c47a..656bedff1a 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -1,6 +1,7 @@
use std::iter;
use hir::{db::ExpandDatabase, Adt, FileRange, HasSource, HirDisplay, InFile, Struct, Union};
+use ide_db::text_edit::TextEdit;
use ide_db::{
assists::{Assist, AssistId, AssistKind},
helpers::is_editable_crate,
@@ -16,7 +17,6 @@ use syntax::{
ast::{edit::AstNodeEdit, Type},
SyntaxNode,
};
-use text_edit::TextEdit;
use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_ident.rs b/crates/ide-diagnostics/src/handlers/unresolved_ident.rs
index 9a81682aae..68f14a97f5 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_ident.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_ident.rs
@@ -11,7 +11,7 @@ pub(crate) fn unresolved_ident(
ctx,
DiagnosticCode::RustcHardError("E0425"),
"no such value in this scope",
- d.expr.map(Into::into),
+ d.expr_or_pat.map(Into::into),
)
.experimental()
}
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
index 5b596123e7..0d1c977506 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
@@ -82,4 +82,29 @@ self::m!(); self::m2!();
"#,
);
}
+
+ #[test]
+ fn no_unresolved_panic_inside_mod_inside_fn() {
+ check_diagnostics(
+ r#"
+//- /core.rs library crate:core
+#[macro_export]
+macro_rules! panic {
+ () => {};
+}
+
+//- /lib.rs crate:foo deps:core
+#[macro_use]
+extern crate core;
+
+fn foo() {
+ mod init {
+ pub fn init() {
+ panic!();
+ }
+ }
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index c0d038a238..81cb452121 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -1,4 +1,5 @@
use hir::{db::ExpandDatabase, AssocItem, FileRange, HirDisplay, InFile};
+use ide_db::text_edit::TextEdit;
use ide_db::{
assists::{Assist, AssistId, AssistKind},
label::Label,
@@ -8,7 +9,6 @@ use syntax::{
ast::{self, make, HasArgList},
format_smolstr, AstNode, SmolStr, TextRange, ToSmolStr,
};
-use text_edit::TextEdit;
use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs
index 84007b16aa..67ece56694 100644
--- a/crates/ide-diagnostics/src/handlers/unused_variables.rs
+++ b/crates/ide-diagnostics/src/handlers/unused_variables.rs
@@ -1,4 +1,5 @@
use hir::Name;
+use ide_db::text_edit::TextEdit;
use ide_db::{
assists::{Assist, AssistId, AssistKind},
label::Label,
@@ -6,7 +7,6 @@ use ide_db::{
FileRange, RootDatabase,
};
use syntax::{Edition, TextRange};
-use text_edit::TextEdit;
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
diff --git a/crates/ide-diagnostics/src/handlers/useless_braces.rs b/crates/ide-diagnostics/src/handlers/useless_braces.rs
index 2d380ae045..e5c2eca171 100644
--- a/crates/ide-diagnostics/src/handlers/useless_braces.rs
+++ b/crates/ide-diagnostics/src/handlers/useless_braces.rs
@@ -1,8 +1,8 @@
use hir::InFile;
+use ide_db::text_edit::TextEdit;
use ide_db::{source_change::SourceChange, EditionedFileId, FileRange};
use itertools::Itertools;
use syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr};
-use text_edit::TextEdit;
use crate::{fix, Diagnostic, DiagnosticCode};
diff --git a/crates/ide-ssr/Cargo.toml b/crates/ide-ssr/Cargo.toml
index fad62fa3b9..2561467628 100644
--- a/crates/ide-ssr/Cargo.toml
+++ b/crates/ide-ssr/Cargo.toml
@@ -24,7 +24,6 @@ ide-db.workspace = true
parser.workspace = true
stdx.workspace = true
syntax.workspace = true
-text-edit.workspace = true
[dev-dependencies]
expect-test = "1.4.0"
@@ -34,4 +33,4 @@ test-utils.workspace = true
test-fixture.workspace = true
[lints]
-workspace = true \ No newline at end of file
+workspace = true
diff --git a/crates/ide-ssr/src/lib.rs b/crates/ide-ssr/src/lib.rs
index 54236ea8bc..eaca95d98c 100644
--- a/crates/ide-ssr/src/lib.rs
+++ b/crates/ide-ssr/src/lib.rs
@@ -84,10 +84,10 @@ pub use crate::{errors::SsrError, from_comment::ssr_from_comment, matching::Matc
use crate::{errors::bail, matching::MatchFailureReason};
use hir::{FileRange, Semantics};
+use ide_db::text_edit::TextEdit;
use ide_db::{base_db::SourceDatabase, EditionedFileId, FileId, FxHashMap, RootDatabase};
use resolving::ResolvedRule;
use syntax::{ast, AstNode, SyntaxNode, TextRange};
-use text_edit::TextEdit;
// A structured search replace rule. Create by calling `parse` on a str.
#[derive(Debug)]
diff --git a/crates/ide-ssr/src/parsing.rs b/crates/ide-ssr/src/parsing.rs
index e752ee3d77..ea40d5b815 100644
--- a/crates/ide-ssr/src/parsing.rs
+++ b/crates/ide-ssr/src/parsing.rs
@@ -190,7 +190,7 @@ impl RawPattern {
let mut res = FxHashMap::default();
for t in &self.tokens {
if let PatternElement::Placeholder(placeholder) = t {
- res.insert(SmolStr::new(placeholder.stand_in_name.clone()), placeholder.clone());
+ res.insert(SmolStr::new(&placeholder.stand_in_name), placeholder.clone());
}
}
res
diff --git a/crates/ide-ssr/src/replacing.rs b/crates/ide-ssr/src/replacing.rs
index 65756601f6..11c1615a56 100644
--- a/crates/ide-ssr/src/replacing.rs
+++ b/crates/ide-ssr/src/replacing.rs
@@ -1,5 +1,6 @@
//! Code for applying replacement templates for matches that have previously been found.
+use ide_db::text_edit::TextEdit;
use ide_db::{FxHashMap, FxHashSet};
use itertools::Itertools;
use parser::Edition;
@@ -7,7 +8,6 @@ use syntax::{
ast::{self, AstNode, AstToken},
SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize,
};
-use text_edit::TextEdit;
use crate::{fragments, resolving::ResolvedRule, Match, SsrMatches};
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index d976d604f1..7c66b36dc8 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -39,7 +39,6 @@ profile.workspace = true
stdx.workspace = true
syntax.workspace = true
span.workspace = true
-text-edit.workspace = true
# ide should depend only on the top-level `hir` package. if you need
# something from some `hir-xxx` subpackage, reexport the API via `hir`.
hir.workspace = true
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index 1b82c00d1d..e5b4ed17b2 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -510,6 +510,7 @@ fn caller$0() {
expect![[]],
);
}
+
#[test]
fn test_call_hierarchy_in_macros_incoming_different_files() {
check_hierarchy(
@@ -591,9 +592,9 @@ macro_rules! call {
"#,
expect!["callee Function FileId(0) 22..37 30..36"],
expect![[r#"
- callee Function FileId(0) 38..52 44..50 : FileId(0):44..50
caller Function FileId(0) 38..52 : FileId(0):44..50
- caller Function FileId(1) 130..136 130..136 : FileId(0):44..50"#]],
+ caller Function FileId(1) 130..136 130..136 : FileId(0):44..50
+ callee Function FileId(0) 38..52 44..50 : FileId(0):44..50"#]],
expect![[]],
);
}
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs
index 9245818584..055080ad17 100644
--- a/crates/ide/src/file_structure.rs
+++ b/crates/ide/src/file_structure.rs
@@ -187,6 +187,24 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
};
Some(node)
},
+ ast::LetStmt(it) => {
+ let pat = it.pat()?;
+
+ let mut label = String::new();
+ collapse_ws(pat.syntax(), &mut label);
+
+ let node = StructureNode {
+ parent: None,
+ label,
+ navigation_range: pat.syntax().text_range(),
+ node_range: it.syntax().text_range(),
+ kind: StructureNodeKind::SymbolKind(SymbolKind::Local),
+ detail: it.ty().map(|ty| ty.to_string()),
+ deprecated: false,
+ };
+
+ Some(node)
+ },
ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)),
_ => None,
}
@@ -308,6 +326,17 @@ fn f() {}
// endregion
fn g() {}
}
+
+fn let_statements() {
+ let x = 42;
+ let mut y = x;
+ let Foo {
+ ..
+ } = Foo { x };
+ if let None = Some(x) {}
+ _ = ();
+ let _ = g();
+}
"#,
expect![[r#"
[
@@ -633,6 +662,71 @@ fn g() {}
),
deprecated: false,
},
+ StructureNode {
+ parent: None,
+ label: "let_statements",
+ navigation_range: 641..655,
+ node_range: 638..798,
+ kind: SymbolKind(
+ Function,
+ ),
+ detail: Some(
+ "fn()",
+ ),
+ deprecated: false,
+ },
+ StructureNode {
+ parent: Some(
+ 26,
+ ),
+ label: "x",
+ navigation_range: 668..669,
+ node_range: 664..675,
+ kind: SymbolKind(
+ Local,
+ ),
+ detail: None,
+ deprecated: false,
+ },
+ StructureNode {
+ parent: Some(
+ 26,
+ ),
+ label: "mut y",
+ navigation_range: 684..689,
+ node_range: 680..694,
+ kind: SymbolKind(
+ Local,
+ ),
+ detail: None,
+ deprecated: false,
+ },
+ StructureNode {
+ parent: Some(
+ 26,
+ ),
+ label: "Foo { .. }",
+ navigation_range: 703..725,
+ node_range: 699..738,
+ kind: SymbolKind(
+ Local,
+ ),
+ detail: None,
+ deprecated: false,
+ },
+ StructureNode {
+ parent: Some(
+ 26,
+ ),
+ label: "_",
+ navigation_range: 788..789,
+ node_range: 784..796,
+ kind: SymbolKind(
+ Local,
+ ),
+ detail: None,
+ deprecated: false,
+ },
]
"#]],
);
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 4cbcb6ed05..363f852e0e 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -13,7 +13,6 @@ use ide_db::{
RootDatabase, SymbolKind,
};
use itertools::Itertools;
-
use span::{Edition, FileId};
use syntax::{
ast::{self, HasLoopBody},
@@ -99,6 +98,7 @@ pub(crate) fn goto_definition(
return Some(vec![x]);
}
}
+
Some(
IdentClass::classify_node(sema, &parent)?
.definitions()
@@ -418,10 +418,10 @@ fn expr_to_nav(
#[cfg(test)]
mod tests {
+ use crate::fixture;
use ide_db::FileRange;
use itertools::Itertools;
-
- use crate::fixture;
+ use syntax::SmolStr;
#[track_caller]
fn check(ra_fixture: &str) {
@@ -450,6 +450,170 @@ mod tests {
assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
}
+ fn check_name(expected_name: &str, ra_fixture: &str) {
+ let (analysis, position, _) = fixture::annotations(ra_fixture);
+ let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
+ assert!(navs.len() < 2, "expected single navigation target but encountered {}", navs.len());
+ let Some(target) = navs.into_iter().next() else {
+ panic!("expected single navigation target but encountered none");
+ };
+ assert_eq!(target.name, SmolStr::new_inline(expected_name));
+ }
+
+ #[test]
+ fn goto_def_pat_range_to_inclusive() {
+ check_name(
+ "RangeToInclusive",
+ r#"
+//- minicore: range
+fn f(ch: char) -> bool {
+ match ch {
+ ..$0='z' => true,
+ _ => false
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn goto_def_pat_range_to() {
+ check_name(
+ "RangeTo",
+ r#"
+//- minicore: range
+fn f(ch: char) -> bool {
+ match ch {
+ .$0.'z' => true,
+ _ => false
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn goto_def_pat_range() {
+ check_name(
+ "Range",
+ r#"
+//- minicore: range
+fn f(ch: char) -> bool {
+ match ch {
+ 'a'.$0.'z' => true,
+ _ => false
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn goto_def_pat_range_inclusive() {
+ check_name(
+ "RangeInclusive",
+ r#"
+//- minicore: range
+fn f(ch: char) -> bool {
+ match ch {
+ 'a'..$0='z' => true,
+ _ => false
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn goto_def_pat_range_from() {
+ check_name(
+ "RangeFrom",
+ r#"
+//- minicore: range
+fn f(ch: char) -> bool {
+ match ch {
+ 'a'..$0 => true,
+ _ => false
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn goto_def_expr_range() {
+ check_name(
+ "Range",
+ r#"
+//- minicore: range
+let x = 0.$0.1;
+"#,
+ );
+ }
+
+ #[test]
+ fn goto_def_expr_range_from() {
+ check_name(
+ "RangeFrom",
+ r#"
+//- minicore: range
+fn f(arr: &[i32]) -> &[i32] {
+ &arr[0.$0.]
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn goto_def_expr_range_inclusive() {
+ check_name(
+ "RangeInclusive",
+ r#"
+//- minicore: range
+let x = 0.$0.=1;
+"#,
+ );
+ }
+
+ #[test]
+ fn goto_def_expr_range_full() {
+ check_name(
+ "RangeFull",
+ r#"
+//- minicore: range
+fn f(arr: &[i32]) -> &[i32] {
+ &arr[.$0.]
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn goto_def_expr_range_to() {
+ check_name(
+ "RangeTo",
+ r#"
+//- minicore: range
+fn f(arr: &[i32]) -> &[i32] {
+ &arr[.$0.10]
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn goto_def_expr_range_to_inclusive() {
+ check_name(
+ "RangeToInclusive",
+ r#"
+//- minicore: range
+fn f(arr: &[i32]) -> &[i32] {
+ &arr[.$0.=10]
+}
+"#,
+ );
+ }
+
#[test]
fn goto_def_in_included_file() {
check(
@@ -2838,4 +3002,24 @@ use foo::m;
"#,
);
}
+
+ #[test]
+ fn macro_label_hygiene() {
+ check(
+ r#"
+macro_rules! m {
+ ($x:stmt) => {
+ 'bar: loop { $x }
+ };
+}
+
+fn foo() {
+ 'bar: loop {
+ // ^^^^
+ m!(continue 'bar$0);
+ }
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 124db2985b..6cac4f1ee4 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -11,7 +11,7 @@ use ide_db::{
defs::{Definition, IdentClass, NameRefClass, OperatorClass},
famous_defs::FamousDefs,
helpers::pick_best_token,
- FileRange, FxIndexSet, RootDatabase,
+ FileRange, FxIndexSet, Ranker, RootDatabase,
};
use itertools::{multizip, Itertools};
use span::Edition;
@@ -182,27 +182,13 @@ fn hover_offset(
// equivalency is more important
let mut descended = sema.descend_into_macros(original_token.clone());
- let kind = original_token.kind();
- let text = original_token.text();
- let ident_kind = kind.is_any_identifier();
-
- descended.sort_by_cached_key(|tok| {
- let tok_kind = tok.kind();
-
- let exact_same_kind = tok_kind == kind;
- let both_idents = exact_same_kind || (tok_kind.is_any_identifier() && ident_kind);
- let same_text = tok.text() == text;
- // anything that mapped into a token tree has likely no semantic information
- let no_tt_parent = tok.parent().map_or(false, |it| it.kind() != TOKEN_TREE);
- !((both_idents as usize)
- | ((exact_same_kind as usize) << 1)
- | ((same_text as usize) << 2)
- | ((no_tt_parent as usize) << 3))
- });
+ let ranker = Ranker::from_token(&original_token);
+
+ descended.sort_by_cached_key(|tok| !ranker.rank_token(tok));
let mut res = vec![];
for token in descended {
- let is_same_kind = token.kind() == kind;
+ let is_same_kind = token.kind() == ranker.kind;
let lint_hover = (|| {
// FIXME: Definition should include known lints and the like instead of having this special case here
let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 01fa316d5f..a31b14dbd3 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -1042,7 +1042,7 @@ fn render_dyn_compatibility(
}
DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait) => {
let name = hir::Trait::from(super_trait).name(db);
- format_to!(buf, "has a object unsafe supertrait `{}`", name.as_str());
+ format_to!(buf, "has a dyn incompatible supertrait `{}`", name.as_str());
}
}
}
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 81397b0785..3e40263041 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -289,7 +289,7 @@ m!(ab$0c);
*abc*
```rust
- test::module
+ test
```
```rust
@@ -298,11 +298,11 @@ m!(ab$0c);
---
- Inner
+ Outer
---
```rust
- test
+ test::module
```
```rust
@@ -311,7 +311,7 @@ m!(ab$0c);
---
- Outer
+ Inner
"#]],
);
}
@@ -9018,3 +9018,156 @@ foo!(BAR_$0);
"#]],
);
}
+
+#[test]
+fn type_alias_without_docs() {
+ // Simple.
+ check(
+ r#"
+/// Docs for B
+struct B;
+
+type A$0 = B;
+"#,
+ expect![[r#"
+ *A*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ // size = 0, align = 1
+ type A = B
+ ```
+
+ ---
+
+ *This is the documentation for* `struct B`
+
+ Docs for B
+ "#]],
+ );
+
+ // Nested.
+ check(
+ r#"
+/// Docs for C
+struct C;
+
+type B = C;
+
+type A$0 = B;
+"#,
+ expect![[r#"
+ *A*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ // size = 0, align = 1
+ type A = B
+ ```
+
+ ---
+
+ *This is the documentation for* `struct C`
+
+ Docs for C
+ "#]],
+ );
+
+ // Showing the docs for aliased struct instead of intermediate type.
+ check(
+ r#"
+/// Docs for C
+struct C;
+
+/// Docs for B
+type B = C;
+
+type A$0 = B;
+"#,
+ expect![[r#"
+ *A*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ // size = 0, align = 1
+ type A = B
+ ```
+
+ ---
+
+ *This is the documentation for* `struct C`
+
+ Docs for C
+ "#]],
+ );
+
+ // No docs found.
+ check(
+ r#"
+struct C;
+
+type B = C;
+
+type A$0 = B;
+"#,
+ expect![[r#"
+ *A*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ // size = 0, align = 1
+ type A = B
+ ```
+ "#]],
+ );
+
+ // Multiple nested crate.
+ check(
+ r#"
+//- /lib.rs crate:c
+/// Docs for C
+pub struct C;
+
+//- /lib.rs crate:b deps:c
+pub use c::C;
+pub type B = C;
+
+//- /lib.rs crate:a deps:b
+pub use b::B;
+pub type A = B;
+
+//- /main.rs crate:main deps:a
+use a::A$0;
+"#,
+ expect![[r#"
+ *A*
+
+ ```rust
+ a
+ ```
+
+ ```rust
+ // size = 0, align = 1
+ pub type A = B
+ ```
+
+ ---
+
+ *This is the documentation for* `pub struct C`
+
+ Docs for C
+ "#]],
+ );
+}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 97e712356b..c58ca0f01c 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -8,6 +8,7 @@ use hir::{
sym, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef,
ModuleDefId, Semantics,
};
+use ide_db::text_edit::TextEdit;
use ide_db::{famous_defs::FamousDefs, FileRange, RootDatabase};
use itertools::Itertools;
use smallvec::{smallvec, SmallVec};
@@ -17,7 +18,6 @@ use syntax::{
ast::{self, AstNode, HasGenericParams},
format_smolstr, match_ast, SmolStr, SyntaxNode, TextRange, TextSize, WalkEvent,
};
-use text_edit::TextEdit;
use crate::{navigation_target::TryToNav, FileId};
@@ -410,19 +410,6 @@ impl InlayHint {
}
}
- fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint {
- InlayHint {
- range,
- kind,
- label: InlayHintLabel::from("("),
- text_edit: None,
- position: InlayHintPosition::Before,
- pad_left: false,
- pad_right: false,
- resolve_parent: None,
- }
- }
-
pub fn needs_resolve(&self) -> Option<TextRange> {
self.resolve_parent.filter(|_| self.text_edit.is_some() || self.label.needs_resolve())
}
@@ -475,6 +462,18 @@ impl InlayHintLabel {
}
}
+ pub fn append_part(&mut self, part: InlayHintLabelPart) {
+ if part.linked_location.is_none() && part.tooltip.is_none() {
+ if let Some(InlayHintLabelPart { text, linked_location: None, tooltip: None }) =
+ self.parts.last_mut()
+ {
+ text.push_str(&part.text);
+ return;
+ }
+ }
+ self.parts.push(part);
+ }
+
pub fn needs_resolve(&self) -> bool {
self.parts.iter().any(|part| part.linked_location.is_some() || part.tooltip.is_some())
}
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs
index c37c469dff..4d7d6e270e 100644
--- a/crates/ide/src/inlay_hints/adjustment.rs
+++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -3,12 +3,15 @@
//! let _: u32 = /* <never-to-any> */ loop {};
//! let _: &u32 = /* &* */ &mut 0;
//! ```
+use std::ops::Not;
+
use either::Either;
use hir::{
Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
};
use ide_db::famous_defs::FamousDefs;
+use ide_db::text_edit::TextEditBuilder;
use span::EditionedFileId;
use stdx::never;
use syntax::{
@@ -17,8 +20,8 @@ use syntax::{
};
use crate::{
- AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintPosition,
- InlayHintsConfig, InlayKind, InlayTooltip,
+ AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintLabelPart,
+ InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip,
};
pub(super) fn hints(
@@ -51,32 +54,47 @@ pub(super) fn hints(
let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?;
if let ast::Expr::BlockExpr(_) | ast::Expr::IfExpr(_) | ast::Expr::MatchExpr(_) = desc_expr {
- if let [Adjustment { kind: Adjust::Deref(_), source, .. }, Adjustment { kind: Adjust::Borrow(_), source: _, target }] =
- &*adjustments
- {
- // Don't show unnecessary reborrows for these, they will just repeat the inner ones again
- if source == target {
- return None;
- }
+ // Don't show unnecessary reborrows for these, they will just repeat the inner ones again
+ if matches!(
+ &*adjustments,
+ [Adjustment { kind: Adjust::Deref(_), source, .. }, Adjustment { kind: Adjust::Borrow(_), target, .. }]
+ if source == target
+ ) {
+ return None;
}
}
let (postfix, needs_outer_parens, needs_inner_parens) =
mode_and_needs_parens_for_adjustment_hints(expr, config.adjustment_hints_mode);
- if needs_outer_parens {
- acc.push(InlayHint::opening_paren_before(
- InlayKind::Adjustment,
- expr.syntax().text_range(),
- ));
+ let range = expr.syntax().text_range();
+ let mut pre = InlayHint {
+ range,
+ position: InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: false,
+ kind: InlayKind::Adjustment,
+ label: InlayHintLabel::default(),
+ text_edit: None,
+ resolve_parent: Some(range),
+ };
+ let mut post = InlayHint {
+ range,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ kind: InlayKind::Adjustment,
+ label: InlayHintLabel::default(),
+ text_edit: None,
+ resolve_parent: Some(range),
+ };
+
+ if needs_outer_parens || (postfix && needs_inner_parens) {
+ pre.label.append_str("(");
}
if postfix && needs_inner_parens {
- acc.push(InlayHint::opening_paren_before(
- InlayKind::Adjustment,
- expr.syntax().text_range(),
- ));
- acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
+ post.label.append_str(")");
}
let mut iter = if postfix {
@@ -86,6 +104,7 @@ pub(super) fn hints(
};
let iter: &mut dyn Iterator<Item = _> = iter.as_mut().either(|it| it as _, |it| it as _);
+ let mut allow_edit = !postfix;
for Adjustment { source, target, kind } in iter {
if source == target {
cov_mark::hit!(same_type_adjustment);
@@ -95,6 +114,7 @@ pub(super) fn hints(
// FIXME: Add some nicer tooltips to each of these
let (text, coercion) = match kind {
Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
+ allow_edit = false;
("<never-to-any>", "never to any")
}
Adjust::Deref(None) => ("*", "dereference"),
@@ -115,6 +135,7 @@ pub(super) fn hints(
// some of these could be represented via `as` casts, but that's not too nice and
// handling everything as a prefix expr makes the `(` and `)` insertion easier
Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => {
+ allow_edit = false;
match cast {
PointerCast::ReifyFnPointer => {
("<fn-item-to-fn-pointer>", "fn item to fn pointer")
@@ -138,36 +159,58 @@ pub(super) fn hints(
}
_ => continue,
};
- let label = InlayHintLabel::simple(
- if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
- Some(InlayTooltip::Markdown(format!(
+ let label = InlayHintLabelPart {
+ text: if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
+ linked_location: None,
+ tooltip: Some(InlayTooltip::Markdown(format!(
"`{}` → `{}` ({coercion} coercion)",
source.display(sema.db, file_id.edition()),
target.display(sema.db, file_id.edition()),
))),
- None,
- );
- acc.push(InlayHint {
- range: expr.syntax().text_range(),
- pad_left: false,
- pad_right: false,
- position: if postfix { InlayHintPosition::After } else { InlayHintPosition::Before },
- kind: InlayKind::Adjustment,
- label,
- text_edit: None,
- resolve_parent: Some(expr.syntax().text_range()),
- });
+ };
+ if postfix { &mut post } else { &mut pre }.label.append_part(label);
}
if !postfix && needs_inner_parens {
- acc.push(InlayHint::opening_paren_before(
- InlayKind::Adjustment,
- expr.syntax().text_range(),
- ));
- acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
+ pre.label.append_str("(");
+ }
+ if needs_outer_parens || (!postfix && needs_inner_parens) {
+ post.label.append_str(")");
}
- if needs_outer_parens {
- acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
+
+ let mut pre = pre.label.parts.is_empty().not().then_some(pre);
+ let mut post = post.label.parts.is_empty().not().then_some(post);
+ if pre.is_none() && post.is_none() {
+ return None;
}
+ if allow_edit {
+ let edit = {
+ let mut b = TextEditBuilder::default();
+ if let Some(pre) = &pre {
+ b.insert(
+ pre.range.start(),
+ pre.label.parts.iter().map(|part| &*part.text).collect::<String>(),
+ );
+ }
+ if let Some(post) = &post {
+ b.insert(
+ post.range.end(),
+ post.label.parts.iter().map(|part| &*part.text).collect::<String>(),
+ );
+ }
+ b.finish()
+ };
+ match (&mut pre, &mut post) {
+ (Some(pre), Some(post)) => {
+ pre.text_edit = Some(edit.clone());
+ post.text_edit = Some(edit);
+ }
+ (Some(pre), None) => pre.text_edit = Some(edit),
+ (None, Some(post)) => post.text_edit = Some(edit),
+ (None, None) => (),
+ }
+ }
+ acc.extend(pre);
+ acc.extend(post);
Some(())
}
@@ -293,25 +336,19 @@ fn main() {
let _: u32 = loop {};
//^^^^^^^<never-to-any>
let _: &u32 = &mut 0;
- //^^^^^^&
- //^^^^^^*
+ //^^^^^^&*
let _: &mut u32 = &mut 0;
- //^^^^^^&mut $
- //^^^^^^*
+ //^^^^^^&mut *
let _: *const u32 = &mut 0;
- //^^^^^^&raw const $
- //^^^^^^*
+ //^^^^^^&raw const *
let _: *mut u32 = &mut 0;
- //^^^^^^&raw mut $
- //^^^^^^*
+ //^^^^^^&raw mut *
let _: fn() = main;
//^^^^<fn-item-to-fn-pointer>
let _: unsafe fn() = main;
- //^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
- //^^^^<fn-item-to-fn-pointer>
+ //^^^^<safe-fn-pointer-to-unsafe-fn-pointer><fn-item-to-fn-pointer>
let _: unsafe fn() = main as fn();
- //^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
- //^^^^^^^^^^^^(
+ //^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer>(
//^^^^^^^^^^^^)
//^^^^<fn-item-to-fn-pointer>
let _: fn() = || {};
@@ -319,72 +356,51 @@ fn main() {
let _: unsafe fn() = || {};
//^^^^^<closure-to-unsafe-fn-pointer>
let _: *const u32 = &mut 0u32 as *mut u32;
- //^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr>
- //^^^^^^^^^^^^^^^^^^^^^(
+ //^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr>(
//^^^^^^^^^^^^^^^^^^^^^)
- //^^^^^^^^^&raw mut $
- //^^^^^^^^^*
+ //^^^^^^^^^&raw mut *
let _: &mut [_] = &mut [0; 0];
- //^^^^^^^^^^^<unsize>
- //^^^^^^^^^^^&mut $
- //^^^^^^^^^^^*
+ //^^^^^^^^^^^<unsize>&mut *
Struct.consume();
Struct.by_ref();
- //^^^^^^(
- //^^^^^^&
+ //^^^^^^(&
//^^^^^^)
Struct.by_ref_mut();
- //^^^^^^(
- //^^^^^^&mut $
+ //^^^^^^(&mut $
//^^^^^^)
(&Struct).consume();
//^^^^^^^*
(&Struct).by_ref();
- //^^^^^^^&
- //^^^^^^^*
+ //^^^^^^^&*
(&mut Struct).consume();
//^^^^^^^^^^^*
(&mut Struct).by_ref();
- //^^^^^^^^^^^&
- //^^^^^^^^^^^*
+ //^^^^^^^^^^^&*
(&mut Struct).by_ref_mut();
- //^^^^^^^^^^^&mut $
- //^^^^^^^^^^^*
+ //^^^^^^^^^^^&mut *
// Check that block-like expressions don't duplicate hints
let _: &mut [u32] = (&mut []);
- //^^^^^^^<unsize>
- //^^^^^^^&mut $
- //^^^^^^^*
+ //^^^^^^^<unsize>&mut *
let _: &mut [u32] = { &mut [] };
- //^^^^^^^<unsize>
- //^^^^^^^&mut $
- //^^^^^^^*
+ //^^^^^^^<unsize>&mut *
let _: &mut [u32] = unsafe { &mut [] };
- //^^^^^^^<unsize>
- //^^^^^^^&mut $
- //^^^^^^^*
+ //^^^^^^^<unsize>&mut *
let _: &mut [u32] = if true {
&mut []
- //^^^^^^^<unsize>
- //^^^^^^^&mut $
- //^^^^^^^*
+ //^^^^^^^<unsize>&mut *
} else {
loop {}
//^^^^^^^<never-to-any>
};
let _: &mut [u32] = match () { () => &mut [] };
- //^^^^^^^<unsize>
- //^^^^^^^&mut $
- //^^^^^^^*
+ //^^^^^^^<unsize>&mut *
let _: &mut dyn Fn() = &mut || ();
- //^^^^^^^^^^<unsize>
- //^^^^^^^^^^&mut $
- //^^^^^^^^^^*
+ //^^^^^^^^^^<unsize>&mut *
() == ();
// ^^&
// ^^&
@@ -393,16 +409,13 @@ fn main() {
// ^^^^&
let closure: dyn Fn = || ();
closure();
- //^^^^^^^(
- //^^^^^^^&
+ //^^^^^^^(&
//^^^^^^^)
Struct[0];
- //^^^^^^(
- //^^^^^^&
+ //^^^^^^(&
//^^^^^^)
&mut Struct[0];
- //^^^^^^(
- //^^^^^^&mut $
+ //^^^^^^(&mut $
//^^^^^^)
}
@@ -442,72 +455,46 @@ fn main() {
(&Struct).consume();
//^^^^^^^(
- //^^^^^^^)
- //^^^^^^^.*
+ //^^^^^^^).*
(&Struct).by_ref();
//^^^^^^^(
- //^^^^^^^)
- //^^^^^^^.*
- //^^^^^^^.&
+ //^^^^^^^).*.&
(&mut Struct).consume();
//^^^^^^^^^^^(
- //^^^^^^^^^^^)
- //^^^^^^^^^^^.*
+ //^^^^^^^^^^^).*
(&mut Struct).by_ref();
//^^^^^^^^^^^(
- //^^^^^^^^^^^)
- //^^^^^^^^^^^.*
- //^^^^^^^^^^^.&
+ //^^^^^^^^^^^).*.&
(&mut Struct).by_ref_mut();
//^^^^^^^^^^^(
- //^^^^^^^^^^^)
- //^^^^^^^^^^^.*
- //^^^^^^^^^^^.&mut
+ //^^^^^^^^^^^).*.&mut
// Check that block-like expressions don't duplicate hints
let _: &mut [u32] = (&mut []);
//^^^^^^^(
- //^^^^^^^)
- //^^^^^^^.*
- //^^^^^^^.&mut
- //^^^^^^^.<unsize>
+ //^^^^^^^).*.&mut.<unsize>
let _: &mut [u32] = { &mut [] };
//^^^^^^^(
- //^^^^^^^)
- //^^^^^^^.*
- //^^^^^^^.&mut
- //^^^^^^^.<unsize>
+ //^^^^^^^).*.&mut.<unsize>
let _: &mut [u32] = unsafe { &mut [] };
//^^^^^^^(
- //^^^^^^^)
- //^^^^^^^.*
- //^^^^^^^.&mut
- //^^^^^^^.<unsize>
+ //^^^^^^^).*.&mut.<unsize>
let _: &mut [u32] = if true {
&mut []
//^^^^^^^(
- //^^^^^^^)
- //^^^^^^^.*
- //^^^^^^^.&mut
- //^^^^^^^.<unsize>
+ //^^^^^^^).*.&mut.<unsize>
} else {
loop {}
//^^^^^^^.<never-to-any>
};
let _: &mut [u32] = match () { () => &mut [] };
//^^^^^^^(
- //^^^^^^^)
- //^^^^^^^.*
- //^^^^^^^.&mut
- //^^^^^^^.<unsize>
+ //^^^^^^^).*.&mut.<unsize>
let _: &mut dyn Fn() = &mut || ();
//^^^^^^^^^^(
- //^^^^^^^^^^)
- //^^^^^^^^^^.*
- //^^^^^^^^^^.&mut
- //^^^^^^^^^^.<unsize>
+ //^^^^^^^^^^).*.&mut.<unsize>
() == ();
// ^^.&
// ^^.&
@@ -619,9 +606,7 @@ fn or_else() {
r#"
unsafe fn enabled() {
f(&&());
- //^^^^&
- //^^^^*
- //^^^^*
+ //^^^^&**
}
fn disabled() {
@@ -633,9 +618,7 @@ fn mixed() {
unsafe {
f(&&());
- //^^^^&
- //^^^^*
- //^^^^*
+ //^^^^&**
}
}
@@ -644,9 +627,7 @@ const _: () = {
unsafe {
f(&&());
- //^^^^&
- //^^^^*
- //^^^^*
+ //^^^^&**
}
};
@@ -655,18 +636,14 @@ static STATIC: () = {
unsafe {
f(&&());
- //^^^^&
- //^^^^*
- //^^^^*
+ //^^^^&**
}
};
enum E {
Disable = { f(&&()); 0 },
Enable = unsafe { f(&&()); 1 },
- //^^^^&
- //^^^^*
- //^^^^*
+ //^^^^&**
}
const fn f(_: &()) {}
@@ -692,8 +669,7 @@ fn a() {
_ = Struct.by_ref();
_ = unsafe { Struct.by_ref() };
- //^^^^^^(
- //^^^^^^&
+ //^^^^^^(&
//^^^^^^)
}
"#,
@@ -726,10 +702,7 @@ trait T<RHS = Self> {}
fn hello(it: &&[impl T]) {
it.len();
- //^^(
- //^^&
- //^^*
- //^^*
+ //^^(&**
//^^)
}
"#,
diff --git a/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs
index d1c0677863..cfe8657fd0 100644
--- a/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/crates/ide/src/inlay_hints/binding_mode.rs
@@ -2,13 +2,16 @@
//! ```no_run
//! let /* & */ (/* ref */ x,) = &(0,);
//! ```
+use std::mem;
+
use hir::Mutability;
use ide_db::famous_defs::FamousDefs;
+use ide_db::text_edit::TextEditBuilder;
use span::EditionedFileId;
use syntax::ast::{self, AstNode};
-use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind};
+use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -21,16 +24,7 @@ pub(super) fn hints(
return None;
}
- let outer_paren_pat = pat
- .syntax()
- .ancestors()
- .skip(1)
- .map_while(ast::Pat::cast)
- .map_while(|pat| match pat {
- ast::Pat::ParenPat(pat) => Some(pat),
- _ => None,
- })
- .last();
+ let outer_paren_pat = pat.syntax().ancestors().skip(1).map_while(ast::ParenPat::cast).last();
let range = outer_paren_pat.as_ref().map_or_else(
|| match pat {
// for ident patterns that @ bind a name, render the un-ref patterns in front of the inner pattern
@@ -42,7 +36,18 @@ pub(super) fn hints(
},
|it| it.syntax().text_range(),
);
+ let mut hint = InlayHint {
+ range,
+ kind: InlayKind::BindingMode,
+ label: InlayHintLabel::default(),
+ text_edit: None,
+ position: InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: false,
+ resolve_parent: Some(pat.syntax().text_range()),
+ };
let pattern_adjustments = sema.pattern_adjustments(pat);
+ let mut was_mut_last = false;
pattern_adjustments.iter().for_each(|ty| {
let reference = ty.is_reference();
let mut_reference = ty.is_mutable_reference();
@@ -51,41 +56,36 @@ pub(super) fn hints(
(true, false) => "&",
_ => return,
};
- acc.push(InlayHint {
- range,
- kind: InlayKind::BindingMode,
- label: r.into(),
- text_edit: None,
- position: InlayHintPosition::Before,
- pad_left: false,
- pad_right: mut_reference,
- resolve_parent: Some(pat.syntax().text_range()),
- });
+ if mem::replace(&mut was_mut_last, mut_reference) {
+ hint.label.append_str(" ");
+ }
+ hint.label.append_str(r);
});
+ hint.pad_right = was_mut_last;
+ let acc_base = acc.len();
match pat {
ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
let bm = sema.binding_mode_of_pat(pat)?;
let bm = match bm {
- hir::BindingMode::Move => return None,
- hir::BindingMode::Ref(Mutability::Mut) => "ref mut",
- hir::BindingMode::Ref(Mutability::Shared) => "ref",
+ hir::BindingMode::Move => None,
+ hir::BindingMode::Ref(Mutability::Mut) => Some("ref mut"),
+ hir::BindingMode::Ref(Mutability::Shared) => Some("ref"),
};
- acc.push(InlayHint {
- range: pat.syntax().text_range(),
- kind: InlayKind::BindingMode,
- label: bm.into(),
- text_edit: None,
- position: InlayHintPosition::Before,
- pad_left: false,
- pad_right: true,
- resolve_parent: Some(pat.syntax().text_range()),
- });
+ if let Some(bm) = bm {
+ acc.push(InlayHint {
+ range: pat.syntax().text_range(),
+ kind: InlayKind::BindingMode,
+ label: bm.into(),
+ text_edit: None,
+ position: InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: true,
+ resolve_parent: Some(pat.syntax().text_range()),
+ });
+ }
}
ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => {
- acc.push(InlayHint::opening_paren_before(
- InlayKind::BindingMode,
- pat.syntax().text_range(),
- ));
+ hint.label.append_str("(");
acc.push(InlayHint::closing_paren_after(
InlayKind::BindingMode,
pat.syntax().text_range(),
@@ -93,6 +93,24 @@ pub(super) fn hints(
}
_ => (),
}
+ if !hint.label.parts.is_empty() {
+ acc.push(hint);
+ }
+
+ if let hints @ [_, ..] = &mut acc[acc_base..] {
+ let mut edit = TextEditBuilder::default();
+ for h in &mut *hints {
+ edit.insert(
+ match h.position {
+ InlayHintPosition::Before => h.range.start(),
+ InlayHintPosition::After => h.range.end(),
+ },
+ h.label.parts.iter().map(|p| &*p.text).collect(),
+ );
+ }
+ let edit = edit.finish();
+ hints.iter_mut().for_each(|h| h.text_edit = Some(edit.clone()));
+ }
Some(())
}
@@ -117,6 +135,13 @@ fn __(
(x,): &mut (u32,)
//^^^^&mut
//^ ref mut
+ (x,): &mut &mut (u32,)
+ //^^^^&mut &mut
+ //^ ref mut
+ (x,): &&(u32,)
+ //^^^^&&
+ //^ ref
+
) {
let (x,) = (0,);
let (x,) = &(0,);
@@ -136,11 +161,10 @@ fn __(
}
match &(0,) {
(x,) | (x,) => (),
- //^^^^^^^^^^^&
+ //^^^^^^^^^^^)
+ //^^^^^^^^^^^&(
//^ ref
//^ ref
- //^^^^^^^^^^^(
- //^^^^^^^^^^^)
((x,) | (x,)) => (),
//^^^^^^^^^^^^^&
//^ ref
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index 58d8f97a8c..028ed1650f 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -77,7 +77,7 @@ pub(super) fn hints(
#[cfg(test)]
mod tests {
use expect_test::{expect, Expect};
- use text_edit::{TextRange, TextSize};
+ use ide_db::text_edit::{TextRange, TextSize};
use crate::{
fixture,
diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs
index f399bd01d0..906f2acf0c 100644
--- a/crates/ide/src/inlay_hints/closure_captures.rs
+++ b/crates/ide/src/inlay_hints/closure_captures.rs
@@ -2,12 +2,14 @@
//!
//! Tests live in [`bind_pat`][super::bind_pat] module.
use ide_db::famous_defs::FamousDefs;
+use ide_db::text_edit::{TextRange, TextSize};
use span::EditionedFileId;
use stdx::{never, TupleExt};
use syntax::ast::{self, AstNode};
-use text_edit::{TextRange, TextSize};
-use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
+use crate::{
+ InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind,
+};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -27,34 +29,27 @@ pub(super) fn hints(
return None;
}
- let move_kw_range = match closure.move_token() {
- Some(t) => t.text_range(),
+ let (range, label) = match closure.move_token() {
+ Some(t) => (t.text_range(), InlayHintLabel::default()),
None => {
- let range = closure.syntax().first_token()?.prev_token()?.text_range();
- let range = TextRange::new(range.end() - TextSize::from(1), range.end());
- acc.push(InlayHint {
- range,
- kind: InlayKind::ClosureCapture,
- label: InlayHintLabel::from("move"),
- text_edit: None,
- position: InlayHintPosition::After,
- pad_left: false,
- pad_right: false,
- resolve_parent: Some(closure.syntax().text_range()),
- });
- range
+ let prev_token = closure.syntax().first_token()?.prev_token()?.text_range();
+ (
+ TextRange::new(prev_token.end() - TextSize::from(1), prev_token.end()),
+ InlayHintLabel::from("move"),
+ )
}
};
- acc.push(InlayHint {
- range: move_kw_range,
+ let mut hint = InlayHint {
+ range,
kind: InlayKind::ClosureCapture,
- label: InlayHintLabel::from("("),
+ label,
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
- pad_right: false,
- resolve_parent: None,
- });
+ pad_right: true,
+ resolve_parent: Some(closure.syntax().text_range()),
+ };
+ hint.label.append_str("(");
let last = captures.len() - 1;
for (idx, capture) in captures.into_iter().enumerate() {
let local = capture.local();
@@ -76,48 +71,20 @@ pub(super) fn hints(
if never!(label.is_empty()) {
continue;
}
- let label = InlayHintLabel::simple(
- label,
- None,
- source.name().and_then(|name| {
+ hint.label.append_part(InlayHintLabelPart {
+ text: label,
+ linked_location: source.name().and_then(|name| {
name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into)
}),
- );
- acc.push(InlayHint {
- range: move_kw_range,
- kind: InlayKind::ClosureCapture,
- label,
- text_edit: None,
- position: InlayHintPosition::After,
- pad_left: false,
- pad_right: false,
- resolve_parent: Some(closure.syntax().text_range()),
+ tooltip: None,
});
if idx != last {
- acc.push(InlayHint {
- range: move_kw_range,
- kind: InlayKind::ClosureCapture,
- label: InlayHintLabel::from(", "),
- text_edit: None,
- position: InlayHintPosition::After,
- pad_left: false,
- pad_right: false,
- resolve_parent: None,
- });
+ hint.label.append_str(", ");
}
}
- acc.push(InlayHint {
- range: move_kw_range,
- kind: InlayKind::ClosureCapture,
- label: InlayHintLabel::from(")"),
- text_edit: None,
- position: InlayHintPosition::After,
- pad_left: false,
- pad_right: true,
- resolve_parent: None,
- });
-
+ hint.label.append_str(")");
+ acc.push(hint);
Some(())
}
@@ -147,51 +114,25 @@ fn main() {
let mut baz = NonCopy;
let qux = &mut NonCopy;
|| {
-// ^ move
-// ^ (
-// ^ &foo
-// ^ , $
-// ^ bar
-// ^ , $
-// ^ baz
-// ^ , $
-// ^ qux
-// ^ )
+// ^ move(&foo, bar, baz, qux)
foo;
bar;
baz;
qux;
};
|| {
-// ^ move
-// ^ (
-// ^ &foo
-// ^ , $
-// ^ &bar
-// ^ , $
-// ^ &baz
-// ^ , $
-// ^ &qux
-// ^ )
+// ^ move(&foo, &bar, &baz, &qux)
&foo;
&bar;
&baz;
&qux;
};
|| {
-// ^ move
-// ^ (
-// ^ &mut baz
-// ^ )
+// ^ move(&mut baz)
&mut baz;
};
|| {
-// ^ move
-// ^ (
-// ^ &mut baz
-// ^ , $
-// ^ &mut *qux
-// ^ )
+// ^ move(&mut baz, &mut *qux)
baz = NonCopy;
*qux = NonCopy;
};
@@ -209,9 +150,7 @@ fn main() {
fn main() {
let foo = u32;
move || {
-// ^^^^ (
-// ^^^^ foo
-// ^^^^ )
+// ^^^^ (foo)
foo;
};
}
diff --git a/crates/ide/src/inlay_hints/discriminant.rs b/crates/ide/src/inlay_hints/discriminant.rs
index 35b6287832..cd77c3ec3e 100644
--- a/crates/ide/src/inlay_hints/discriminant.rs
+++ b/crates/ide/src/inlay_hints/discriminant.rs
@@ -5,6 +5,7 @@
//! }
//! ```
use hir::Semantics;
+use ide_db::text_edit::TextEdit;
use ide_db::{famous_defs::FamousDefs, RootDatabase};
use span::EditionedFileId;
use syntax::ast::{self, AstNode, HasName};
@@ -65,11 +66,11 @@ fn variant_hints(
let eq_ = if eq_token.is_none() { " =" } else { "" };
let label = InlayHintLabel::simple(
match d {
- Ok(x) => {
- if x >= 10 {
- format!("{eq_} {x} ({x:#X})")
+ Ok(val) => {
+ if val >= 10 {
+ format!("{eq_} {val} ({val:#X})")
} else {
- format!("{eq_} {x}")
+ format!("{eq_} {val}")
}
}
Err(_) => format!("{eq_} ?"),
@@ -87,7 +88,7 @@ fn variant_hints(
},
kind: InlayKind::Discriminant,
label,
- text_edit: None,
+ text_edit: d.ok().map(|val| TextEdit::insert(range.start(), format!("{eq_} {val}"))),
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
diff --git a/crates/ide/src/inlay_hints/implicit_static.rs b/crates/ide/src/inlay_hints/implicit_static.rs
index 8d422478cb..1560df37d0 100644
--- a/crates/ide/src/inlay_hints/implicit_static.rs
+++ b/crates/ide/src/inlay_hints/implicit_static.rs
@@ -4,6 +4,7 @@
//! ```
use either::Either;
use ide_db::famous_defs::FamousDefs;
+use ide_db::text_edit::TextEdit;
use span::EditionedFileId;
use syntax::{
ast::{self, AstNode},
@@ -38,7 +39,7 @@ pub(super) fn hints(
range: t.text_range(),
kind: InlayKind::Lifetime,
label: "'static".into(),
- text_edit: None,
+ text_edit: Some(TextEdit::insert(t.text_range().start(), "'static ".into())),
position: InlayHintPosition::After,
pad_left: false,
pad_right: true,
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 9d8ba90b2f..5192f91a4a 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -8,7 +8,7 @@ use syntax::{
SyntaxToken, TextRange, TextSize, T,
};
-use text_edit::{TextEdit, TextEditBuilder};
+use ide_db::text_edit::{TextEdit, TextEditBuilder};
pub struct JoinLinesConfig {
pub join_else_if: bool,
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index d7163d57d2..d053c4b3c9 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -122,6 +122,7 @@ pub use ide_completion::{
CallableSnippets, CompletionConfig, CompletionFieldsToResolve, CompletionItem,
CompletionItemKind, CompletionRelevance, Snippet, SnippetScope,
};
+pub use ide_db::text_edit::{Indel, TextEdit};
pub use ide_db::{
base_db::{Cancelled, CrateGraph, CrateId, FileChange, SourceRoot, SourceRootId},
documentation::Documentation,
@@ -139,7 +140,6 @@ pub use ide_diagnostics::{
pub use ide_ssr::SsrError;
pub use span::Edition;
pub use syntax::{TextRange, TextSize};
-pub use text_edit::{Indel, TextEdit};
pub type Cancellable<T> = Result<T, Cancelled>;
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index ea6cc9d6de..a232df2b82 100644
--- a/crates/ide/src/move_item.rs
+++ b/crates/ide/src/move_item.rs
@@ -1,10 +1,11 @@
use std::{iter::once, mem};
use hir::Semantics;
+use ide_db::syntax_helpers::tree_diff::diff;
+use ide_db::text_edit::{TextEdit, TextEditBuilder};
use ide_db::{helpers::pick_best_token, FileRange, RootDatabase};
use itertools::Itertools;
-use syntax::{algo, ast, match_ast, AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange};
-use text_edit::{TextEdit, TextEditBuilder};
+use syntax::{ast, match_ast, AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange};
#[derive(Copy, Clone, Debug)]
pub enum Direction {
@@ -166,7 +167,7 @@ fn replace_nodes<'a>(
let mut edit = TextEditBuilder::default();
- algo::diff(first, second).into_text_edit(&mut edit);
+ diff(first, second).into_text_edit(&mut edit);
edit.replace(second.text_range(), first_with_cursor);
edit.finish()
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index e7cb8a253f..339315db57 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -1701,14 +1701,14 @@ fn f() {
}
"#,
expect![[r#"
- func Function FileId(0) 137..146 140..144
+ func Function FileId(0) 137..146 140..144 module
- FileId(0) 161..165
+ FileId(0) 181..185
- func Function FileId(0) 137..146 140..144 module
+ func Function FileId(0) 137..146 140..144
- FileId(0) 181..185
+ FileId(0) 161..165
"#]],
)
}
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index f17c1fa5c6..665fc954d2 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -15,7 +15,7 @@ use itertools::Itertools;
use stdx::{always, never};
use syntax::{ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize};
-use text_edit::TextEdit;
+use ide_db::text_edit::TextEdit;
use crate::{FilePosition, RangeInfo, SourceChange};
@@ -449,9 +449,9 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Opt
mod tests {
use expect_test::{expect, Expect};
use ide_db::source_change::SourceChange;
+ use ide_db::text_edit::TextEdit;
use stdx::trim_indent;
use test_utils::assert_eq_text;
- use text_edit::TextEdit;
use crate::fixture;
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 961b2a4c93..0747d1b404 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -16,7 +16,7 @@ mod tests;
use std::ops::ControlFlow;
use hir::{InRealFile, Name, Semantics};
-use ide_db::{FxHashMap, RootDatabase, SymbolKind};
+use ide_db::{FxHashMap, Ranker, RootDatabase, SymbolKind};
use span::EditionedFileId;
use syntax::{
ast::{self, IsString},
@@ -397,13 +397,12 @@ fn traverse(
Some(AttrOrDerive::Derive(_)) => inside_attribute,
None => false,
};
+
let descended_element = if in_macro {
// Attempt to descend tokens into macro-calls.
let res = match element {
NodeOrToken::Token(token) if token.kind() != COMMENT => {
- let kind = token.kind();
- let text = token.text();
- let ident_kind = kind.is_any_identifier();
+ let ranker = Ranker::from_token(&token);
let mut t = None;
let mut r = 0;
@@ -412,21 +411,9 @@ fn traverse(
|tok, _ctx| {
// FIXME: Consider checking ctx transparency for being opaque?
let tok = tok.value;
- let tok_kind = tok.kind();
-
- let exact_same_kind = tok_kind == kind;
- let both_idents =
- exact_same_kind || (tok_kind.is_any_identifier() && ident_kind);
- let same_text = tok.text() == text;
- // anything that mapped into a token tree has likely no semantic information
- let no_tt_parent =
- tok.parent().map_or(false, |it| it.kind() != TOKEN_TREE);
- let my_rank = (both_idents as usize)
- | ((exact_same_kind as usize) << 1)
- | ((same_text as usize) << 2)
- | ((no_tt_parent as usize) << 3);
-
- if my_rank > 0b1110 {
+ let my_rank = ranker.rank_token(&tok);
+
+ if my_rank >= Ranker::MAX_RANK {
// a rank of 0b1110 means that we have found a maximally interesting
// token so stop early.
t = Some(tok);
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html b/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
index d07ba74db2..361dcd1bc3 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
@@ -50,4 +50,4 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="module attribute crate_root library">proc_macros</span><span class="operator attribute">::</span><span class="attribute attribute library">issue_18089</span><span class="attribute_bracket attribute">]</span>
-<span class="keyword">fn</span> <span class="function declaration">template</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file
+<span class="keyword">fn</span> <span class="macro declaration">template</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index a09e1e85ae..9bb5de9f2e 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -23,7 +23,7 @@ use syntax::{
AstNode, Parse, SourceFile, SyntaxKind, TextRange, TextSize, T,
};
-use text_edit::{Indel, TextEdit};
+use ide_db::text_edit::TextEdit;
use crate::SourceChange;
@@ -126,7 +126,7 @@ fn on_opening_bracket_typed(
return None;
}
// FIXME: Edition
- let file = file.reparse(&Indel::delete(range), span::Edition::CURRENT_FIXME);
+ let file = file.reparse(range, "", span::Edition::CURRENT_FIXME);
if let Some(edit) = bracket_expr(&file.tree(), offset, opening_bracket, closing_bracket) {
return Some(edit);
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 6e56bd6185..773e352220 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -12,7 +12,7 @@ use syntax::{
SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset,
};
-use text_edit::TextEdit;
+use ide_db::text_edit::TextEdit;
// Feature: On Enter
//
diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs
index ecfabca092..92311238c2 100644
--- a/crates/parser/src/grammar/generic_params.rs
+++ b/crates/parser/src/grammar/generic_params.rs
@@ -144,10 +144,31 @@ fn type_bound(p: &mut Parser<'_>) -> bool {
LIFETIME_IDENT => lifetime(p),
T![for] => types::for_type(p, false),
// test precise_capturing
- // fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T> {}
+ // fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T, Self> {}
T![use] if p.nth_at(1, T![<]) => {
p.bump_any();
- generic_param_list(p)
+ let m = p.start();
+ delimited(
+ p,
+ T![<],
+ T![>],
+ T![,],
+ || "expected identifier or lifetime".into(),
+ TokenSet::new(&[T![Self], IDENT, LIFETIME_IDENT]),
+ |p| {
+ if p.at(T![Self]) {
+ let m = p.start();
+ p.bump(T![Self]);
+ m.complete(p, NAME_REF);
+ } else if p.at(LIFETIME_IDENT) {
+ lifetime(p);
+ } else {
+ name_ref(p);
+ }
+ true
+ },
+ );
+ m.complete(p, USE_BOUND_GENERIC_ARGS);
}
T![?] if p.nth_at(1, T![for]) => {
// test question_for_type_trait_bound
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index 882c243b0c..ed01fca2ac 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -21,7 +21,8 @@ const RANGE_PAT_END_FIRST: TokenSet =
expressions::LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[T![-], T![const]]));
pub(crate) fn pattern(p: &mut Parser<'_>) {
- pattern_r(p, PAT_RECOVERY_SET);
+ let m = p.start();
+ pattern_r(p, m, false, PAT_RECOVERY_SET);
}
/// Parses a pattern list separated by pipes `|`.
@@ -36,13 +37,11 @@ pub(crate) fn pattern_single(p: &mut Parser<'_>) {
/// Parses a pattern list separated by pipes `|`
/// using the given `recovery_set`.
pub(super) fn pattern_top_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
- p.eat(T![|]);
- pattern_r(p, recovery_set);
+ let m = p.start();
+ let has_leading_pipe = p.eat(T![|]);
+ pattern_r(p, m, has_leading_pipe, recovery_set);
}
-/// Parses a pattern list separated by pipes `|`, with no leading `|`,using the
-/// given `recovery_set`.
-
// test or_pattern
// fn main() {
// match () {
@@ -52,11 +51,12 @@ pub(super) fn pattern_top_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
// [_ | _,] => (),
// }
// }
-fn pattern_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
- let m = p.start();
+/// Parses a pattern list separated by pipes `|`, with no leading `|`,using the
+/// given `recovery_set`.
+fn pattern_r(p: &mut Parser<'_>, m: Marker, has_leading_pipe: bool, recovery_set: TokenSet) {
pattern_single_r(p, recovery_set);
- if !p.at(T![|]) {
+ if !p.at(T![|]) && !has_leading_pipe {
m.abandon(p);
return;
}
diff --git a/crates/parser/src/lexed_str.rs b/crates/parser/src/lexed_str.rs
index 5322463a71..3c0eb1b42a 100644
--- a/crates/parser/src/lexed_str.rs
+++ b/crates/parser/src/lexed_str.rs
@@ -39,7 +39,9 @@ impl<'a> LexedStr<'a> {
conv.offset = shebang_len;
};
- for token in rustc_lexer::tokenize(&text[conv.offset..]) {
+ // Re-create the tokenizer from scratch every token because `GuardedStrPrefix` is one token in the lexer
+ // but we want to split it to two in edition <2024.
+ while let Some(token) = rustc_lexer::tokenize(&text[conv.offset..]).next() {
let token_text = &text[conv.offset..][..token.len as usize];
conv.extend_token(&token.kind, token_text);
@@ -158,7 +160,7 @@ impl<'a> Converter<'a> {
}
}
- fn extend_token(&mut self, kind: &rustc_lexer::TokenKind, token_text: &str) {
+ fn extend_token(&mut self, kind: &rustc_lexer::TokenKind, mut token_text: &str) {
// A note on an intended tradeoff:
// We drop some useful information here (see patterns with double dots `..`)
// Storing that info in `SyntaxKind` is not possible due to its layout requirements of
@@ -189,10 +191,15 @@ impl<'a> Converter<'a> {
rustc_lexer::TokenKind::RawIdent => IDENT,
rustc_lexer::TokenKind::GuardedStrPrefix if self.edition.at_least_2024() => {
+ // FIXME: rustc does something better for recovery.
err = "Invalid string literal (reserved syntax)";
ERROR
}
- rustc_lexer::TokenKind::GuardedStrPrefix => POUND,
+ rustc_lexer::TokenKind::GuardedStrPrefix => {
+ // The token is `#"` or `##`, split it into two.
+ token_text = &token_text[1..];
+ POUND
+ }
rustc_lexer::TokenKind::Literal { kind, .. } => {
self.extend_literal(token_text.len(), kind);
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 8da338c0a2..21730244a3 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -312,6 +312,8 @@ pub enum SyntaxKind {
UNDERSCORE_EXPR,
UNION,
USE,
+ USE_BOUND_GENERIC_ARG,
+ USE_BOUND_GENERIC_ARGS,
USE_TREE,
USE_TREE_LIST,
VARIANT,
diff --git a/crates/parser/src/tests.rs b/crates/parser/src/tests.rs
index e7bccb6685..4b19ddc752 100644
--- a/crates/parser/src/tests.rs
+++ b/crates/parser/src/tests.rs
@@ -15,11 +15,20 @@ use crate::{Edition, LexedStr, TopEntryPoint};
#[path = "../test_data/generated/runner.rs"]
mod runner;
+fn infer_edition(file_path: &Path) -> Edition {
+ let file_content = std::fs::read_to_string(file_path).unwrap();
+ if let Some(edition) = file_content.strip_prefix("//@ edition: ") {
+ edition[..4].parse().expect("invalid edition directive")
+ } else {
+ Edition::CURRENT
+ }
+}
+
#[test]
fn lex_ok() {
for case in TestCase::list("lexer/ok") {
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
- let actual = lex(&case.text);
+ let actual = lex(&case.text, infer_edition(&case.rs));
expect_file![case.rast].assert_eq(&actual)
}
}
@@ -28,13 +37,13 @@ fn lex_ok() {
fn lex_err() {
for case in TestCase::list("lexer/err") {
let _guard = stdx::panic_context::enter(format!("{:?}", case.rs));
- let actual = lex(&case.text);
+ let actual = lex(&case.text, infer_edition(&case.rs));
expect_file![case.rast].assert_eq(&actual)
}
}
-fn lex(text: &str) -> String {
- let lexed = LexedStr::new(Edition::CURRENT, text);
+fn lex(text: &str, edition: Edition) -> String {
+ let lexed = LexedStr::new(edition, text);
let mut res = String::new();
for i in 0..lexed.len() {
diff --git a/crates/parser/src/tests/top_entries.rs b/crates/parser/src/tests/top_entries.rs
index c56bf0b644..7076e03ba4 100644
--- a/crates/parser/src/tests/top_entries.rs
+++ b/crates/parser/src/tests/top_entries.rs
@@ -195,6 +195,38 @@ fn macro_pattern() {
error 0: expected pattern
"#]],
);
+
+ check(
+ TopEntryPoint::Pattern,
+ "| 42 | 43",
+ expect![[r#"
+ OR_PAT
+ PIPE "|"
+ WHITESPACE " "
+ LITERAL_PAT
+ LITERAL
+ INT_NUMBER "42"
+ WHITESPACE " "
+ PIPE "|"
+ WHITESPACE " "
+ LITERAL_PAT
+ LITERAL
+ INT_NUMBER "43"
+ "#]],
+ );
+
+ check(
+ TopEntryPoint::Pattern,
+ "| 42",
+ expect![[r#"
+ OR_PAT
+ PIPE "|"
+ WHITESPACE " "
+ LITERAL_PAT
+ LITERAL
+ INT_NUMBER "42"
+ "#]],
+ );
}
#[test]
diff --git a/crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rast b/crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rast
new file mode 100644
index 0000000000..1bdd672044
--- /dev/null
+++ b/crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rast
@@ -0,0 +1,4 @@
+COMMENT "//@ edition: 2021"
+WHITESPACE "\n\n"
+POUND "#"
+STRING "\"foo\""
diff --git a/crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rs b/crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rs
new file mode 100644
index 0000000000..f00f949f0d
--- /dev/null
+++ b/crates/parser/test_data/lexer/ok/guarded_str_prefix_edition_2021.rs
@@ -0,0 +1,3 @@
+//@ edition: 2021
+
+#"foo" \ No newline at end of file
diff --git a/crates/parser/test_data/parser/inline/ok/match_arm.rast b/crates/parser/test_data/parser/inline/ok/match_arm.rast
index 8189cf0a8e..9221028162 100644
--- a/crates/parser/test_data/parser/inline/ok/match_arm.rast
+++ b/crates/parser/test_data/parser/inline/ok/match_arm.rast
@@ -102,9 +102,9 @@ SOURCE_FILE
COMMA ","
WHITESPACE "\n "
MATCH_ARM
- PIPE "|"
- WHITESPACE " "
OR_PAT
+ PIPE "|"
+ WHITESPACE " "
IDENT_PAT
NAME
IDENT "X"
@@ -132,11 +132,12 @@ SOURCE_FILE
COMMA ","
WHITESPACE "\n "
MATCH_ARM
- PIPE "|"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "X"
+ OR_PAT
+ PIPE "|"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "X"
WHITESPACE " "
FAT_ARROW "=>"
WHITESPACE " "
diff --git a/crates/parser/test_data/parser/inline/ok/precise_capturing.rast b/crates/parser/test_data/parser/inline/ok/precise_capturing.rast
index cf52f1e479..f9c0a245af 100644
--- a/crates/parser/test_data/parser/inline/ok/precise_capturing.rast
+++ b/crates/parser/test_data/parser/inline/ok/precise_capturing.rast
@@ -50,16 +50,18 @@ SOURCE_FILE
WHITESPACE " "
TYPE_BOUND
USE_KW "use"
- GENERIC_PARAM_LIST
+ USE_BOUND_GENERIC_ARGS
L_ANGLE "<"
- LIFETIME_PARAM
- LIFETIME
- LIFETIME_IDENT "'b"
+ LIFETIME
+ LIFETIME_IDENT "'b"
COMMA ","
WHITESPACE " "
- TYPE_PARAM
- NAME
- IDENT "T"
+ NAME_REF
+ IDENT "T"
+ COMMA ","
+ WHITESPACE " "
+ NAME_REF
+ SELF_TYPE_KW "Self"
R_ANGLE ">"
WHITESPACE " "
BLOCK_EXPR
diff --git a/crates/parser/test_data/parser/inline/ok/precise_capturing.rs b/crates/parser/test_data/parser/inline/ok/precise_capturing.rs
index ec208d5062..9ac2305f3a 100644
--- a/crates/parser/test_data/parser/inline/ok/precise_capturing.rs
+++ b/crates/parser/test_data/parser/inline/ok/precise_capturing.rs
@@ -1 +1 @@
-fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T> {}
+fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T, Self> {}
diff --git a/crates/parser/test_data/parser/inline/ok/slice_pat.rast b/crates/parser/test_data/parser/inline/ok/slice_pat.rast
index dff72ba886..06c30bba59 100644
--- a/crates/parser/test_data/parser/inline/ok/slice_pat.rast
+++ b/crates/parser/test_data/parser/inline/ok/slice_pat.rast
@@ -43,11 +43,12 @@ SOURCE_FILE
WHITESPACE " "
SLICE_PAT
L_BRACK "["
- PIPE "|"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "a"
+ OR_PAT
+ PIPE "|"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "a"
COMMA ","
WHITESPACE " "
REST_PAT
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_pat.rast b/crates/parser/test_data/parser/inline/ok/tuple_pat.rast
index 1a01e0f693..c7cd11f774 100644
--- a/crates/parser/test_data/parser/inline/ok/tuple_pat.rast
+++ b/crates/parser/test_data/parser/inline/ok/tuple_pat.rast
@@ -91,9 +91,9 @@ SOURCE_FILE
WHITESPACE " "
TUPLE_PAT
L_PAREN "("
- PIPE "|"
- WHITESPACE " "
OR_PAT
+ PIPE "|"
+ WHITESPACE " "
IDENT_PAT
NAME
IDENT "a"
@@ -105,11 +105,12 @@ SOURCE_FILE
IDENT "a"
COMMA ","
WHITESPACE " "
- PIPE "|"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "b"
+ OR_PAT
+ PIPE "|"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "b"
R_PAREN ")"
WHITESPACE " "
EQ "="
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast b/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast
index 55baf2fdcb..96353f4697 100644
--- a/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast
+++ b/crates/parser/test_data/parser/inline/ok/tuple_pat_fields.rast
@@ -110,11 +110,12 @@ SOURCE_FILE
NAME_REF
IDENT "S"
L_PAREN "("
- PIPE "|"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "a"
+ OR_PAT
+ PIPE "|"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "a"
R_PAREN ")"
WHITESPACE " "
EQ "="
diff --git a/crates/proc-macro-api/src/msg/flat.rs b/crates/proc-macro-api/src/msg/flat.rs
index 88256e98b5..5443a9bd67 100644
--- a/crates/proc-macro-api/src/msg/flat.rs
+++ b/crates/proc-macro-api/src/msg/flat.rs
@@ -401,7 +401,7 @@ struct Writer<'a, 'span, S: InternableSpan> {
text: Vec<String>,
}
-impl<'a, 'span, S: InternableSpan> Writer<'a, 'span, S> {
+impl<'a, S: InternableSpan> Writer<'a, '_, S> {
fn write(&mut self, root: &'a tt::Subtree<S>) {
self.enqueue(root);
while let Some((idx, subtree)) = self.work.pop_front() {
@@ -524,7 +524,7 @@ struct Reader<'span, S: InternableSpan> {
span_data_table: &'span S::Table,
}
-impl<'span, S: InternableSpan> Reader<'span, S> {
+impl<S: InternableSpan> Reader<'_, S> {
pub(crate) fn read(self) -> tt::Subtree<S> {
let mut res: Vec<Option<tt::Subtree<S>>> = vec![None; self.subtree.len()];
let read_span = |id| S::span_for_token_id(self.span_data_table, id);
diff --git a/crates/project-model/src/rustc_cfg.rs b/crates/project-model/src/rustc_cfg.rs
index aa73ff8910..bc1f0e6fbf 100644
--- a/crates/project-model/src/rustc_cfg.rs
+++ b/crates/project-model/src/rustc_cfg.rs
@@ -24,14 +24,15 @@ pub(crate) fn get(
config: RustcCfgConfig<'_>,
) -> Vec<CfgAtom> {
let _p = tracing::info_span!("rustc_cfg::get").entered();
- let mut res: Vec<_> = Vec::with_capacity(6 * 2 + 1);
+ let mut res: Vec<_> = Vec::with_capacity(7 * 2 + 1);
// Some nightly-only cfgs, which are required for stdlib
res.push(CfgAtom::Flag(Symbol::intern("target_thread_local")));
- for ty in ["8", "16", "32", "64", "cas", "ptr"] {
- for key in ["target_has_atomic", "target_has_atomic_load_store"] {
+ for key in ["target_has_atomic", "target_has_atomic_load_store"] {
+ for ty in ["8", "16", "32", "64", "cas", "ptr"] {
res.push(CfgAtom::KeyValue { key: Symbol::intern(key), value: Symbol::intern(ty) });
}
+ res.push(CfgAtom::Flag(Symbol::intern(key)));
}
let rustc_cfgs = get_rust_cfgs(target, extra_env, config);
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index d1ee579c0d..d53639e242 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -1062,7 +1062,7 @@ fn cargo_to_crate_graph(
proc_macros,
cargo,
pkg_data,
- build_data,
+ build_data.zip(Some(build_scripts.error().is_some())),
cfg_options.clone(),
file_id,
name,
@@ -1285,7 +1285,7 @@ fn handle_rustc_crates(
proc_macros,
rustc_workspace,
&rustc_workspace[pkg],
- build_scripts.get_output(pkg),
+ build_scripts.get_output(pkg).zip(Some(build_scripts.error().is_some())),
cfg_options.clone(),
file_id,
&rustc_workspace[tgt].name,
@@ -1345,7 +1345,7 @@ fn add_target_crate_root(
proc_macros: &mut ProcMacroPaths,
cargo: &CargoWorkspace,
pkg: &PackageData,
- build_data: Option<&BuildScriptOutput>,
+ build_data: Option<(&BuildScriptOutput, bool)>,
cfg_options: CfgOptions,
file_id: FileId,
cargo_name: &str,
@@ -1368,7 +1368,7 @@ fn add_target_crate_root(
for feature in pkg.active_features.iter() {
opts.insert_key_value(sym::feature.clone(), Symbol::intern(feature));
}
- if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
+ if let Some(cfgs) = build_data.map(|(it, _)| &it.cfgs) {
opts.extend(cfgs.iter().cloned());
}
opts
@@ -1379,7 +1379,7 @@ fn add_target_crate_root(
inject_cargo_env(&mut env);
inject_rustc_tool_env(&mut env, cargo, cargo_name, kind);
- if let Some(envs) = build_data.map(|it| &it.envs) {
+ if let Some(envs) = build_data.map(|(it, _)| &it.envs) {
for (k, v) in envs {
env.set(k, v.clone());
}
@@ -1396,11 +1396,14 @@ fn add_target_crate_root(
origin,
);
if let TargetKind::Lib { is_proc_macro: true } = kind {
- let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
- Some(it) => match it {
- Some(path) => Ok((cargo_name.to_owned(), path.clone())),
- None => Err("proc-macro crate build data is missing dylib path".to_owned()),
- },
+ let proc_macro = match build_data {
+ Some((BuildScriptOutput { proc_macro_dylib_path, .. }, has_errors)) => {
+ match proc_macro_dylib_path {
+ Some(path) => Ok((cargo_name.to_owned(), path.clone())),
+ None if has_errors => Err("failed to build proc-macro".to_owned()),
+ None => Err("proc-macro crate build data is missing dylib path".to_owned()),
+ }
+ }
None => Err("proc-macro crate is missing its build data".to_owned()),
};
proc_macros.insert(crate_id, proc_macro);
diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
index 3401d7f7e4..880e90c52a 100644
--- a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
+++ b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
@@ -19,6 +19,7 @@
[
"rust_analyzer",
"test",
+ "true",
],
),
potential_cfg_options: None,
@@ -81,6 +82,7 @@
[
"rust_analyzer",
"test",
+ "true",
],
),
potential_cfg_options: None,
@@ -151,6 +153,7 @@
[
"rust_analyzer",
"test",
+ "true",
],
),
potential_cfg_options: None,
@@ -221,6 +224,7 @@
[
"rust_analyzer",
"test",
+ "true",
],
),
potential_cfg_options: None,
@@ -291,6 +295,7 @@
[
"feature=default",
"feature=std",
+ "true",
],
),
potential_cfg_options: Some(
@@ -303,6 +308,7 @@
"feature=rustc-dep-of-std",
"feature=std",
"feature=use_std",
+ "true",
],
),
),
diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
index 3401d7f7e4..880e90c52a 100644
--- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
+++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
@@ -19,6 +19,7 @@
[
"rust_analyzer",
"test",
+ "true",
],
),
potential_cfg_options: None,
@@ -81,6 +82,7 @@
[
"rust_analyzer",
"test",
+ "true",
],
),
potential_cfg_options: None,
@@ -151,6 +153,7 @@
[
"rust_analyzer",
"test",
+ "true",
],
),
potential_cfg_options: None,
@@ -221,6 +224,7 @@
[
"rust_analyzer",
"test",
+ "true",
],
),
potential_cfg_options: None,
@@ -291,6 +295,7 @@
[
"feature=default",
"feature=std",
+ "true",
],
),
potential_cfg_options: Some(
@@ -303,6 +308,7 @@
"feature=rustc-dep-of-std",
"feature=std",
"feature=use_std",
+ "true",
],
),
),
diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
index 491568d4b7..7746acd225 100644
--- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
+++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
@@ -18,6 +18,7 @@
cfg_options: CfgOptions(
[
"rust_analyzer",
+ "true",
],
),
potential_cfg_options: None,
@@ -79,6 +80,7 @@
cfg_options: CfgOptions(
[
"rust_analyzer",
+ "true",
],
),
potential_cfg_options: None,
@@ -148,6 +150,7 @@
cfg_options: CfgOptions(
[
"rust_analyzer",
+ "true",
],
),
potential_cfg_options: None,
@@ -217,6 +220,7 @@
cfg_options: CfgOptions(
[
"rust_analyzer",
+ "true",
],
),
potential_cfg_options: None,
@@ -287,6 +291,7 @@
[
"feature=default",
"feature=std",
+ "true",
],
),
potential_cfg_options: Some(
@@ -299,6 +304,7 @@
"feature=rustc-dep-of-std",
"feature=std",
"feature=use_std",
+ "true",
],
),
),
diff --git a/crates/project-model/test_data/output/rust_project_cfg_groups.txt b/crates/project-model/test_data/output/rust_project_cfg_groups.txt
index 8261e5a2d9..90f41a9c2f 100644
--- a/crates/project-model/test_data/output/rust_project_cfg_groups.txt
+++ b/crates/project-model/test_data/output/rust_project_cfg_groups.txt
@@ -17,6 +17,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -56,6 +57,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -86,6 +88,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -116,6 +119,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -146,6 +150,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -193,6 +198,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -223,6 +229,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -318,6 +325,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -348,6 +356,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -378,6 +387,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -410,6 +420,7 @@
"group1_other_cfg=other_config",
"group2_cfg=yet_another_config",
"rust_analyzer",
+ "true",
],
),
potential_cfg_options: None,
@@ -485,6 +496,7 @@
"group2_cfg=fourth_config",
"group2_cfg=yet_another_config",
"rust_analyzer",
+ "true",
"unrelated_cfg",
],
),
diff --git a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
index c123df80a6..a0e14b8fcb 100644
--- a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
+++ b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
@@ -17,6 +17,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -56,6 +57,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -86,6 +88,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -116,6 +119,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -146,6 +150,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -193,6 +198,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -223,6 +229,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -318,6 +325,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -348,6 +356,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -378,6 +387,7 @@
[
"debug_assertions",
"miri",
+ "true",
],
),
potential_cfg_options: None,
@@ -407,6 +417,7 @@
cfg_options: CfgOptions(
[
"rust_analyzer",
+ "true",
],
),
potential_cfg_options: None,
diff --git a/crates/ra-salsa/src/derived/slot.rs b/crates/ra-salsa/src/derived/slot.rs
index de7a397607..6c5ccba173 100644
--- a/crates/ra-salsa/src/derived/slot.rs
+++ b/crates/ra-salsa/src/derived/slot.rs
@@ -616,7 +616,7 @@ Please report this bug to https://github.com/salsa-rs/salsa/issues."
}
}
-impl<'me, Q> Drop for PanicGuard<'me, Q>
+impl<Q> Drop for PanicGuard<'_, Q>
where
Q: QueryFunction,
Q::Value: Eq,
diff --git a/crates/ra-salsa/src/derived_lru/slot.rs b/crates/ra-salsa/src/derived_lru/slot.rs
index d0e4b5422b..ff9cc4eade 100644
--- a/crates/ra-salsa/src/derived_lru/slot.rs
+++ b/crates/ra-salsa/src/derived_lru/slot.rs
@@ -666,7 +666,7 @@ Please report this bug to https://github.com/salsa-rs/salsa/issues."
}
}
-impl<'me, Q, MP> Drop for PanicGuard<'me, Q, MP>
+impl<Q, MP> Drop for PanicGuard<'_, Q, MP>
where
Q: QueryFunction,
MP: MemoizationPolicy<Q>,
diff --git a/crates/ra-salsa/src/interned.rs b/crates/ra-salsa/src/interned.rs
index 359662ec6b..42c398d697 100644
--- a/crates/ra-salsa/src/interned.rs
+++ b/crates/ra-salsa/src/interned.rs
@@ -493,7 +493,7 @@ where
is_static::<Slot<K>>();
}
-impl<'me, Q> QueryTable<'me, Q>
+impl<Q> QueryTable<'_, Q>
where
Q: Query<Storage = InternedStorage<Q>>,
Q::Key: InternValue,
diff --git a/crates/ra-salsa/src/lib.rs b/crates/ra-salsa/src/lib.rs
index 1b327773ec..bd1ab6971c 100644
--- a/crates/ra-salsa/src/lib.rs
+++ b/crates/ra-salsa/src/lib.rs
@@ -149,7 +149,7 @@ where
db: &'me D,
}
-impl<'me, D: ?Sized> fmt::Debug for EventDebug<'me, D>
+impl<D: ?Sized> fmt::Debug for EventDebug<'_, D>
where
D: plumbing::DatabaseOps,
{
@@ -242,7 +242,7 @@ where
db: &'me D,
}
-impl<'me, D: ?Sized> fmt::Debug for EventKindDebug<'me, D>
+impl<D: ?Sized> fmt::Debug for EventKindDebug<'_, D>
where
D: plumbing::DatabaseOps,
{
@@ -729,7 +729,7 @@ impl Cycle {
db: &'me dyn Database,
}
- impl<'me> std::fmt::Debug for UnexpectedCycleDebug<'me> {
+ impl std::fmt::Debug for UnexpectedCycleDebug<'_> {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.debug_struct("UnexpectedCycle")
.field("all_participants", &self.c.all_participants(self.db))
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index ecc8333503..e872585c57 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -85,7 +85,9 @@ fn actual_main() -> anyhow::Result<ExitCode> {
flags::RustAnalyzerCmd::UnresolvedReferences(cmd) => cmd.run()?,
flags::RustAnalyzerCmd::Ssr(cmd) => cmd.run()?,
flags::RustAnalyzerCmd::Search(cmd) => cmd.run()?,
- flags::RustAnalyzerCmd::Lsif(cmd) => cmd.run()?,
+ flags::RustAnalyzerCmd::Lsif(cmd) => {
+ cmd.run(&mut std::io::stdout(), Some(project_model::RustLibSource::Discover))?
+ }
flags::RustAnalyzerCmd::Scip(cmd) => cmd.run()?,
flags::RustAnalyzerCmd::RunTests(cmd) => cmd.run()?,
flags::RustAnalyzerCmd::RustcTests(cmd) => cmd.run()?,
diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs
index ca8acf57bf..33c4f31fbe 100644
--- a/crates/rust-analyzer/src/cli/lsif.rs
+++ b/crates/rust-analyzer/src/cli/lsif.rs
@@ -12,6 +12,7 @@ use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
use lsp_types::lsif;
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource};
use rustc_hash::FxHashMap;
+use stdx::format_to;
use vfs::{AbsPathBuf, Vfs};
use crate::{
@@ -21,7 +22,7 @@ use crate::{
version::version,
};
-struct LsifManager<'a> {
+struct LsifManager<'a, 'w> {
count: i32,
token_map: FxHashMap<TokenId, Id>,
range_map: FxHashMap<FileRange, Id>,
@@ -30,6 +31,7 @@ struct LsifManager<'a> {
analysis: &'a Analysis,
db: &'a RootDatabase,
vfs: &'a Vfs,
+ out: &'w mut dyn std::io::Write,
}
#[derive(Clone, Copy)]
@@ -41,8 +43,13 @@ impl From<Id> for lsp_types::NumberOrString {
}
}
-impl LsifManager<'_> {
- fn new<'a>(analysis: &'a Analysis, db: &'a RootDatabase, vfs: &'a Vfs) -> LsifManager<'a> {
+impl LsifManager<'_, '_> {
+ fn new<'a, 'w>(
+ analysis: &'a Analysis,
+ db: &'a RootDatabase,
+ vfs: &'a Vfs,
+ out: &'w mut dyn std::io::Write,
+ ) -> LsifManager<'a, 'w> {
LsifManager {
count: 0,
token_map: FxHashMap::default(),
@@ -52,6 +59,7 @@ impl LsifManager<'_> {
analysis,
db,
vfs,
+ out,
}
}
@@ -70,9 +78,8 @@ impl LsifManager<'_> {
self.add(lsif::Element::Edge(edge))
}
- // FIXME: support file in addition to stdout here
- fn emit(&self, data: &str) {
- println!("{data}");
+ fn emit(&mut self, data: &str) {
+ format_to!(self.out, "{data}\n");
}
fn get_token_id(&mut self, id: TokenId) -> Id {
@@ -272,14 +279,14 @@ impl LsifManager<'_> {
}
impl flags::Lsif {
- pub fn run(self) -> anyhow::Result<()> {
+ pub fn run(
+ self,
+ out: &mut dyn std::io::Write,
+ sysroot: Option<RustLibSource>,
+ ) -> anyhow::Result<()> {
let now = Instant::now();
- let cargo_config = &CargoConfig {
- sysroot: Some(RustLibSource::Discover),
- all_targets: true,
- set_test: true,
- ..Default::default()
- };
+ let cargo_config =
+ &CargoConfig { sysroot, all_targets: true, set_test: true, ..Default::default() };
let no_progress = &|_| ();
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: true,
@@ -308,7 +315,7 @@ impl flags::Lsif {
let si = StaticIndex::compute(&analysis, vendored_libs_config);
- let mut lsif = LsifManager::new(&analysis, db, &vfs);
+ let mut lsif = LsifManager::new(&analysis, db, &vfs, out);
lsif.add_vertex(lsif::Vertex::MetaData(lsif::MetaData {
version: String::from("0.5.0"),
project_root: lsp_types::Url::from_file_path(path).unwrap(),
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 518b588cb7..f5b0fcecf3 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -727,7 +727,7 @@ enum RatomlFile {
Crate(LocalConfigInput),
}
-#[derive(Debug, Clone)]
+#[derive(Clone)]
pub struct Config {
/// Projects that have a Cargo.toml or a rust-project.json in a
/// parent directory, so we can discover them by walking the
@@ -765,6 +765,26 @@ pub struct Config {
detached_files: Vec<AbsPathBuf>,
}
+impl fmt::Debug for Config {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Config")
+ .field("discovered_projects_from_filesystem", &self.discovered_projects_from_filesystem)
+ .field("discovered_projects_from_command", &self.discovered_projects_from_command)
+ .field("workspace_roots", &self.workspace_roots)
+ .field("caps", &self.caps)
+ .field("root_path", &self.root_path)
+ .field("snippets", &self.snippets)
+ .field("visual_studio_code_version", &self.visual_studio_code_version)
+ .field("client_config", &self.client_config)
+ .field("user_config", &self.user_config)
+ .field("ratoml_file", &self.ratoml_file)
+ .field("source_root_parent_map", &self.source_root_parent_map)
+ .field("validation_errors", &self.validation_errors)
+ .field("detached_files", &self.detached_files)
+ .finish()
+ }
+}
+
// Delegate capability fetching methods
impl std::ops::Deref for Config {
type Target = ClientCapabilities;
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 5f2871ac99..22910ee4c6 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -173,21 +173,6 @@ pub(crate) fn fetch_native_diagnostics(
let _p = tracing::info_span!("fetch_native_diagnostics").entered();
let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned());
- let convert_diagnostic =
- |line_index: &crate::line_index::LineIndex, d: ide::Diagnostic| lsp_types::Diagnostic {
- range: lsp::to_proto::range(line_index, d.range.range),
- severity: Some(lsp::to_proto::diagnostic_severity(d.severity)),
- code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_owned())),
- code_description: Some(lsp_types::CodeDescription {
- href: lsp_types::Url::parse(&d.code.url()).unwrap(),
- }),
- source: Some("rust-analyzer".to_owned()),
- message: d.message,
- related_information: None,
- tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]),
- data: None,
- };
-
// the diagnostics produced may point to different files not requested by the concrete request,
// put those into here and filter later
let mut odd_ones = Vec::new();
@@ -203,10 +188,12 @@ pub(crate) fn fetch_native_diagnostics(
NativeDiagnosticsFetchKind::Syntax => {
snapshot.analysis.syntax_diagnostics(config, file_id).ok()?
}
- NativeDiagnosticsFetchKind::Semantic => snapshot
+
+ NativeDiagnosticsFetchKind::Semantic if config.enabled => snapshot
.analysis
.semantic_diagnostics(config, ide::AssistResolveStrategy::None, file_id)
.ok()?,
+ NativeDiagnosticsFetchKind::Semantic => return None,
};
let diagnostics = diagnostics
.into_iter()
@@ -246,3 +233,22 @@ pub(crate) fn fetch_native_diagnostics(
}
diagnostics
}
+
+pub(crate) fn convert_diagnostic(
+ line_index: &crate::line_index::LineIndex,
+ d: ide::Diagnostic,
+) -> lsp_types::Diagnostic {
+ lsp_types::Diagnostic {
+ range: lsp::to_proto::range(line_index, d.range.range),
+ severity: Some(lsp::to_proto::diagnostic_severity(d.severity)),
+ code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_owned())),
+ code_description: Some(lsp_types::CodeDescription {
+ href: lsp_types::Url::parse(&d.code.url()).unwrap(),
+ }),
+ source: Some("rust-analyzer".to_owned()),
+ message: d.message,
+ related_information: None,
+ tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]),
+ data: None,
+ }
+}
diff --git a/crates/rust-analyzer/src/handlers/dispatch.rs b/crates/rust-analyzer/src/handlers/dispatch.rs
index ed7bf27843..03759b036b 100644
--- a/crates/rust-analyzer/src/handlers/dispatch.rs
+++ b/crates/rust-analyzer/src/handlers/dispatch.rs
@@ -5,7 +5,7 @@ use std::{
};
use ide::Cancelled;
-use lsp_server::ExtractError;
+use lsp_server::{ExtractError, Response, ResponseError};
use serde::{de::DeserializeOwned, Serialize};
use stdx::thread::ThreadIntent;
@@ -117,7 +117,36 @@ impl RequestDispatcher<'_> {
}
return self;
}
- self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(ThreadIntent::Worker, f)
+ self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(
+ ThreadIntent::Worker,
+ f,
+ Self::content_modified_error,
+ )
+ }
+
+ /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not
+ /// ready this will return a `default` constructed [`R::Result`].
+ pub(crate) fn on_with<R>(
+ &mut self,
+ f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>,
+ default: impl FnOnce() -> R::Result,
+ on_cancelled: fn() -> ResponseError,
+ ) -> &mut Self
+ where
+ R: lsp_types::request::Request<
+ Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
+ Result: Serialize,
+ > + 'static,
+ {
+ if !self.global_state.vfs_done {
+ if let Some(lsp_server::Request { id, .. }) =
+ self.req.take_if(|it| it.method == R::METHOD)
+ {
+ self.global_state.respond(lsp_server::Response::new_ok(id, default()));
+ }
+ return self;
+ }
+ self.on_with_thread_intent::<true, false, R>(ThreadIntent::Worker, f, on_cancelled)
}
/// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not
@@ -136,7 +165,11 @@ impl RequestDispatcher<'_> {
}
return self;
}
- self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(ThreadIntent::Worker, f)
+ self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(
+ ThreadIntent::Worker,
+ f,
+ Self::content_modified_error,
+ )
}
/// Dispatches a latency-sensitive request onto the thread pool. When the VFS is marked not
@@ -159,7 +192,11 @@ impl RequestDispatcher<'_> {
}
return self;
}
- self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(ThreadIntent::LatencySensitive, f)
+ self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(
+ ThreadIntent::LatencySensitive,
+ f,
+ Self::content_modified_error,
+ )
}
/// Formatting requests should never block on waiting a for task thread to open up, editors will wait
@@ -174,7 +211,11 @@ impl RequestDispatcher<'_> {
R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
R::Result: Serialize,
{
- self.on_with_thread_intent::<false, false, R>(ThreadIntent::LatencySensitive, f)
+ self.on_with_thread_intent::<false, false, R>(
+ ThreadIntent::LatencySensitive,
+ f,
+ Self::content_modified_error,
+ )
}
pub(crate) fn finish(&mut self) {
@@ -193,6 +234,7 @@ impl RequestDispatcher<'_> {
&mut self,
intent: ThreadIntent,
f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>,
+ on_cancelled: fn() -> ResponseError,
) -> &mut Self
where
R: lsp_types::request::Request + 'static,
@@ -221,11 +263,10 @@ impl RequestDispatcher<'_> {
match thread_result_to_response::<R>(req.id.clone(), result) {
Ok(response) => Task::Response(response),
Err(_cancelled) if ALLOW_RETRYING => Task::Retry(req),
- Err(_cancelled) => Task::Response(lsp_server::Response::new_err(
- req.id,
- lsp_server::ErrorCode::ContentModified as i32,
- "content modified".to_owned(),
- )),
+ Err(_cancelled) => {
+ let error = on_cancelled();
+ Task::Response(Response { id: req.id, result: None, error: Some(error) })
+ }
}
});
@@ -256,6 +297,14 @@ impl RequestDispatcher<'_> {
}
}
}
+
+ fn content_modified_error() -> ResponseError {
+ ResponseError {
+ code: lsp_server::ErrorCode::ContentModified as i32,
+ message: "content modified".to_owned(),
+ data: None,
+ }
+ }
}
fn thread_result_to_response<R>(
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index a9f8ac3a80..fa584ab4d2 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -4,6 +4,7 @@
use std::{
fs,
io::Write as _,
+ ops::Not,
process::{self, Stdio},
};
@@ -14,7 +15,7 @@ use ide::{
FilePosition, FileRange, HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query,
RangeInfo, ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
};
-use ide_db::SymbolKind;
+use ide_db::{FxHashMap, SymbolKind};
use itertools::Itertools;
use lsp_server::ErrorCode;
use lsp_types::{
@@ -36,6 +37,7 @@ use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
use crate::{
config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
+ diagnostics::convert_diagnostic,
global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
hack_recover_crate_name,
line_index::LineEndings,
@@ -119,7 +121,7 @@ pub(crate) fn handle_analyzer_status(
format_to!(buf, "{}", crate::version());
buf.push_str("\nConfiguration: \n");
- format_to!(buf, "{:?}", snap.config);
+ format_to!(buf, "{:#?}", snap.config);
Ok(buf)
}
@@ -473,6 +475,74 @@ pub(crate) fn handle_on_type_formatting(
Ok(Some(change))
}
+pub(crate) fn handle_document_diagnostics(
+ snap: GlobalStateSnapshot,
+ params: lsp_types::DocumentDiagnosticParams,
+) -> anyhow::Result<lsp_types::DocumentDiagnosticReportResult> {
+ const EMPTY: lsp_types::DocumentDiagnosticReportResult =
+ lsp_types::DocumentDiagnosticReportResult::Report(
+ lsp_types::DocumentDiagnosticReport::Full(
+ lsp_types::RelatedFullDocumentDiagnosticReport {
+ related_documents: None,
+ full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
+ result_id: None,
+ items: vec![],
+ },
+ },
+ ),
+ );
+
+ let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let source_root = snap.analysis.source_root_id(file_id)?;
+ if !snap.analysis.is_local_source_root(source_root)? {
+ return Ok(EMPTY);
+ }
+ let config = snap.config.diagnostics(Some(source_root));
+ if !config.enabled {
+ return Ok(EMPTY);
+ }
+ let line_index = snap.file_line_index(file_id)?;
+ let supports_related = snap.config.text_document_diagnostic_related_document_support();
+
+ let mut related_documents = FxHashMap::default();
+ let diagnostics = snap
+ .analysis
+ .full_diagnostics(&config, AssistResolveStrategy::None, file_id)?
+ .into_iter()
+ .filter_map(|d| {
+ let file = d.range.file_id;
+ let diagnostic = convert_diagnostic(&line_index, d);
+ if file == file_id {
+ return Some(diagnostic);
+ }
+ if supports_related {
+ related_documents.entry(file).or_insert_with(Vec::new).push(diagnostic);
+ }
+ None
+ });
+ Ok(lsp_types::DocumentDiagnosticReportResult::Report(
+ lsp_types::DocumentDiagnosticReport::Full(lsp_types::RelatedFullDocumentDiagnosticReport {
+ full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
+ result_id: None,
+ items: diagnostics.collect(),
+ },
+ related_documents: related_documents.is_empty().not().then(|| {
+ related_documents
+ .into_iter()
+ .map(|(id, items)| {
+ (
+ to_proto::url(&snap, id),
+ lsp_types::DocumentDiagnosticReportKind::Full(
+ lsp_types::FullDocumentDiagnosticReport { result_id: None, items },
+ ),
+ )
+ })
+ .collect()
+ }),
+ }),
+ ))
+}
+
pub(crate) fn handle_document_symbol(
snap: GlobalStateSnapshot,
params: lsp_types::DocumentSymbolParams,
@@ -539,18 +609,11 @@ pub(crate) fn handle_document_symbol(
url: &Url,
res: &mut Vec<SymbolInformation>,
) {
- let mut tags = Vec::new();
-
- #[allow(deprecated)]
- if let Some(true) = symbol.deprecated {
- tags.push(SymbolTag::DEPRECATED)
- }
-
#[allow(deprecated)]
res.push(SymbolInformation {
name: symbol.name.clone(),
kind: symbol.kind,
- tags: Some(tags),
+ tags: symbol.tags.clone(),
deprecated: symbol.deprecated,
location: Location::new(url.clone(), symbol.range),
container_name,
diff --git a/crates/rust-analyzer/src/lsp/capabilities.rs b/crates/rust-analyzer/src/lsp/capabilities.rs
index 3b19284f24..271a9c0f3d 100644
--- a/crates/rust-analyzer/src/lsp/capabilities.rs
+++ b/crates/rust-analyzer/src/lsp/capabilities.rs
@@ -155,7 +155,15 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
"ssr": true,
"workspaceSymbolScopeKindFiltering": true,
})),
- diagnostic_provider: None,
+ diagnostic_provider: Some(lsp_types::DiagnosticServerCapabilities::Options(
+ lsp_types::DiagnosticOptions {
+ identifier: None,
+ inter_file_dependencies: true,
+ // FIXME
+ workspace_diagnostics: false,
+ work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
+ },
+ )),
inline_completion_provider: None,
}
}
@@ -210,9 +218,7 @@ impl ClientCapabilities {
.completion_item
.as_ref()?
.label_details_support
- .as_ref()
- })()
- .is_some()
+ })() == Some(true)
}
fn completion_item(&self) -> Option<CompletionOptionsCompletionItem> {
@@ -382,6 +388,15 @@ impl ClientCapabilities {
.unwrap_or_default()
}
+ pub fn text_document_diagnostic(&self) -> bool {
+ (|| -> _ { self.0.text_document.as_ref()?.diagnostic.as_ref() })().is_some()
+ }
+
+ pub fn text_document_diagnostic_related_document_support(&self) -> bool {
+ (|| -> _ { self.0.text_document.as_ref()?.diagnostic.as_ref()?.related_document_support })()
+ == Some(true)
+ }
+
pub fn code_action_group(&self) -> bool {
self.experimental_bool("codeActionGroup")
}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 20be38a9e4..9a51df80fe 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -417,6 +417,8 @@ impl GlobalState {
}
}
+ let supports_diagnostic_pull_model = self.config.text_document_diagnostic();
+
let client_refresh = became_quiescent || state_changed;
if client_refresh {
// Refresh semantic tokens if the client supports it.
@@ -434,11 +436,21 @@ impl GlobalState {
if self.config.inlay_hints_refresh() {
self.send_request::<lsp_types::request::InlayHintRefreshRequest>((), |_, _| ());
}
+
+ if supports_diagnostic_pull_model {
+ self.send_request::<lsp_types::request::WorkspaceDiagnosticRefresh>(
+ (),
+ |_, _| (),
+ );
+ }
}
let project_or_mem_docs_changed =
became_quiescent || state_changed || memdocs_added_or_removed;
- if project_or_mem_docs_changed && self.config.publish_diagnostics(None) {
+ if project_or_mem_docs_changed
+ && !supports_diagnostic_pull_model
+ && self.config.publish_diagnostics(None)
+ {
self.update_diagnostics();
}
if project_or_mem_docs_changed && self.config.test_explorer() {
@@ -1080,6 +1092,23 @@ impl GlobalState {
.on_latency_sensitive::<NO_RETRY, lsp_request::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range)
// FIXME: Some of these NO_RETRY could be retries if the file they are interested didn't change.
// All other request handlers
+ .on_with::<lsp_request::DocumentDiagnosticRequest>(handlers::handle_document_diagnostics, || lsp_types::DocumentDiagnosticReportResult::Report(
+ lsp_types::DocumentDiagnosticReport::Full(
+ lsp_types::RelatedFullDocumentDiagnosticReport {
+ related_documents: None,
+ full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
+ result_id: None,
+ items: vec![],
+ },
+ },
+ ),
+ ), || lsp_server::ResponseError {
+ code: lsp_server::ErrorCode::ServerCancelled as i32,
+ message: "server cancelled the request".to_owned(),
+ data: serde_json::to_value(lsp_types::DiagnosticServerCancellationData {
+ retrigger_request: true
+ }).ok(),
+ })
.on::<RETRY, lsp_request::DocumentSymbolRequest>(handlers::handle_document_symbol)
.on::<RETRY, lsp_request::FoldingRangeRequest>(handlers::handle_folding_range)
.on::<NO_RETRY, lsp_request::SignatureHelpRequest>(handlers::handle_signature_help)
diff --git a/crates/rust-analyzer/src/tracing/config.rs b/crates/rust-analyzer/src/tracing/config.rs
index 5ab2dc2b67..02ae4186ab 100644
--- a/crates/rust-analyzer/src/tracing/config.rs
+++ b/crates/rust-analyzer/src/tracing/config.rs
@@ -58,14 +58,22 @@ where
let writer = self.writer;
let ra_fmt_layer = tracing_subscriber::fmt::layer()
- .with_timer(
- time::OffsetTime::local_rfc_3339()
- .expect("Could not get local offset, make sure you're on the main thread"),
- )
.with_target(false)
.with_ansi(false)
- .with_writer(writer)
- .with_filter(targets_filter);
+ .with_writer(writer);
+
+ let ra_fmt_layer = match time::OffsetTime::local_rfc_3339() {
+ Ok(timer) => {
+ // If we can get the time offset, format logs with the timezone.
+ ra_fmt_layer.with_timer(timer).boxed()
+ }
+ Err(_) => {
+ // Use system time if we can't get the time offset. This should
+ // never happen on Linux, but can happen on e.g. OpenBSD.
+ ra_fmt_layer.boxed()
+ }
+ }
+ .with_filter(targets_filter);
let chalk_layer = match self.chalk_filter {
Some(chalk_filter) => {
diff --git a/crates/rust-analyzer/src/tracing/hprof.rs b/crates/rust-analyzer/src/tracing/hprof.rs
index cad92962f3..d466acef01 100644
--- a/crates/rust-analyzer/src/tracing/hprof.rs
+++ b/crates/rust-analyzer/src/tracing/hprof.rs
@@ -120,7 +120,7 @@ pub struct DataVisitor<'a> {
string: &'a mut String,
}
-impl<'a> Visit for DataVisitor<'a> {
+impl Visit for DataVisitor<'_> {
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
write!(self.string, "{} = {:?} ", field.name(), value).unwrap();
}
diff --git a/crates/rust-analyzer/tests/slow-tests/cli.rs b/crates/rust-analyzer/tests/slow-tests/cli.rs
new file mode 100644
index 0000000000..fba5466691
--- /dev/null
+++ b/crates/rust-analyzer/tests/slow-tests/cli.rs
@@ -0,0 +1,131 @@
+use expect_test::expect;
+use test_utils::skip_slow_tests;
+
+use crate::support::Project;
+
+// If you choose to change the test fixture here, please inform the ferrocene/needy maintainers by
+// opening an issue at https://github.com/ferrocene/needy as the tool relies on specific token
+// mapping behavior.
+#[test]
+fn lsif_contains_generated_constant() {
+ if skip_slow_tests() {
+ return;
+ }
+
+ let stdout = Project::with_fixture(
+ r#"
+//- /Cargo.toml
+[package]
+name = "foo"
+version = "0.0.0"
+
+//- /src/lib.rs
+#![allow(unused)]
+
+macro_rules! generate_const_from_identifier(
+ ($id:ident) => (
+ const _: () = { const $id: &str = "encoded_data"; };
+ )
+);
+
+generate_const_from_identifier!(REQ_001);
+mod tests {
+ use super::*;
+ generate_const_from_identifier!(REQ_002);
+}
+"#,
+ )
+ .root("foo")
+ .run_lsif();
+ let n = stdout.find(r#"{"id":2,"#).unwrap();
+ // the first 2 entries contain paths that are not stable
+ let stdout = &stdout[n..];
+ expect![[r#"
+ {"id":2,"type":"vertex","label":"foldingRangeResult","result":[{"startLine":2,"startCharacter":43,"endLine":6,"endCharacter":1},{"startLine":3,"startCharacter":19,"endLine":5,"endCharacter":5},{"startLine":9,"startCharacter":10,"endLine":12,"endCharacter":1}]}
+ {"id":3,"type":"edge","label":"textDocument/foldingRange","inV":2,"outV":1}
+ {"id":4,"type":"vertex","label":"range","start":{"line":0,"character":3},"end":{"line":0,"character":8}}
+ {"id":5,"type":"vertex","label":"resultSet"}
+ {"id":6,"type":"edge","label":"next","inV":5,"outV":4}
+ {"id":7,"type":"vertex","label":"range","start":{"line":2,"character":13},"end":{"line":2,"character":43}}
+ {"id":8,"type":"vertex","label":"resultSet"}
+ {"id":9,"type":"edge","label":"next","inV":8,"outV":7}
+ {"id":10,"type":"vertex","label":"range","start":{"line":8,"character":0},"end":{"line":8,"character":30}}
+ {"id":11,"type":"edge","label":"next","inV":8,"outV":10}
+ {"id":12,"type":"vertex","label":"range","start":{"line":8,"character":32},"end":{"line":8,"character":39}}
+ {"id":13,"type":"vertex","label":"resultSet"}
+ {"id":14,"type":"edge","label":"next","inV":13,"outV":12}
+ {"id":15,"type":"vertex","label":"range","start":{"line":9,"character":4},"end":{"line":9,"character":9}}
+ {"id":16,"type":"vertex","label":"resultSet"}
+ {"id":17,"type":"edge","label":"next","inV":16,"outV":15}
+ {"id":18,"type":"vertex","label":"range","start":{"line":10,"character":8},"end":{"line":10,"character":13}}
+ {"id":19,"type":"vertex","label":"resultSet"}
+ {"id":20,"type":"edge","label":"next","inV":19,"outV":18}
+ {"id":21,"type":"vertex","label":"range","start":{"line":11,"character":4},"end":{"line":11,"character":34}}
+ {"id":22,"type":"edge","label":"next","inV":8,"outV":21}
+ {"id":23,"type":"vertex","label":"range","start":{"line":11,"character":36},"end":{"line":11,"character":43}}
+ {"id":24,"type":"vertex","label":"resultSet"}
+ {"id":25,"type":"edge","label":"next","inV":24,"outV":23}
+ {"id":26,"type":"edge","label":"contains","inVs":[4,7,10,12,15,18,21,23],"outV":1}
+ {"id":27,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\n#[allow]\n```\n\n---\n\nValid forms are:\n\n* \\#\\[allow(lint1, lint2, ..., /\\*opt\\*/ reason = \"...\")\\]"}}}
+ {"id":28,"type":"edge","label":"textDocument/hover","inV":27,"outV":5}
+ {"id":29,"type":"vertex","label":"referenceResult"}
+ {"id":30,"type":"edge","label":"textDocument/references","inV":29,"outV":5}
+ {"id":31,"type":"edge","label":"item","document":1,"property":"references","inVs":[4],"outV":29}
+ {"id":32,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmacro_rules! generate_const_from_identifier\n```"}}}
+ {"id":33,"type":"edge","label":"textDocument/hover","inV":32,"outV":8}
+ {"id":34,"type":"vertex","label":"packageInformation","name":"foo","manager":"cargo","version":"0.0.0"}
+ {"id":35,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::generate_const_from_identifier","unique":"scheme","kind":"export"}
+ {"id":36,"type":"edge","label":"packageInformation","inV":34,"outV":35}
+ {"id":37,"type":"edge","label":"moniker","inV":35,"outV":8}
+ {"id":38,"type":"vertex","label":"definitionResult"}
+ {"id":39,"type":"edge","label":"item","document":1,"inVs":[7],"outV":38}
+ {"id":40,"type":"edge","label":"textDocument/definition","inV":38,"outV":8}
+ {"id":41,"type":"vertex","label":"referenceResult"}
+ {"id":42,"type":"edge","label":"textDocument/references","inV":41,"outV":8}
+ {"id":43,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[7],"outV":41}
+ {"id":44,"type":"edge","label":"item","document":1,"property":"references","inVs":[10,21],"outV":41}
+ {"id":45,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nconst REQ_001: &str = \"encoded_data\"\n```"}}}
+ {"id":46,"type":"edge","label":"textDocument/hover","inV":45,"outV":13}
+ {"id":47,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::REQ_001","unique":"scheme","kind":"export"}
+ {"id":48,"type":"edge","label":"packageInformation","inV":34,"outV":47}
+ {"id":49,"type":"edge","label":"moniker","inV":47,"outV":13}
+ {"id":50,"type":"vertex","label":"definitionResult"}
+ {"id":51,"type":"edge","label":"item","document":1,"inVs":[12],"outV":50}
+ {"id":52,"type":"edge","label":"textDocument/definition","inV":50,"outV":13}
+ {"id":53,"type":"vertex","label":"referenceResult"}
+ {"id":54,"type":"edge","label":"textDocument/references","inV":53,"outV":13}
+ {"id":55,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[12],"outV":53}
+ {"id":56,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmod tests\n```"}}}
+ {"id":57,"type":"edge","label":"textDocument/hover","inV":56,"outV":16}
+ {"id":58,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests","unique":"scheme","kind":"export"}
+ {"id":59,"type":"edge","label":"packageInformation","inV":34,"outV":58}
+ {"id":60,"type":"edge","label":"moniker","inV":58,"outV":16}
+ {"id":61,"type":"vertex","label":"definitionResult"}
+ {"id":62,"type":"edge","label":"item","document":1,"inVs":[15],"outV":61}
+ {"id":63,"type":"edge","label":"textDocument/definition","inV":61,"outV":16}
+ {"id":64,"type":"vertex","label":"referenceResult"}
+ {"id":65,"type":"edge","label":"textDocument/references","inV":64,"outV":16}
+ {"id":66,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[15],"outV":64}
+ {"id":67,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nextern crate foo\n```"}}}
+ {"id":68,"type":"edge","label":"textDocument/hover","inV":67,"outV":19}
+ {"id":69,"type":"vertex","label":"definitionResult"}
+ {"id":70,"type":"vertex","label":"range","start":{"line":0,"character":0},"end":{"line":13,"character":0}}
+ {"id":71,"type":"edge","label":"contains","inVs":[70],"outV":1}
+ {"id":72,"type":"edge","label":"item","document":1,"inVs":[70],"outV":69}
+ {"id":73,"type":"edge","label":"textDocument/definition","inV":69,"outV":19}
+ {"id":74,"type":"vertex","label":"referenceResult"}
+ {"id":75,"type":"edge","label":"textDocument/references","inV":74,"outV":19}
+ {"id":76,"type":"edge","label":"item","document":1,"property":"references","inVs":[18],"outV":74}
+ {"id":77,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo::tests\n```\n\n```rust\nconst REQ_002: &str = \"encoded_data\"\n```"}}}
+ {"id":78,"type":"edge","label":"textDocument/hover","inV":77,"outV":24}
+ {"id":79,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests::REQ_002","unique":"scheme","kind":"export"}
+ {"id":80,"type":"edge","label":"packageInformation","inV":34,"outV":79}
+ {"id":81,"type":"edge","label":"moniker","inV":79,"outV":24}
+ {"id":82,"type":"vertex","label":"definitionResult"}
+ {"id":83,"type":"edge","label":"item","document":1,"inVs":[23],"outV":82}
+ {"id":84,"type":"edge","label":"textDocument/definition","inV":82,"outV":24}
+ {"id":85,"type":"vertex","label":"referenceResult"}
+ {"id":86,"type":"edge","label":"textDocument/references","inV":85,"outV":24}
+ {"id":87,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[23],"outV":85}
+ "#]].assert_eq(stdout);
+}
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index 54cd27f4b3..97c76bf8d1 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -10,6 +10,7 @@
#![allow(clippy::disallowed_types)]
+mod cli;
mod ratoml;
mod support;
mod testdir;
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index 18aface632..78572e37a9 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -6,11 +6,13 @@ use std::{
};
use crossbeam_channel::{after, select, Receiver};
+use itertools::Itertools;
use lsp_server::{Connection, Message, Notification, Request};
use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url};
use parking_lot::{Mutex, MutexGuard};
use paths::{Utf8Path, Utf8PathBuf};
use rust_analyzer::{
+ cli::flags,
config::{Config, ConfigChange, ConfigErrors},
lsp, main_loop,
};
@@ -84,6 +86,46 @@ impl Project<'_> {
self
}
+ pub(crate) fn run_lsif(self) -> String {
+ let tmp_dir = self.tmp_dir.unwrap_or_else(|| {
+ if self.root_dir_contains_symlink {
+ TestDir::new_symlink()
+ } else {
+ TestDir::new()
+ }
+ });
+
+ let FixtureWithProjectMeta {
+ fixture,
+ mini_core,
+ proc_macro_names,
+ toolchain,
+ target_data_layout: _,
+ } = FixtureWithProjectMeta::parse(self.fixture);
+ assert!(proc_macro_names.is_empty());
+ assert!(mini_core.is_none());
+ assert!(toolchain.is_none());
+
+ for entry in fixture {
+ let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]);
+ fs::create_dir_all(path.parent().unwrap()).unwrap();
+ fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
+ }
+
+ let tmp_dir_path = AbsPathBuf::assert(tmp_dir.path().to_path_buf());
+ let mut buf = Vec::new();
+ flags::Lsif::run(
+ flags::Lsif {
+ path: tmp_dir_path.join(self.roots.iter().exactly_one().unwrap()).into(),
+ exclude_vendored_libraries: false,
+ },
+ &mut buf,
+ None,
+ )
+ .unwrap();
+ String::from_utf8(buf).unwrap()
+ }
+
pub(crate) fn server(self) -> Server {
static CONFIG_DIR_LOCK: Mutex<()> = Mutex::new(());
let tmp_dir = self.tmp_dir.unwrap_or_else(|| {
diff --git a/crates/span/src/ast_id.rs b/crates/span/src/ast_id.rs
index 0ebd72e151..1d81d68451 100644
--- a/crates/span/src/ast_id.rs
+++ b/crates/span/src/ast_id.rs
@@ -224,9 +224,10 @@ impl AstIdMap {
match self.map.raw_entry().from_hash(hash, |&idx| self.arena[idx] == ptr) {
Some((&idx, &())) => ErasedFileAstId(idx.into_raw().into_u32()),
None => panic!(
- "Can't find {:?} in AstIdMap:\n{:?}",
+ "Can't find {:?} in AstIdMap:\n{:?}\n source text: {}",
item,
self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
+ item
),
}
}
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 76dbd42ff6..04c2153abf 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -10,6 +10,7 @@ pub mod non_empty_vec;
pub mod panic_context;
pub mod process;
pub mod rand;
+pub mod thin_vec;
pub mod thread;
pub use always_assert::{always, never};
@@ -304,22 +305,6 @@ pub fn slice_tails<T>(this: &[T]) -> impl Iterator<Item = &[T]> {
(0..this.len()).map(|i| &this[i..])
}
-pub trait IsNoneOr {
- type Type;
- #[allow(clippy::wrong_self_convention)]
- fn is_none_or(self, s: impl FnOnce(Self::Type) -> bool) -> bool;
-}
-#[allow(unstable_name_collisions)]
-impl<T> IsNoneOr for Option<T> {
- type Type = T;
- fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool {
- match self {
- Some(v) => f(v),
- None => true,
- }
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
diff --git a/crates/stdx/src/thin_vec.rs b/crates/stdx/src/thin_vec.rs
new file mode 100644
index 0000000000..700220e1d3
--- /dev/null
+++ b/crates/stdx/src/thin_vec.rs
@@ -0,0 +1,472 @@
+use std::alloc::{dealloc, handle_alloc_error, Layout};
+use std::fmt;
+use std::hash::{Hash, Hasher};
+use std::marker::PhantomData;
+use std::ops::{Deref, DerefMut};
+use std::ptr::{addr_of_mut, slice_from_raw_parts_mut, NonNull};
+
+/// A type that is functionally equivalent to `(Header, Box<[Item]>)`,
+/// but all data is stored in one heap allocation and the pointer is thin,
+/// so the whole thing's size is like a pointer.
+pub struct ThinVecWithHeader<Header, Item> {
+ /// INVARIANT: Points to a valid heap allocation that contains `ThinVecInner<Header>`,
+ /// followed by (suitably aligned) `len` `Item`s.
+ ptr: NonNull<ThinVecInner<Header>>,
+ _marker: PhantomData<(Header, Box<[Item]>)>,
+}
+
+// SAFETY: We essentially own both the header and the items.
+unsafe impl<Header: Send, Item: Send> Send for ThinVecWithHeader<Header, Item> {}
+unsafe impl<Header: Sync, Item: Sync> Sync for ThinVecWithHeader<Header, Item> {}
+
+#[derive(Clone)]
+struct ThinVecInner<Header> {
+ header: Header,
+ len: usize,
+}
+
+impl<Header, Item> ThinVecWithHeader<Header, Item> {
+ /// # Safety
+ ///
+ /// The iterator must produce `len` elements.
+ #[inline]
+ unsafe fn from_trusted_len_iter(
+ header: Header,
+ len: usize,
+ items: impl Iterator<Item = Item>,
+ ) -> Self {
+ let (ptr, layout, items_offset) = Self::allocate(len);
+
+ struct DeallocGuard(*mut u8, Layout);
+ impl Drop for DeallocGuard {
+ fn drop(&mut self) {
+ // SAFETY: We allocated this above.
+ unsafe {
+ dealloc(self.0, self.1);
+ }
+ }
+ }
+ let _dealloc_guard = DeallocGuard(ptr.as_ptr().cast::<u8>(), layout);
+
+ // INVARIANT: Between `0..1` there are only initialized items.
+ struct ItemsGuard<Item>(*mut Item, *mut Item);
+ impl<Item> Drop for ItemsGuard<Item> {
+ fn drop(&mut self) {
+ // SAFETY: Our invariant.
+ unsafe {
+ slice_from_raw_parts_mut(self.0, self.1.offset_from(self.0) as usize)
+ .drop_in_place();
+ }
+ }
+ }
+
+ // SAFETY: We allocated enough space.
+ let mut items_ptr = unsafe { ptr.as_ptr().byte_add(items_offset).cast::<Item>() };
+ // INVARIANT: There are zero elements in this range.
+ let mut items_guard = ItemsGuard(items_ptr, items_ptr);
+ items.for_each(|item| {
+ // SAFETY: Our precondition guarantee we won't get more than `len` items, and we allocated
+ // enough space for `len` items.
+ unsafe {
+ items_ptr.write(item);
+ items_ptr = items_ptr.add(1);
+ }
+ // INVARIANT: We just initialized this item.
+ items_guard.1 = items_ptr;
+ });
+
+ // SAFETY: We allocated enough space.
+ unsafe {
+ ptr.write(ThinVecInner { header, len });
+ }
+
+ std::mem::forget(items_guard);
+
+ std::mem::forget(_dealloc_guard);
+
+ // INVARIANT: We allocated and initialized all fields correctly.
+ Self { ptr, _marker: PhantomData }
+ }
+
+ #[inline]
+ fn allocate(len: usize) -> (NonNull<ThinVecInner<Header>>, Layout, usize) {
+ let (layout, items_offset) = Self::layout(len);
+ // SAFETY: We always have `len`, so our allocation cannot be zero-sized.
+ let ptr = unsafe { std::alloc::alloc(layout).cast::<ThinVecInner<Header>>() };
+ let Some(ptr) = NonNull::<ThinVecInner<Header>>::new(ptr) else {
+ handle_alloc_error(layout);
+ };
+ (ptr, layout, items_offset)
+ }
+
+ #[inline]
+ #[allow(clippy::should_implement_trait)]
+ pub fn from_iter<I>(header: Header, items: I) -> Self
+ where
+ I: IntoIterator,
+ I::IntoIter: TrustedLen<Item = Item>,
+ {
+ let items = items.into_iter();
+ // SAFETY: `TrustedLen` guarantees the iterator length is exact.
+ unsafe { Self::from_trusted_len_iter(header, items.len(), items) }
+ }
+
+ #[inline]
+ fn items_offset(&self) -> usize {
+ // SAFETY: We `pad_to_align()` in `layout()`, so at most where accessing past the end of the allocation,
+ // which is allowed.
+ unsafe {
+ Layout::new::<ThinVecInner<Header>>().extend(Layout::new::<Item>()).unwrap_unchecked().1
+ }
+ }
+
+ #[inline]
+ fn header_and_len(&self) -> &ThinVecInner<Header> {
+ // SAFETY: By `ptr`'s invariant, it is correctly allocated and initialized.
+ unsafe { &*self.ptr.as_ptr() }
+ }
+
+ #[inline]
+ fn items_ptr(&self) -> *mut [Item] {
+ let len = self.header_and_len().len;
+ // SAFETY: `items_offset()` returns the correct offset of the items, where they are allocated.
+ let ptr = unsafe { self.ptr.as_ptr().byte_add(self.items_offset()).cast::<Item>() };
+ slice_from_raw_parts_mut(ptr, len)
+ }
+
+ #[inline]
+ pub fn header(&self) -> &Header {
+ &self.header_and_len().header
+ }
+
+ #[inline]
+ pub fn header_mut(&mut self) -> &mut Header {
+ // SAFETY: By `ptr`'s invariant, it is correctly allocated and initialized.
+ unsafe { &mut *addr_of_mut!((*self.ptr.as_ptr()).header) }
+ }
+
+ #[inline]
+ pub fn items(&self) -> &[Item] {
+ // SAFETY: `items_ptr()` gives a valid pointer.
+ unsafe { &*self.items_ptr() }
+ }
+
+ #[inline]
+ pub fn items_mut(&mut self) -> &mut [Item] {
+ // SAFETY: `items_ptr()` gives a valid pointer.
+ unsafe { &mut *self.items_ptr() }
+ }
+
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.header_and_len().len
+ }
+
+ #[inline]
+ fn layout(len: usize) -> (Layout, usize) {
+ let (layout, items_offset) = Layout::new::<ThinVecInner<Header>>()
+ .extend(Layout::array::<Item>(len).expect("too big `ThinVec` requested"))
+ .expect("too big `ThinVec` requested");
+ let layout = layout.pad_to_align();
+ (layout, items_offset)
+ }
+}
+
+/// # Safety
+///
+/// The length reported must be exactly the number of items yielded.
+pub unsafe trait TrustedLen: ExactSizeIterator {}
+
+unsafe impl<T> TrustedLen for std::vec::IntoIter<T> {}
+unsafe impl<T> TrustedLen for std::slice::Iter<'_, T> {}
+unsafe impl<'a, T: Clone + 'a, I: TrustedLen<Item = &'a T>> TrustedLen for std::iter::Cloned<I> {}
+unsafe impl<T, I: TrustedLen, F: FnMut(I::Item) -> T> TrustedLen for std::iter::Map<I, F> {}
+unsafe impl<T> TrustedLen for std::vec::Drain<'_, T> {}
+unsafe impl<T, const N: usize> TrustedLen for std::array::IntoIter<T, N> {}
+
+impl<Header: Clone, Item: Clone> Clone for ThinVecWithHeader<Header, Item> {
+ #[inline]
+ fn clone(&self) -> Self {
+ Self::from_iter(self.header().clone(), self.items().iter().cloned())
+ }
+}
+
+impl<Header, Item> Drop for ThinVecWithHeader<Header, Item> {
+ #[inline]
+ fn drop(&mut self) {
+ // This must come before we drop `header`, because after that we cannot make a reference to it in `len()`.
+ let len = self.len();
+
+ // SAFETY: The contents are allocated and initialized.
+ unsafe {
+ addr_of_mut!((*self.ptr.as_ptr()).header).drop_in_place();
+ self.items_ptr().drop_in_place();
+ }
+
+ let (layout, _) = Self::layout(len);
+ // SAFETY: This was allocated in `new()` with the same layout calculation.
+ unsafe {
+ dealloc(self.ptr.as_ptr().cast::<u8>(), layout);
+ }
+ }
+}
+
+impl<Header: fmt::Debug, Item: fmt::Debug> fmt::Debug for ThinVecWithHeader<Header, Item> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("ThinVecWithHeader")
+ .field("header", self.header())
+ .field("items", &self.items())
+ .finish()
+ }
+}
+
+impl<Header: PartialEq, Item: PartialEq> PartialEq for ThinVecWithHeader<Header, Item> {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.header() == other.header() && self.items() == other.items()
+ }
+}
+
+impl<Header: Eq, Item: Eq> Eq for ThinVecWithHeader<Header, Item> {}
+
+impl<Header: Hash, Item: Hash> Hash for ThinVecWithHeader<Header, Item> {
+ #[inline]
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.header().hash(state);
+ self.items().hash(state);
+ }
+}
+
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct ThinVec<T>(ThinVecWithHeader<(), T>);
+
+impl<T> ThinVec<T> {
+ #[inline]
+ #[allow(clippy::should_implement_trait)]
+ pub fn from_iter<I>(values: I) -> Self
+ where
+ I: IntoIterator,
+ I::IntoIter: TrustedLen<Item = T>,
+ {
+ Self(ThinVecWithHeader::from_iter((), values))
+ }
+
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.0.len()
+ }
+
+ #[inline]
+ pub fn iter(&self) -> std::slice::Iter<'_, T> {
+ (**self).iter()
+ }
+
+ #[inline]
+ pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
+ (**self).iter_mut()
+ }
+}
+
+impl<T> Deref for ThinVec<T> {
+ type Target = [T];
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ self.0.items()
+ }
+}
+
+impl<T> DerefMut for ThinVec<T> {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.0.items_mut()
+ }
+}
+
+impl<'a, T> IntoIterator for &'a ThinVec<T> {
+ type IntoIter = std::slice::Iter<'a, T>;
+ type Item = &'a T;
+
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+impl<'a, T> IntoIterator for &'a mut ThinVec<T> {
+ type IntoIter = std::slice::IterMut<'a, T>;
+ type Item = &'a mut T;
+
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter_mut()
+ }
+}
+
+impl<T: fmt::Debug> fmt::Debug for ThinVec<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().entries(&**self).finish()
+ }
+}
+
+/// A [`ThinVec`] that requires no allocation for the empty case.
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct EmptyOptimizedThinVec<T>(Option<ThinVec<T>>);
+
+impl<T> EmptyOptimizedThinVec<T> {
+ #[inline]
+ #[allow(clippy::should_implement_trait)]
+ pub fn from_iter<I>(values: I) -> Self
+ where
+ I: IntoIterator,
+ I::IntoIter: TrustedLen<Item = T>,
+ {
+ let values = values.into_iter();
+ if values.len() == 0 {
+ Self::empty()
+ } else {
+ Self(Some(ThinVec::from_iter(values)))
+ }
+ }
+
+ #[inline]
+ pub fn empty() -> Self {
+ Self(None)
+ }
+
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.0.as_ref().map_or(0, ThinVec::len)
+ }
+
+ #[inline]
+ pub fn iter(&self) -> std::slice::Iter<'_, T> {
+ (**self).iter()
+ }
+
+ #[inline]
+ pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
+ (**self).iter_mut()
+ }
+}
+
+impl<T> Default for EmptyOptimizedThinVec<T> {
+ #[inline]
+ fn default() -> Self {
+ Self::empty()
+ }
+}
+
+impl<T> Deref for EmptyOptimizedThinVec<T> {
+ type Target = [T];
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ self.0.as_deref().unwrap_or_default()
+ }
+}
+
+impl<T> DerefMut for EmptyOptimizedThinVec<T> {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ self.0.as_deref_mut().unwrap_or_default()
+ }
+}
+
+impl<'a, T> IntoIterator for &'a EmptyOptimizedThinVec<T> {
+ type IntoIter = std::slice::Iter<'a, T>;
+ type Item = &'a T;
+
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+impl<'a, T> IntoIterator for &'a mut EmptyOptimizedThinVec<T> {
+ type IntoIter = std::slice::IterMut<'a, T>;
+ type Item = &'a mut T;
+
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter_mut()
+ }
+}
+
+impl<T: fmt::Debug> fmt::Debug for EmptyOptimizedThinVec<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().entries(&**self).finish()
+ }
+}
+
+/// Syntax:
+///
+/// ```ignore
+/// thin_vec_with_header_struct! {
+/// pub new(pub(crate)) struct MyCoolStruct, MyCoolStructHeader {
+/// pub(crate) variable_length: [Ty],
+/// pub field1: CopyTy,
+/// pub field2: NonCopyTy; ref,
+/// }
+/// }
+/// ```
+#[doc(hidden)]
+#[macro_export]
+macro_rules! thin_vec_with_header_struct_ {
+ (@maybe_ref (ref) $($t:tt)*) => { &$($t)* };
+ (@maybe_ref () $($t:tt)*) => { $($t)* };
+ (
+ $vis:vis new($new_vis:vis) struct $struct:ident, $header:ident {
+ $items_vis:vis $items:ident : [$items_ty:ty],
+ $( $header_var_vis:vis $header_var:ident : $header_var_ty:ty $(; $ref:ident)?, )+
+ }
+ ) => {
+ #[derive(Debug, Clone, Eq, PartialEq, Hash)]
+ struct $header {
+ $( $header_var : $header_var_ty, )+
+ }
+
+ #[derive(Clone, Eq, PartialEq, Hash)]
+ $vis struct $struct($crate::thin_vec::ThinVecWithHeader<$header, $items_ty>);
+
+ impl $struct {
+ #[inline]
+ #[allow(unused)]
+ $new_vis fn new<I>(
+ $( $header_var: $header_var_ty, )+
+ $items: I,
+ ) -> Self
+ where
+ I: ::std::iter::IntoIterator,
+ I::IntoIter: $crate::thin_vec::TrustedLen<Item = $items_ty>,
+ {
+ Self($crate::thin_vec::ThinVecWithHeader::from_iter(
+ $header { $( $header_var, )+ },
+ $items,
+ ))
+ }
+
+ #[inline]
+ $items_vis fn $items(&self) -> &[$items_ty] {
+ self.0.items()
+ }
+
+ $(
+ #[inline]
+ $header_var_vis fn $header_var(&self) -> $crate::thin_vec_with_header_struct_!(@maybe_ref ($($ref)?) $header_var_ty) {
+ $crate::thin_vec_with_header_struct_!(@maybe_ref ($($ref)?) self.0.header().$header_var)
+ }
+ )+
+ }
+
+ impl ::std::fmt::Debug for $struct {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ f.debug_struct(stringify!($struct))
+ $( .field(stringify!($header_var), &self.$header_var()) )*
+ .field(stringify!($items), &self.$items())
+ .finish()
+ }
+ }
+ };
+}
+pub use crate::thin_vec_with_header_struct_ as thin_vec_with_header_struct;
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index fcb9b0ea35..51eaea5434 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -27,7 +27,6 @@ ra-ap-rustc_lexer.workspace = true
parser.workspace = true
stdx.workspace = true
-text-edit.workspace = true
[dev-dependencies]
rayon.workspace = true
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 90441c27f6..02c59646a9 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -657,7 +657,14 @@ TypeBoundList =
TypeBound =
Lifetime
| ('~' 'const' | 'const')? 'async'? '?'? Type
-| 'use' GenericParamList
+| 'use' UseBoundGenericArgs
+
+UseBoundGenericArgs =
+ '<' (UseBoundGenericArg (',' UseBoundGenericArg)* ','?)? '>'
+
+UseBoundGenericArg =
+ Lifetime
+| NameRef
//************************//
// Patterns //
@@ -729,7 +736,7 @@ PathPat =
Path
OrPat =
- (Pat ('|' Pat)* '|'?)
+ '|'? (Pat ('|' Pat)*)
BoxPat =
'box' Pat
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 8dc6d36a7e..2acb215831 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -1,11 +1,6 @@
//! Collection of assorted algorithms for syntax trees.
-use std::hash::BuildHasherDefault;
-
-use indexmap::IndexMap;
use itertools::Itertools;
-use rustc_hash::FxHashMap;
-use text_edit::TextEditBuilder;
use crate::{
AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange,
@@ -101,559 +96,3 @@ pub fn neighbor<T: AstNode>(me: &T, direction: Direction) -> Option<T> {
pub fn has_errors(node: &SyntaxNode) -> bool {
node.children().any(|it| it.kind() == SyntaxKind::ERROR)
}
-
-type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>;
-
-#[derive(Debug, Hash, PartialEq, Eq)]
-enum TreeDiffInsertPos {
- After(SyntaxElement),
- AsFirstChild(SyntaxElement),
-}
-
-#[derive(Debug)]
-pub struct TreeDiff {
- replacements: FxHashMap<SyntaxElement, SyntaxElement>,
- deletions: Vec<SyntaxElement>,
- // the vec as well as the indexmap are both here to preserve order
- insertions: FxIndexMap<TreeDiffInsertPos, Vec<SyntaxElement>>,
-}
-
-impl TreeDiff {
- pub fn into_text_edit(&self, builder: &mut TextEditBuilder) {
- let _p = tracing::info_span!("into_text_edit").entered();
-
- for (anchor, to) in &self.insertions {
- let offset = match anchor {
- TreeDiffInsertPos::After(it) => it.text_range().end(),
- TreeDiffInsertPos::AsFirstChild(it) => it.text_range().start(),
- };
- to.iter().for_each(|to| builder.insert(offset, to.to_string()));
- }
- for (from, to) in &self.replacements {
- builder.replace(from.text_range(), to.to_string());
- }
- for text_range in self.deletions.iter().map(SyntaxElement::text_range) {
- builder.delete(text_range);
- }
- }
-
- pub fn is_empty(&self) -> bool {
- self.replacements.is_empty() && self.deletions.is_empty() && self.insertions.is_empty()
- }
-}
-
-/// Finds a (potentially minimal) diff, which, applied to `from`, will result in `to`.
-///
-/// Specifically, returns a structure that consists of a replacements, insertions and deletions
-/// such that applying this map on `from` will result in `to`.
-///
-/// This function tries to find a fine-grained diff.
-pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
- let _p = tracing::info_span!("diff").entered();
-
- let mut diff = TreeDiff {
- replacements: FxHashMap::default(),
- insertions: FxIndexMap::default(),
- deletions: Vec::new(),
- };
- let (from, to) = (from.clone().into(), to.clone().into());
-
- if !syntax_element_eq(&from, &to) {
- go(&mut diff, from, to);
- }
- return diff;
-
- fn syntax_element_eq(lhs: &SyntaxElement, rhs: &SyntaxElement) -> bool {
- lhs.kind() == rhs.kind()
- && lhs.text_range().len() == rhs.text_range().len()
- && match (&lhs, &rhs) {
- (NodeOrToken::Node(lhs), NodeOrToken::Node(rhs)) => {
- lhs == rhs || lhs.text() == rhs.text()
- }
- (NodeOrToken::Token(lhs), NodeOrToken::Token(rhs)) => lhs.text() == rhs.text(),
- _ => false,
- }
- }
-
- // FIXME: this is horribly inefficient. I bet there's a cool algorithm to diff trees properly.
- fn go(diff: &mut TreeDiff, lhs: SyntaxElement, rhs: SyntaxElement) {
- let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) {
- Some((lhs, rhs)) => (lhs, rhs),
- _ => {
- cov_mark::hit!(diff_node_token_replace);
- diff.replacements.insert(lhs, rhs);
- return;
- }
- };
-
- let mut look_ahead_scratch = Vec::default();
-
- let mut rhs_children = rhs.children_with_tokens();
- let mut lhs_children = lhs.children_with_tokens();
- let mut last_lhs = None;
- loop {
- let lhs_child = lhs_children.next();
- match (lhs_child.clone(), rhs_children.next()) {
- (None, None) => break,
- (None, Some(element)) => {
- let insert_pos = match last_lhs.clone() {
- Some(prev) => {
- cov_mark::hit!(diff_insert);
- TreeDiffInsertPos::After(prev)
- }
- // first iteration, insert into out parent as the first child
- None => {
- cov_mark::hit!(diff_insert_as_first_child);
- TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
- }
- };
- diff.insertions.entry(insert_pos).or_default().push(element);
- }
- (Some(element), None) => {
- cov_mark::hit!(diff_delete);
- diff.deletions.push(element);
- }
- (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {}
- (Some(lhs_ele), Some(rhs_ele)) => {
- // nodes differ, look for lhs_ele in rhs, if its found we can mark everything up
- // until that element as insertions. This is important to keep the diff minimal
- // in regards to insertions that have been actually done, this is important for
- // use insertions as we do not want to replace the entire module node.
- look_ahead_scratch.push(rhs_ele.clone());
- let mut rhs_children_clone = rhs_children.clone();
- let mut insert = false;
- for rhs_child in &mut rhs_children_clone {
- if syntax_element_eq(&lhs_ele, &rhs_child) {
- cov_mark::hit!(diff_insertions);
- insert = true;
- break;
- }
- look_ahead_scratch.push(rhs_child);
- }
- let drain = look_ahead_scratch.drain(..);
- if insert {
- let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) {
- TreeDiffInsertPos::After(prev)
- } else {
- cov_mark::hit!(insert_first_child);
- TreeDiffInsertPos::AsFirstChild(lhs.clone().into())
- };
-
- diff.insertions.entry(insert_pos).or_default().extend(drain);
- rhs_children = rhs_children_clone;
- } else {
- go(diff, lhs_ele, rhs_ele);
- }
- }
- }
- last_lhs = lhs_child.or(last_lhs);
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use expect_test::{expect, Expect};
- use itertools::Itertools;
- use parser::{Edition, SyntaxKind};
- use text_edit::TextEdit;
-
- use crate::{AstNode, SyntaxElement};
-
- #[test]
- fn replace_node_token() {
- cov_mark::check!(diff_node_token_replace);
- check_diff(
- r#"use node;"#,
- r#"ident"#,
- expect![[r#"
- insertions:
-
-
-
- replacements:
-
- Line 0: Token([email protected] "use") -> ident
-
- deletions:
-
- Line 1: " "
- Line 1: node
- Line 1: ;
- "#]],
- );
- }
-
- #[test]
- fn replace_parent() {
- cov_mark::check!(diff_insert_as_first_child);
- check_diff(
- r#""#,
- r#"use foo::bar;"#,
- expect![[r#"
- insertions:
-
- Line 0: AsFirstChild(Node([email protected]))
- -> use foo::bar;
-
- replacements:
-
-
-
- deletions:
-
-
- "#]],
- );
- }
-
- #[test]
- fn insert_last() {
- cov_mark::check!(diff_insert);
- check_diff(
- r#"
-use foo;
-use bar;"#,
- r#"
-use foo;
-use bar;
-use baz;"#,
- expect![[r#"
- insertions:
-
- Line 2: After(Node([email protected]))
- -> "\n"
- -> use baz;
-
- replacements:
-
-
-
- deletions:
-
-
- "#]],
- );
- }
-
- #[test]
- fn insert_middle() {
- check_diff(
- r#"
-use foo;
-use baz;"#,
- r#"
-use foo;
-use bar;
-use baz;"#,
- expect![[r#"
- insertions:
-
- Line 2: After(Token([email protected] "\n"))
- -> use bar;
- -> "\n"
-
- replacements:
-
-
-
- deletions:
-
-
- "#]],
- )
- }
-
- #[test]
- fn insert_first() {
- check_diff(
- r#"
-use bar;
-use baz;"#,
- r#"
-use foo;
-use bar;
-use baz;"#,
- expect![[r#"
- insertions:
-
- Line 0: After(Token([email protected] "\n"))
- -> use foo;
- -> "\n"
-
- replacements:
-
-
-
- deletions:
-
-
- "#]],
- )
- }
-
- #[test]
- fn first_child_insertion() {
- cov_mark::check!(insert_first_child);
- check_diff(
- r#"fn main() {
- stdi
- }"#,
- r#"use foo::bar;
-
- fn main() {
- stdi
- }"#,
- expect![[r#"
- insertions:
-
- Line 0: AsFirstChild(Node([email protected]))
- -> use foo::bar;
- -> "\n\n "
-
- replacements:
-
-
-
- deletions:
-
-
- "#]],
- );
- }
-
- #[test]
- fn delete_last() {
- cov_mark::check!(diff_delete);
- check_diff(
- r#"use foo;
- use bar;"#,
- r#"use foo;"#,
- expect![[r#"
- insertions:
-
-
-
- replacements:
-
-
-
- deletions:
-
- Line 1: "\n "
- Line 2: use bar;
- "#]],
- );
- }
-
- #[test]
- fn delete_middle() {
- cov_mark::check!(diff_insertions);
- check_diff(
- r#"
-use expect_test::{expect, Expect};
-use text_edit::TextEdit;
-
-use crate::AstNode;
-"#,
- r#"
-use expect_test::{expect, Expect};
-
-use crate::AstNode;
-"#,
- expect![[r#"
- insertions:
-
- Line 1: After(Node([email protected]))
- -> "\n\n"
- -> use crate::AstNode;
-
- replacements:
-
-
-
- deletions:
-
- Line 2: use text_edit::TextEdit;
- Line 3: "\n\n"
- Line 4: use crate::AstNode;
- Line 5: "\n"
- "#]],
- )
- }
-
- #[test]
- fn delete_first() {
- check_diff(
- r#"
-use text_edit::TextEdit;
-
-use crate::AstNode;
-"#,
- r#"
-use crate::AstNode;
-"#,
- expect![[r#"
- insertions:
-
-
-
- replacements:
-
- Line 2: Token([email protected] "text_edit") -> crate
- Line 2: Token([email protected] "TextEdit") -> AstNode
- Line 2: Token([email protected] "\n\n") -> "\n"
-
- deletions:
-
- Line 3: use crate::AstNode;
- Line 4: "\n"
- "#]],
- )
- }
-
- #[test]
- fn merge_use() {
- check_diff(
- r#"
-use std::{
- fmt,
- hash::BuildHasherDefault,
- ops::{self, RangeInclusive},
-};
-"#,
- r#"
-use std::fmt;
-use std::hash::BuildHasherDefault;
-use std::ops::{self, RangeInclusive};
-"#,
- expect![[r#"
- insertions:
-
- Line 2: After(Node([email protected]))
- -> ::
- -> fmt
- Line 6: After(Token([email protected] "\n"))
- -> use std::hash::BuildHasherDefault;
- -> "\n"
- -> use std::ops::{self, RangeInclusive};
- -> "\n"
-
- replacements:
-
- Line 2: Token([email protected] "std") -> std
-
- deletions:
-
- Line 2: ::
- Line 2: {
- fmt,
- hash::BuildHasherDefault,
- ops::{self, RangeInclusive},
- }
- "#]],
- )
- }
-
- #[test]
- fn early_return_assist() {
- check_diff(
- r#"
-fn main() {
- if let Ok(x) = Err(92) {
- foo(x);
- }
-}
- "#,
- r#"
-fn main() {
- let x = match Err(92) {
- Ok(it) => it,
- _ => return,
- };
- foo(x);
-}
- "#,
- expect![[r#"
- insertions:
-
- Line 3: After(Node([email protected]))
- -> " "
- -> match Err(92) {
- Ok(it) => it,
- _ => return,
- }
- -> ;
- Line 3: After(Node([email protected]))
- -> "\n "
- -> foo(x);
-
- replacements:
-
- Line 3: Token([email protected] "if") -> let
- Line 3: Token([email protected] "let") -> x
- Line 3: Node([email protected]) -> =
-
- deletions:
-
- Line 3: " "
- Line 3: Ok(x)
- Line 3: " "
- Line 3: =
- Line 3: " "
- Line 3: Err(92)
- "#]],
- )
- }
-
- fn check_diff(from: &str, to: &str, expected_diff: Expect) {
- let from_node = crate::SourceFile::parse(from, Edition::CURRENT).tree().syntax().clone();
- let to_node = crate::SourceFile::parse(to, Edition::CURRENT).tree().syntax().clone();
- let diff = super::diff(&from_node, &to_node);
-
- let line_number =
- |syn: &SyntaxElement| from[..syn.text_range().start().into()].lines().count();
-
- let fmt_syntax = |syn: &SyntaxElement| match syn.kind() {
- SyntaxKind::WHITESPACE => format!("{:?}", syn.to_string()),
- _ => format!("{syn}"),
- };
-
- let insertions =
- diff.insertions.iter().format_with("\n", |(k, v), f| -> Result<(), std::fmt::Error> {
- f(&format!(
- "Line {}: {:?}\n-> {}",
- line_number(match k {
- super::TreeDiffInsertPos::After(syn) => syn,
- super::TreeDiffInsertPos::AsFirstChild(syn) => syn,
- }),
- k,
- v.iter().format_with("\n-> ", |v, f| f(&fmt_syntax(v)))
- ))
- });
-
- let replacements = diff
- .replacements
- .iter()
- .sorted_by_key(|(syntax, _)| syntax.text_range().start())
- .format_with("\n", |(k, v), f| {
- f(&format!("Line {}: {k:?} -> {}", line_number(k), fmt_syntax(v)))
- });
-
- let deletions = diff
- .deletions
- .iter()
- .format_with("\n", |v, f| f(&format!("Line {}: {}", line_number(v), fmt_syntax(v))));
-
- let actual = format!(
- "insertions:\n\n{insertions}\n\nreplacements:\n\n{replacements}\n\ndeletions:\n\n{deletions}\n"
- );
- expected_diff.assert_eq(&actual);
-
- let mut from = from.to_owned();
- let mut text_edit = TextEdit::builder();
- diff.into_text_edit(&mut text_edit);
- text_edit.finish().apply(&mut from);
- assert_eq!(&*from, to, "diff did not turn `from` to `to`");
- }
-}
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs
index 6ed205e285..f3053f5983 100644
--- a/crates/syntax/src/ast/expr_ext.rs
+++ b/crates/syntax/src/ast/expr_ext.rs
@@ -232,6 +232,10 @@ impl ast::RangeExpr {
Some((ix, token, bin_op))
})
}
+
+ pub fn is_range_full(&self) -> bool {
+ support::children::<Expr>(&self.syntax).next().is_none()
+ }
}
impl RangeItem for ast::RangeExpr {
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 4f8bff489c..23d2b355a9 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -1283,6 +1283,8 @@ pub struct OrPat {
impl OrPat {
#[inline]
pub fn pats(&self) -> AstChildren<Pat> { support::children(&self.syntax) }
+ #[inline]
+ pub fn pipe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![|]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -1994,12 +1996,14 @@ pub struct TypeBound {
}
impl TypeBound {
#[inline]
- pub fn generic_param_list(&self) -> Option<GenericParamList> { support::child(&self.syntax) }
- #[inline]
pub fn lifetime(&self) -> Option<Lifetime> { support::child(&self.syntax) }
#[inline]
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
#[inline]
+ pub fn use_bound_generic_args(&self) -> Option<UseBoundGenericArgs> {
+ support::child(&self.syntax)
+ }
+ #[inline]
pub fn question_mark_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![?]) }
#[inline]
pub fn async_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![async]) }
@@ -2077,6 +2081,21 @@ impl Use {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct UseBoundGenericArgs {
+ pub(crate) syntax: SyntaxNode,
+}
+impl UseBoundGenericArgs {
+ #[inline]
+ pub fn use_bound_generic_args(&self) -> AstChildren<UseBoundGenericArg> {
+ support::children(&self.syntax)
+ }
+ #[inline]
+ pub fn l_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![<]) }
+ #[inline]
+ pub fn r_angle_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![>]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UseTree {
pub(crate) syntax: SyntaxNode,
}
@@ -2403,6 +2422,12 @@ pub enum Type {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum UseBoundGenericArg {
+ Lifetime(Lifetime),
+ NameRef(NameRef),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AnyHasArgList {
pub(crate) syntax: SyntaxNode,
}
@@ -4435,6 +4460,20 @@ impl AstNode for Use {
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for UseBoundGenericArgs {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == USE_BOUND_GENERIC_ARGS }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
impl AstNode for UseTree {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == USE_TREE }
@@ -5560,6 +5599,34 @@ impl AstNode for Type {
}
}
}
+impl From<Lifetime> for UseBoundGenericArg {
+ #[inline]
+ fn from(node: Lifetime) -> UseBoundGenericArg { UseBoundGenericArg::Lifetime(node) }
+}
+impl From<NameRef> for UseBoundGenericArg {
+ #[inline]
+ fn from(node: NameRef) -> UseBoundGenericArg { UseBoundGenericArg::NameRef(node) }
+}
+impl AstNode for UseBoundGenericArg {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, LIFETIME | NAME_REF) }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ LIFETIME => UseBoundGenericArg::Lifetime(Lifetime { syntax }),
+ NAME_REF => UseBoundGenericArg::NameRef(NameRef { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ UseBoundGenericArg::Lifetime(it) => &it.syntax,
+ UseBoundGenericArg::NameRef(it) => &it.syntax,
+ }
+ }
+}
impl AnyHasArgList {
#[inline]
pub fn new<T: ast::HasArgList>(node: T) -> AnyHasArgList {
@@ -6570,6 +6637,11 @@ impl std::fmt::Display for Type {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for UseBoundGenericArg {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Abi {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -7275,6 +7347,11 @@ impl std::fmt::Display for Use {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for UseBoundGenericArgs {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for UseTree {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 693bfe330b..6ec73e76f7 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -795,7 +795,7 @@ pub enum TypeBoundKind {
/// for<'a> ...
ForType(ast::ForType),
/// use
- Use(ast::GenericParamList),
+ Use(ast::UseBoundGenericArgs),
/// 'a
Lifetime(ast::Lifetime),
}
@@ -806,8 +806,8 @@ impl ast::TypeBound {
TypeBoundKind::PathType(path_type)
} else if let Some(for_type) = support::children(self.syntax()).next() {
TypeBoundKind::ForType(for_type)
- } else if let Some(generic_param_list) = self.generic_param_list() {
- TypeBoundKind::Use(generic_param_list)
+ } else if let Some(args) = self.use_bound_generic_args() {
+ TypeBoundKind::Use(args)
} else if let Some(lifetime) = self.lifetime() {
TypeBoundKind::Lifetime(lifetime)
} else {
@@ -1140,3 +1140,13 @@ impl From<ast::AssocItem> for ast::AnyHasAttrs {
Self::new(node)
}
}
+
+impl ast::OrPat {
+ pub fn leading_pipe(&self) -> Option<SyntaxToken> {
+ self.syntax
+ .children_with_tokens()
+ .find(|it| !it.kind().is_trivia())
+ .and_then(NodeOrToken::into_token)
+ .filter(|it| it.kind() == T![|])
+ }
+}
diff --git a/crates/syntax/src/fuzz.rs b/crates/syntax/src/fuzz.rs
index 682dcd7cc4..fd20e603ed 100644
--- a/crates/syntax/src/fuzz.rs
+++ b/crates/syntax/src/fuzz.rs
@@ -5,7 +5,6 @@
use std::str::{self, FromStr};
use parser::Edition;
-use text_edit::Indel;
use crate::{validation, AstNode, SourceFile, TextRange};
@@ -22,7 +21,8 @@ pub fn check_parser(text: &str) {
#[derive(Debug, Clone)]
pub struct CheckReparse {
text: String,
- edit: Indel,
+ delete: TextRange,
+ insert: String,
edited_text: String,
}
@@ -43,14 +43,13 @@ impl CheckReparse {
TextRange::at(delete_start.try_into().unwrap(), delete_len.try_into().unwrap());
let edited_text =
format!("{}{}{}", &text[..delete_start], &insert, &text[delete_start + delete_len..]);
- let edit = Indel { insert, delete };
- Some(CheckReparse { text, edit, edited_text })
+ Some(CheckReparse { text, insert, delete, edited_text })
}
#[allow(clippy::print_stderr)]
pub fn run(&self) {
let parse = SourceFile::parse(&self.text, Edition::CURRENT);
- let new_parse = parse.reparse(&self.edit, Edition::CURRENT);
+ let new_parse = parse.reparse(self.delete, &self.insert, Edition::CURRENT);
check_file_invariants(&new_parse.tree());
assert_eq!(&new_parse.tree().syntax().text().to_string(), &self.edited_text);
let full_reparse = SourceFile::parse(&self.edited_text, Edition::CURRENT);
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index c1554c4b29..c9e9f468dc 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -44,10 +44,9 @@ pub mod syntax_editor;
pub mod ted;
pub mod utils;
-use std::marker::PhantomData;
+use std::{marker::PhantomData, ops::Range};
use stdx::format_to;
-use text_edit::Indel;
use triomphe::Arc;
pub use crate::{
@@ -150,16 +149,22 @@ impl Parse<SourceFile> {
buf
}
- pub fn reparse(&self, indel: &Indel, edition: Edition) -> Parse<SourceFile> {
- self.incremental_reparse(indel, edition)
- .unwrap_or_else(|| self.full_reparse(indel, edition))
+ pub fn reparse(&self, delete: TextRange, insert: &str, edition: Edition) -> Parse<SourceFile> {
+ self.incremental_reparse(delete, insert, edition)
+ .unwrap_or_else(|| self.full_reparse(delete, insert, edition))
}
- fn incremental_reparse(&self, indel: &Indel, edition: Edition) -> Option<Parse<SourceFile>> {
+ fn incremental_reparse(
+ &self,
+ delete: TextRange,
+ insert: &str,
+ edition: Edition,
+ ) -> Option<Parse<SourceFile>> {
// FIXME: validation errors are not handled here
parsing::incremental_reparse(
self.tree().syntax(),
- indel,
+ delete,
+ insert,
self.errors.as_deref().unwrap_or_default().iter().cloned(),
edition,
)
@@ -170,9 +175,9 @@ impl Parse<SourceFile> {
})
}
- fn full_reparse(&self, indel: &Indel, edition: Edition) -> Parse<SourceFile> {
+ fn full_reparse(&self, delete: TextRange, insert: &str, edition: Edition) -> Parse<SourceFile> {
let mut text = self.tree().syntax().text().to_string();
- indel.apply(&mut text);
+ text.replace_range(Range::<usize>::from(delete), insert);
SourceFile::parse(&text, edition)
}
}
diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs
index a5cc4e90df..f2eab18c27 100644
--- a/crates/syntax/src/parsing/reparsing.rs
+++ b/crates/syntax/src/parsing/reparsing.rs
@@ -6,8 +6,9 @@
//! - otherwise, we search for the nearest `{}` block which contains the edit
//! and try to parse only this block.
+use std::ops::Range;
+
use parser::{Edition, Reparser};
-use text_edit::Indel;
use crate::{
parsing::build_tree,
@@ -19,38 +20,48 @@ use crate::{
pub(crate) fn incremental_reparse(
node: &SyntaxNode,
- edit: &Indel,
+ delete: TextRange,
+ insert: &str,
errors: impl IntoIterator<Item = SyntaxError>,
edition: Edition,
) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
- if let Some((green, new_errors, old_range)) = reparse_token(node, edit, edition) {
- return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
+ if let Some((green, new_errors, old_range)) = reparse_token(node, delete, insert, edition) {
+ return Some((
+ green,
+ merge_errors(errors, new_errors, old_range, delete, insert),
+ old_range,
+ ));
}
- if let Some((green, new_errors, old_range)) = reparse_block(node, edit, edition) {
- return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
+ if let Some((green, new_errors, old_range)) = reparse_block(node, delete, insert, edition) {
+ return Some((
+ green,
+ merge_errors(errors, new_errors, old_range, delete, insert),
+ old_range,
+ ));
}
None
}
fn reparse_token(
root: &SyntaxNode,
- edit: &Indel,
+ delete: TextRange,
+ insert: &str,
edition: Edition,
) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
- let prev_token = root.covering_element(edit.delete).as_token()?.clone();
+ let prev_token = root.covering_element(delete).as_token()?.clone();
let prev_token_kind = prev_token.kind();
match prev_token_kind {
WHITESPACE | COMMENT | IDENT | STRING | BYTE_STRING | C_STRING => {
if prev_token_kind == WHITESPACE || prev_token_kind == COMMENT {
// removing a new line may extends previous token
- let deleted_range = edit.delete - prev_token.text_range().start();
+ let deleted_range = delete - prev_token.text_range().start();
if prev_token.text()[deleted_range].contains('\n') {
return None;
}
}
- let mut new_text = get_text_after_edit(prev_token.clone().into(), edit);
+ let mut new_text = get_text_after_edit(prev_token.clone().into(), delete, insert);
let (new_token_kind, new_err) = parser::LexedStr::single_token(edition, &new_text)?;
if new_token_kind != prev_token_kind
@@ -85,11 +96,12 @@ fn reparse_token(
fn reparse_block(
root: &SyntaxNode,
- edit: &Indel,
+ delete: TextRange,
+ insert: &str,
edition: parser::Edition,
) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
- let (node, reparser) = find_reparsable_node(root, edit.delete)?;
- let text = get_text_after_edit(node.clone().into(), edit);
+ let (node, reparser) = find_reparsable_node(root, delete)?;
+ let text = get_text_after_edit(node.clone().into(), delete, insert);
let lexed = parser::LexedStr::new(edition, text.as_str());
let parser_input = lexed.to_input(edition);
@@ -104,14 +116,14 @@ fn reparse_block(
Some((node.replace_with(green), new_parser_errors, node.text_range()))
}
-fn get_text_after_edit(element: SyntaxElement, edit: &Indel) -> String {
- let edit = Indel::replace(edit.delete - element.text_range().start(), edit.insert.clone());
+fn get_text_after_edit(element: SyntaxElement, mut delete: TextRange, insert: &str) -> String {
+ delete -= element.text_range().start();
let mut text = match element {
NodeOrToken::Token(token) => token.text().to_owned(),
NodeOrToken::Node(node) => node.text().to_string(),
};
- edit.apply(&mut text);
+ text.replace_range(Range::<usize>::from(delete), insert);
text
}
@@ -153,7 +165,8 @@ fn merge_errors(
old_errors: impl IntoIterator<Item = SyntaxError>,
new_errors: Vec<SyntaxError>,
range_before_reparse: TextRange,
- edit: &Indel,
+ delete: TextRange,
+ insert: &str,
) -> Vec<SyntaxError> {
let mut res = Vec::new();
@@ -162,8 +175,8 @@ fn merge_errors(
if old_err_range.end() <= range_before_reparse.start() {
res.push(old_err);
} else if old_err_range.start() >= range_before_reparse.end() {
- let inserted_len = TextSize::of(&edit.insert);
- res.push(old_err.with_range((old_err_range + inserted_len) - edit.delete.len()));
+ let inserted_len = TextSize::of(insert);
+ res.push(old_err.with_range((old_err_range + inserted_len) - delete.len()));
// Note: extra parens are intentional to prevent uint underflow, HWAB (here was a bug)
}
}
@@ -177,6 +190,8 @@ fn merge_errors(
#[cfg(test)]
mod tests {
+ use std::ops::Range;
+
use parser::Edition;
use test_utils::{assert_eq_text, extract_range};
@@ -185,10 +200,9 @@ mod tests {
fn do_check(before: &str, replace_with: &str, reparsed_len: u32) {
let (range, before) = extract_range(before);
- let edit = Indel::replace(range, replace_with.to_owned());
let after = {
let mut after = before.clone();
- edit.apply(&mut after);
+ after.replace_range(Range::<usize>::from(range), replace_with);
after
};
@@ -197,7 +211,8 @@ mod tests {
let before = SourceFile::parse(&before, Edition::CURRENT);
let (green, new_errors, range) = incremental_reparse(
before.tree().syntax(),
- &edit,
+ range,
+ replace_with,
before.errors.as_deref().unwrap_or_default().iter().cloned(),
Edition::CURRENT,
)
diff --git a/crates/text-edit/Cargo.toml b/crates/text-edit/Cargo.toml
deleted file mode 100644
index dc6b3d31a0..0000000000
--- a/crates/text-edit/Cargo.toml
+++ /dev/null
@@ -1,20 +0,0 @@
-[package]
-name = "text-edit"
-version = "0.0.0"
-repository.workspace = true
-description = "Representation of a `TextEdit` for rust-analyzer."
-
-authors.workspace = true
-edition.workspace = true
-license.workspace = true
-rust-version.workspace = true
-
-[lib]
-doctest = false
-
-[dependencies]
-itertools.workspace = true
-text-size.workspace = true
-
-[lints]
-workspace = true \ No newline at end of file
diff --git a/crates/tt/src/buffer.rs b/crates/tt/src/buffer.rs
index 1319739371..acb7e2d6c5 100644
--- a/crates/tt/src/buffer.rs
+++ b/crates/tt/src/buffer.rs
@@ -134,7 +134,7 @@ pub enum TokenTreeRef<'a, Span> {
Leaf(&'a Leaf<Span>, &'a TokenTree<Span>),
}
-impl<'a, Span: Copy> TokenTreeRef<'a, Span> {
+impl<Span: Copy> TokenTreeRef<'_, Span> {
pub fn span(&self) -> Span {
match self {
TokenTreeRef::Subtree(subtree, _) => subtree.delimiter.open,
diff --git a/editors/code/.vscodeignore b/editors/code/.vscodeignore
index 09dc27056b..1712a1477e 100644
--- a/editors/code/.vscodeignore
+++ b/editors/code/.vscodeignore
@@ -12,3 +12,4 @@
!ra_syntax_tree.tmGrammar.json
!server
!README.md
+!walkthrough-setup-tips.md
diff --git a/editors/code/package.json b/editors/code/package.json
index e55eceff78..6eebdf9f01 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -3224,10 +3224,9 @@
{
"id": "setup",
"title": "Useful Setup Tips",
- "description": "There are a couple of things you might want to configure upfront to your tastes. We'll name a few here but be sure to check out the docs linked below!\n\n**Marking library sources as readonly**\n\nAdding the following to your settings.json will mark all Rust library sources as readonly:\n```json\n\"files.readonlyInclude\": {\n \"**/.cargo/registry/src/**/*.rs\": true,\n \"**/lib/rustlib/src/rust/library/**/*.rs\": true,\n},\n```\n\n**Check on Save**\n\nBy default, rust-analyzer will run `cargo check` on your codebase when you save a file, rendering diagnostics emitted by `cargo check` within your code. This can potentially collide with other `cargo` commands running concurrently, blocking them from running for a certain amount of time. In these cases it is recommended to disable the `rust-analyzer.checkOnSave` configuration and running the `rust-analyzer: Run flycheck` command on-demand instead.",
+ "description": "There are a couple of things you might want to configure upfront to your tastes. We'll name a few here but be sure to check out the docs linked below!\n\n**Marking library sources as readonly**\n\nAdding the snippet on the right to your settings.json will mark all Rust library sources as readonly.\n\n**Check on Save**\n\nBy default, rust-analyzer will run ``cargo check`` on your codebase when you save a file, rendering diagnostics emitted by ``cargo check`` within your code. This can potentially collide with other ``cargo`` commands running concurrently, blocking them from running for a certain amount of time. In these cases it is recommended to disable the ``rust-analyzer.checkOnSave`` configuration and running the ``rust-analyzer: Run flycheck`` command on-demand instead.",
"media": {
- "image": "./icon.png",
- "altText": "rust-analyzer logo"
+ "markdown": "./walkthrough-setup-tips.md"
}
},
{
@@ -3245,7 +3244,7 @@
{
"id": "faq",
"title": "FAQ",
- "description": "What are these code hints that are being inserted into my code?\n\nThese hints are called inlay hints which rust-analyzer support and are enabled by default in VSCode. If you wish to disable them you can do so via the `editor.inlayHints.enabled` setting.",
+ "description": "What are these code hints that are being inserted into my code?\n\nThese hints are called inlay hints which rust-analyzer support and are enabled by default in VSCode. If you wish to disable them you can do so via the ``editor.inlayHints.enabled`` setting.",
"media": {
"image": "icon.png",
"altText": "rust-analyzer logo"
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 0f2a758db4..234fe6ab02 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -446,7 +446,7 @@ export class Ctx implements RustAnalyzerExtensionApi {
return;
}
if (status.message) {
- statusBar.tooltip.appendText(status.message);
+ statusBar.tooltip.appendMarkdown(status.message);
}
if (statusBar.tooltip.value) {
statusBar.tooltip.appendMarkdown("\n\n---\n\n");
diff --git a/editors/code/walkthrough-setup-tips.md b/editors/code/walkthrough-setup-tips.md
new file mode 100644
index 0000000000..fda4ac8002
--- /dev/null
+++ b/editors/code/walkthrough-setup-tips.md
@@ -0,0 +1,10 @@
+# Settings Example
+
+Add the following to settings.json to mark Rust library sources as read-only:
+
+```json
+"files.readonlyInclude": {
+ "**/.cargo/registry/src/**/*.rs": true,
+ "**/lib/rustlib/src/rust/library/**/*.rs": true,
+},
+```
diff --git a/rust-bors.toml b/rust-bors.toml
deleted file mode 100644
index c31ba66c50..0000000000
--- a/rust-bors.toml
+++ /dev/null
@@ -1 +0,0 @@
-timeout = 3600
diff --git a/rust-version b/rust-version
index bc324402a9..ffb312d06e 100644
--- a/rust-version
+++ b/rust-version
@@ -1 +1 @@
-1de57a5ce952c722f7053aeacfc6c90bc139b678
+a9d17627d241645a54c1134a20f1596127fedb60
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index 4c7b07c5e0..bc04b9474f 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -79,7 +79,7 @@ impl CommentBlock {
let mut block = dummy_block.clone();
for (line_num, line) in lines.enumerate() {
match line.strip_prefix("//") {
- Some(mut contents) => {
+ Some(mut contents) if !contents.starts_with('/') => {
if let Some('/' | '!') = contents.chars().next() {
contents = &contents[1..];
block.is_doc = true;
@@ -89,7 +89,7 @@ impl CommentBlock {
}
block.contents.push(contents.to_owned());
}
- None => {
+ _ => {
if !block.contents.is_empty() {
let block = mem::replace(&mut block, dummy_block.clone());
res.push(block);
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index 742cf7f609..c6a0be8aeb 100644
--- a/xtask/src/dist.rs
+++ b/xtask/src/dist.rs
@@ -101,9 +101,10 @@ fn dist_server(
cmd!(sh, "cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target_name} {features...} --release").run()?;
let dst = Path::new("dist").join(&target.artifact_name);
- gzip(&target.server_path, &dst.with_extension("gz"))?;
if target_name.contains("-windows-") {
zip(&target.server_path, target.symbols_path.as_ref(), &dst.with_extension("zip"))?;
+ } else {
+ gzip(&target.server_path, &dst.with_extension("gz"))?;
}
Ok(())
diff --git a/xtask/src/release/changelog.rs b/xtask/src/release/changelog.rs
index 086a4d463e..343a9efbbc 100644
--- a/xtask/src/release/changelog.rs
+++ b/xtask/src/release/changelog.rs
@@ -128,9 +128,10 @@ fn unescape(s: &str) -> String {
}
fn parse_pr_number(s: &str) -> Option<u32> {
- const BORS_PREFIX: &str = "Merge #";
+ const GITHUB_PREFIX: &str = "Merge pull request #";
const HOMU_PREFIX: &str = "Auto merge of #";
- if let Some(s) = s.strip_prefix(BORS_PREFIX) {
+ if let Some(s) = s.strip_prefix(GITHUB_PREFIX) {
+ let s = if let Some(space) = s.find(' ') { &s[..space] } else { s };
s.parse().ok()
} else if let Some(s) = s.strip_prefix(HOMU_PREFIX) {
if let Some(space) = s.find(' ') {
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs
index 0268e2473c..c3d531344a 100644
--- a/xtask/src/tidy.rs
+++ b/xtask/src/tidy.rs
@@ -223,7 +223,7 @@ struct TidyDocs {
impl TidyDocs {
fn visit(&mut self, path: &Path, text: &str) {
// Tests and diagnostic fixes don't need module level comments.
- if is_exclude_dir(path, &["tests", "test_data", "fixes", "grammar", "ra-salsa"]) {
+ if is_exclude_dir(path, &["tests", "test_data", "fixes", "grammar", "ra-salsa", "stdx"]) {
return;
}