Unnamed repository; edit this file 'description' to name the repository.
Rollup merge of #134170 - lnicola:sync-from-ra, r=lnicola
Subtree update of `rust-analyzer` r? `@ghost`
Matthias Krüger 2024-12-12
parent d41a896 · parent e7a4c99 · commit 48f34bb
-rw-r--r--Cargo.lock44
-rw-r--r--Cargo.toml10
-rw-r--r--crates/base-db/src/input.rs23
-rw-r--r--crates/hir-def/src/body.rs140
-rw-r--r--crates/hir-def/src/body/lower.rs14
-rw-r--r--crates/hir-def/src/body/tests.rs26
-rw-r--r--crates/hir-def/src/data.rs2
-rw-r--r--crates/hir-def/src/generics.rs27
-rw-r--r--crates/hir-def/src/hir/type_ref.rs86
-rw-r--r--crates/hir-def/src/item_tree/lower.rs9
-rw-r--r--crates/hir-def/src/item_tree/pretty.rs6
-rw-r--r--crates/hir-def/src/item_tree/tests.rs6
-rw-r--r--crates/hir-def/src/lang_item.rs3
-rw-r--r--crates/hir-def/src/lib.rs3
-rw-r--r--crates/hir-def/src/lower.rs32
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe.rs7
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe/matching.rs28
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mod.rs3
-rw-r--r--crates/hir-def/src/nameres/collector.rs9
-rw-r--r--crates/hir-def/src/nameres/tests.rs4
-rw-r--r--crates/hir-def/src/nameres/tests/globs.rs39
-rw-r--r--crates/hir-def/src/nameres/tests/incremental.rs105
-rw-r--r--crates/hir-def/src/path.rs12
-rw-r--r--crates/hir-def/src/path/lower.rs88
-rw-r--r--crates/hir-def/src/path/tests.rs126
-rw-r--r--crates/hir-def/src/pretty.rs4
-rw-r--r--crates/hir-def/src/resolver.rs14
-rw-r--r--crates/hir-def/src/test_db.rs13
-rw-r--r--crates/hir-expand/src/files.rs7
-rw-r--r--crates/hir-expand/src/fixup.rs18
-rw-r--r--crates/hir-expand/src/lib.rs7
-rw-r--r--crates/hir-ty/src/db.rs30
-rw-r--r--crates/hir-ty/src/diagnostics.rs2
-rw-r--r--crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs6
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs301
-rw-r--r--crates/hir-ty/src/display.rs12
-rw-r--r--crates/hir-ty/src/generics.rs16
-rw-r--r--crates/hir-ty/src/infer.rs96
-rw-r--r--crates/hir-ty/src/infer/coerce.rs6
-rw-r--r--crates/hir-ty/src/infer/expr.rs8
-rw-r--r--crates/hir-ty/src/infer/path.rs6
-rw-r--r--crates/hir-ty/src/infer/unify.rs118
-rw-r--r--crates/hir-ty/src/inhabitedness.rs8
-rw-r--r--crates/hir-ty/src/lib.rs15
-rw-r--r--crates/hir-ty/src/lower.rs433
-rw-r--r--crates/hir-ty/src/mir/lower.rs12
-rw-r--r--crates/hir-ty/src/tests.rs4
-rw-r--r--crates/hir-ty/src/tests/coercion.rs16
-rw-r--r--crates/hir-ty/src/tests/traits.rs73
-rw-r--r--crates/hir-ty/src/traits.rs26
-rw-r--r--crates/hir-ty/src/utils.rs2
-rw-r--r--crates/hir/Cargo.toml8
-rw-r--r--crates/hir/src/diagnostics.rs79
-rw-r--r--crates/hir/src/display.rs18
-rw-r--r--crates/hir/src/lib.rs258
-rw-r--r--crates/hir/src/semantics.rs37
-rw-r--r--crates/hir/src/source_analyzer.rs14
-rw-r--r--crates/ide-assists/src/handlers/add_missing_impl_members.rs45
-rw-r--r--crates/ide-assists/src/handlers/add_turbo_fish.rs83
-rw-r--r--crates/ide-assists/src/handlers/bool_to_enum.rs6
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs57
-rw-r--r--crates/ide-assists/src/handlers/flip_binexpr.rs54
-rw-r--r--crates/ide-assists/src/handlers/flip_comma.rs121
-rw-r--r--crates/ide-assists/src/handlers/flip_trait_bound.rs14
-rw-r--r--crates/ide-assists/src/handlers/generate_documentation_template.rs20
-rw-r--r--crates/ide-assists/src/handlers/generate_enum_variant.rs170
-rw-r--r--crates/ide-assists/src/handlers/introduce_named_generic.rs49
-rw-r--r--crates/ide-assists/src/handlers/reorder_fields.rs7
-rw-r--r--crates/ide-assists/src/handlers/reorder_impl_items.rs8
-rw-r--r--crates/ide-assists/src/handlers/sort_items.rs43
-rw-r--r--crates/ide-assists/src/tests/generated.rs2
-rw-r--r--crates/ide-completion/src/completions/attribute.rs13
-rw-r--r--crates/ide-completion/src/completions/extern_abi.rs1
-rw-r--r--crates/ide-completion/src/completions/lifetime.rs36
-rw-r--r--crates/ide-completion/src/context.rs10
-rw-r--r--crates/ide-completion/src/context/analysis.rs34
-rw-r--r--crates/ide-completion/src/item.rs3
-rw-r--r--crates/ide-completion/src/lib.rs15
-rw-r--r--crates/ide-completion/src/tests/attribute.rs64
-rw-r--r--crates/ide-db/src/defs.rs17
-rw-r--r--crates/ide-db/src/path_transform.rs3
-rw-r--r--crates/ide-db/src/syntax_helpers/suggest_name.rs28
-rw-r--r--crates/ide-diagnostics/src/handlers/expected_function.rs21
-rw-r--r--crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs442
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_match_arms.rs19
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_unsafe.rs154
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs458
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_method.rs6
-rw-r--r--crates/ide-diagnostics/src/lib.rs8
-rw-r--r--crates/ide-ssr/src/matching.rs4
-rw-r--r--crates/ide/src/hover/tests.rs794
-rw-r--r--crates/ide/src/inlay_hints.rs33
-rw-r--r--crates/ide/src/inlay_hints/extern_block.rs138
-rw-r--r--crates/ide/src/inlay_hints/lifetime.rs94
-rw-r--r--crates/ide/src/lib.rs10
-rw-r--r--crates/ide/src/rename.rs69
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs8
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_const.html4
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html4
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_macros.html12
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html8
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html4
-rw-r--r--crates/ide/src/typing.rs403
-rw-r--r--crates/intern/src/symbol.rs30
-rw-r--r--crates/intern/src/symbol/symbols.rs10
-rw-r--r--crates/load-cargo/src/lib.rs24
-rw-r--r--crates/parser/src/grammar.rs48
-rw-r--r--crates/parser/src/grammar/attributes.rs10
-rw-r--r--crates/parser/src/grammar/expressions.rs66
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs30
-rw-r--r--crates/parser/src/grammar/generic_args.rs76
-rw-r--r--crates/parser/src/grammar/generic_params.rs26
-rw-r--r--crates/parser/src/grammar/items.rs12
-rw-r--r--crates/parser/src/grammar/params.rs18
-rw-r--r--crates/parser/src/grammar/paths.rs175
-rw-r--r--crates/parser/src/grammar/types.rs6
-rw-r--r--crates/parser/src/parser.rs19
-rw-r--r--crates/parser/src/syntax_kind/generated.rs1
-rw-r--r--crates/parser/test_data/generated/runner.rs51
-rw-r--r--crates/parser/test_data/parser/err/0004_use_path_bad_segment.rast7
-rw-r--r--crates/parser/test_data/parser/err/0048_double_fish.rast14
-rw-r--r--crates/parser/test_data/parser/inline/err/arg_list_recovery.rast2
-rw-r--r--crates/parser/test_data/parser/inline/err/bad_asm_expr.rast50
-rw-r--r--crates/parser/test_data/parser/inline/err/bad_asm_expr.rs5
-rw-r--r--crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast8
-rw-r--r--crates/parser/test_data/parser/inline/err/empty_segment.rast2
-rw-r--r--crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rast33
-rw-r--r--crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rs1
-rw-r--r--crates/parser/test_data/parser/inline/err/generic_arg_list_recover_expr.rast79
-rw-r--r--crates/parser/test_data/parser/inline/err/generic_arg_list_recover_expr.rs2
-rw-r--r--crates/parser/test_data/parser/inline/err/meta_recovery.rast83
-rw-r--r--crates/parser/test_data/parser/inline/err/meta_recovery.rs6
-rw-r--r--crates/parser/test_data/parser/inline/err/precise_capturing_invalid.rast28
-rw-r--r--crates/parser/test_data/parser/inline/err/precise_capturing_invalid.rs1
-rw-r--r--crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast37
-rw-r--r--crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs1
-rw-r--r--crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rast29
-rw-r--r--crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rs1
-rw-r--r--crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rast78
-rw-r--r--crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rs3
-rw-r--r--crates/parser/test_data/parser/inline/ok/assoc_type_bound.rast39
-rw-r--r--crates/parser/test_data/parser/inline/ok/assoc_type_bound.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/associated_type_bounds.rast111
-rw-r--r--crates/parser/test_data/parser/inline/ok/associated_type_bounds.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/async_trait_bound.rast4
-rw-r--r--crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rast175
-rw-r--r--crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rs4
-rw-r--r--crates/parser/test_data/parser/inline/ok/edition_2015_dyn_prefix_inside_generic_arg.rast32
-rw-r--r--crates/parser/test_data/parser/inline/ok/edition_2015_dyn_prefix_inside_generic_arg.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/extern_crate.rast9
-rw-r--r--crates/parser/test_data/parser/inline/ok/extern_crate.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/extern_crate_rename.rast15
-rw-r--r--crates/parser/test_data/parser/inline/ok/extern_crate_rename.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/extern_crate_self.rast10
-rw-r--r--crates/parser/test_data/parser/inline/ok/extern_crate_self.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/field_expr.rast24
-rw-r--r--crates/parser/test_data/parser/inline/ok/field_expr.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/generic_arg.rast21
-rw-r--r--crates/parser/test_data/parser/inline/ok/generic_arg.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/generic_arg_bounds.rast461
-rw-r--r--crates/parser/test_data/parser/inline/ok/generic_arg_bounds.rs4
-rw-r--r--crates/parser/test_data/parser/inline/ok/lifetime_param.rast6
-rw-r--r--crates/parser/test_data/parser/inline/ok/method_call_expr.rast14
-rw-r--r--crates/parser/test_data/parser/inline/ok/method_call_expr.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/param_list_opt_patterns.rast48
-rw-r--r--crates/parser/test_data/parser/inline/ok/param_list_opt_patterns.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rast128
-rw-r--r--crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rs3
-rw-r--r--crates/parser/test_data/parser/inline/ok/precise_capturing.rast12
-rw-r--r--crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rast49
-rw-r--r--crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rast43
-rw-r--r--crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/typepathfn_with_coloncolon.rast10
-rw-r--r--crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rast60
-rw-r--r--crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/where_pred_for.rast4
-rw-r--r--crates/parser/test_data/parser/ok/0018_struct_type_params.rast48
-rw-r--r--crates/parser/test_data/parser/ok/0020_type_param_bounds.rast23
-rw-r--r--crates/parser/test_data/parser/ok/0045_block_attrs.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0051_parameter_attrs.rast57
-rw-r--r--crates/parser/test_data/parser/ok/0051_parameter_attrs.rs2
-rw-r--r--crates/parser/test_data/parser/ok/0054_qual_path_in_type_arg.rast8
-rw-r--r--crates/parser/test_data/parser/ok/0065_plus_after_fn_trait_bound.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0067_where_for_pred.rast8
-rw-r--r--crates/project-model/src/workspace.rs48
-rw-r--r--crates/rust-analyzer/Cargo.toml3
-rw-r--r--crates/rust-analyzer/src/bin/main.rs12
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs3
-rw-r--r--crates/rust-analyzer/src/config.rs95
-rw-r--r--crates/rust-analyzer/src/global_state.rs11
-rw-r--r--crates/rust-analyzer/src/handlers/notification.rs4
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs37
-rw-r--r--crates/rust-analyzer/src/lib.rs78
-rw-r--r--crates/rust-analyzer/src/lsp/capabilities.rs40
-rw-r--r--crates/rust-analyzer/src/lsp/ext.rs3
-rw-r--r--crates/rust-analyzer/src/lsp/to_proto.rs19
-rw-r--r--crates/rust-analyzer/src/main_loop.rs2
-rw-r--r--crates/rust-analyzer/src/reload.rs8
-rw-r--r--crates/rust-analyzer/tests/slow-tests/ratoml.rs19
-rw-r--r--crates/rust-analyzer/tests/slow-tests/support.rs64
-rw-r--r--crates/syntax/rust.ungram5
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs124
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs38
-rw-r--r--crates/syntax/src/ast/make.rs30
-rw-r--r--crates/syntax/src/ast/syntax_factory/constructors.rs351
-rw-r--r--crates/syntax/src/syntax_editor.rs49
-rw-r--r--crates/syntax/src/syntax_editor/edit_algo.rs22
-rw-r--r--crates/syntax/src/syntax_editor/edits.rs274
-rw-r--r--crates/test-fixture/src/lib.rs2
-rw-r--r--crates/test-utils/src/minicore.rs115
-rw-r--r--docs/dev/lsp-extensions.md2
-rw-r--r--docs/user/generated_config.adoc4
-rw-r--r--editors/code/package.json46
-rw-r--r--editors/code/src/client.ts2
-rw-r--r--editors/code/src/config.ts31
-rw-r--r--editors/code/src/ctx.ts18
-rw-r--r--editors/code/src/debug.ts12
-rw-r--r--editors/code/src/run.ts35
-rw-r--r--editors/code/tests/unit/runnable_env.test.ts122
-rw-r--r--rust-version2
226 files changed, 7515 insertions, 2865 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2bd4d17fe2..8c1d82de1d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -85,6 +85,12 @@ dependencies = [
]
[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -509,6 +515,7 @@ dependencies = [
"base-db",
"cfg",
"either",
+ "expect-test",
"hir-def",
"hir-expand",
"hir-ty",
@@ -519,6 +526,9 @@ dependencies = [
"span",
"stdx",
"syntax",
+ "syntax-bridge",
+ "test-fixture",
+ "test-utils",
"tracing",
"triomphe",
"tt",
@@ -1492,9 +1502,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_abi"
-version = "0.80.0"
+version = "0.85.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613760a3071b25a67a8d7bc97b37c7fd4722562e9479137b83ae9cf8f8c1601a"
+checksum = "af462c3a2d524b84a51b6848b439787f01b35c6c1086d3e3086a5f5eea92ed9a"
dependencies = [
"bitflags 2.6.0",
"ra-ap-rustc_index",
@@ -1503,20 +1513,19 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_index"
-version = "0.80.0"
+version = "0.85.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b2bc6b4ecede8ff28295041e22c2e66853f8e0125990c05135bad3c30bad12c"
+checksum = "be6bb8cb0ab78d94a222f1ffd3e87254cdfb57413382b8d6ebe26a85482f99d1"
dependencies = [
- "arrayvec",
"ra-ap-rustc_index_macros",
"smallvec",
]
[[package]]
name = "ra-ap-rustc_index_macros"
-version = "0.80.0"
+version = "0.85.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2374a39fb2d92d0509178c2b442eadca3cc10e403ef9729a040c1855b08ff261"
+checksum = "c24b1641455b46e87435b7321219672077066e678963d239a4a2904732979b16"
dependencies = [
"proc-macro2",
"quote",
@@ -1525,9 +1534,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_lexer"
-version = "0.80.0"
+version = "0.85.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a2cf8e48b69af3ecc29ed3449892e8a999111d2f75212a78aa242e117cf1711"
+checksum = "94daa86974417981fed2f12bd8fb00158dfa6fee561152bed689278c846d0272"
dependencies = [
"unicode-properties",
"unicode-xid",
@@ -1535,9 +1544,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_parse_format"
-version = "0.80.0"
+version = "0.85.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d6f59a22b559263c5c42747ae362cf5d4fb272293fa119a4623f8ec288f9656"
+checksum = "fc07f6bd581746f358e39c4b6bfe8d455b3d6ad1a857821016d0d42eeb5e1e3e"
dependencies = [
"ra-ap-rustc_index",
"ra-ap-rustc_lexer",
@@ -1545,9 +1554,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_pattern_analysis"
-version = "0.80.0"
+version = "0.85.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7d0575b54ffe09bc5d2f158454bc05f0c30c01d9992310965f854be50ae22b8"
+checksum = "2f49b86e1276c1c3c72898410def29b699415f4e7d1dfb3531daf79794694372"
dependencies = [
"ra-ap-rustc_index",
"rustc-hash 2.0.0",
@@ -1645,6 +1654,7 @@ version = "0.0.0"
dependencies = [
"always-assert",
"anyhow",
+ "base64",
"cargo_metadata",
"cfg",
"crossbeam-channel",
@@ -1655,6 +1665,7 @@ dependencies = [
"hir-def",
"hir-ty",
"ide",
+ "ide-completion",
"ide-db",
"ide-ssr",
"intern",
@@ -1683,6 +1694,7 @@ dependencies = [
"stdx",
"syntax",
"syntax-bridge",
+ "tenthash",
"test-fixture",
"test-utils",
"tikv-jemallocator",
@@ -1987,6 +1999,12 @@ dependencies = [
]
[[package]]
+name = "tenthash"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d67f9f3cf70e0852941d7bc3cb884b49b24b8ee956baf91ad0abae31f5ef11fb"
+
+[[package]]
name = "test-fixture"
version = "0.0.0"
dependencies = [
diff --git a/Cargo.toml b/Cargo.toml
index 632b290ba9..f7074f9135 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -84,11 +84,11 @@ 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.80", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.80", default-features = false }
-ra-ap-rustc_index = { version = "0.80", default-features = false }
-ra-ap-rustc_abi = { version = "0.80", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.80", default-features = false }
+ra-ap-rustc_lexer = { version = "0.85", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.85", default-features = false }
+ra-ap-rustc_index = { version = "0.85", default-features = false }
+ra-ap-rustc_abi = { version = "0.85", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.85", default-features = false }
# local crates that aren't published to crates.io. These should not have versions.
test-fixture = { path = "./crates/test-fixture" }
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index 57522d6932..e86944eeb3 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -547,29 +547,6 @@ impl CrateGraph {
None
}
- // Work around for https://github.com/rust-lang/rust-analyzer/issues/6038.
- // As hacky as it gets.
- pub fn patch_cfg_if(&mut self) -> bool {
- // we stupidly max by version in an attempt to have all duplicated std's depend on the same cfg_if so that deduplication still works
- let cfg_if =
- self.hacky_find_crate("cfg_if").max_by_key(|&it| self.arena[it].version.clone());
- let std = self.hacky_find_crate("std").next();
- match (cfg_if, std) {
- (Some(cfg_if), Some(std)) => {
- self.arena[cfg_if].dependencies.clear();
- self.arena[std]
- .dependencies
- .push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if));
- true
- }
- _ => false,
- }
- }
-
- fn hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator<Item = CrateId> + 'a {
- self.iter().filter(move |it| self[*it].display_name.as_deref() == Some(display_name))
- }
-
/// Removes all crates from this crate graph except for the ones in `to_keep` and fixes up the dependencies.
/// Returns a mapping from old crate ids to new crate ids.
pub fn remove_crates_except(&mut self, to_keep: &[CrateId]) -> Vec<Option<CrateId>> {
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index 5a386f6cf8..867bee95be 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/body.rs
@@ -31,7 +31,7 @@ use crate::{
path::{ModPath, Path},
src::HasSource,
type_ref::{TypeRef, TypeRefId, TypesMap, TypesSourceMap},
- BlockId, DefWithBodyId, HasModule, Lookup,
+ BlockId, DefWithBodyId, HasModule, Lookup, SyntheticSyntax,
};
/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
@@ -141,7 +141,7 @@ pub struct BodySourceMap {
field_map_back: FxHashMap<ExprId, FieldSource>,
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
- types: TypesSourceMap,
+ pub types: TypesSourceMap,
// FIXME: Make this a sane struct.
template_map: Option<
@@ -160,9 +160,6 @@ pub struct BodySourceMap {
diagnostics: Vec<BodyDiagnostic>,
}
-#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
-pub struct SyntheticSyntax;
-
#[derive(Debug, Eq, PartialEq)]
pub enum BodyDiagnostic {
InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
@@ -408,7 +405,8 @@ impl Body {
f(else_branch);
}
}
- Expr::Let { expr, .. } => {
+ Expr::Let { expr, pat } => {
+ self.walk_exprs_in_pat(*pat, &mut f);
f(*expr);
}
Expr::Block { statements, tail, .. }
@@ -444,7 +442,10 @@ impl Body {
}
Expr::Match { expr, arms } => {
f(*expr);
- arms.iter().map(|arm| arm.expr).for_each(f);
+ arms.iter().for_each(|arm| {
+ f(arm.expr);
+ self.walk_exprs_in_pat(arm.pat, &mut f);
+ });
}
Expr::Break { expr, .. }
| Expr::Return { expr }
@@ -505,6 +506,131 @@ impl Body {
}
}
+ pub fn walk_child_exprs_without_pats(&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, .. } => {
+ 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::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 } => 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] {
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 1ab49e9156..3b73d40963 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -1510,20 +1510,20 @@ impl ExprCollector<'_> {
BuiltinShadowMode::Other,
None,
);
+ // Funnily enough, record structs/variants *can* be shadowed
+ // by pattern bindings (but unit or tuple structs/variants
+ // can't).
match resolved.take_values() {
Some(ModuleDefId::ConstId(_)) => (None, Pat::Path(name.into())),
- Some(ModuleDefId::EnumVariantId(_)) => {
- // this is only really valid for unit variants, but
- // shadowing other enum variants with a pattern is
- // an error anyway
+ Some(ModuleDefId::EnumVariantId(variant))
+ if self.db.variant_data(variant.into()).kind()
+ != StructKind::Record =>
+ {
(None, Pat::Path(name.into()))
}
Some(ModuleDefId::AdtId(AdtId::StructId(s)))
if self.db.struct_data(s).variant_data.kind() != StructKind::Record =>
{
- // Funnily enough, record structs *can* be shadowed
- // by pattern bindings (but unit or tuple structs
- // can't).
(None, Pat::Path(name.into()))
}
// shadowing statics is an error as well, so we just ignore that case here
diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs
index 3b29d98d19..8f01091584 100644
--- a/crates/hir-def/src/body/tests.rs
+++ b/crates/hir-def/src/body/tests.rs
@@ -1,6 +1,5 @@
mod block;
-use base_db::SourceDatabase;
use expect_test::{expect, Expect};
use test_fixture::WithFixture;
@@ -11,7 +10,7 @@ use super::*;
fn lower(ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
let db = TestDB::with_files(ra_fixture);
- let krate = db.crate_graph().iter().next().unwrap();
+ let krate = db.fetch_test_crate();
let def_map = db.crate_def_map(krate);
let mut fn_def = None;
'outer: for (_, module) in def_map.modules() {
@@ -404,3 +403,26 @@ fn foo() {
}"#]]
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
}
+
+#[test]
+fn shadowing_record_variant() {
+ let (_, body, _) = lower(
+ r#"
+enum A {
+ B { field: i32 },
+}
+fn f() {
+ use A::*;
+ match () {
+ B => {}
+ };
+}
+ "#,
+ );
+ assert_eq!(body.bindings.len(), 1, "should have a binding for `B`");
+ assert_eq!(
+ body.bindings[BindingId::from_raw(RawIdx::from_u32(0))].name.as_str(),
+ "B",
+ "should have a binding for `B`",
+ );
+}
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index 2a13f74aac..15dd6aba31 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -369,7 +369,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 target_trait = impl_def.target_trait;
let self_ty = impl_def.self_ty;
let is_negative = impl_def.is_negative;
let is_unsafe = impl_def.is_unsafe;
diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs
index 11e9bb0d88..7b3f1d06d2 100644
--- a/crates/hir-def/src/generics.rs
+++ b/crates/hir-def/src/generics.rs
@@ -26,8 +26,8 @@ use crate::{
nameres::{DefMap, MacroSubNs},
path::{AssociatedTypeBinding, GenericArg, GenericArgs, NormalPath, Path},
type_ref::{
- ArrayType, ConstRef, FnType, LifetimeRef, RefType, TypeBound, TypeRef, TypeRefId, TypesMap,
- TypesSourceMap,
+ ArrayType, ConstRef, FnType, LifetimeRef, PathId, RefType, TypeBound, TypeRef, TypeRefId,
+ TypesMap, TypesSourceMap,
},
AdtId, ConstParamId, GenericDefId, HasModule, ItemTreeLoc, LifetimeParamId,
LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
@@ -225,6 +225,11 @@ impl GenericParams {
}
#[inline]
+ pub fn no_predicates(&self) -> bool {
+ self.where_predicates.is_empty()
+ }
+
+ #[inline]
pub fn where_predicates(&self) -> std::slice::Iter<'_, WherePredicate> {
self.where_predicates.iter()
}
@@ -874,14 +879,20 @@ fn copy_type_bound(
to: &mut TypesMap,
to_source_map: &mut TypesSourceMap,
) -> TypeBound {
+ let mut copy_path_id = |path: PathId| {
+ let new_path = copy_path(&from[path], from, from_source_map, to, to_source_map);
+ let new_path_id = to.types.alloc(TypeRef::Path(new_path));
+ if let Some(&ptr) = from_source_map.types_map_back.get(path.type_ref()) {
+ to_source_map.types_map_back.insert(new_path_id, ptr);
+ }
+ PathId::from_type_ref_unchecked(new_path_id)
+ };
+
match bound {
- TypeBound::Path(path, modifier) => {
- TypeBound::Path(copy_path(path, from, from_source_map, to, to_source_map), *modifier)
+ &TypeBound::Path(path, modifier) => TypeBound::Path(copy_path_id(path), modifier),
+ TypeBound::ForLifetime(lifetimes, path) => {
+ TypeBound::ForLifetime(lifetimes.clone(), copy_path_id(*path))
}
- 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/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs
index 4d83ef99c8..6d4d519cd2 100644
--- a/crates/hir-def/src/hir/type_ref.rs
+++ b/crates/hir-def/src/hir/type_ref.rs
@@ -23,6 +23,7 @@ use crate::{
hir::Literal,
lower::LowerCtx,
path::{GenericArg, Path},
+ SyntheticSyntax,
};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@@ -91,19 +92,37 @@ impl Rawness {
}
}
-#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+/// A `TypeRefId` that is guaranteed to always be `TypeRef::Path`. We use this for things like
+/// impl's trait, that are always paths but need to be traced back to source code.
+pub struct PathId(TypeRefId);
+
+impl PathId {
+ #[inline]
+ pub fn from_type_ref_unchecked(type_ref: TypeRefId) -> Self {
+ Self(type_ref)
+ }
+
+ #[inline]
+ pub fn type_ref(self) -> TypeRefId {
+ self.0
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct TraitRef {
- pub path: Path,
+ pub path: PathId,
}
impl TraitRef {
/// Converts an `ast::PathType` to a `hir::TraitRef`.
pub(crate) fn from_ast(ctx: &mut LowerCtx<'_>, node: ast::Type) -> Option<Self> {
// FIXME: Use `Path::from_src`
- match node {
- ast::Type::PathType(path) => {
- path.path().and_then(|it| ctx.lower_path(it)).map(|path| TraitRef { path })
- }
+ match &node {
+ ast::Type::PathType(path) => path
+ .path()
+ .and_then(|it| ctx.lower_path(it))
+ .map(|path| TraitRef { path: ctx.alloc_path(path, AstPtr::new(&node)) }),
_ => None,
}
}
@@ -173,11 +192,24 @@ impl TypesMap {
impl Index<TypeRefId> for TypesMap {
type Output = TypeRef;
+ #[inline]
fn index(&self, index: TypeRefId) -> &Self::Output {
&self.types[index]
}
}
+impl Index<PathId> for TypesMap {
+ type Output = Path;
+
+ #[inline]
+ fn index(&self, index: PathId) -> &Self::Output {
+ let TypeRef::Path(path) = &self[index.type_ref()] else {
+ unreachable!("`PathId` always points to `TypeRef::Path`");
+ };
+ path
+ }
+}
+
pub type TypePtr = AstPtr<ast::Type>;
pub type TypeSource = InFile<TypePtr>;
@@ -187,6 +219,12 @@ pub struct TypesSourceMap {
}
impl TypesSourceMap {
+ pub const EMPTY: Self = Self { types_map_back: ArenaMap::new() };
+
+ pub fn type_syntax(&self, id: TypeRefId) -> Result<TypeSource, SyntheticSyntax> {
+ self.types_map_back.get(id).cloned().ok_or(SyntheticSyntax)
+ }
+
pub(crate) fn shrink_to_fit(&mut self) {
let TypesSourceMap { types_map_back } = self;
types_map_back.shrink_to_fit();
@@ -214,15 +252,15 @@ impl LifetimeRef {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TypeBound {
- Path(Path, TraitBoundModifier),
- ForLifetime(Box<[Name]>, Path),
+ Path(PathId, TraitBoundModifier),
+ ForLifetime(Box<[Name]>, PathId),
Lifetime(LifetimeRef),
Use(Box<[UseArgRef]>),
Error,
}
#[cfg(target_pointer_width = "64")]
-const _: [(); 32] = [(); ::std::mem::size_of::<TypeBound>()];
+const _: [(); 24] = [(); ::std::mem::size_of::<TypeBound>()];
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum UseArgRef {
@@ -365,8 +403,8 @@ impl TypeRef {
TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
for bound in bounds {
match bound {
- TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
- go_path(path, f, map)
+ &TypeBound::Path(path, _) | &TypeBound::ForLifetime(_, path) => {
+ go_path(&map[path], f, map)
}
TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (),
}
@@ -397,8 +435,8 @@ impl TypeRef {
}
for bound in binding.bounds.iter() {
match bound {
- TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => {
- go_path(path, f, map)
+ &TypeBound::Path(path, _) | &TypeBound::ForLifetime(_, path) => {
+ go_path(&map[path], f, map)
}
TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (),
}
@@ -425,7 +463,7 @@ pub(crate) fn type_bounds_from_ast(
impl TypeBound {
pub(crate) fn from_ast(ctx: &mut LowerCtx<'_>, node: ast::TypeBound) -> Self {
- let mut lower_path_type = |path_type: ast::PathType| ctx.lower_path(path_type.path()?);
+ let mut lower_path_type = |path_type: &ast::PathType| ctx.lower_path(path_type.path()?);
match node.kind() {
ast::TypeBoundKind::PathType(path_type) => {
@@ -433,8 +471,10 @@ impl TypeBound {
Some(_) => TraitBoundModifier::Maybe,
None => TraitBoundModifier::None,
};
- lower_path_type(path_type)
- .map(|p| TypeBound::Path(p, m))
+ lower_path_type(&path_type)
+ .map(|p| {
+ TypeBound::Path(ctx.alloc_path(p, AstPtr::new(&path_type).upcast()), m)
+ })
.unwrap_or(TypeBound::Error)
}
ast::TypeBoundKind::ForType(for_type) => {
@@ -445,12 +485,14 @@ impl TypeBound {
.collect(),
None => Box::default(),
};
- let path = for_type.ty().and_then(|ty| match ty {
- ast::Type::PathType(path_type) => lower_path_type(path_type),
+ let path = for_type.ty().and_then(|ty| match &ty {
+ ast::Type::PathType(path_type) => lower_path_type(path_type).map(|p| (p, ty)),
_ => None,
});
match path {
- Some(p) => TypeBound::ForLifetime(lt_refs, p),
+ Some((p, ty)) => {
+ TypeBound::ForLifetime(lt_refs, ctx.alloc_path(p, AstPtr::new(&ty)))
+ }
None => TypeBound::Error,
}
}
@@ -470,10 +512,10 @@ impl TypeBound {
}
}
- pub fn as_path(&self) -> Option<(&Path, &TraitBoundModifier)> {
+ pub fn as_path<'a>(&self, map: &'a TypesMap) -> Option<(&'a Path, TraitBoundModifier)> {
match self {
- TypeBound::Path(p, m) => Some((p, m)),
- TypeBound::ForLifetime(_, p) => Some((p, &TraitBoundModifier::None)),
+ &TypeBound::Path(p, m) => Some((&map[p], m)),
+ &TypeBound::ForLifetime(_, p) => Some((&map[p], TraitBoundModifier::None)),
TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => None,
}
}
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index d519c1708b..71848845a8 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -34,7 +34,7 @@ use crate::{
lower::LowerCtx,
path::AssociatedTypeBinding,
type_ref::{
- LifetimeRef, RefType, TraitBoundModifier, TraitRef, TypeBound, TypeRef, TypeRefId,
+ LifetimeRef, PathId, RefType, TraitBoundModifier, TraitRef, TypeBound, TypeRef, TypeRefId,
TypesMap, TypesSourceMap,
},
visibility::RawVisibility,
@@ -514,7 +514,7 @@ impl<'a> Ctx<'a> {
};
let ret_type = if func.async_token().is_some() {
- let future_impl = desugar_future_path(ret_type);
+ let future_impl = desugar_future_path(&mut body_ctx, ret_type);
let ty_bound = TypeBound::Path(future_impl, TraitBoundModifier::None);
body_ctx.alloc_type_ref_desugared(TypeRef::ImplTrait(ThinVec::from_iter([ty_bound])))
} else {
@@ -936,7 +936,7 @@ impl<'a> Ctx<'a> {
}
}
-fn desugar_future_path(orig: TypeRefId) -> Path {
+fn desugar_future_path(ctx: &mut LowerCtx<'_>, orig: TypeRefId) -> PathId {
let path = path![core::future::Future];
let mut generic_args: Vec<_> =
std::iter::repeat(None).take(path.segments().len() - 1).collect();
@@ -948,7 +948,8 @@ fn desugar_future_path(orig: TypeRefId) -> Path {
};
generic_args.push(Some(GenericArgs { bindings: Box::new([binding]), ..GenericArgs::empty() }));
- Path::from_known_path(path, generic_args)
+ let path = Path::from_known_path(path, generic_args);
+ PathId::from_type_ref_unchecked(ctx.alloc_type_ref_desugared(TypeRef::Path(path)))
}
enum HasImplicitSelf {
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index b6816a1f96..70bf2f13c8 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -484,7 +484,7 @@ impl Printer<'_> {
w!(self, "!");
}
if let Some(tr) = target_trait {
- self.print_path(&tr.path, types_map);
+ self.print_path(&types_map[tr.path], types_map);
w!(self, " for ");
}
self.print_type_ref(*self_ty, types_map);
@@ -648,9 +648,9 @@ impl Printer<'_> {
let (target, bound) = match pred {
WherePredicate::TypeBound { target, bound } => (target, bound),
WherePredicate::Lifetime { target, bound } => {
- wln!(
+ w!(
this,
- "{}: {},",
+ "{}: {}",
target.name.display(self.db.upcast(), edition),
bound.name.display(self.db.upcast(), edition)
);
diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs
index 5c07369f4b..0f53969d6c 100644
--- a/crates/hir-def/src/item_tree/tests.rs
+++ b/crates/hir-def/src/item_tree/tests.rs
@@ -351,7 +351,8 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
where
T: Copy,
T: 'a,
- T: 'b
+ T: 'b,
+ 'b: 'a
{
pub(self) field: &'a &'b T,
}
@@ -370,7 +371,8 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
where
T: Copy,
T: 'a,
- T: 'b
+ T: 'b,
+ 'b: 'a
{
// AstId: 9
pub(self) fn f<G>(
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index 166c965d14..0629d87e54 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -376,6 +376,9 @@ language_item_table! {
Fn, sym::fn_, fn_trait, Target::Trait, GenericRequirement::Exact(1);
FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
+ AsyncFn, sym::async_fn, async_fn_trait, Target::Trait, GenericRequirement::Exact(1);
+ AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
+ AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index eb55ba1d53..8af27513eb 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -1535,3 +1535,6 @@ fn macro_call_as_call_id_with_eager(
pub struct UnresolvedMacro {
pub path: hir_expand::mod_path::ModPath,
}
+
+#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
+pub struct SyntheticSyntax;
diff --git a/crates/hir-def/src/lower.rs b/crates/hir-def/src/lower.rs
index 6d1a3d1744..7cddd48eb1 100644
--- a/crates/hir-def/src/lower.rs
+++ b/crates/hir-def/src/lower.rs
@@ -2,7 +2,7 @@
use std::{cell::OnceCell, mem};
use hir_expand::{span_map::SpanMap, AstId, HirFileId, InFile};
-use span::{AstIdMap, AstIdNode};
+use span::{AstIdMap, AstIdNode, Edition, EditionedFileId, FileId, RealSpanMap};
use stdx::thin_vec::ThinVec;
use syntax::ast;
use triomphe::Arc;
@@ -10,7 +10,7 @@ use triomphe::Arc;
use crate::{
db::DefDatabase,
path::Path,
- type_ref::{TypeBound, TypePtr, TypeRef, TypeRefId, TypesMap, TypesSourceMap},
+ type_ref::{PathId, TypeBound, TypePtr, TypeRef, TypeRefId, TypesMap, TypesSourceMap},
};
pub struct LowerCtx<'a> {
@@ -63,6 +63,30 @@ impl<'a> LowerCtx<'a> {
}
}
+ /// Prepares a `LowerCtx` for synthetic AST that needs to be lowered. This is intended for IDE things.
+ pub fn for_synthetic_ast(
+ db: &'a dyn DefDatabase,
+ ast_id_map: Arc<AstIdMap>,
+ types_map: &'a mut TypesMap,
+ types_source_map: &'a mut TypesSourceMap,
+ ) -> Self {
+ let file_id = EditionedFileId::new(
+ FileId::from_raw(EditionedFileId::MAX_FILE_ID),
+ Edition::Edition2015,
+ );
+ LowerCtx {
+ db,
+ // Make up an invalid file id, so that if we will try to actually access it salsa will panic.
+ file_id: file_id.into(),
+ span_map: SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(file_id))).into(),
+ ast_id_map: ast_id_map.into(),
+ impl_trait_bounds: Vec::new(),
+ outer_impl_trait: false,
+ types_map,
+ types_source_map,
+ }
+ }
+
pub(crate) fn span_map(&self) -> &SpanMap {
self.span_map.get_or_init(|| self.db.span_map(self.file_id))
}
@@ -118,4 +142,8 @@ impl<'a> LowerCtx<'a> {
pub(crate) fn alloc_error_type(&mut self) -> TypeRefId {
self.types_map.types.alloc(TypeRef::Error)
}
+
+ pub(crate) fn alloc_path(&mut self, path: Path, node: TypePtr) -> PathId {
+ PathId::from_type_ref_unchecked(self.alloc_type_ref(TypeRef::Path(path), node))
+ }
}
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index d568f6faa7..511626b5ed 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -1733,7 +1733,7 @@ m!(C("0"));
macro_rules! m {
($k:expr) => { fn f() { K::$k; } }
}
-/* parse error: expected identifier */
+/* parse error: expected identifier, `self`, `super`, `crate`, or `Self` */
/* parse error: expected SEMICOLON */
/* parse error: expected SEMICOLON */
/* parse error: expected expression, item or let statement */
@@ -1759,8 +1759,9 @@ fn f() {
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
index 23d8b023b8..e9a977da91 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
@@ -184,3 +184,31 @@ fn test() {
"#]],
);
}
+
+#[test]
+fn meta_variable_raw_name_equals_non_raw() {
+ check(
+ r#"
+macro_rules! m {
+ ($r#name:tt) => {
+ $name
+ }
+}
+
+fn test() {
+ m!(1234)
+}
+"#,
+ expect![[r#"
+macro_rules! m {
+ ($r#name:tt) => {
+ $name
+ }
+}
+
+fn test() {
+ 1234
+}
+"#]],
+ );
+}
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index d5b94f0ae4..0475e40c5b 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -16,7 +16,6 @@ mod proc_macros;
use std::{iter, ops::Range, sync};
-use base_db::SourceDatabase;
use expect_test::Expect;
use hir_expand::{
db::ExpandDatabase,
@@ -63,7 +62,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
},
)];
let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);
- let krate = db.crate_graph().iter().next().unwrap();
+ let krate = db.fetch_test_crate();
let def_map = db.crate_def_map(krate);
let local_id = DefMap::ROOT;
let module = def_map.module_id(local_id);
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index a37e3c70e2..98b08bcf70 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -910,8 +910,13 @@ impl DefCollector<'_> {
self.update(module_id, &items, vis, Some(ImportType::Glob(id)));
// record the glob import in case we add further items
let glob = self.glob_imports.entry(m.local_id).or_default();
- if !glob.iter().any(|(mid, _, _)| *mid == module_id) {
- glob.push((module_id, vis, id));
+ match glob.iter_mut().find(|(mid, _, _)| *mid == module_id) {
+ None => glob.push((module_id, vis, id)),
+ Some((_, old_vis, _)) => {
+ if let Some(new_vis) = old_vis.max(vis, &self.def_map) {
+ *old_vis = new_vis;
+ }
+ }
}
}
}
diff --git a/crates/hir-def/src/nameres/tests.rs b/crates/hir-def/src/nameres/tests.rs
index e1e30e5cec..32c158415b 100644
--- a/crates/hir-def/src/nameres/tests.rs
+++ b/crates/hir-def/src/nameres/tests.rs
@@ -13,13 +13,13 @@ use crate::{db::DefDatabase, nameres::DefMap, test_db::TestDB};
fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> {
let db = TestDB::with_files(ra_fixture);
- let krate = db.crate_graph().iter().next().unwrap();
+ let krate = db.fetch_test_crate();
db.crate_def_map(krate)
}
fn render_crate_def_map(ra_fixture: &str) -> String {
let db = TestDB::with_files(ra_fixture);
- let krate = db.crate_graph().iter().next().unwrap();
+ let krate = db.fetch_test_crate();
db.crate_def_map(krate).dump(&db)
}
diff --git a/crates/hir-def/src/nameres/tests/globs.rs b/crates/hir-def/src/nameres/tests/globs.rs
index 543ab41cd5..8963a57679 100644
--- a/crates/hir-def/src/nameres/tests/globs.rs
+++ b/crates/hir-def/src/nameres/tests/globs.rs
@@ -451,3 +451,42 @@ mod glob_target {
"#]],
);
}
+
+#[test]
+fn regression_18580() {
+ check(
+ r#"
+pub mod libs {
+ pub struct Placeholder;
+}
+
+pub mod reexport_2 {
+ use reexport_1::*;
+ pub use reexport_1::*;
+
+ pub mod reexport_1 {
+ pub use crate::libs::*;
+ }
+}
+
+use reexport_2::*;
+"#,
+ expect![[r#"
+ crate
+ Placeholder: t v
+ libs: t
+ reexport_1: t
+ reexport_2: t
+
+ crate::libs
+ Placeholder: t v
+
+ crate::reexport_2
+ Placeholder: t v
+ reexport_1: t
+
+ crate::reexport_2::reexport_1
+ Placeholder: t v
+ "#]],
+ );
+}
diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs
index d920c10826..1cfbabca28 100644
--- a/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/crates/hir-def/src/nameres/tests/incremental.rs
@@ -1,15 +1,11 @@
-use base_db::{SourceDatabase, SourceDatabaseFileInputExt as _};
+use base_db::SourceDatabaseFileInputExt as _;
use test_fixture::WithFixture;
use crate::{db::DefDatabase, nameres::tests::TestDB, AdtId, ModuleDefId};
fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change: &str) {
let (mut db, pos) = TestDB::with_position(ra_fixture_initial);
- let krate = {
- let crate_graph = db.crate_graph();
- // Some of these tests use minicore/proc-macros which will be injected as the first crate
- crate_graph.iter().last().unwrap()
- };
+ let krate = db.fetch_test_crate();
{
let events = db.log_executed(|| {
db.crate_def_map(krate);
@@ -120,28 +116,31 @@ fn f() { foo }
);
}
-#[test]
-fn typing_inside_an_attribute_arg_should_not_invalidate_def_map() {
- check_def_map_is_not_recomputed(
- r"
-//- proc_macros: identity
-//- /lib.rs
-mod foo;
+// Would be nice if this was the case, but as attribute inputs are stored in the item tree, this is
+// not currently the case.
+// #[test]
+// fn typing_inside_an_attribute_arg_should_not_invalidate_def_map() {
+// check_def_map_is_not_recomputed(
+// r"
+// //- proc_macros: identity
+// //- /lib.rs
+// mod foo;
+
+// //- /foo/mod.rs
+// pub mod bar;
+
+// //- /foo/bar.rs
+// $0
+// #[proc_macros::identity]
+// fn f() {}
+// ",
+// r"
+// #[proc_macros::identity(foo)]
+// fn f() {}
+// ",
+// );
+// }
-//- /foo/mod.rs
-pub mod bar;
-
-//- /foo/bar.rs
-$0
-#[proc_macros::identity]
-fn f() {}
-",
- r"
-#[proc_macros::identity(foo)]
-fn f() {}
-",
- );
-}
#[test]
fn typing_inside_macro_heavy_file_should_not_invalidate_def_map() {
check_def_map_is_not_recomputed(
@@ -198,31 +197,33 @@ pub struct S {}
);
}
-#[test]
-fn typing_inside_a_derive_should_not_invalidate_def_map() {
- check_def_map_is_not_recomputed(
- r"
-//- proc_macros: derive_identity
-//- minicore:derive
-//- /lib.rs
-mod foo;
-
-//- /foo/mod.rs
-pub mod bar;
-
-//- /foo/bar.rs
-$0
-#[derive(proc_macros::DeriveIdentity)]
-#[allow()]
-struct S;
-",
- r"
-#[derive(proc_macros::DeriveIdentity)]
-#[allow(dead_code)]
-struct S;
-",
- );
-}
+// Would be nice if this was the case, but as attribute inputs are stored in the item tree, this is
+// not currently the case.
+// #[test]
+// fn typing_inside_a_derive_should_not_invalidate_def_map() {
+// check_def_map_is_not_recomputed(
+// r"
+// //- proc_macros: derive_identity
+// //- minicore:derive
+// //- /lib.rs
+// mod foo;
+
+// //- /foo/mod.rs
+// pub mod bar;
+
+// //- /foo/bar.rs
+// $0
+// #[derive(proc_macros::DeriveIdentity)]
+// #[allow()]
+// struct S;
+// ",
+// r"
+// #[derive(proc_macros::DeriveIdentity)]
+// #[allow(dead_code)]
+// struct S;
+// ",
+// );
+// }
#[test]
fn typing_inside_a_function_should_not_invalidate_item_expansions() {
diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs
index aa2c4a6f1b..44e132061a 100644
--- a/crates/hir-def/src/path.rs
+++ b/crates/hir-def/src/path.rs
@@ -1,5 +1,7 @@
//! A desugared representation of paths like `crate::foo` or `<Type as Trait>::bar`.
mod lower;
+#[cfg(test)]
+mod tests;
use std::{
fmt::{self, Display},
@@ -19,6 +21,8 @@ use syntax::ast;
pub use hir_expand::mod_path::{path, ModPath, PathKind};
+pub use lower::hir_segment_to_ast_segment;
+
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ImportAlias {
/// Unnamed alias, as in `use Foo as _;`
@@ -230,7 +234,7 @@ impl Path {
}
}
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PathSegment<'a> {
pub name: &'a Name,
pub args_and_bindings: Option<&'a GenericArgs>,
@@ -274,6 +278,12 @@ impl<'a> PathSegments<'a> {
generic_args: self.generic_args.map(|it| it.get(..len).unwrap_or(it)),
}
}
+ pub fn strip_last(&self) -> PathSegments<'a> {
+ PathSegments {
+ segments: self.segments.split_last().map_or(&[], |it| it.1),
+ generic_args: self.generic_args.map(|it| it.split_last().map_or(&[][..], |it| it.1)),
+ }
+ }
pub fn iter(&self) -> impl Iterator<Item = PathSegment<'a>> {
self.segments
.iter()
diff --git a/crates/hir-def/src/path/lower.rs b/crates/hir-def/src/path/lower.rs
index 553e615b94..3b7e7653fb 100644
--- a/crates/hir-def/src/path/lower.rs
+++ b/crates/hir-def/src/path/lower.rs
@@ -17,13 +17,31 @@ use crate::{
type_ref::{LifetimeRef, TypeBound, TypeRef},
};
+#[cfg(test)]
+thread_local! {
+ /// This is used to test `hir_segment_to_ast_segment()`. It's a hack, but it makes testing much easier.
+ pub(super) static SEGMENT_LOWERING_MAP: std::cell::RefCell<rustc_hash::FxHashMap<ast::PathSegment, usize>> = std::cell::RefCell::default();
+}
+
/// Converts an `ast::Path` to `Path`. Works with use trees.
/// It correctly handles `$crate` based path from macro call.
+// If you modify the logic of the lowering, make sure to check if `hir_segment_to_ast_segment()`
+// also needs an update.
pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<Path> {
let mut kind = PathKind::Plain;
let mut type_anchor = None;
let mut segments = Vec::new();
let mut generic_args = Vec::new();
+ #[cfg(test)]
+ let mut ast_segments = Vec::new();
+ #[cfg(test)]
+ let mut ast_segments_offset = 0;
+ #[allow(unused_mut)]
+ let mut push_segment = |_segment: &ast::PathSegment, segments: &mut Vec<Name>, name| {
+ #[cfg(test)]
+ ast_segments.push(_segment.clone());
+ segments.push(name);
+ };
loop {
let segment = path.segment()?;
@@ -34,6 +52,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
match segment.kind()? {
ast::PathSegmentKind::Name(name_ref) => {
if name_ref.text() == "$crate" {
+ if path.qualifier().is_some() {
+ // FIXME: Report an error.
+ return None;
+ }
break kind = resolve_crate_root(
ctx.db.upcast(),
ctx.span_map().span_for_range(name_ref.syntax().text_range()).ctx,
@@ -48,7 +70,7 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
.or_else(|| {
lower_generic_args_from_fn_path(
ctx,
- segment.param_list(),
+ segment.parenthesized_arg_list(),
segment.ret_type(),
)
});
@@ -56,10 +78,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
generic_args.resize(segments.len(), None);
generic_args.push(args);
}
- segments.push(name);
+ push_segment(&segment, &mut segments, name);
}
ast::PathSegmentKind::SelfTypeKw => {
- segments.push(Name::new_symbol_root(sym::Self_.clone()));
+ push_segment(&segment, &mut segments, Name::new_symbol_root(sym::Self_.clone()));
}
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
assert!(path.qualifier().is_none()); // this can only occur at the first segment
@@ -81,6 +103,10 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
kind = mod_path.kind;
segments.extend(mod_path.segments().iter().cloned().rev());
+ #[cfg(test)]
+ {
+ ast_segments_offset = mod_path.segments().len();
+ }
if let Some(path_generic_args) = path_generic_args {
generic_args.resize(segments.len() - num_segments, None);
generic_args.extend(Vec::from(path_generic_args).into_iter().rev());
@@ -112,10 +138,18 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
}
}
ast::PathSegmentKind::CrateKw => {
+ if path.qualifier().is_some() {
+ // FIXME: Report an error.
+ return None;
+ }
kind = PathKind::Crate;
break;
}
ast::PathSegmentKind::SelfKw => {
+ if path.qualifier().is_some() {
+ // FIXME: Report an error.
+ return None;
+ }
// don't break out if `self` is the last segment of a path, this mean we got a
// use tree like `foo::{self}` which we want to resolve as `foo`
if !segments.is_empty() {
@@ -162,6 +196,13 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
}
}
+ #[cfg(test)]
+ {
+ ast_segments.reverse();
+ SEGMENT_LOWERING_MAP
+ .with_borrow_mut(|map| map.extend(ast_segments.into_iter().zip(ast_segments_offset..)));
+ }
+
let mod_path = Interned::new(ModPath::from_segments(kind, segments));
if type_anchor.is_none() && generic_args.is_empty() {
return Some(Path::BarePath(mod_path));
@@ -181,6 +222,41 @@ pub(super) fn lower_path(ctx: &mut LowerCtx<'_>, mut path: ast::Path) -> Option<
}
}
+/// This function finds the AST segment that corresponds to the HIR segment
+/// with index `segment_idx` on the path that is lowered from `path`.
+pub fn hir_segment_to_ast_segment(path: &ast::Path, segment_idx: u32) -> Option<ast::PathSegment> {
+ // Too tightly coupled to `lower_path()`, but unfortunately we cannot decouple them,
+ // as keeping source maps for all paths segments will have a severe impact on memory usage.
+
+ let mut segments = path.segments();
+ if let Some(ast::PathSegmentKind::Type { trait_ref: Some(trait_ref), .. }) =
+ segments.clone().next().and_then(|it| it.kind())
+ {
+ segments.next();
+ return find_segment(trait_ref.path()?.segments().chain(segments), segment_idx);
+ }
+ return find_segment(segments, segment_idx);
+
+ fn find_segment(
+ segments: impl Iterator<Item = ast::PathSegment>,
+ segment_idx: u32,
+ ) -> Option<ast::PathSegment> {
+ segments
+ .filter(|segment| match segment.kind() {
+ Some(
+ ast::PathSegmentKind::CrateKw
+ | ast::PathSegmentKind::SelfKw
+ | ast::PathSegmentKind::SuperKw
+ | ast::PathSegmentKind::Type { .. },
+ )
+ | None => false,
+ Some(ast::PathSegmentKind::Name(name)) => name.text() != "$crate",
+ Some(ast::PathSegmentKind::SelfTypeKw) => true,
+ })
+ .nth(segment_idx as usize)
+ }
+}
+
pub(super) fn lower_generic_args(
lower_ctx: &mut LowerCtx<'_>,
node: ast::GenericArgList,
@@ -247,12 +323,12 @@ pub(super) fn lower_generic_args(
/// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
fn lower_generic_args_from_fn_path(
ctx: &mut LowerCtx<'_>,
- params: Option<ast::ParamList>,
+ args: Option<ast::ParenthesizedArgList>,
ret_type: Option<ast::RetType>,
) -> Option<GenericArgs> {
- let params = params?;
+ let params = args?;
let mut param_types = Vec::new();
- for param in params.params() {
+ for param in params.type_args() {
let type_ref = TypeRef::from_ast_opt(ctx, param.ty());
param_types.push(type_ref);
}
diff --git a/crates/hir-def/src/path/tests.rs b/crates/hir-def/src/path/tests.rs
new file mode 100644
index 0000000000..67a27bf85e
--- /dev/null
+++ b/crates/hir-def/src/path/tests.rs
@@ -0,0 +1,126 @@
+use expect_test::{expect, Expect};
+use span::Edition;
+use syntax::ast::{self, make};
+use test_fixture::WithFixture;
+
+use crate::{
+ lower::LowerCtx,
+ path::{
+ lower::{hir_segment_to_ast_segment, SEGMENT_LOWERING_MAP},
+ Path,
+ },
+ pretty,
+ test_db::TestDB,
+ type_ref::{TypesMap, TypesSourceMap},
+};
+
+fn lower_path(path: ast::Path) -> (TestDB, TypesMap, Option<Path>) {
+ let (db, file_id) = TestDB::with_single_file("");
+ let mut types_map = TypesMap::default();
+ let mut types_source_map = TypesSourceMap::default();
+ let mut ctx = LowerCtx::new(&db, file_id.into(), &mut types_map, &mut types_source_map);
+ let lowered_path = ctx.lower_path(path);
+ (db, types_map, lowered_path)
+}
+
+#[track_caller]
+fn check_hir_to_ast(path: &str, ignore_segments: &[&str]) {
+ let path = make::path_from_text(path);
+ SEGMENT_LOWERING_MAP.with_borrow_mut(|map| map.clear());
+ let _ = lower_path(path.clone()).2.expect("failed to lower path");
+ SEGMENT_LOWERING_MAP.with_borrow(|map| {
+ for (segment, segment_idx) in map {
+ if ignore_segments.contains(&&*segment.to_string()) {
+ continue;
+ }
+
+ let restored_segment = hir_segment_to_ast_segment(&path, *segment_idx as u32)
+ .unwrap_or_else(|| {
+ panic!(
+ "failed to map back segment `{segment}` \
+ numbered {segment_idx} in HIR from path `{path}`"
+ )
+ });
+ assert_eq!(
+ segment, &restored_segment,
+ "mapping back `{segment}` numbered {segment_idx} in HIR \
+ from path `{path}` produced incorrect segment `{restored_segment}`"
+ );
+ }
+ });
+}
+
+#[test]
+fn hir_to_ast_trait_ref() {
+ check_hir_to_ast("<A as B::C::D>::E::F", &["A"]);
+}
+
+#[test]
+fn hir_to_ast_plain_path() {
+ check_hir_to_ast("A::B::C::D::E::F", &[]);
+}
+
+#[test]
+fn hir_to_ast_crate_path() {
+ check_hir_to_ast("crate::A::B::C", &[]);
+ check_hir_to_ast("crate::super::super::A::B::C", &[]);
+}
+
+#[test]
+fn hir_to_ast_self_path() {
+ check_hir_to_ast("self::A::B::C", &[]);
+ check_hir_to_ast("self::super::super::A::B::C", &[]);
+}
+
+#[test]
+fn hir_to_ast_super_path() {
+ check_hir_to_ast("super::A::B::C", &[]);
+ check_hir_to_ast("super::super::super::A::B::C", &[]);
+}
+
+#[test]
+fn hir_to_ast_type_anchor_path() {
+ check_hir_to_ast("<A::B>::C::D", &["A", "B"]);
+}
+
+#[test]
+fn hir_to_ast_path_super_in_middle() {
+ check_hir_to_ast("A::super::B::super::super::C::D", &[]);
+}
+
+#[track_caller]
+fn check_fail_lowering(path: &str) {
+ let (_, _, lowered_path) = lower_path(make::path_from_text(path));
+ assert!(lowered_path.is_none(), "path `{path}` should fail lowering");
+}
+
+#[test]
+fn keywords_in_middle_fail_lowering1() {
+ check_fail_lowering("self::A::self::B::super::C::crate::D");
+}
+
+#[test]
+fn keywords_in_middle_fail_lowering2() {
+ check_fail_lowering("A::super::self::C::D");
+}
+
+#[test]
+fn keywords_in_middle_fail_lowering3() {
+ check_fail_lowering("A::crate::B::C::D");
+}
+
+#[track_caller]
+fn check_path_lowering(path: &str, expected: Expect) {
+ let (db, types_map, lowered_path) = lower_path(make::path_from_text(path));
+ let lowered_path = lowered_path.expect("failed to lower path");
+ let mut buf = String::new();
+ pretty::print_path(&db, &lowered_path, &types_map, &mut buf, Edition::CURRENT)
+ .expect("failed to pretty-print path");
+ expected.assert_eq(&buf);
+}
+
+#[test]
+fn fn_like_path_with_coloncolon() {
+ check_path_lowering("Fn::(A, B) -> C", expect![[r#"Fn::<(A, B), Output = C>"#]]);
+ check_path_lowering("Fn::(A, B)", expect![[r#"Fn::<(A, B), Output = ()>"#]]);
+}
diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs
index 9ceb82d5fd..eb9488feaa 100644
--- a/crates/hir-def/src/pretty.rs
+++ b/crates/hir-def/src/pretty.rs
@@ -271,7 +271,7 @@ pub(crate) fn print_type_bounds(
TraitBoundModifier::None => (),
TraitBoundModifier::Maybe => write!(buf, "?")?,
}
- print_path(db, path, map, buf, edition)?;
+ print_path(db, &map[*path], map, buf, edition)?;
}
TypeBound::ForLifetime(lifetimes, path) => {
write!(
@@ -279,7 +279,7 @@ pub(crate) fn print_type_bounds(
"for<{}> ",
lifetimes.iter().map(|it| it.display(db.upcast(), edition)).format(", ")
)?;
- print_path(db, path, map, buf, edition)?;
+ print_path(db, &map[*path], map, buf, edition)?;
}
TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast(), edition))?,
TypeBound::Use(args) => {
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 26655e40ca..316406c151 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -576,10 +576,12 @@ impl Resolver {
match scope {
Scope::BlockScope(m) => traits.extend(m.def_map[m.module_id].scope.traits()),
&Scope::ImplDefScope(impl_) => {
- if let Some(target_trait) = &db.impl_data(impl_).target_trait {
- if let Some(TypeNs::TraitId(trait_)) =
- self.resolve_path_in_type_ns_fully(db, &target_trait.path)
- {
+ let impl_data = db.impl_data(impl_);
+ if let Some(target_trait) = impl_data.target_trait {
+ if let Some(TypeNs::TraitId(trait_)) = self.resolve_path_in_type_ns_fully(
+ db,
+ &impl_data.types_map[target_trait.path],
+ ) {
traits.insert(trait_);
}
}
@@ -640,9 +642,9 @@ impl Resolver {
})
}
- pub fn generic_params(&self) -> Option<&Arc<GenericParams>> {
+ pub fn generic_params(&self) -> Option<&GenericParams> {
self.scopes().find_map(|scope| match scope {
- Scope::GenericParams { params, .. } => Some(params),
+ Scope::GenericParams { params, .. } => Some(&**params),
_ => None,
})
}
diff --git a/crates/hir-def/src/test_db.rs b/crates/hir-def/src/test_db.rs
index 0c36c88fb0..54e6c1fd20 100644
--- a/crates/hir-def/src/test_db.rs
+++ b/crates/hir-def/src/test_db.rs
@@ -78,6 +78,19 @@ impl FileLoader for TestDB {
}
impl TestDB {
+ pub(crate) fn fetch_test_crate(&self) -> CrateId {
+ let crate_graph = self.crate_graph();
+ let it = crate_graph
+ .iter()
+ .find(|&idx| {
+ crate_graph[idx].display_name.as_ref().map(|it| it.canonical_name().as_str())
+ == Some("ra_test_fixture")
+ })
+ .or_else(|| crate_graph.iter().next())
+ .unwrap();
+ it
+ }
+
pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId {
for &krate in self.relevant_crates(file_id).iter() {
let crate_def_map = self.crate_def_map(krate);
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index d41f69812e..8c04d05402 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -180,6 +180,13 @@ impl<FileId: FileIdToSyntax, T> InFileWrapper<FileId, T> {
}
}
+#[allow(private_bounds)]
+impl<FileId: FileIdToSyntax, N: AstNode> InFileWrapper<FileId, AstPtr<N>> {
+ pub fn to_node(&self, db: &dyn ExpandDatabase) -> N {
+ self.value.to_node(&self.file_syntax(db))
+ }
+}
+
impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, N> {
pub fn syntax(&self) -> InFileWrapper<FileId, &SyntaxNode> {
self.with_value(self.value.syntax())
diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs
index b6d5828da9..0af29681a1 100644
--- a/crates/hir-expand/src/fixup.rs
+++ b/crates/hir-expand/src/fixup.rs
@@ -110,7 +110,8 @@ pub(crate) fn fixup_syntax(
}
},
ast::ExprStmt(it) => {
- if it.semicolon_token().is_none() {
+ let needs_semi = it.semicolon_token().is_none() && it.expr().map_or(false, |e| e.syntax().kind() != SyntaxKind::BLOCK_EXPR);
+ if needs_semi {
append.insert(node.clone().into(), vec![
Leaf::Punct(Punct {
char: ';',
@@ -908,4 +909,19 @@ fn foo () {|| __ra_fixup}
"#]],
);
}
+
+ #[test]
+ fn fixup_regression_() {
+ check(
+ r#"
+fn foo() {
+ {}
+ {}
+}
+"#,
+ expect![[r#"
+fn foo () {{} {}}
+"#]],
+ );
+ }
}
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 7d2f556406..2ee598dfbf 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -269,6 +269,13 @@ pub enum MacroDefKind {
ProcMacro(AstId<ast::Fn>, CustomProcMacroExpander, ProcMacroKind),
}
+impl MacroDefKind {
+ #[inline]
+ pub fn is_declarative(&self) -> bool {
+ matches!(self, MacroDefKind::Declarative(..))
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EagerCallInfo {
/// The expanded argument of the eager macro.
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 3a3a05c369..6856eaa3e0 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -22,7 +22,7 @@ use crate::{
consteval::ConstEvalError,
dyn_compatibility::DynCompatibilityViolation,
layout::{Layout, LayoutError},
- lower::{GenericDefaults, GenericPredicates},
+ lower::{Diagnostics, GenericDefaults, GenericPredicates},
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
mir::{BorrowckResult, MirBody, MirLowerError},
Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner,
@@ -115,21 +115,35 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[ra_salsa::cycle(crate::lower::ty_recover)]
fn ty(&self, def: TyDefId) -> Binders<Ty>;
+ #[ra_salsa::invoke(crate::lower::type_for_type_alias_with_diagnostics_query)]
+ fn type_for_type_alias_with_diagnostics(&self, def: TypeAliasId) -> (Binders<Ty>, Diagnostics);
+
/// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is
/// a `StructId` or `EnumVariantId` with a record constructor.
#[ra_salsa::invoke(crate::lower::value_ty_query)]
fn value_ty(&self, def: ValueTyDefId) -> Option<Binders<Ty>>;
+ #[ra_salsa::invoke(crate::lower::impl_self_ty_with_diagnostics_query)]
+ #[ra_salsa::cycle(crate::lower::impl_self_ty_with_diagnostics_recover)]
+ fn impl_self_ty_with_diagnostics(&self, def: ImplId) -> (Binders<Ty>, Diagnostics);
#[ra_salsa::invoke(crate::lower::impl_self_ty_query)]
- #[ra_salsa::cycle(crate::lower::impl_self_ty_recover)]
fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>;
+ #[ra_salsa::invoke(crate::lower::const_param_ty_with_diagnostics_query)]
+ fn const_param_ty_with_diagnostics(&self, def: ConstParamId) -> (Ty, Diagnostics);
#[ra_salsa::invoke(crate::lower::const_param_ty_query)]
fn const_param_ty(&self, def: ConstParamId) -> Ty;
+ #[ra_salsa::invoke(crate::lower::impl_trait_with_diagnostics_query)]
+ fn impl_trait_with_diagnostics(&self, def: ImplId) -> Option<(Binders<TraitRef>, Diagnostics)>;
#[ra_salsa::invoke(crate::lower::impl_trait_query)]
fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
+ #[ra_salsa::invoke(crate::lower::field_types_with_diagnostics_query)]
+ fn field_types_with_diagnostics(
+ &self,
+ var: VariantId,
+ ) -> (Arc<ArenaMap<LocalFieldId, Binders<Ty>>>, Diagnostics);
#[ra_salsa::invoke(crate::lower::field_types_query)]
fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>;
@@ -154,6 +168,11 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[ra_salsa::invoke(crate::lower::generic_predicates_query)]
fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates;
+ #[ra_salsa::invoke(crate::lower::generic_predicates_without_parent_with_diagnostics_query)]
+ fn generic_predicates_without_parent_with_diagnostics(
+ &self,
+ def: GenericDefId,
+ ) -> (GenericPredicates, Diagnostics);
#[ra_salsa::invoke(crate::lower::generic_predicates_without_parent_query)]
fn generic_predicates_without_parent(&self, def: GenericDefId) -> GenericPredicates;
@@ -164,8 +183,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[ra_salsa::invoke(crate::lower::trait_environment_query)]
fn trait_environment(&self, def: GenericDefId) -> Arc<TraitEnvironment>;
+ #[ra_salsa::invoke(crate::lower::generic_defaults_with_diagnostics_query)]
+ #[ra_salsa::cycle(crate::lower::generic_defaults_with_diagnostics_recover)]
+ fn generic_defaults_with_diagnostics(
+ &self,
+ def: GenericDefId,
+ ) -> (GenericDefaults, Diagnostics);
#[ra_salsa::invoke(crate::lower::generic_defaults_query)]
- #[ra_salsa::cycle(crate::lower::generic_defaults_recover)]
fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults;
#[ra_salsa::invoke(InherentImpls::inherent_impls_in_crate_query)]
diff --git a/crates/hir-ty/src/diagnostics.rs b/crates/hir-ty/src/diagnostics.rs
index af4d2c9fc0..30c02a2936 100644
--- a/crates/hir-ty/src/diagnostics.rs
+++ b/crates/hir-ty/src/diagnostics.rs
@@ -9,5 +9,5 @@ pub use crate::diagnostics::{
expr::{
record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic,
},
- unsafe_check::{missing_unsafe, unsafe_expressions, UnsafeExpr},
+ unsafe_check::{missing_unsafe, unsafe_expressions, InsideUnsafeBlock, UnsafetyReason},
};
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 58de19ba81..5452f5c680 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -383,9 +383,6 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
} else {
let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
- // Whether we must not match the fields of this variant exhaustively.
- let is_non_exhaustive =
- LazyCell::new(|| self.is_foreign_non_exhaustive(adt));
let visibilities = LazyCell::new(|| self.db.field_visibilities(variant));
self.list_variant_fields(ty, variant)
@@ -396,8 +393,7 @@ impl<'db> PatCx for MatchCheckCtx<'db> {
.is_visible_from(self.db.upcast(), self.module)
};
let is_uninhabited = self.is_uninhabited(&ty);
- let private_uninhabited =
- is_uninhabited && (!is_visible() || *is_non_exhaustive);
+ let private_uninhabited = is_uninhabited && !is_visible();
(ty, PrivateUninhabitedField(private_uninhabited))
})
.collect()
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index c7f7fb7ad3..193aaa52c2 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -1,12 +1,16 @@
//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
//! unsafe blocks.
+use std::mem;
+
+use either::Either;
use hir_def::{
body::Body,
- hir::{Expr, ExprId, ExprOrPatId, Pat, UnaryOp},
- resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
+ hir::{Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp},
+ path::Path,
+ resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
type_ref::Rawness,
- DefWithBodyId,
+ AdtId, DefWithBodyId, FieldId, VariantId,
};
use crate::{
@@ -16,7 +20,10 @@ 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<ExprOrPatId>, bool) {
+pub fn missing_unsafe(
+ db: &dyn HirDatabase,
+ def: DefWithBodyId,
+) -> (Vec<(ExprOrPatId, UnsafetyReason)>, bool) {
let _p = tracing::info_span!("missing_unsafe").entered();
let mut res = Vec::new();
@@ -30,111 +37,243 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> (Vec<ExprOrPa
let body = db.body(def);
let infer = db.infer(def);
- unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| {
- if !expr.inside_unsafe_block {
- res.push(expr.node);
+ let mut callback = |node, inside_unsafe_block, reason| {
+ if inside_unsafe_block == InsideUnsafeBlock::No {
+ res.push((node, reason));
}
- });
+ };
+ let mut visitor = UnsafeVisitor::new(db, &infer, &body, def, &mut callback);
+ visitor.walk_expr(body.body_expr);
+
+ if !is_unsafe {
+ // Unsafety in function parameter patterns (that can only be union destructuring)
+ // cannot be inserted into an unsafe block, so even with `unsafe_op_in_unsafe_fn`
+ // it is turned off for unsafe functions.
+ for &param in &body.params {
+ visitor.walk_pat(param);
+ }
+ }
(res, is_unsafe)
}
-pub struct UnsafeExpr {
- pub node: ExprOrPatId,
- pub inside_unsafe_block: bool,
+#[derive(Debug, Clone, Copy)]
+pub enum UnsafetyReason {
+ UnionField,
+ UnsafeFnCall,
+ InlineAsm,
+ RawPtrDeref,
+ MutableStatic,
+ ExternStatic,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum InsideUnsafeBlock {
+ No,
+ Yes,
}
-// FIXME: Move this out, its not a diagnostic only thing anymore, and handle unsafe pattern accesses as well
pub fn unsafe_expressions(
db: &dyn HirDatabase,
infer: &InferenceResult,
def: DefWithBodyId,
body: &Body,
current: ExprId,
- unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
+ unsafe_expr_cb: &mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason),
) {
- walk_unsafe(
- db,
- infer,
- body,
- &mut resolver_for_expr(db.upcast(), def, current),
- def,
- current,
- false,
- unsafe_expr_cb,
- )
+ let mut visitor = UnsafeVisitor::new(db, infer, body, def, unsafe_expr_cb);
+ _ = visitor.resolver.update_to_inner_scope(db.upcast(), def, current);
+ visitor.walk_expr(current);
}
-fn walk_unsafe(
- db: &dyn HirDatabase,
- infer: &InferenceResult,
- body: &Body,
- resolver: &mut Resolver,
+struct UnsafeVisitor<'a> {
+ db: &'a dyn HirDatabase,
+ infer: &'a InferenceResult,
+ body: &'a Body,
+ resolver: Resolver,
def: DefWithBodyId,
- current: ExprId,
- 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 });
+ inside_unsafe_block: InsideUnsafeBlock,
+ inside_assignment: bool,
+ inside_union_destructure: bool,
+ unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason),
+}
+
+impl<'a> UnsafeVisitor<'a> {
+ fn new(
+ db: &'a dyn HirDatabase,
+ infer: &'a InferenceResult,
+ body: &'a Body,
+ def: DefWithBodyId,
+ unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason),
+ ) -> Self {
+ let resolver = def.resolver(db.upcast());
+ Self {
+ db,
+ infer,
+ body,
+ resolver,
+ def,
+ inside_unsafe_block: InsideUnsafeBlock::No,
+ inside_assignment: false,
+ inside_union_destructure: false,
+ unsafe_expr_cb,
+ }
+ }
+
+ fn call_cb(&mut self, node: ExprOrPatId, reason: UnsafetyReason) {
+ (self.unsafe_expr_cb)(node, self.inside_unsafe_block, reason);
+ }
+
+ fn walk_pats_top(&mut self, pats: impl Iterator<Item = PatId>, parent_expr: ExprId) {
+ let guard = self.resolver.update_to_inner_scope(self.db.upcast(), self.def, parent_expr);
+ pats.for_each(|pat| self.walk_pat(pat));
+ self.resolver.reset_to_guard(guard);
+ }
+
+ fn walk_pat(&mut self, current: PatId) {
+ let pat = &self.body.pats[current];
+
+ if self.inside_union_destructure {
+ match pat {
+ Pat::Tuple { .. }
+ | Pat::Record { .. }
+ | Pat::Range { .. }
+ | Pat::Slice { .. }
+ | Pat::Path(..)
+ | Pat::Lit(..)
+ | Pat::Bind { .. }
+ | Pat::TupleStruct { .. }
+ | Pat::Ref { .. }
+ | Pat::Box { .. }
+ | Pat::Expr(..)
+ | Pat::ConstBlock(..) => self.call_cb(current.into(), UnsafetyReason::UnionField),
+ // `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read.
+ Pat::Missing | Pat::Wild | Pat::Or(_) => {}
}
}
- 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 { node: current.into(), inside_unsafe_block });
+ match pat {
+ Pat::Record { .. } => {
+ if let Some((AdtId::UnionId(_), _)) = self.infer[current].as_adt() {
+ let old_inside_union_destructure =
+ mem::replace(&mut self.inside_union_destructure, true);
+ self.body.walk_pats_shallow(current, |pat| self.walk_pat(pat));
+ self.inside_union_destructure = old_inside_union_destructure;
+ return;
}
}
- }
- 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)`,
- // see https://github.com/rust-lang/rust/pull/125834.
- return;
+ Pat::Path(path) => self.mark_unsafe_path(current.into(), path),
+ &Pat::ConstBlock(expr) => {
+ let old_inside_assignment = mem::replace(&mut self.inside_assignment, false);
+ self.walk_expr(expr);
+ self.inside_assignment = old_inside_assignment;
}
+ &Pat::Expr(expr) => self.walk_expr(expr),
+ _ => {}
}
- Expr::MethodCall { .. } => {
- if infer
- .method_resolution(current)
- .map(|(func, _)| is_fn_unsafe_to_call(db, func))
- .unwrap_or(false)
- {
- unsafe_expr_cb(UnsafeExpr { node: current.into(), inside_unsafe_block });
+
+ self.body.walk_pats_shallow(current, |pat| self.walk_pat(pat));
+ }
+
+ fn walk_expr(&mut self, current: ExprId) {
+ let expr = &self.body.exprs[current];
+ let inside_assignment = mem::replace(&mut self.inside_assignment, false);
+ match expr {
+ &Expr::Call { callee, .. } => {
+ if let Some(func) = self.infer[callee].as_fn_def(self.db) {
+ if is_fn_unsafe_to_call(self.db, func) {
+ self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall);
+ }
+ }
}
- }
- Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
- if let TyKind::Raw(..) = &infer[*expr].kind(Interner) {
- unsafe_expr_cb(UnsafeExpr { node: current.into(), inside_unsafe_block });
+ Expr::Path(path) => {
+ let guard =
+ self.resolver.update_to_inner_scope(self.db.upcast(), self.def, current);
+ self.mark_unsafe_path(current.into(), path);
+ self.resolver.reset_to_guard(guard);
}
- }
- Expr::Unsafe { .. } => {
- 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::Ref { expr, rawness: Rawness::RawPtr, mutability: _ } => {
+ if let Expr::Path(_) = self.body.exprs[*expr] {
+ // Do not report unsafe for `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)`,
+ // see https://github.com/rust-lang/rust/pull/125834.
+ return;
+ }
+ }
+ Expr::MethodCall { .. } => {
+ if self
+ .infer
+ .method_resolution(current)
+ .map(|(func, _)| is_fn_unsafe_to_call(self.db, func))
+ .unwrap_or(false)
+ {
+ self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall);
}
- });
+ }
+ Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
+ if let TyKind::Raw(..) = &self.infer[*expr].kind(Interner) {
+ self.call_cb(current.into(), UnsafetyReason::RawPtrDeref);
+ }
+ }
+ Expr::Unsafe { .. } => {
+ let old_inside_unsafe_block =
+ mem::replace(&mut self.inside_unsafe_block, InsideUnsafeBlock::Yes);
+ self.body.walk_child_exprs_without_pats(current, |child| self.walk_expr(child));
+ self.inside_unsafe_block = old_inside_unsafe_block;
+ return;
+ }
+ &Expr::Assignment { target, value: _ } => {
+ let old_inside_assignment = mem::replace(&mut self.inside_assignment, true);
+ self.walk_pats_top(std::iter::once(target), current);
+ self.inside_assignment = old_inside_assignment;
+ }
+ Expr::InlineAsm(_) => self.call_cb(current.into(), UnsafetyReason::InlineAsm),
+ // rustc allows union assignment to propagate through field accesses and casts.
+ Expr::Cast { .. } => self.inside_assignment = inside_assignment,
+ Expr::Field { .. } => {
+ self.inside_assignment = inside_assignment;
+ if !inside_assignment {
+ if let Some(Either::Left(FieldId { parent: VariantId::UnionId(_), .. })) =
+ self.infer.field_resolution(current)
+ {
+ self.call_cb(current.into(), UnsafetyReason::UnionField);
+ }
+ }
+ }
+ Expr::Block { statements, .. } | Expr::Async { statements, .. } => {
+ self.walk_pats_top(
+ statements.iter().filter_map(|statement| match statement {
+ &Statement::Let { pat, .. } => Some(pat),
+ _ => None,
+ }),
+ current,
+ );
+ }
+ Expr::Match { arms, .. } => {
+ self.walk_pats_top(arms.iter().map(|arm| arm.pat), current);
+ }
+ &Expr::Let { pat, .. } => {
+ self.walk_pats_top(std::iter::once(pat), current);
+ }
+ Expr::Closure { args, .. } => {
+ self.walk_pats_top(args.iter().copied(), current);
+ }
+ _ => {}
}
- _ => {}
+
+ self.body.walk_child_exprs_without_pats(current, |child| self.walk_expr(child));
}
- body.walk_child_exprs(current, |child| {
- walk_unsafe(db, infer, body, resolver, def, child, inside_unsafe_block, unsafe_expr_cb);
- });
+ fn mark_unsafe_path(&mut self, node: ExprOrPatId, path: &Path) {
+ let hygiene = self.body.expr_or_pat_path_hygiene(node);
+ let value_or_partial =
+ self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, hygiene);
+ if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
+ let static_data = self.db.static_data(id);
+ if static_data.mutable {
+ self.call_cb(node, UnsafetyReason::MutableStatic);
+ } else if static_data.is_extern && !static_data.has_safe_kw {
+ self.call_cb(node, UnsafetyReason::ExternStatic);
+ }
+ }
+ }
}
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 4e95bdf219..3dfa0e97ce 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -1047,10 +1047,14 @@ impl HirDisplay for Ty {
);
// We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
if parameters.len() - impl_ > 0 {
+ let params_len = parameters.len();
// `parameters` are in the order of fn's params (including impl traits), fn's lifetimes
let parameters =
generic_args_sans_defaults(f, Some(generic_def_id), parameters);
- let without_impl = self_param as usize + type_ + const_ + lifetime;
+ assert!(params_len >= parameters.len());
+ let defaults = params_len - parameters.len();
+ let without_impl =
+ self_param as usize + type_ + const_ + lifetime - defaults;
// parent's params (those from enclosing impl or trait, if any).
let (fn_params, parent_params) = parameters.split_at(without_impl + impl_);
@@ -2062,12 +2066,12 @@ impl HirDisplayWithTypesMap for TypeBound {
types_map: &TypesMap,
) -> Result<(), HirDisplayError> {
match self {
- TypeBound::Path(path, modifier) => {
+ &TypeBound::Path(path, modifier) => {
match modifier {
TraitBoundModifier::None => (),
TraitBoundModifier::Maybe => write!(f, "?")?,
}
- path.hir_fmt(f, types_map)
+ types_map[path].hir_fmt(f, types_map)
}
TypeBound::Lifetime(lifetime) => {
write!(f, "{}", lifetime.name.display(f.db.upcast(), f.edition()))
@@ -2079,7 +2083,7 @@ impl HirDisplayWithTypesMap for TypeBound {
"for<{}> ",
lifetimes.iter().map(|it| it.display(f.db.upcast(), edition)).format(", ")
)?;
- path.hir_fmt(f, types_map)
+ types_map[*path].hir_fmt(f, types_map)
}
TypeBound::Use(args) => {
let edition = f.edition();
diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs
index c094bc3951..fe7541d237 100644
--- a/crates/hir-ty/src/generics.rs
+++ b/crates/hir-ty/src/generics.rs
@@ -55,6 +55,10 @@ impl Generics {
self.def
}
+ pub(crate) fn self_types_map(&self) -> &TypesMap {
+ &self.params.types_map
+ }
+
pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
self.iter_self_id().chain(self.iter_parent_id())
}
@@ -86,15 +90,13 @@ impl Generics {
self.iter_self().chain(self.iter_parent())
}
- pub(crate) fn iter_with_types_map(
+ pub(crate) fn iter_parents_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)),
- ),
+ self.iter_parent().zip(
+ self.parent_generics()
+ .into_iter()
+ .flat_map(|it| std::iter::repeat(&it.params.types_map)),
)
}
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 01e0b635b2..dbee5a1a91 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -58,7 +58,7 @@ use crate::{
fold_tys,
generics::Generics,
infer::{coerce::CoerceMany, expr::ExprIsRead, unify::InferenceTable},
- lower::ImplTraitLoweringMode,
+ lower::{ImplTraitLoweringMode, TyLoweringDiagnostic},
mir::MirSpan,
to_assoc_type_id,
traits::FnTrait,
@@ -191,6 +191,14 @@ impl<T> InferOk<T> {
}
}
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum InferenceTyDiagnosticSource {
+ /// Diagnostics that come from types in the body.
+ Body,
+ /// Diagnostics that come from types in fn parameters/return type, or static & const types.
+ Signature,
+}
+
#[derive(Debug)]
pub(crate) struct TypeError;
pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
@@ -264,6 +272,10 @@ pub enum InferenceDiagnostic {
expr_ty: Ty,
cast_ty: Ty,
},
+ TyDiagnostic {
+ source: InferenceTyDiagnosticSource,
+ diag: TyLoweringDiagnostic,
+ },
}
/// A mismatch between an expected and an inferred type.
@@ -858,7 +870,8 @@ impl<'a> InferenceContext<'a> {
}
fn collect_const(&mut self, data: &ConstData) {
- let return_ty = self.make_ty(data.type_ref, &data.types_map);
+ let return_ty =
+ self.make_ty(data.type_ref, &data.types_map, InferenceTyDiagnosticSource::Signature);
// Constants might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty));
@@ -867,7 +880,8 @@ impl<'a> InferenceContext<'a> {
}
fn collect_static(&mut self, data: &StaticData) {
- let return_ty = self.make_ty(data.type_ref, &data.types_map);
+ let return_ty =
+ self.make_ty(data.type_ref, &data.types_map, InferenceTyDiagnosticSource::Signature);
// Statics might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty));
@@ -877,11 +891,12 @@ impl<'a> InferenceContext<'a> {
fn collect_fn(&mut self, func: FunctionId) {
let data = self.db.function_data(func);
- 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<_>>()
- });
+ let mut param_tys =
+ self.with_ty_lowering(&data.types_map, InferenceTyDiagnosticSource::Signature, |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() {
@@ -918,11 +933,12 @@ impl<'a> InferenceContext<'a> {
}
let return_ty = data.ret_type;
- 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.with_ty_lowering(&data.types_map, InferenceTyDiagnosticSource::Signature, |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) {
@@ -1226,9 +1242,20 @@ impl<'a> InferenceContext<'a> {
self.result.diagnostics.push(diagnostic);
}
+ fn push_ty_diagnostics(
+ &mut self,
+ source: InferenceTyDiagnosticSource,
+ diagnostics: Vec<TyLoweringDiagnostic>,
+ ) {
+ self.result.diagnostics.extend(
+ diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }),
+ );
+ }
+
fn with_ty_lowering<R>(
- &self,
+ &mut self,
types_map: &TypesMap,
+ types_source: InferenceTyDiagnosticSource,
f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
) -> R {
let mut ctx = crate::lower::TyLoweringContext::new(
@@ -1237,32 +1264,41 @@ impl<'a> InferenceContext<'a> {
types_map,
self.owner.into(),
);
- f(&mut ctx)
+ let result = f(&mut ctx);
+ self.push_ty_diagnostics(types_source, ctx.diagnostics);
+ result
}
fn with_body_ty_lowering<R>(
- &self,
+ &mut self,
f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
) -> R {
- self.with_ty_lowering(&self.body.types, f)
+ self.with_ty_lowering(&self.body.types, InferenceTyDiagnosticSource::Body, 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));
+ fn make_ty(
+ &mut self,
+ type_ref: TypeRefId,
+ types_map: &TypesMap,
+ type_source: InferenceTyDiagnosticSource,
+ ) -> Ty {
+ let ty = self.with_ty_lowering(types_map, type_source, |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)
+ self.make_ty(type_ref, &self.body.types, InferenceTyDiagnosticSource::Body)
}
fn err_ty(&self) -> Ty {
self.result.standard_types.unknown.clone()
}
- fn make_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime {
- let lt = self.with_ty_lowering(TypesMap::EMPTY, |ctx| ctx.lower_lifetime(lifetime_ref));
+ fn make_body_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime {
+ let lt = self.with_ty_lowering(TypesMap::EMPTY, InferenceTyDiagnosticSource::Body, |ctx| {
+ ctx.lower_lifetime(lifetime_ref)
+ });
self.insert_type_vars(lt)
}
@@ -1431,12 +1467,20 @@ impl<'a> InferenceContext<'a> {
Some(ResolveValueResult::ValueNs(value, _)) => match value {
ValueNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
+ self.push_ty_diagnostics(
+ InferenceTyDiagnosticSource::Body,
+ ctx.diagnostics,
+ );
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
return (ty, Some(var.into()));
}
ValueNs::StructId(strukt) => {
let substs = ctx.substs_from_path(path, strukt.into(), true);
+ self.push_ty_diagnostics(
+ InferenceTyDiagnosticSource::Body,
+ ctx.diagnostics,
+ );
let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
return (ty, Some(strukt.into()));
@@ -1462,18 +1506,21 @@ impl<'a> InferenceContext<'a> {
return match resolution {
TypeNs::AdtId(AdtId::StructId(strukt)) => {
let substs = ctx.substs_from_path(path, strukt.into(), true);
+ self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
}
TypeNs::AdtId(AdtId::UnionId(u)) => {
let substs = ctx.substs_from_path(path, u.into(), true);
+ self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
let ty = self.db.ty(u.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(u.into())), unresolved)
}
TypeNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
+ self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(var.into())), unresolved)
@@ -1519,6 +1566,9 @@ impl<'a> InferenceContext<'a> {
resolved_segment,
current_segment,
false,
+ &mut |_, _reason| {
+ // FIXME: Report an error.
+ },
);
ty = self.table.insert_type_vars(ty);
@@ -1532,6 +1582,7 @@ impl<'a> InferenceContext<'a> {
remaining_idx += 1;
remaining_segments = remaining_segments.skip(1);
}
+ self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
let variant = ty.as_adt().and_then(|(id, _)| match id {
AdtId::StructId(s) => Some(VariantId::StructId(s)),
@@ -1550,6 +1601,7 @@ impl<'a> InferenceContext<'a> {
};
let substs =
ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None);
+ self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
let ty = self.db.ty(it.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 366c3cb0f1..2fe90a8a92 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -125,7 +125,11 @@ impl CoerceMany {
// pointers to have a chance at getting a match. See
// https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
let sig = match (self.merged_ty().kind(Interner), expr_ty.kind(Interner)) {
- (TyKind::FnDef(x, _), TyKind::FnDef(y, _)) if x == y => None,
+ (TyKind::FnDef(x, _), TyKind::FnDef(y, _))
+ if x == y && ctx.table.unify(&self.merged_ty(), &expr_ty) =>
+ {
+ None
+ }
(TyKind::Closure(x, _), TyKind::Closure(y, _)) if x == y => None,
(TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => {
// FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 32b4ea2f28..a13541be69 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -1287,8 +1287,8 @@ impl InferenceContext<'_> {
tgt_expr: ExprId,
) {
match fn_x {
- FnTrait::FnOnce => (),
- FnTrait::FnMut => {
+ FnTrait::FnOnce | FnTrait::AsyncFnOnce => (),
+ FnTrait::FnMut | FnTrait::AsyncFnMut => {
if let TyKind::Ref(Mutability::Mut, lt, inner) = derefed_callee.kind(Interner) {
if adjustments
.last()
@@ -1312,7 +1312,7 @@ impl InferenceContext<'_> {
));
}
}
- FnTrait::Fn => {
+ FnTrait::Fn | FnTrait::AsyncFn => {
if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Not, _, _)) {
adjustments.push(Adjustment::borrow(
Mutability::Not,
@@ -2155,7 +2155,7 @@ impl InferenceContext<'_> {
DebruijnIndex::INNERMOST,
)
},
- |this, lt_ref| this.make_lifetime(lt_ref),
+ |this, lt_ref| this.make_body_lifetime(lt_ref),
),
};
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 7550d197a3..a6296c3af2 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -19,7 +19,7 @@ use crate::{
TyBuilder, TyExt, TyKind, ValueTyDefId,
};
-use super::{ExprOrPatId, InferenceContext};
+use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource};
impl InferenceContext<'_> {
pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
@@ -163,6 +163,7 @@ impl InferenceContext<'_> {
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty);
+ self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
let ty = self.table.insert_type_vars(ty);
let ty = self.table.normalize_associated_types_in(ty);
self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
@@ -265,6 +266,9 @@ impl InferenceContext<'_> {
resolved_segment,
remaining_segments_for_ty,
true,
+ &mut |_, _reason| {
+ // FIXME: Report an error.
+ },
)
});
if ty.is_unknown() {
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 54aa18ce20..165861c1b1 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -666,7 +666,7 @@ impl<'a> InferenceTable<'a> {
highest_known_var: InferenceVar,
}
impl TypeFolder<Interner> for VarFudger<'_, '_> {
- fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
+ fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner> {
self
}
@@ -794,69 +794,75 @@ impl<'a> InferenceTable<'a> {
ty: &Ty,
num_args: usize,
) -> Option<(FnTrait, Vec<Ty>, Ty)> {
- let krate = self.trait_env.krate;
- let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
- let trait_data = self.db.trait_data(fn_once_trait);
- let output_assoc_type =
- trait_data.associated_type_by_name(&Name::new_symbol_root(sym::Output.clone()))?;
-
- let mut arg_tys = Vec::with_capacity(num_args);
- let arg_ty = TyBuilder::tuple(num_args)
- .fill(|it| {
- let arg = match it {
- ParamKind::Type => self.new_type_var(),
- ParamKind::Lifetime => unreachable!("Tuple with lifetime parameter"),
- ParamKind::Const(_) => unreachable!("Tuple with const parameter"),
- };
- arg_tys.push(arg.clone());
- arg.cast(Interner)
- })
- .build();
-
- let b = TyBuilder::trait_ref(self.db, fn_once_trait);
- if b.remaining() != 2 {
- return None;
- }
- let mut trait_ref = b.push(ty.clone()).push(arg_ty).build();
+ for (fn_trait_name, output_assoc_name, subtraits) in [
+ (FnTrait::FnOnce, sym::Output.clone(), &[FnTrait::Fn, FnTrait::FnMut][..]),
+ (FnTrait::AsyncFnMut, sym::CallRefFuture.clone(), &[FnTrait::AsyncFn]),
+ (FnTrait::AsyncFnOnce, sym::CallOnceFuture.clone(), &[]),
+ ] {
+ let krate = self.trait_env.krate;
+ let fn_trait = fn_trait_name.get_id(self.db, krate)?;
+ let trait_data = self.db.trait_data(fn_trait);
+ let output_assoc_type =
+ trait_data.associated_type_by_name(&Name::new_symbol_root(output_assoc_name))?;
+
+ let mut arg_tys = Vec::with_capacity(num_args);
+ let arg_ty = TyBuilder::tuple(num_args)
+ .fill(|it| {
+ let arg = match it {
+ ParamKind::Type => self.new_type_var(),
+ ParamKind::Lifetime => unreachable!("Tuple with lifetime parameter"),
+ ParamKind::Const(_) => unreachable!("Tuple with const parameter"),
+ };
+ arg_tys.push(arg.clone());
+ arg.cast(Interner)
+ })
+ .build();
+
+ let b = TyBuilder::trait_ref(self.db, fn_trait);
+ if b.remaining() != 2 {
+ return None;
+ }
+ let mut trait_ref = b.push(ty.clone()).push(arg_ty).build();
- let projection = {
- TyBuilder::assoc_type_projection(
+ let projection = TyBuilder::assoc_type_projection(
self.db,
output_assoc_type,
Some(trait_ref.substitution.clone()),
)
- .build()
- };
+ .fill_with_unknown()
+ .build();
- let trait_env = self.trait_env.env.clone();
- let obligation = InEnvironment {
- goal: trait_ref.clone().cast(Interner),
- environment: trait_env.clone(),
- };
- let canonical = self.canonicalize(obligation.clone());
- if self.db.trait_solve(krate, self.trait_env.block, canonical.cast(Interner)).is_some() {
- self.register_obligation(obligation.goal);
- let return_ty = self.normalize_projection_ty(projection);
- for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
- let fn_x_trait = fn_x.get_id(self.db, krate)?;
- trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
- let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> = InEnvironment {
- goal: trait_ref.clone().cast(Interner),
- environment: trait_env.clone(),
- };
- let canonical = self.canonicalize(obligation.clone());
- if self
- .db
- .trait_solve(krate, self.trait_env.block, canonical.cast(Interner))
- .is_some()
- {
- return Some((fn_x, arg_tys, return_ty));
+ let trait_env = self.trait_env.env.clone();
+ let obligation = InEnvironment {
+ goal: trait_ref.clone().cast(Interner),
+ environment: trait_env.clone(),
+ };
+ let canonical = self.canonicalize(obligation.clone());
+ if self.db.trait_solve(krate, self.trait_env.block, canonical.cast(Interner)).is_some()
+ {
+ self.register_obligation(obligation.goal);
+ let return_ty = self.normalize_projection_ty(projection);
+ for &fn_x in subtraits {
+ let fn_x_trait = fn_x.get_id(self.db, krate)?;
+ trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
+ let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> =
+ InEnvironment {
+ goal: trait_ref.clone().cast(Interner),
+ environment: trait_env.clone(),
+ };
+ let canonical = self.canonicalize(obligation.clone());
+ if self
+ .db
+ .trait_solve(krate, self.trait_env.block, canonical.cast(Interner))
+ .is_some()
+ {
+ return Some((fn_x, arg_tys, return_ty));
+ }
}
+ return Some((fn_trait_name, arg_tys, return_ty));
}
- unreachable!("It should at least implement FnOnce at this point");
- } else {
- None
}
+ None
}
pub(super) fn insert_type_vars<T>(&mut self, ty: T) -> T
@@ -1004,7 +1010,7 @@ mod resolve {
where
F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
{
- fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
+ fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner> {
self
}
diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs
index c0a781b17e..56b549436c 100644
--- a/crates/hir-ty/src/inhabitedness.rs
+++ b/crates/hir-ty/src/inhabitedness.rs
@@ -5,8 +5,7 @@ use chalk_ir::{
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
DebruijnIndex,
};
-use hir_def::{visibility::Visibility, AdtId, EnumVariantId, HasModule, ModuleId, VariantId};
-use intern::sym;
+use hir_def::{visibility::Visibility, AdtId, EnumVariantId, ModuleId, VariantId};
use rustc_hash::FxHashSet;
use crate::{
@@ -118,11 +117,6 @@ impl UninhabitedFrom<'_> {
variant: VariantId,
subst: &Substitution,
) -> ControlFlow<VisiblyUninhabited> {
- let is_local = variant.krate(self.db.upcast()) == self.target_mod.krate();
- if !is_local && self.db.attrs(variant.into()).by_key(&sym::non_exhaustive).exists() {
- return CONTINUE_OPAQUELY_INHABITED;
- }
-
let variant_data = self.db.variant_data(variant);
let fields = variant_data.fields();
if fields.is_empty() {
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 22e7b1d920..8bb90ca31e 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -84,12 +84,14 @@ pub use infer::{
cast::CastError,
closure::{CaptureKind, CapturedItem},
could_coerce, could_unify, could_unify_deeply, Adjust, Adjustment, AutoBorrow, BindingMode,
- InferenceDiagnostic, InferenceResult, OverloadedDeref, PointerCast,
+ InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref,
+ PointerCast,
};
pub use interner::Interner;
pub use lower::{
- associated_type_shorthand_candidates, ImplTraitLoweringMode, ParamLoweringMode, TyDefId,
- TyLoweringContext, ValueTyDefId,
+ associated_type_shorthand_candidates, GenericArgsProhibitedReason, ImplTraitLoweringMode,
+ ParamLoweringMode, TyDefId, TyLoweringContext, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
+ ValueTyDefId,
};
pub use mapping::{
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
@@ -385,7 +387,6 @@ pub enum FnAbi {
Fastcall,
FastcallUnwind,
Msp430Interrupt,
- PlatformIntrinsic,
PtxKernel,
RiscvInterruptM,
RiscvInterruptS,
@@ -444,7 +445,6 @@ impl FnAbi {
s if *s == sym::fastcall_dash_unwind => FnAbi::FastcallUnwind,
s if *s == sym::fastcall => FnAbi::Fastcall,
s if *s == sym::msp430_dash_interrupt => FnAbi::Msp430Interrupt,
- s if *s == sym::platform_dash_intrinsic => FnAbi::PlatformIntrinsic,
s if *s == sym::ptx_dash_kernel => FnAbi::PtxKernel,
s if *s == sym::riscv_dash_interrupt_dash_m => FnAbi::RiscvInterruptM,
s if *s == sym::riscv_dash_interrupt_dash_s => FnAbi::RiscvInterruptS,
@@ -487,7 +487,6 @@ impl FnAbi {
FnAbi::Fastcall => "fastcall",
FnAbi::FastcallUnwind => "fastcall-unwind",
FnAbi::Msp430Interrupt => "msp430-interrupt",
- FnAbi::PlatformIntrinsic => "platform-intrinsic",
FnAbi::PtxKernel => "ptx-kernel",
FnAbi::RiscvInterruptM => "riscv-interrupt-m",
FnAbi::RiscvInterruptS => "riscv-interrupt-s",
@@ -646,7 +645,7 @@ pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<
F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
> TypeFolder<Interner> for FreeVarFolder<F1, F2>
{
- fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
+ fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner> {
self
}
@@ -697,7 +696,7 @@ pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + TypeFold
impl<F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>> TypeFolder<Interner>
for TyFolder<F>
{
- fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
+ fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner> {
self
}
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index b868ea95f8..b23f2749ab 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -33,8 +33,8 @@ use hir_def::{
path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
type_ref::{
- ConstRef, LifetimeRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef,
- TypeRefId, TypesMap, TypesSourceMap,
+ ConstRef, LifetimeRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound,
+ TypeRef, TypeRefId, TypesMap, TypesSourceMap,
},
AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, ItemContainerId,
@@ -48,7 +48,7 @@ use rustc_pattern_analysis::Captures;
use smallvec::SmallVec;
use stdx::{impl_from, never};
use syntax::ast;
-use triomphe::Arc;
+use triomphe::{Arc, ThinArc};
use crate::{
all_super_traits,
@@ -102,6 +102,31 @@ impl ImplTraitLoweringState {
}
}
+type TypeSource = Either<TypeRefId, hir_def::type_ref::TypeSource>;
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct TyLoweringDiagnostic {
+ pub source: TypeSource,
+ pub kind: TyLoweringDiagnosticKind,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum TyLoweringDiagnosticKind {
+ GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason },
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum GenericArgsProhibitedReason {
+ Module,
+ TyParam,
+ SelfTy,
+ PrimitiveTy,
+ /// When there is a generic enum, within the expression `Enum::Variant`,
+ /// either `Enum` or `Variant` are allowed to have generic arguments, but not both.
+ // FIXME: This is not used now but it should be.
+ EnumVariant,
+}
+
#[derive(Debug)]
pub struct TyLoweringContext<'a> {
pub db: &'a dyn HirDatabase,
@@ -125,6 +150,7 @@ pub struct TyLoweringContext<'a> {
expander: Option<Expander>,
/// Tracks types with explicit `?Sized` bounds.
pub(crate) unsized_types: FxHashSet<Ty>,
+ pub(crate) diagnostics: Vec<TyLoweringDiagnostic>,
}
impl<'a> TyLoweringContext<'a> {
@@ -159,6 +185,7 @@ impl<'a> TyLoweringContext<'a> {
type_param_mode,
expander: None,
unsized_types: FxHashSet::default(),
+ diagnostics: Vec::new(),
}
}
@@ -198,6 +225,20 @@ impl<'a> TyLoweringContext<'a> {
self.type_param_mode = type_param_mode;
self
}
+
+ pub fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) {
+ let source = match self.types_source_map {
+ Some(source_map) => {
+ let Ok(source) = source_map.type_syntax(type_ref) else {
+ stdx::never!("error in synthetic type");
+ return;
+ };
+ Either::Right(source)
+ }
+ None => Either::Left(type_ref),
+ };
+ self.diagnostics.push(TyLoweringDiagnostic { source, kind });
+ }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
@@ -264,7 +305,8 @@ impl<'a> TyLoweringContext<'a> {
.intern(Interner)
}
TypeRef::Path(path) => {
- let (ty, res_) = self.lower_path(path);
+ let (ty, res_) =
+ self.lower_path(path, PathId::from_type_ref_unchecked(type_ref_id));
res = res_;
ty
}
@@ -463,6 +505,7 @@ impl<'a> TyLoweringContext<'a> {
impl_trait_mode: mem::take(&mut self.impl_trait_mode),
expander: self.expander.take(),
unsized_types: mem::take(&mut self.unsized_types),
+ diagnostics: mem::take(&mut self.diagnostics),
};
let ty = inner_ctx.lower_ty(type_ref);
@@ -470,6 +513,7 @@ impl<'a> TyLoweringContext<'a> {
self.impl_trait_mode = inner_ctx.impl_trait_mode;
self.expander = inner_ctx.expander;
self.unsized_types = inner_ctx.unsized_types;
+ self.diagnostics = inner_ctx.diagnostics;
self.expander.as_mut().unwrap().exit(mark);
Some(ty)
@@ -541,6 +585,10 @@ impl<'a> TyLoweringContext<'a> {
resolved_segment: PathSegment<'_>,
remaining_segments: PathSegments<'_>,
infer_args: bool,
+ on_prohibited_generics_for_resolved_segment: &mut dyn FnMut(
+ &mut Self,
+ GenericArgsProhibitedReason,
+ ),
) -> (Ty, Option<TypeNs>) {
let ty = match resolution {
TypeNs::TraitId(trait_) => {
@@ -607,28 +655,44 @@ impl<'a> TyLoweringContext<'a> {
// FIXME(trait_alias): Implement trait alias.
return (TyKind::Error.intern(Interner), None);
}
- TypeNs::GenericParam(param_id) => match self.type_param_mode {
- ParamLoweringMode::Placeholder => {
- TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
+ TypeNs::GenericParam(param_id) => {
+ if resolved_segment.args_and_bindings.is_some() {
+ on_prohibited_generics_for_resolved_segment(
+ self,
+ GenericArgsProhibitedReason::TyParam,
+ );
}
- ParamLoweringMode::Variable => {
- let idx = match self
- .generics()
- .expect("generics in scope")
- .type_or_const_param_idx(param_id.into())
- {
- None => {
- never!("no matching generics");
- return (TyKind::Error.intern(Interner), None);
- }
- Some(idx) => idx,
- };
- TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
+ match self.type_param_mode {
+ ParamLoweringMode::Placeholder => {
+ TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
+ }
+ ParamLoweringMode::Variable => {
+ let idx = match self
+ .generics()
+ .expect("generics in scope")
+ .type_or_const_param_idx(param_id.into())
+ {
+ None => {
+ never!("no matching generics");
+ return (TyKind::Error.intern(Interner), None);
+ }
+ Some(idx) => idx,
+ };
+
+ TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
+ }
}
+ .intern(Interner)
}
- .intern(Interner),
TypeNs::SelfType(impl_id) => {
+ if resolved_segment.args_and_bindings.is_some() {
+ on_prohibited_generics_for_resolved_segment(
+ self,
+ GenericArgsProhibitedReason::SelfTy,
+ );
+ }
+
let generics = self.generics().expect("impl should have generic param scope");
match self.type_param_mode {
@@ -654,6 +718,13 @@ impl<'a> TyLoweringContext<'a> {
}
}
TypeNs::AdtSelfType(adt) => {
+ if resolved_segment.args_and_bindings.is_some() {
+ on_prohibited_generics_for_resolved_segment(
+ self,
+ GenericArgsProhibitedReason::SelfTy,
+ );
+ }
+
let generics = generics(self.db.upcast(), adt.into());
let substs = match self.type_param_mode {
ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db),
@@ -666,6 +737,12 @@ impl<'a> TyLoweringContext<'a> {
TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args),
TypeNs::BuiltinType(it) => {
+ if resolved_segment.args_and_bindings.is_some() {
+ on_prohibited_generics_for_resolved_segment(
+ self,
+ GenericArgsProhibitedReason::PrimitiveTy,
+ );
+ }
self.lower_path_inner(resolved_segment, it.into(), infer_args)
}
TypeNs::TypeAliasId(it) => {
@@ -677,7 +754,7 @@ impl<'a> TyLoweringContext<'a> {
self.lower_ty_relative_path(ty, Some(resolution), remaining_segments)
}
- pub(crate) fn lower_path(&mut self, path: &Path) -> (Ty, Option<TypeNs>) {
+ pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty, Option<TypeNs>) {
// Resolve the path (in type namespace)
if let Some(type_ref) = path.type_anchor() {
let (ty, res) = self.lower_ty_ext(type_ref);
@@ -692,19 +769,44 @@ 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 bound = TypeBound::Path(path_id, TraitBoundModifier::None);
let ty = self.lower_dyn_trait(&[bound]);
return (ty, None);
}
- let (resolved_segment, remaining_segments) = match remaining_index {
- None => (
- path.segments().last().expect("resolved path has at least one element"),
- PathSegments::EMPTY,
- ),
- Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
- };
- self.lower_partly_resolved_path(resolution, resolved_segment, remaining_segments, false)
+ let (module_segments, resolved_segment_idx, resolved_segment, remaining_segments) =
+ match remaining_index {
+ None => (
+ path.segments().strip_last(),
+ path.segments().len() - 1,
+ path.segments().last().expect("resolved path has at least one element"),
+ PathSegments::EMPTY,
+ ),
+ Some(i) => (
+ path.segments().take(i - 1),
+ i - 1,
+ path.segments().get(i - 1).unwrap(),
+ path.segments().skip(i),
+ ),
+ };
+
+ self.prohibit_generics(path_id, 0, module_segments, GenericArgsProhibitedReason::Module);
+
+ self.lower_partly_resolved_path(
+ resolution,
+ resolved_segment,
+ remaining_segments,
+ false,
+ &mut |this, reason| {
+ this.push_diagnostic(
+ path_id.type_ref(),
+ TyLoweringDiagnosticKind::GenericArgsProhibited {
+ segment: resolved_segment_idx as u32,
+ reason,
+ },
+ )
+ },
+ )
}
fn select_associated_type(&mut self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
@@ -741,12 +843,8 @@ impl<'a> TyLoweringContext<'a> {
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`t.substitution`).
- let substs = self.substs_from_path_segment(
- segment.clone(),
- Some(associated_ty.into()),
- false,
- None,
- );
+ let substs =
+ self.substs_from_path_segment(segment, Some(associated_ty.into()), false, None);
let len_self =
crate::generics::generics(self.db.upcast(), associated_ty.into()).len_self();
@@ -998,12 +1096,41 @@ impl<'a> TyLoweringContext<'a> {
TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs }
}
- fn lower_trait_ref_from_path(&mut self, path: &Path, explicit_self_ty: Ty) -> Option<TraitRef> {
+ fn prohibit_generics(
+ &mut self,
+ path_id: PathId,
+ idx: u32,
+ segments: PathSegments<'_>,
+ reason: GenericArgsProhibitedReason,
+ ) {
+ segments.iter().zip(idx..).for_each(|(segment, idx)| {
+ if segment.args_and_bindings.is_some() {
+ self.push_diagnostic(
+ path_id.type_ref(),
+ TyLoweringDiagnosticKind::GenericArgsProhibited { segment: idx, reason },
+ );
+ }
+ });
+ }
+
+ fn lower_trait_ref_from_path(
+ &mut self,
+ path_id: PathId,
+ explicit_self_ty: Ty,
+ ) -> Option<TraitRef> {
+ let path = &self.types_map[path_id];
let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? {
// FIXME(trait_alias): We need to handle trait alias here.
TypeNs::TraitId(tr) => tr,
_ => return None,
};
+ // Do this after we verify it's indeed a trait to not confuse the user if they're not modules.
+ self.prohibit_generics(
+ path_id,
+ 0,
+ path.segments().strip_last(),
+ GenericArgsProhibitedReason::Module,
+ );
let segment = path.segments().last().expect("path should have at least one segment");
Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty))
}
@@ -1013,7 +1140,7 @@ impl<'a> TyLoweringContext<'a> {
trait_ref: &HirTraitRef,
explicit_self_ty: Ty,
) -> Option<TraitRef> {
- self.lower_trait_ref_from_path(&trait_ref.path, explicit_self_ty)
+ self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty)
}
fn trait_ref_substs_from_path(
@@ -1072,11 +1199,11 @@ impl<'a> TyLoweringContext<'a> {
) -> impl Iterator<Item = QuantifiedWhereClause> + use<'b, 'a> {
let mut trait_ref = None;
let clause = match bound {
- TypeBound::Path(path, TraitBoundModifier::None) => {
+ &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)
}
- TypeBound::Path(path, TraitBoundModifier::Maybe) => {
+ &TypeBound::Path(path, TraitBoundModifier::Maybe) => {
let sized_trait = self
.db
.lang_item(self.resolver.krate(), LangItem::Sized)
@@ -1092,7 +1219,7 @@ impl<'a> TyLoweringContext<'a> {
}
None
}
- TypeBound::ForLifetime(_, path) => {
+ &TypeBound::ForLifetime(_, path) => {
// FIXME Don't silently drop the hrtb lifetimes here
trait_ref = self.lower_trait_ref_from_path(path, self_ty);
trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
@@ -1121,8 +1248,8 @@ impl<'a> TyLoweringContext<'a> {
trait_ref: TraitRef,
) -> impl Iterator<Item = QuantifiedWhereClause> + use<'b, 'a> {
let last_segment = match bound {
- TypeBound::Path(path, TraitBoundModifier::None) | TypeBound::ForLifetime(_, path) => {
- path.segments().last()
+ &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => {
+ self.types_map[path].segments().last()
}
TypeBound::Path(_, TraitBoundModifier::Maybe)
| TypeBound::Use(_)
@@ -1227,7 +1354,9 @@ impl<'a> TyLoweringContext<'a> {
}
_ => unreachable!(),
}
- ext.lower_ty(type_ref)
+ let ty = ext.lower_ty(type_ref);
+ self.diagnostics.extend(ext.diagnostics);
+ ty
} else {
self.lower_ty(type_ref)
};
@@ -1523,11 +1652,24 @@ fn named_associated_type_shorthand_candidates<R>(
}
}
-/// Build the type of all specific fields of a struct or enum variant.
+pub(crate) type Diagnostics = Option<ThinArc<(), TyLoweringDiagnostic>>;
+
+fn create_diagnostics(diagnostics: Vec<TyLoweringDiagnostic>) -> Diagnostics {
+ (!diagnostics.is_empty()).then(|| ThinArc::from_header_and_iter((), diagnostics.into_iter()))
+}
+
pub(crate) fn field_types_query(
db: &dyn HirDatabase,
variant_id: VariantId,
) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>> {
+ db.field_types_with_diagnostics(variant_id).0
+}
+
+/// Build the type of all specific fields of a struct or enum variant.
+pub(crate) fn field_types_with_diagnostics_query(
+ db: &dyn HirDatabase,
+ variant_id: VariantId,
+) -> (Arc<ArenaMap<LocalFieldId, Binders<Ty>>>, Diagnostics) {
let var_data = variant_id.variant_data(db.upcast());
let (resolver, def): (_, GenericDefId) = match variant_id {
VariantId::StructId(it) => (it.resolver(db.upcast()), it.into()),
@@ -1543,7 +1685,7 @@ pub(crate) fn field_types_query(
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)));
}
- Arc::new(res)
+ (Arc::new(res), create_diagnostics(ctx.diagnostics))
}
/// This query exists only to be used when resolving short-hand associated types
@@ -1593,9 +1735,10 @@ pub(crate) fn generic_predicates_for_param_query(
}
match bound {
- TypeBound::ForLifetime(_, path) | TypeBound::Path(path, _) => {
+ &TypeBound::ForLifetime(_, path) | &TypeBound::Path(path, _) => {
// Only lower the bound if the trait could possibly define the associated
// type we're looking for.
+ let path = &ctx.types_map[path];
let Some(assoc_name) = &assoc_name else { return true };
let Some(TypeNs::TraitId(tr)) =
@@ -1743,15 +1886,22 @@ pub(crate) fn generic_predicates_query(
db: &dyn HirDatabase,
def: GenericDefId,
) -> GenericPredicates {
- generic_predicates_filtered_by(db, def, |_, _| true)
+ generic_predicates_filtered_by(db, def, |_, _| true).0
}
-/// Resolve the where clause(s) of an item with generics,
-/// except the ones inherited from the parent
pub(crate) fn generic_predicates_without_parent_query(
db: &dyn HirDatabase,
def: GenericDefId,
) -> GenericPredicates {
+ db.generic_predicates_without_parent_with_diagnostics(def).0
+}
+
+/// Resolve the where clause(s) of an item with generics,
+/// except the ones inherited from the parent
+pub(crate) fn generic_predicates_without_parent_with_diagnostics_query(
+ db: &dyn HirDatabase,
+ def: GenericDefId,
+) -> (GenericPredicates, Diagnostics) {
generic_predicates_filtered_by(db, def, |_, d| *d == def)
}
@@ -1761,7 +1911,7 @@ fn generic_predicates_filtered_by<F>(
db: &dyn HirDatabase,
def: GenericDefId,
filter: F,
-) -> GenericPredicates
+) -> (GenericPredicates, Diagnostics)
where
F: Fn(&WherePredicate, &GenericDefId) -> bool,
{
@@ -1802,7 +1952,10 @@ where
);
};
}
- GenericPredicates(predicates.is_empty().not().then(|| predicates.into()))
+ (
+ GenericPredicates(predicates.is_empty().not().then(|| predicates.into())),
+ create_diagnostics(ctx.diagnostics),
+ )
}
/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound.
@@ -1855,75 +2008,110 @@ impl ops::Deref for GenericDefaults {
}
}
-/// Resolve the default type params from generics
pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) -> GenericDefaults {
+ db.generic_defaults_with_diagnostics(def).0
+}
+
+/// Resolve the default type params from generics.
+///
+/// Diagnostics are only returned for this `GenericDefId` (returned defaults include parents).
+pub(crate) fn generic_defaults_with_diagnostics_query(
+ db: &dyn HirDatabase,
+ def: GenericDefId,
+) -> (GenericDefaults, Diagnostics) {
let generic_params = generics(db.upcast(), def);
if generic_params.len() == 0 {
- return GenericDefaults(None);
+ return (GenericDefaults(None), None);
}
let resolver = def.resolver(db.upcast());
let parent_start_idx = generic_params.len_self();
- 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_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)
- });
- crate::make_binders(db, &generic_params, ty.cast(Interner))
- }
- GenericParamDataRef::ConstParamData(p) => {
- let GenericParamId::ConstParamId(id) = id else {
- unreachable!("Unexpected lifetime or type argument")
- };
+ let mut ctx =
+ TyLoweringContext::new(db, &resolver, generic_params.self_types_map(), def.into())
+ .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed)
+ .with_type_param_mode(ParamLoweringMode::Variable);
+ let mut idx = 0;
+ let mut defaults = generic_params
+ .iter_self()
+ .map(|(id, p)| {
+ let result =
+ handle_generic_param(&mut ctx, idx, id, p, parent_start_idx, &generic_params);
+ idx += 1;
+ result
+ })
+ .collect::<Vec<_>>();
+ let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics));
+ defaults.extend(generic_params.iter_parents_with_types_map().map(|((id, p), types_map)| {
+ ctx.types_map = types_map;
+ let result = handle_generic_param(&mut ctx, idx, id, p, parent_start_idx, &generic_params);
+ idx += 1;
+ result
+ }));
+ let defaults = GenericDefaults(Some(Arc::from_iter(defaults)));
+ return (defaults, diagnostics);
+
+ fn handle_generic_param(
+ ctx: &mut TyLoweringContext<'_>,
+ idx: usize,
+ id: GenericParamId,
+ p: GenericParamDataRef<'_>,
+ parent_start_idx: usize,
+ generic_params: &Generics,
+ ) -> Binders<crate::GenericArg> {
+ 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)
+ });
+ crate::make_binders(ctx.db, generic_params, ty.cast(Interner))
+ }
+ GenericParamDataRef::ConstParamData(p) => {
+ let GenericParamId::ConstParamId(id) = id else {
+ unreachable!("Unexpected lifetime or type argument")
+ };
- let mut val = p.default.as_ref().map_or_else(
- || unknown_const_as_generic(db.const_param_ty(id)),
- |c| {
- let param_ty = ctx.lower_ty(p.ty);
- let c = ctx.lower_const(c, param_ty);
- c.cast(Interner)
- },
- );
- // Each default can only refer to previous parameters, see above.
- val = fallback_bound_vars(val, idx, parent_start_idx);
- make_binders(db, &generic_params, val)
- }
- GenericParamDataRef::LifetimeParamData(_) => {
- make_binders(db, &generic_params, error_lifetime().cast(Interner))
- }
+ let mut val = p.default.as_ref().map_or_else(
+ || unknown_const_as_generic(ctx.db.const_param_ty(id)),
+ |c| {
+ let param_ty = ctx.lower_ty(p.ty);
+ let c = ctx.lower_const(c, param_ty);
+ c.cast(Interner)
+ },
+ );
+ // Each default can only refer to previous parameters, see above.
+ val = fallback_bound_vars(val, idx, parent_start_idx);
+ make_binders(ctx.db, generic_params, val)
}
- },
- ))))
+ GenericParamDataRef::LifetimeParamData(_) => {
+ make_binders(ctx.db, generic_params, error_lifetime().cast(Interner))
+ }
+ }
+ }
}
-pub(crate) fn generic_defaults_recover(
+pub(crate) fn generic_defaults_with_diagnostics_recover(
db: &dyn HirDatabase,
_cycle: &Cycle,
def: &GenericDefId,
-) -> GenericDefaults {
+) -> (GenericDefaults, Diagnostics) {
let generic_params = generics(db.upcast(), *def);
if generic_params.len() == 0 {
- return GenericDefaults(None);
+ return (GenericDefaults(None), None);
}
// FIXME: this code is not covered in tests.
// we still need one default per parameter
- GenericDefaults(Some(Arc::from_iter(generic_params.iter_id().map(|id| {
+ let defaults = GenericDefaults(Some(Arc::from_iter(generic_params.iter_id().map(|id| {
let val = match id {
GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)),
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
};
crate::make_binders(db, &generic_params, val)
- }))))
+ }))));
+ (defaults, None)
}
fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
@@ -2066,7 +2254,10 @@ fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
make_binders(db, &generics, ty)
}
-fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
+pub(crate) fn type_for_type_alias_with_diagnostics_query(
+ db: &dyn HirDatabase,
+ t: TypeAliasId,
+) -> (Binders<Ty>, Diagnostics) {
let generics = generics(db.upcast(), t.into());
let resolver = t.resolver(db.upcast());
let type_alias_data = db.type_alias_data(t);
@@ -2081,7 +2272,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
.map(|type_ref| ctx.lower_ty(type_ref))
.unwrap_or_else(|| TyKind::Error.intern(Interner))
};
- make_binders(db, &generics, inner)
+ (make_binders(db, &generics, inner), create_diagnostics(ctx.diagnostics))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -2124,7 +2315,7 @@ pub(crate) fn ty_query(db: &dyn HirDatabase, def: TyDefId) -> Binders<Ty> {
match def {
TyDefId::BuiltinType(it) => Binders::empty(Interner, TyBuilder::builtin(it)),
TyDefId::AdtId(it) => type_for_adt(db, it),
- TyDefId::TypeAliasId(it) => type_for_type_alias(db, it),
+ TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).0,
}
}
@@ -2149,47 +2340,73 @@ pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Option<
}
pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binders<Ty> {
+ db.impl_self_ty_with_diagnostics(impl_id).0
+}
+
+pub(crate) fn impl_self_ty_with_diagnostics_query(
+ db: &dyn HirDatabase,
+ impl_id: ImplId,
+) -> (Binders<Ty>, Diagnostics) {
let impl_data = db.impl_data(impl_id);
let resolver = impl_id.resolver(db.upcast());
let generics = generics(db.upcast(), impl_id.into());
let mut 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)),
+ create_diagnostics(ctx.diagnostics),
+ )
}
-// returns None if def is a type arg
pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
+ db.const_param_ty_with_diagnostics(def).0
+}
+
+// returns None if def is a type arg
+pub(crate) fn const_param_ty_with_diagnostics_query(
+ db: &dyn HirDatabase,
+ def: ConstParamId,
+) -> (Ty, Diagnostics) {
let parent_data = db.generic_params(def.parent());
let data = &parent_data[def.local_id()];
let resolver = def.parent().resolver(db.upcast());
let mut ctx =
TyLoweringContext::new(db, &resolver, &parent_data.types_map, def.parent().into());
- match data {
+ let ty = match data {
TypeOrConstParamData::TypeParamData(_) => {
never!();
Ty::new(Interner, TyKind::Error)
}
TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(d.ty),
- }
+ };
+ (ty, create_diagnostics(ctx.diagnostics))
}
-pub(crate) fn impl_self_ty_recover(
+pub(crate) fn impl_self_ty_with_diagnostics_recover(
db: &dyn HirDatabase,
_cycle: &Cycle,
impl_id: &ImplId,
-) -> Binders<Ty> {
+) -> (Binders<Ty>, Diagnostics) {
let generics = generics(db.upcast(), (*impl_id).into());
- make_binders(db, &generics, TyKind::Error.intern(Interner))
+ (make_binders(db, &generics, TyKind::Error.intern(Interner)), None)
}
pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<Binders<TraitRef>> {
+ db.impl_trait_with_diagnostics(impl_id).map(|it| it.0)
+}
+
+pub(crate) fn impl_trait_with_diagnostics_query(
+ db: &dyn HirDatabase,
+ impl_id: ImplId,
+) -> Option<(Binders<TraitRef>, Diagnostics)> {
let impl_data = db.impl_data(impl_id);
let resolver = impl_id.resolver(db.upcast());
let mut 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()?;
- Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?))
+ let trait_ref = Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?);
+ Some((trait_ref, create_diagnostics(ctx.diagnostics)))
}
pub(crate) fn return_type_impl_traits(
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index c4e0640051..1d1044df6e 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -2023,11 +2023,11 @@ pub fn mir_body_for_closure_query(
ctx.result.locals.alloc(Local { ty: infer[*root].clone() });
let closure_local = ctx.result.locals.alloc(Local {
ty: match kind {
- FnTrait::FnOnce => infer[expr].clone(),
- FnTrait::FnMut => {
+ FnTrait::FnOnce | FnTrait::AsyncFnOnce => infer[expr].clone(),
+ FnTrait::FnMut | FnTrait::AsyncFnMut => {
TyKind::Ref(Mutability::Mut, error_lifetime(), infer[expr].clone()).intern(Interner)
}
- FnTrait::Fn => {
+ FnTrait::Fn | FnTrait::AsyncFn => {
TyKind::Ref(Mutability::Not, error_lifetime(), infer[expr].clone()).intern(Interner)
}
},
@@ -2055,8 +2055,10 @@ pub fn mir_body_for_closure_query(
let mut err = None;
let closure_local = ctx.result.locals.iter().nth(1).unwrap().0;
let closure_projection = match kind {
- FnTrait::FnOnce => vec![],
- FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref],
+ FnTrait::FnOnce | FnTrait::AsyncFnOnce => vec![],
+ FnTrait::FnMut | FnTrait::Fn | FnTrait::AsyncFnMut | FnTrait::AsyncFn => {
+ vec![ProjectionElem::Deref]
+ }
};
ctx.result.walk_places(|p, store| {
if let Some(it) = upvar_map.get(&p.local) {
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index bcf9d5ceff..cabeeea2bd 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -18,13 +18,13 @@ use std::sync::LazyLock;
use base_db::SourceDatabaseFileInputExt as _;
use expect_test::Expect;
use hir_def::{
- body::{Body, BodySourceMap, SyntheticSyntax},
+ body::{Body, BodySourceMap},
db::DefDatabase,
hir::{ExprId, Pat, PatId},
item_scope::ItemScope,
nameres::DefMap,
src::HasSource,
- AssocItemId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleDefId,
+ AssocItemId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleDefId, SyntheticSyntax,
};
use hir_expand::{db::ExpandDatabase, FileRange, InFile};
use itertools::Itertools;
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index 273571901a..7992f1feee 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -942,3 +942,19 @@ fn main() {
"#,
)
}
+
+#[test]
+fn regression_18626() {
+ check_no_mismatches(
+ r#"
+fn f() {
+ trait T {
+ fn f() {}
+ }
+ impl T for i32 {}
+ impl T for u32 {}
+ &[i32::f, u32::f] as &[fn()];
+}
+ "#,
+ );
+}
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 624148cab2..e15d44bd6d 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -1631,6 +1631,29 @@ fn test<'lifetime>(
}
#[test]
+fn lifetime_bounds() {
+ check_infer(
+ r#"
+//- minicore: sized, coerce_unsized
+trait Trait<'a>: Sized {
+ fn f(&'a self) {}
+}
+fn test<'a, 'b: 'a>(it: impl Trait<'a>){
+ it.f();
+}
+"#,
+ expect![[r#"
+ 38..42 'self': &'a Self
+ 44..46 '{}': ()
+ 69..71 'it': impl Trait<'a>
+ 88..103 '{ it.f(); }': ()
+ 94..96 'it': impl Trait<'a>
+ 94..100 'it.f()': ()
+ "#]],
+ );
+}
+
+#[test]
fn error_bound_chalk() {
check_types(
r#"
@@ -4811,3 +4834,53 @@ fn bar(v: *const ()) {
"#]],
);
}
+
+#[test]
+fn async_fn_traits() {
+ check_infer(
+ r#"
+//- minicore: async_fn
+async fn foo<T: AsyncFn(u32) -> i32>(a: T) {
+ let fut1 = a(0);
+ fut1.await;
+}
+async fn bar<T: AsyncFnMut(u32) -> i32>(mut b: T) {
+ let fut2 = b(0);
+ fut2.await;
+}
+async fn baz<T: AsyncFnOnce(u32) -> i32>(c: T) {
+ let fut3 = c(0);
+ fut3.await;
+}
+ "#,
+ expect![[r#"
+ 37..38 'a': T
+ 43..83 '{ ...ait; }': ()
+ 43..83 '{ ...ait; }': impl Future<Output = ()>
+ 53..57 'fut1': AsyncFnMut::CallRefFuture<'?, T, (u32,)>
+ 60..61 'a': T
+ 60..64 'a(0)': AsyncFnMut::CallRefFuture<'?, T, (u32,)>
+ 62..63 '0': u32
+ 70..74 'fut1': AsyncFnMut::CallRefFuture<'?, T, (u32,)>
+ 70..80 'fut1.await': i32
+ 124..129 'mut b': T
+ 134..174 '{ ...ait; }': ()
+ 134..174 '{ ...ait; }': impl Future<Output = ()>
+ 144..148 'fut2': AsyncFnMut::CallRefFuture<'?, T, (u32,)>
+ 151..152 'b': T
+ 151..155 'b(0)': AsyncFnMut::CallRefFuture<'?, T, (u32,)>
+ 153..154 '0': u32
+ 161..165 'fut2': AsyncFnMut::CallRefFuture<'?, T, (u32,)>
+ 161..171 'fut2.await': i32
+ 216..217 'c': T
+ 222..262 '{ ...ait; }': ()
+ 222..262 '{ ...ait; }': impl Future<Output = ()>
+ 232..236 'fut3': AsyncFnOnce::CallOnceFuture<T, (u32,)>
+ 239..240 'c': T
+ 239..243 'c(0)': AsyncFnOnce::CallOnceFuture<T, (u32,)>
+ 241..242 '0': u32
+ 249..253 'fut3': AsyncFnOnce::CallOnceFuture<T, (u32,)>
+ 249..259 'fut3.await': i32
+ "#]],
+ );
+}
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index 51ccd4ef29..8cb7dbf60f 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -220,6 +220,10 @@ pub enum FnTrait {
FnOnce,
FnMut,
Fn,
+
+ AsyncFnOnce,
+ AsyncFnMut,
+ AsyncFn,
}
impl fmt::Display for FnTrait {
@@ -228,6 +232,9 @@ impl fmt::Display for FnTrait {
FnTrait::FnOnce => write!(f, "FnOnce"),
FnTrait::FnMut => write!(f, "FnMut"),
FnTrait::Fn => write!(f, "Fn"),
+ FnTrait::AsyncFnOnce => write!(f, "AsyncFnOnce"),
+ FnTrait::AsyncFnMut => write!(f, "AsyncFnMut"),
+ FnTrait::AsyncFn => write!(f, "AsyncFn"),
}
}
}
@@ -238,6 +245,9 @@ impl FnTrait {
FnTrait::FnOnce => "call_once",
FnTrait::FnMut => "call_mut",
FnTrait::Fn => "call",
+ FnTrait::AsyncFnOnce => "async_call_once",
+ FnTrait::AsyncFnMut => "async_call_mut",
+ FnTrait::AsyncFn => "async_call",
}
}
@@ -246,6 +256,9 @@ impl FnTrait {
FnTrait::FnOnce => LangItem::FnOnce,
FnTrait::FnMut => LangItem::FnMut,
FnTrait::Fn => LangItem::Fn,
+ FnTrait::AsyncFnOnce => LangItem::AsyncFnOnce,
+ FnTrait::AsyncFnMut => LangItem::AsyncFnMut,
+ FnTrait::AsyncFn => LangItem::AsyncFn,
}
}
@@ -254,15 +267,19 @@ impl FnTrait {
LangItem::FnOnce => Some(FnTrait::FnOnce),
LangItem::FnMut => Some(FnTrait::FnMut),
LangItem::Fn => Some(FnTrait::Fn),
+ LangItem::AsyncFnOnce => Some(FnTrait::AsyncFnOnce),
+ LangItem::AsyncFnMut => Some(FnTrait::AsyncFnMut),
+ LangItem::AsyncFn => Some(FnTrait::AsyncFn),
_ => None,
}
}
pub const fn to_chalk_ir(self) -> rust_ir::ClosureKind {
+ // Chalk doesn't support async fn traits.
match self {
- FnTrait::FnOnce => rust_ir::ClosureKind::FnOnce,
- FnTrait::FnMut => rust_ir::ClosureKind::FnMut,
- FnTrait::Fn => rust_ir::ClosureKind::Fn,
+ FnTrait::AsyncFnOnce | FnTrait::FnOnce => rust_ir::ClosureKind::FnOnce,
+ FnTrait::AsyncFnMut | FnTrait::FnMut => rust_ir::ClosureKind::FnMut,
+ FnTrait::AsyncFn | FnTrait::Fn => rust_ir::ClosureKind::Fn,
}
}
@@ -271,6 +288,9 @@ impl FnTrait {
FnTrait::FnOnce => Name::new_symbol_root(sym::call_once.clone()),
FnTrait::FnMut => Name::new_symbol_root(sym::call_mut.clone()),
FnTrait::Fn => Name::new_symbol_root(sym::call.clone()),
+ FnTrait::AsyncFnOnce => Name::new_symbol_root(sym::async_call_once.clone()),
+ FnTrait::AsyncFnMut => Name::new_symbol_root(sym::async_call_mut.clone()),
+ FnTrait::AsyncFn => Name::new_symbol_root(sym::async_call.clone()),
}
}
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 28bda1e10e..06719b09f7 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -185,7 +185,7 @@ fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(
}
};
match is_trait {
- true => bound.as_path(),
+ true => bound.as_path(&generic_params.types_map),
false => None,
}
}
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index 26666d6feb..6aadc5c4f7 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -33,6 +33,14 @@ syntax.workspace = true
tt.workspace = true
span.workspace = true
+[dev-dependencies]
+expect-test.workspace = true
+
+# local deps
+test-utils.workspace = true
+test-fixture.workspace = true
+syntax-bridge.workspace = true
+
[features]
in-rust-tree = ["hir-expand/in-rust-tree"]
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 8297acde85..612c6adb20 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -3,21 +3,35 @@
//!
//! This probably isn't the best way to do this -- ideally, diagnostics should
//! be expressed in terms of hir types themselves.
-pub use hir_ty::diagnostics::{CaseType, IncorrectCase};
-use hir_ty::{
- db::HirDatabase, diagnostics::BodyValidationDiagnostic, CastError, InferenceDiagnostic,
-};
-
use cfg::{CfgExpr, CfgOptions};
use either::Either;
-pub use hir_def::VariantId;
-use hir_def::{body::SyntheticSyntax, hir::ExprOrPatId, path::ModPath, AssocItemId, DefWithBodyId};
+use hir_def::{
+ hir::ExprOrPatId,
+ path::{hir_segment_to_ast_segment, ModPath},
+ type_ref::TypesSourceMap,
+ AssocItemId, DefWithBodyId, SyntheticSyntax,
+};
use hir_expand::{name::Name, HirFileId, InFile};
-use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange};
+use hir_ty::{
+ db::HirDatabase,
+ diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
+ CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic,
+ TyLoweringDiagnosticKind,
+};
+use syntax::{
+ ast::{self, HasGenericArgs},
+ AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
+};
use triomphe::Arc;
use crate::{AssocItem, Field, Local, Trait, Type};
+pub use hir_def::VariantId;
+pub use hir_ty::{
+ diagnostics::{CaseType, IncorrectCase},
+ GenericArgsProhibitedReason,
+};
+
macro_rules! diagnostics {
($($diag:ident,)*) => {
#[derive(Debug)]
@@ -96,6 +110,7 @@ diagnostics![
UnresolvedIdent,
UnusedMut,
UnusedVariable,
+ GenericArgsProhibited,
];
#[derive(Debug)]
@@ -258,9 +273,10 @@ pub struct PrivateField {
#[derive(Debug)]
pub struct MissingUnsafe {
- pub expr: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
+ pub node: 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,
+ pub reason: UnsafetyReason,
}
#[derive(Debug)]
@@ -385,6 +401,12 @@ pub struct InvalidCast {
pub cast_ty: Type,
}
+#[derive(Debug)]
+pub struct GenericArgsProhibited {
+ pub args: InFile<AstPtr<Either<ast::GenericArgList, ast::ParenthesizedArgList>>>,
+ pub reason: GenericArgsProhibitedReason,
+}
+
impl AnyDiagnostic {
pub(crate) fn body_validation_diagnostic(
db: &dyn HirDatabase,
@@ -524,6 +546,7 @@ impl AnyDiagnostic {
db: &dyn HirDatabase,
def: DefWithBodyId,
d: &InferenceDiagnostic,
+ outer_types_source_map: &TypesSourceMap,
source_map: &hir_def::body::BodySourceMap,
) -> Option<AnyDiagnostic> {
let expr_syntax = |expr| {
@@ -637,6 +660,44 @@ impl AnyDiagnostic {
let cast_ty = Type::new(db, def, cast_ty.clone());
InvalidCast { expr, error: *error, expr_ty, cast_ty }.into()
}
+ InferenceDiagnostic::TyDiagnostic { source, diag } => {
+ let source_map = match source {
+ InferenceTyDiagnosticSource::Body => &source_map.types,
+ InferenceTyDiagnosticSource::Signature => outer_types_source_map,
+ };
+ Self::ty_diagnostic(diag, source_map, db)?
+ }
+ })
+ }
+
+ pub(crate) fn ty_diagnostic(
+ diag: &TyLoweringDiagnostic,
+ source_map: &TypesSourceMap,
+ db: &dyn HirDatabase,
+ ) -> Option<AnyDiagnostic> {
+ let source = match diag.source {
+ Either::Left(type_ref_id) => {
+ let Ok(source) = source_map.type_syntax(type_ref_id) else {
+ stdx::never!("error on synthetic type syntax");
+ return None;
+ };
+ source
+ }
+ Either::Right(source) => source,
+ };
+ let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id));
+ Some(match diag.kind {
+ TyLoweringDiagnosticKind::GenericArgsProhibited { segment, reason } => {
+ let ast::Type::PathType(syntax) = syntax() else { return None };
+ let segment = hir_segment_to_ast_segment(&syntax.path()?, segment)?;
+ let args = if let Some(generics) = segment.generic_arg_list() {
+ AstPtr::new(&generics).wrap_left()
+ } else {
+ AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right()
+ };
+ let args = source.with_value(args);
+ GenericArgsProhibited { args, reason }.into()
+ }
})
}
}
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 9275f45d88..959d62d595 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -132,12 +132,18 @@ impl HirDisplay for Function {
} else {
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(),
+ &TypeBound::Path(path, _) => Some(
+ *data.types_map[path]
+ .segments()
+ .iter()
+ .last()
+ .unwrap()
+ .args_and_bindings
+ .unwrap()
+ .bindings[0]
+ .type_ref
+ .as_ref()
+ .unwrap(),
),
_ => None,
},
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index c9498b3aea..83d72dfcf1 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -20,12 +20,11 @@
#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
#![recursion_limit = "512"]
-mod semantics;
-mod source_analyzer;
-
mod attrs;
mod from_id;
mod has_source;
+mod semantics;
+mod source_analyzer;
pub mod db;
pub mod diagnostics;
@@ -43,7 +42,7 @@ use arrayvec::ArrayVec;
use base_db::{CrateDisplayName, CrateId, CrateOrigin};
use either::Either;
use hir_def::{
- body::{BodyDiagnostic, SyntheticSyntax},
+ body::BodyDiagnostic,
data::adt::VariantData,
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat},
@@ -54,11 +53,12 @@ use hir_def::{
path::ImportAlias,
per_ns::PerNs,
resolver::{HasResolver, Resolver},
+ type_ref::TypesSourceMap,
AssocItemId, AssocItemLoc, AttrDefId, CallableDefId, ConstId, ConstParamId, CrateRootModuleId,
DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId,
HasModule, ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup,
- MacroExpander, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TupleId, TypeAliasId,
- TypeOrConstParamId, TypeParamId, UnionId,
+ MacroExpander, ModuleId, StaticId, StructId, SyntheticSyntax, TraitAliasId, TraitId, TupleId,
+ TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
};
use hir_expand::{
attrs::collect_attrs, proc_macro::ProcMacroKind, AstId, MacroCallKind, RenderedExpandError,
@@ -76,8 +76,8 @@ use hir_ty::{
traits::FnTrait,
AliasTy, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
- TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId,
- WhereClause,
+ TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, TyLoweringDiagnostic,
+ ValueTyDefId, WhereClause,
};
use itertools::Itertools;
use nameres::diagnostics::DefDiagnosticKind;
@@ -89,7 +89,7 @@ use syntax::{
ast::{self, HasAttrs as _, HasGenericParams, HasName},
format_smolstr, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ToSmolStr, T,
};
-use triomphe::Arc;
+use triomphe::{Arc, ThinArc};
use crate::db::{DefDatabase, HirDatabase};
@@ -147,6 +147,7 @@ pub use {
},
hir_ty::{
consteval::ConstEvalError,
+ diagnostics::UnsafetyReason,
display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
dyn_compatibility::{DynCompatibilityViolation, MethodViolationCode},
layout::LayoutError,
@@ -410,6 +411,10 @@ impl ModuleDef {
}
}
+ if let Some(def) = self.as_self_generic_def() {
+ def.diagnostics(db, &mut acc);
+ }
+
acc
}
@@ -430,6 +435,23 @@ impl ModuleDef {
}
}
+ /// Returns only defs that have generics from themselves, not their parent.
+ pub fn as_self_generic_def(self) -> Option<GenericDef> {
+ match self {
+ ModuleDef::Function(it) => Some(it.into()),
+ ModuleDef::Adt(it) => Some(it.into()),
+ ModuleDef::Trait(it) => Some(it.into()),
+ ModuleDef::TraitAlias(it) => Some(it.into()),
+ ModuleDef::TypeAlias(it) => Some(it.into()),
+ ModuleDef::Module(_)
+ | ModuleDef::Variant(_)
+ | ModuleDef::Static(_)
+ | ModuleDef::Const(_)
+ | ModuleDef::BuiltinType(_)
+ | ModuleDef::Macro(_) => None,
+ }
+ }
+
pub fn attrs(&self, db: &dyn HirDatabase) -> Option<AttrsWithOwner> {
Some(match self {
ModuleDef::Module(it) => it.attrs(db),
@@ -604,17 +626,42 @@ impl Module {
ModuleDef::Adt(adt) => {
match adt {
Adt::Struct(s) => {
+ let tree_id = s.id.lookup(db.upcast()).id;
+ let tree_source_maps = tree_id.item_tree_with_source_map(db.upcast()).1;
+ push_ty_diagnostics(
+ db,
+ acc,
+ db.field_types_with_diagnostics(s.id.into()).1,
+ tree_source_maps.strukt(tree_id.value).item(),
+ );
for diag in db.struct_data_with_diagnostics(s.id).1.iter() {
emit_def_diagnostic(db, acc, diag, edition);
}
}
Adt::Union(u) => {
+ let tree_id = u.id.lookup(db.upcast()).id;
+ let tree_source_maps = tree_id.item_tree_with_source_map(db.upcast()).1;
+ push_ty_diagnostics(
+ db,
+ acc,
+ db.field_types_with_diagnostics(u.id.into()).1,
+ tree_source_maps.union(tree_id.value).item(),
+ );
for diag in db.union_data_with_diagnostics(u.id).1.iter() {
emit_def_diagnostic(db, acc, diag, edition);
}
}
Adt::Enum(e) => {
for v in e.variants(db) {
+ let tree_id = v.id.lookup(db.upcast()).id;
+ let tree_source_maps =
+ tree_id.item_tree_with_source_map(db.upcast()).1;
+ push_ty_diagnostics(
+ db,
+ acc,
+ db.field_types_with_diagnostics(v.id.into()).1,
+ tree_source_maps.variant(tree_id.value),
+ );
acc.extend(ModuleDef::Variant(v).diagnostics(db, style_lints));
for diag in db.enum_variant_data_with_diagnostics(v.id).1.iter() {
emit_def_diagnostic(db, acc, diag, edition);
@@ -625,6 +672,17 @@ impl Module {
acc.extend(def.diagnostics(db, style_lints))
}
ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m),
+ ModuleDef::TypeAlias(type_alias) => {
+ let tree_id = type_alias.id.lookup(db.upcast()).id;
+ let tree_source_maps = tree_id.item_tree_with_source_map(db.upcast()).1;
+ push_ty_diagnostics(
+ db,
+ acc,
+ db.type_for_type_alias_with_diagnostics(type_alias.id).1,
+ tree_source_maps.type_alias(tree_id.value).item(),
+ );
+ acc.extend(def.diagnostics(db, style_lints));
+ }
_ => acc.extend(def.diagnostics(db, style_lints)),
}
}
@@ -634,8 +692,11 @@ impl Module {
let mut impl_assoc_items_scratch = vec![];
for impl_def in self.impl_defs(db) {
+ GenericDef::Impl(impl_def).diagnostics(db, acc);
+
let loc = impl_def.id.lookup(db.upcast());
- let tree = loc.id.item_tree(db.upcast());
+ let (tree, tree_source_maps) = loc.id.item_tree_with_source_map(db.upcast());
+ let source_map = tree_source_maps.impl_(loc.id.value).item();
let node = &tree[loc.id.value];
let file_id = loc.id.file_id();
if file_id.macro_file().map_or(false, |it| it.is_builtin_derive(db.upcast())) {
@@ -770,6 +831,19 @@ impl Module {
impl_assoc_items_scratch.clear();
}
+ push_ty_diagnostics(
+ db,
+ acc,
+ db.impl_self_ty_with_diagnostics(impl_def.id).1,
+ source_map,
+ );
+ push_ty_diagnostics(
+ db,
+ acc,
+ db.impl_trait_with_diagnostics(impl_def.id).and_then(|it| it.1),
+ source_map,
+ );
+
for &item in db.impl_data(impl_def.id).items.iter() {
AssocItem::from(item).diagnostics(db, acc, style_lints);
}
@@ -1801,6 +1875,25 @@ impl DefWithBody {
let krate = self.module(db).id.krate();
let (body, source_map) = db.body_with_source_map(self.into());
+ let item_tree_source_maps;
+ let outer_types_source_map = match self {
+ DefWithBody::Function(function) => {
+ let function = function.id.lookup(db.upcast()).id;
+ item_tree_source_maps = function.item_tree_with_source_map(db.upcast()).1;
+ item_tree_source_maps.function(function.value).item()
+ }
+ DefWithBody::Static(statik) => {
+ let statik = statik.id.lookup(db.upcast()).id;
+ item_tree_source_maps = statik.item_tree_with_source_map(db.upcast()).1;
+ item_tree_source_maps.statik(statik.value)
+ }
+ DefWithBody::Const(konst) => {
+ let konst = konst.id.lookup(db.upcast()).id;
+ item_tree_source_maps = konst.item_tree_with_source_map(db.upcast()).1;
+ item_tree_source_maps.konst(konst.value)
+ }
+ DefWithBody::Variant(_) | DefWithBody::InTypeConst(_) => &TypesSourceMap::EMPTY,
+ };
for (_, def_map) in body.blocks(db.upcast()) {
Module { id: def_map.module_id(DefMap::ROOT) }.diagnostics(db, acc, style_lints);
@@ -1860,7 +1953,13 @@ impl DefWithBody {
let infer = db.infer(self.into());
for d in &infer.diagnostics {
- acc.extend(AnyDiagnostic::inference_diagnostic(db, self.into(), d, &source_map));
+ acc.extend(AnyDiagnostic::inference_diagnostic(
+ db,
+ self.into(),
+ d,
+ outer_types_source_map,
+ &source_map,
+ ));
}
for (pat_or_expr, mismatch) in infer.type_mismatches() {
@@ -1890,10 +1989,10 @@ impl DefWithBody {
);
}
- let (unafe_exprs, only_lint) = hir_ty::diagnostics::missing_unsafe(db, self.into());
- for expr in unafe_exprs {
- match source_map.expr_or_pat_syntax(expr) {
- Ok(expr) => acc.push(MissingUnsafe { expr, only_lint }.into()),
+ let (unsafe_exprs, only_lint) = hir_ty::diagnostics::missing_unsafe(db, self.into());
+ for (node, reason) in unsafe_exprs {
+ match source_map.expr_or_pat_syntax(node) {
+ Ok(node) => acc.push(MissingUnsafe { node, only_lint, reason }.into()),
Err(SyntheticSyntax) => {
// FIXME: Here and elsewhere in this file, the `expr` was
// desugared, report or assert that this doesn't happen.
@@ -3324,12 +3423,22 @@ impl AssocItem {
) {
match self {
AssocItem::Function(func) => {
+ GenericDef::Function(func).diagnostics(db, acc);
DefWithBody::from(func).diagnostics(db, acc, style_lints);
}
AssocItem::Const(const_) => {
DefWithBody::from(const_).diagnostics(db, acc, style_lints);
}
AssocItem::TypeAlias(type_alias) => {
+ GenericDef::TypeAlias(type_alias).diagnostics(db, acc);
+ let tree_id = type_alias.id.lookup(db.upcast()).id;
+ let tree_source_maps = tree_id.item_tree_with_source_map(db.upcast()).1;
+ push_ty_diagnostics(
+ db,
+ acc,
+ db.type_for_type_alias_with_diagnostics(type_alias.id).1,
+ tree_source_maps.type_alias(tree_id.value).item(),
+ );
for diag in hir_ty::diagnostics::incorrect_case(db, type_alias.id.into()) {
acc.push(diag.into());
}
@@ -3416,6 +3525,97 @@ impl GenericDef {
})
.collect()
}
+
+ fn id(self) -> GenericDefId {
+ match self {
+ GenericDef::Function(it) => it.id.into(),
+ GenericDef::Adt(it) => it.into(),
+ GenericDef::Trait(it) => it.id.into(),
+ GenericDef::TraitAlias(it) => it.id.into(),
+ GenericDef::TypeAlias(it) => it.id.into(),
+ GenericDef::Impl(it) => it.id.into(),
+ GenericDef::Const(it) => it.id.into(),
+ }
+ }
+
+ pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
+ let def = self.id();
+
+ let item_tree_source_maps;
+ let (generics, generics_source_map) = db.generic_params_with_source_map(def);
+
+ if generics.is_empty() && generics.no_predicates() {
+ return;
+ }
+
+ let source_map = match &generics_source_map {
+ Some(it) => it,
+ None => match def {
+ GenericDefId::FunctionId(it) => {
+ let id = it.lookup(db.upcast()).id;
+ item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
+ item_tree_source_maps.function(id.value).generics()
+ }
+ GenericDefId::AdtId(AdtId::EnumId(it)) => {
+ let id = it.lookup(db.upcast()).id;
+ item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
+ item_tree_source_maps.enum_generic(id.value)
+ }
+ GenericDefId::AdtId(AdtId::StructId(it)) => {
+ let id = it.lookup(db.upcast()).id;
+ item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
+ item_tree_source_maps.strukt(id.value).generics()
+ }
+ GenericDefId::AdtId(AdtId::UnionId(it)) => {
+ let id = it.lookup(db.upcast()).id;
+ item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
+ item_tree_source_maps.union(id.value).generics()
+ }
+ GenericDefId::TraitId(it) => {
+ let id = it.lookup(db.upcast()).id;
+ item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
+ item_tree_source_maps.trait_generic(id.value)
+ }
+ GenericDefId::TraitAliasId(it) => {
+ let id = it.lookup(db.upcast()).id;
+ item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
+ item_tree_source_maps.trait_alias_generic(id.value)
+ }
+ GenericDefId::TypeAliasId(it) => {
+ let id = it.lookup(db.upcast()).id;
+ item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
+ item_tree_source_maps.type_alias(id.value).generics()
+ }
+ GenericDefId::ImplId(it) => {
+ let id = it.lookup(db.upcast()).id;
+ item_tree_source_maps = id.item_tree_with_source_map(db.upcast()).1;
+ item_tree_source_maps.impl_(id.value).generics()
+ }
+ GenericDefId::ConstId(_) => return,
+ },
+ };
+
+ push_ty_diagnostics(db, acc, db.generic_defaults_with_diagnostics(def).1, source_map);
+ push_ty_diagnostics(
+ db,
+ acc,
+ db.generic_predicates_without_parent_with_diagnostics(def).1,
+ source_map,
+ );
+ for (param_id, param) in generics.iter_type_or_consts() {
+ if let TypeOrConstParamData::ConstParamData(_) = param {
+ push_ty_diagnostics(
+ db,
+ acc,
+ db.const_param_ty_with_diagnostics(ConstParamId::from_unchecked(
+ TypeOrConstParamId { parent: def, local_id: param_id },
+ ))
+ .1,
+ source_map,
+ );
+ }
+ }
+ }
}
/// A single local definition.
@@ -3581,6 +3781,18 @@ impl Local {
}
}
+impl PartialOrd for Local {
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Local {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.binding_id.cmp(&other.binding_id)
+ }
+}
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DeriveHelper {
pub(crate) derive: MacroId,
@@ -5799,3 +6011,19 @@ pub enum DocLinkDef {
Field(Field),
SelfType(Trait),
}
+
+fn push_ty_diagnostics(
+ db: &dyn HirDatabase,
+ acc: &mut Vec<AnyDiagnostic>,
+ diagnostics: Option<ThinArc<(), TyLoweringDiagnostic>>,
+ source_map: &TypesSourceMap,
+) {
+ if let Some(diagnostics) = diagnostics {
+ acc.extend(
+ diagnostics
+ .slice
+ .iter()
+ .filter_map(|diagnostic| AnyDiagnostic::ty_diagnostic(diagnostic, source_map, db)),
+ );
+ }
+}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 9d3f8e5fba..f9d3f9d07e 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -34,7 +34,7 @@ use intern::Symbol;
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec};
-use span::{EditionedFileId, FileId, HirFileIdRepr, SyntaxContextId};
+use span::{AstIdMap, EditionedFileId, FileId, HirFileIdRepr, SyntaxContextId};
use stdx::TupleExt;
use syntax::{
algo::skip_trivia_token,
@@ -42,6 +42,7 @@ use syntax::{
AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
TextSize,
};
+use triomphe::Arc;
use crate::{
db::HirDatabase,
@@ -509,6 +510,22 @@ impl<'db> SemanticsImpl<'db> {
self.with_ctx(|ctx| ctx.has_derives(adt))
}
+ pub fn derive_helpers_in_scope(&self, adt: &ast::Adt) -> Option<Vec<(Symbol, Symbol)>> {
+ let sa = self.analyze_no_infer(adt.syntax())?;
+ let id = self.db.ast_id_map(sa.file_id).ast_id(adt);
+ let result = sa
+ .resolver
+ .def_map()
+ .derive_helpers_in_scope(InFile::new(sa.file_id, id))?
+ .iter()
+ .map(|(name, macro_, _)| {
+ let macro_name = Macro::from(*macro_).name(self.db).symbol().clone();
+ (name.symbol().clone(), macro_name)
+ })
+ .collect();
+ Some(result)
+ }
+
pub fn derive_helper(&self, attr: &ast::Attr) -> Option<Vec<(Macro, MacroFileId)>> {
let adt = attr.syntax().ancestors().find_map(ast::Item::cast).and_then(|it| match it {
ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
@@ -1500,6 +1517,10 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(path.syntax())?.resolve_path(self.db, path)
}
+ pub fn resolve_use_type_arg(&self, name: &ast::NameRef) -> Option<TypeParam> {
+ self.analyze(name.syntax())?.resolve_use_type_arg(name)
+ }
+
pub fn resolve_mod_path(
&self,
scope: &SyntaxNode,
@@ -1973,10 +1994,16 @@ 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, ast_path: &ast::Path) -> Option<PathResolution> {
+ let root = ast_path.syntax().ancestors().last().unwrap();
+ let ast_id_map = Arc::new(AstIdMap::from_source(&root));
let (mut types_map, mut types_source_map) =
(TypesMap::default(), TypesSourceMap::default());
- let mut ctx =
- LowerCtx::new(self.db.upcast(), self.file_id, &mut types_map, &mut types_source_map);
+ let mut ctx = LowerCtx::for_synthetic_ast(
+ self.db.upcast(),
+ ast_id_map,
+ &mut types_map,
+ &mut types_source_map,
+ );
let path = Path::from_src(&mut ctx, ast_path.clone())?;
resolve_hir_path(
self.db,
@@ -2003,6 +2030,10 @@ impl SemanticsScope<'_> {
)
}
+ pub fn generic_def(&self) -> Option<crate::GenericDef> {
+ self.resolver.generic_def().map(|id| id.into())
+ }
+
pub fn extern_crates(&self) -> impl Iterator<Item = (Name, Module)> + '_ {
self.resolver.extern_crates_in_scope().map(|(name, id)| (name, Module { id }))
}
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index c16454cff6..4329a888b3 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -36,7 +36,7 @@ use hir_expand::{
use hir_ty::{
diagnostics::{
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
- UnsafeExpr,
+ InsideUnsafeBlock,
},
lang_items::lang_items_for_bin_op,
method_resolution, Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
@@ -642,6 +642,14 @@ impl SourceAnalyzer {
}
}
+ pub(crate) fn resolve_use_type_arg(&self, name: &ast::NameRef) -> Option<crate::TypeParam> {
+ let name = name.as_name();
+ self.resolver
+ .all_generic_params()
+ .find_map(|(params, parent)| params.find_type_by_name(&name, *parent))
+ .map(crate::TypeParam::from)
+ }
+
pub(crate) fn resolve_path(
&self,
db: &dyn HirDatabase,
@@ -939,8 +947,8 @@ impl SourceAnalyzer {
*def,
body,
expr_id,
- &mut |UnsafeExpr { inside_unsafe_block, .. }| {
- is_unsafe |= !inside_unsafe_block
+ &mut |_, inside_unsafe_block, _| {
+ is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No
},
)
};
diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 7f8ea44fb1..57df39d541 100644
--- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -2318,4 +2318,49 @@ impl<'a> Test<'a, i32> for bool {
"#,
);
}
+
+ #[test]
+ fn issue_17321() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+fn main() {}
+
+mod other_file_1 {
+ pub const SOME_CONSTANT: usize = 8;
+}
+
+mod other_file_2 {
+ use crate::other_file_1::SOME_CONSTANT;
+
+ pub trait Trait {
+ type Iter: Iterator<Item = [u8; SOME_CONSTANT]>;
+ }
+}
+
+pub struct MyStruct;
+
+impl other_file_2::Trait for MyStruct$0 {}"#,
+ r#"
+fn main() {}
+
+mod other_file_1 {
+ pub const SOME_CONSTANT: usize = 8;
+}
+
+mod other_file_2 {
+ use crate::other_file_1::SOME_CONSTANT;
+
+ pub trait Trait {
+ type Iter: Iterator<Item = [u8; SOME_CONSTANT]>;
+ }
+}
+
+pub struct MyStruct;
+
+impl other_file_2::Trait for MyStruct {
+ $0type Iter;
+}"#,
+ );
+ }
}
diff --git a/crates/ide-assists/src/handlers/add_turbo_fish.rs b/crates/ide-assists/src/handlers/add_turbo_fish.rs
index 17efbcbd6c..0f6970d940 100644
--- a/crates/ide-assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ide-assists/src/handlers/add_turbo_fish.rs
@@ -1,8 +1,9 @@
use either::Either;
use ide_db::defs::{Definition, NameRefClass};
use syntax::{
- ast::{self, make, HasArgList, HasGenericArgs},
- ted, AstNode,
+ ast::{self, make, syntax_factory::SyntaxFactory, HasArgList, HasGenericArgs},
+ syntax_editor::Position,
+ AstNode,
};
use crate::{
@@ -91,20 +92,34 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
AssistId("add_type_ascription", AssistKind::RefactorRewrite),
"Add `: _` before assignment operator",
ident.text_range(),
- |edit| {
- let let_stmt = edit.make_mut(let_stmt);
+ |builder| {
+ let mut editor = builder.make_editor(let_stmt.syntax());
if let_stmt.semicolon_token().is_none() {
- ted::append_child(let_stmt.syntax(), make::tokens::semicolon());
+ editor.insert(
+ Position::last_child_of(let_stmt.syntax()),
+ make::tokens::semicolon(),
+ );
}
let placeholder_ty = make::ty_placeholder().clone_for_update();
- let_stmt.set_ty(Some(placeholder_ty.clone()));
-
- if let Some(cap) = ctx.config.snippet_cap {
- edit.add_placeholder_snippet(cap, placeholder_ty);
+ if let Some(pat) = let_stmt.pat() {
+ let elements = vec![
+ make::token(syntax::SyntaxKind::COLON).into(),
+ make::token(syntax::SyntaxKind::WHITESPACE).into(),
+ placeholder_ty.syntax().clone().into(),
+ ];
+ editor.insert_all(Position::after(pat.syntax()), elements);
+ if let Some(cap) = ctx.config.snippet_cap {
+ editor.add_annotation(
+ placeholder_ty.syntax(),
+ builder.make_placeholder_snippet(cap),
+ );
+ }
}
+
+ builder.add_file_edits(ctx.file_id(), editor);
},
)?
} else {
@@ -123,38 +138,58 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
AssistId("add_turbo_fish", AssistKind::RefactorRewrite),
"Add `::<>`",
ident.text_range(),
- |edit| {
- edit.trigger_parameter_hints();
+ |builder| {
+ builder.trigger_parameter_hints();
+
+ let make = SyntaxFactory::new();
+ let mut editor = match &turbofish_target {
+ Either::Left(it) => builder.make_editor(it.syntax()),
+ Either::Right(it) => builder.make_editor(it.syntax()),
+ };
+
+ let fish_head = get_fish_head(&make, number_of_arguments);
- let new_arg_list = match turbofish_target {
+ match turbofish_target {
Either::Left(path_segment) => {
- edit.make_mut(path_segment).get_or_create_generic_arg_list()
+ if let Some(generic_arg_list) = path_segment.generic_arg_list() {
+ editor.replace(generic_arg_list.syntax(), fish_head.syntax());
+ } else {
+ editor.insert(
+ Position::last_child_of(path_segment.syntax()),
+ fish_head.syntax(),
+ );
+ }
}
Either::Right(method_call) => {
- edit.make_mut(method_call).get_or_create_generic_arg_list()
+ if let Some(generic_arg_list) = method_call.generic_arg_list() {
+ editor.replace(generic_arg_list.syntax(), fish_head.syntax());
+ } else {
+ let position = if let Some(arg_list) = method_call.arg_list() {
+ Position::before(arg_list.syntax())
+ } else {
+ Position::last_child_of(method_call.syntax())
+ };
+ editor.insert(position, fish_head.syntax());
+ }
}
};
- let fish_head = get_fish_head(number_of_arguments).clone_for_update();
-
- // Note: we need to replace the `new_arg_list` instead of being able to use something like
- // `GenericArgList::add_generic_arg` as `PathSegment::get_or_create_generic_arg_list`
- // always creates a non-turbofish form generic arg list.
- ted::replace(new_arg_list.syntax(), fish_head.syntax());
-
if let Some(cap) = ctx.config.snippet_cap {
for arg in fish_head.generic_args() {
- edit.add_placeholder_snippet(cap, arg)
+ editor.add_annotation(arg.syntax(), builder.make_placeholder_snippet(cap));
}
}
+
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.file_id(), editor);
},
)
}
/// This will create a turbofish generic arg list corresponding to the number of arguments
-fn get_fish_head(number_of_arguments: usize) -> ast::GenericArgList {
+fn get_fish_head(make: &SyntaxFactory, number_of_arguments: usize) -> ast::GenericArgList {
let args = (0..number_of_arguments).map(|_| make::type_arg(make::ty_placeholder()).into());
- make::turbofish_generic_arg_list(args)
+ make.turbofish_generic_arg_list(args)
}
#[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/bool_to_enum.rs b/crates/ide-assists/src/handlers/bool_to_enum.rs
index 605fd14052..f699899066 100644
--- a/crates/ide-assists/src/handlers/bool_to_enum.rs
+++ b/crates/ide-assists/src/handlers/bool_to_enum.rs
@@ -512,9 +512,11 @@ fn make_bool_enum(make_pub: bool) -> ast::Enum {
let enum_def = make::enum_(
if make_pub { Some(make::visibility_pub()) } else { None },
make::name("Bool"),
+ None,
+ None,
make::variant_list(vec![
- make::variant(make::name("True"), None),
- make::variant(make::name("False"), None),
+ make::variant(None, make::name("True"), None, None),
+ make::variant(None, make::name("False"), None, None),
]),
)
.clone_for_update();
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index ad0be89645..6937d33ebc 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -18,6 +18,7 @@ use ide_db::{
},
FxIndexSet, RootDatabase,
};
+use itertools::Itertools;
use syntax::{
ast::{
self, edit::IndentLevel, edit_in_place::Indent, AstNode, AstToken, HasGenericParams,
@@ -114,8 +115,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
return;
}
- let params =
- body.extracted_function_params(ctx, &container_info, locals_used.iter().copied());
+ let params = body.extracted_function_params(ctx, &container_info, locals_used);
let name = make_function_name(&semantics_scope);
@@ -1067,9 +1067,11 @@ impl FunctionBody {
&self,
ctx: &AssistContext<'_>,
container_info: &ContainerInfo,
- locals: impl Iterator<Item = Local>,
+ locals: FxIndexSet<Local>,
) -> Vec<Param> {
locals
+ .into_iter()
+ .sorted()
.map(|local| (local, local.primary_source(ctx.db())))
.filter(|(_, src)| is_defined_outside_of_body(ctx, self, src))
.filter_map(|(local, src)| match src.into_ident_pat() {
@@ -3167,11 +3169,11 @@ fn foo() {
let mut c = C { p: P { n: 0 } };
let mut v = C { p: P { n: 0 } };
let u = C { p: P { n: 0 } };
- fun_name(&mut c, &u, &mut v);
+ fun_name(&mut c, &mut v, &u);
let m = c.p.n + v.p.n + u.p.n;
}
-fn $0fun_name(c: &mut C, u: &C, v: &mut C) {
+fn $0fun_name(c: &mut C, v: &mut C, u: &C) {
c.p.n += u.p.n;
let r = &mut v.p.n;
}
@@ -5602,10 +5604,10 @@ fn parent(factor: i32) {
fn parent(factor: i32) {
let v = &[1, 2, 3];
- fun_name(v, factor);
+ fun_name(factor, v);
}
-fn $0fun_name(v: &[i32; 3], factor: i32) {
+fn $0fun_name(factor: i32, v: &[i32; 3]) {
v.iter().map(|it| it * factor);
}
"#,
@@ -5786,11 +5788,11 @@ struct Struct<T: Into<i32>>(T);
impl <T: Into<i32> + Copy> Struct<T> {
fn func<V: Into<i32>>(&self, v: V) -> i32 {
let t = self.0;
- fun_name(t, v)
+ fun_name(v, t)
}
}
-fn $0fun_name<T: Into<i32> + Copy, V: Into<i32>>(t: T, v: V) -> i32 {
+fn $0fun_name<T: Into<i32> + Copy, V: Into<i32>>(v: V, t: T) -> i32 {
t.into() + v.into()
}
"#,
@@ -5815,11 +5817,11 @@ struct Struct<T: Into<i32>, U: Debug>(T, U);
impl <T: Into<i32> + Copy, U: Debug> Struct<T, U> {
fn func<V: Into<i32>>(&self, v: V) -> i32 {
let t = self.0;
- fun_name(t, v)
+ fun_name(v, t)
}
}
-fn $0fun_name<T: Into<i32> + Copy, V: Into<i32>>(t: T, v: V) -> i32 {
+fn $0fun_name<T: Into<i32> + Copy, V: Into<i32>>(v: V, t: T) -> i32 {
t.into() + v.into()
}
"#,
@@ -5844,11 +5846,11 @@ struct Struct<T>(T) where T: Into<i32>;
impl <T> Struct<T> where T: Into<i32> + Copy {
fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
let t = self.0;
- fun_name(t, v)
+ fun_name(v, t)
}
}
-fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
+fn $0fun_name<T, V>(v: V, t: T) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
t.into() + v.into()
}
"#,
@@ -5873,11 +5875,11 @@ struct Struct<T, U>(T, U) where T: Into<i32>, U: Debug;
impl <T, U> Struct<T, U> where T: Into<i32> + Copy, U: Debug {
fn func<V>(&self, v: V) -> i32 where V: Into<i32> {
let t = self.0;
- fun_name(t, v)
+ fun_name(v, t)
}
}
-fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
+fn $0fun_name<T, V>(v: V, t: T) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
t.into() + v.into()
}
"#,
@@ -6107,6 +6109,31 @@ fn $0fun_name() -> i32 {
}
#[test]
+ fn sort_params_in_order() {
+ check_assist(
+ extract_function,
+ r#"
+fn existing(a: i32, b: i32, c: i32) {
+ let x = 32;
+
+ let p = $0x + b + c + a$0;
+}
+"#,
+ r#"
+fn existing(a: i32, b: i32, c: i32) {
+ let x = 32;
+
+ let p = fun_name(a, b, c, x);
+}
+
+fn $0fun_name(a: i32, b: i32, c: i32, x: i32) -> i32 {
+ x + b + c + a
+}
+"#,
+ );
+ }
+
+ #[test]
fn in_left_curly_is_not_applicable() {
cov_mark::check!(extract_function_in_braces_is_not_applicable);
check_assist_not_applicable(extract_function, r"fn foo() { $0}$0");
diff --git a/crates/ide-assists/src/handlers/flip_binexpr.rs b/crates/ide-assists/src/handlers/flip_binexpr.rs
index 8b46a23f9a..818a868fe3 100644
--- a/crates/ide-assists/src/handlers/flip_binexpr.rs
+++ b/crates/ide-assists/src/handlers/flip_binexpr.rs
@@ -1,4 +1,7 @@
-use syntax::ast::{self, AstNode, BinExpr};
+use syntax::{
+ ast::{self, syntax_factory::SyntaxFactory, AstNode, BinExpr},
+ SyntaxKind, T,
+};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -19,22 +22,17 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
// ```
pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let expr = ctx.find_node_at_offset::<BinExpr>()?;
- let rhs = expr.rhs()?.syntax().clone();
- let lhs = expr.lhs()?.syntax().clone();
-
- let lhs = if let Some(bin_expr) = BinExpr::cast(lhs.clone()) {
- if bin_expr.op_kind() == expr.op_kind() {
- bin_expr.rhs()?.syntax().clone()
- } else {
- lhs
- }
- } else {
- lhs
+ let lhs = expr.lhs()?;
+ let rhs = expr.rhs()?;
+
+ let lhs = match &lhs {
+ ast::Expr::BinExpr(bin_expr) if bin_expr.op_kind() == expr.op_kind() => bin_expr.rhs()?,
+ _ => lhs,
};
- let op_range = expr.op_token()?.text_range();
+ let op_token = expr.op_token()?;
// The assist should be applied only if the cursor is on the operator
- let cursor_in_range = op_range.contains_range(ctx.selection_trimmed());
+ let cursor_in_range = op_token.text_range().contains_range(ctx.selection_trimmed());
if !cursor_in_range {
return None;
}
@@ -47,13 +45,17 @@ pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
acc.add(
AssistId("flip_binexpr", AssistKind::RefactorRewrite),
"Flip binary expression",
- op_range,
- |edit| {
- if let FlipAction::FlipAndReplaceOp(new_op) = action {
- edit.replace(op_range, new_op);
- }
- edit.replace(lhs.text_range(), rhs.text());
- edit.replace(rhs.text_range(), lhs.text());
+ op_token.text_range(),
+ |builder| {
+ let mut editor = builder.make_editor(&expr.syntax().parent().unwrap());
+ let make = SyntaxFactory::new();
+ if let FlipAction::FlipAndReplaceOp(binary_op) = action {
+ editor.replace(op_token, make.token(binary_op))
+ };
+ editor.replace(lhs.syntax(), rhs.syntax());
+ editor.replace(rhs.syntax(), lhs.syntax());
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.file_id(), editor);
},
)
}
@@ -62,7 +64,7 @@ enum FlipAction {
// Flip the expression
Flip,
// Flip the expression and replace the operator with this string
- FlipAndReplaceOp(&'static str),
+ FlipAndReplaceOp(SyntaxKind),
// Do not flip the expression
DontFlip,
}
@@ -73,10 +75,10 @@ impl From<ast::BinaryOp> for FlipAction {
ast::BinaryOp::Assignment { .. } => FlipAction::DontFlip,
ast::BinaryOp::CmpOp(ast::CmpOp::Ord { ordering, strict }) => {
let rev_op = match (ordering, strict) {
- (ast::Ordering::Less, true) => ">",
- (ast::Ordering::Less, false) => ">=",
- (ast::Ordering::Greater, true) => "<",
- (ast::Ordering::Greater, false) => "<=",
+ (ast::Ordering::Less, true) => T![>],
+ (ast::Ordering::Less, false) => T![>=],
+ (ast::Ordering::Greater, true) => T![<],
+ (ast::Ordering::Greater, false) => T![<=],
};
FlipAction::FlipAndReplaceOp(rev_op)
}
diff --git a/crates/ide-assists/src/handlers/flip_comma.rs b/crates/ide-assists/src/handlers/flip_comma.rs
index af2c2c759e..95e035c053 100644
--- a/crates/ide-assists/src/handlers/flip_comma.rs
+++ b/crates/ide-assists/src/handlers/flip_comma.rs
@@ -1,7 +1,8 @@
-use ide_db::base_db::SourceDatabase;
-use syntax::TextSize;
use syntax::{
- algo::non_trivia_sibling, ast, AstNode, Direction, SyntaxKind, SyntaxToken, TextRange, T,
+ algo::non_trivia_sibling,
+ ast::{self, syntax_factory::SyntaxFactory},
+ syntax_editor::{Element, SyntaxMapping},
+ AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxToken, T,
};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -25,8 +26,6 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
let comma = ctx.find_token_syntax_at_offset(T![,])?;
let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?;
let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?;
- let (mut prev_text, mut next_text) = (prev.to_string(), next.to_string());
- let (mut prev_range, mut next_range) = (prev.text_range(), next.text_range());
// Don't apply a "flip" in case of a last comma
// that typically comes before punctuation
@@ -40,53 +39,84 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
return None;
}
- if let Some(parent) = comma.parent().and_then(ast::TokenTree::cast) {
- // An attribute. It often contains a path followed by a token tree (e.g. `align(2)`), so we have
- // to be smarter.
- let prev_start =
- match comma.siblings_with_tokens(Direction::Prev).skip(1).find(|it| it.kind() == T![,])
- {
- Some(it) => position_after_token(it.as_token().unwrap()),
- None => position_after_token(&parent.left_delimiter_token()?),
- };
- let prev_end = prev.text_range().end();
- let next_start = next.text_range().start();
- let next_end =
- match comma.siblings_with_tokens(Direction::Next).skip(1).find(|it| it.kind() == T![,])
- {
- Some(it) => position_before_token(it.as_token().unwrap()),
- None => position_before_token(&parent.right_delimiter_token()?),
- };
- prev_range = TextRange::new(prev_start, prev_end);
- next_range = TextRange::new(next_start, next_end);
- let file_text = ctx.db().file_text(ctx.file_id().file_id());
- prev_text = file_text[prev_range].to_owned();
- next_text = file_text[next_range].to_owned();
- }
+ let prev = match prev {
+ SyntaxElement::Node(node) => node.syntax_element(),
+ _ => prev,
+ };
+ let next = match next {
+ SyntaxElement::Node(node) => node.syntax_element(),
+ _ => next,
+ };
acc.add(
AssistId("flip_comma", AssistKind::RefactorRewrite),
"Flip comma",
comma.text_range(),
- |edit| {
- edit.replace(prev_range, next_text);
- edit.replace(next_range, prev_text);
+ |builder| {
+ let parent = comma.parent().unwrap();
+ let mut editor = builder.make_editor(&parent);
+
+ if let Some(parent) = ast::TokenTree::cast(parent) {
+ // An attribute. It often contains a path followed by a
+ // token tree (e.g. `align(2)`), so we have to be smarter.
+ let (new_tree, mapping) = flip_tree(parent.clone(), comma);
+ editor.replace(parent.syntax(), new_tree.syntax());
+ editor.add_mappings(mapping);
+ } else {
+ editor.replace(prev.clone(), next.clone());
+ editor.replace(next.clone(), prev.clone());
+ }
+
+ builder.add_file_edits(ctx.file_id(), editor);
},
)
}
-fn position_before_token(token: &SyntaxToken) -> TextSize {
- match non_trivia_sibling(token.clone().into(), Direction::Prev) {
- Some(prev_token) => prev_token.text_range().end(),
- None => token.text_range().start(),
- }
-}
-
-fn position_after_token(token: &SyntaxToken) -> TextSize {
- match non_trivia_sibling(token.clone().into(), Direction::Next) {
- Some(prev_token) => prev_token.text_range().start(),
- None => token.text_range().end(),
- }
+fn flip_tree(tree: ast::TokenTree, comma: SyntaxToken) -> (ast::TokenTree, SyntaxMapping) {
+ let mut tree_iter = tree.token_trees_and_tokens();
+ let before: Vec<_> =
+ tree_iter.by_ref().take_while(|it| it.as_token() != Some(&comma)).collect();
+ let after: Vec<_> = tree_iter.collect();
+
+ let not_ws = |element: &NodeOrToken<_, SyntaxToken>| match element {
+ NodeOrToken::Token(token) => token.kind() != SyntaxKind::WHITESPACE,
+ NodeOrToken::Node(_) => true,
+ };
+
+ let is_comma = |element: &NodeOrToken<_, SyntaxToken>| match element {
+ NodeOrToken::Token(token) => token.kind() == T![,],
+ NodeOrToken::Node(_) => false,
+ };
+
+ let prev_start_untrimmed = match before.iter().rposition(is_comma) {
+ Some(pos) => pos + 1,
+ None => 1,
+ };
+ let prev_end = 1 + before.iter().rposition(not_ws).unwrap();
+ let prev_start = prev_start_untrimmed
+ + before[prev_start_untrimmed..prev_end].iter().position(not_ws).unwrap();
+
+ let next_start = after.iter().position(not_ws).unwrap();
+ let next_end_untrimmed = match after.iter().position(is_comma) {
+ Some(pos) => pos,
+ None => after.len() - 1,
+ };
+ let next_end = 1 + after[..next_end_untrimmed].iter().rposition(not_ws).unwrap();
+
+ let result = [
+ &before[1..prev_start],
+ &after[next_start..next_end],
+ &before[prev_end..],
+ &[NodeOrToken::Token(comma)],
+ &after[..next_start],
+ &before[prev_start..prev_end],
+ &after[next_end..after.len() - 1],
+ ]
+ .concat();
+
+ let make = SyntaxFactory::new();
+ let new_token_tree = make.token_tree(tree.left_delimiter_token().unwrap().kind(), result);
+ (new_token_tree, make.finish_with_mappings())
}
#[cfg(test)]
@@ -147,4 +177,9 @@ mod tests {
r#"#[foo(bar, qux, baz(1 + 1), other)] struct Foo;"#,
);
}
+
+ #[test]
+ fn flip_comma_attribute_incomplete() {
+ check_assist_not_applicable(flip_comma, r#"#[repr(align(2),$0)] struct Foo;"#);
+ }
}
diff --git a/crates/ide-assists/src/handlers/flip_trait_bound.rs b/crates/ide-assists/src/handlers/flip_trait_bound.rs
index 70b5efcb64..298e5bd82c 100644
--- a/crates/ide-assists/src/handlers/flip_trait_bound.rs
+++ b/crates/ide-assists/src/handlers/flip_trait_bound.rs
@@ -23,11 +23,11 @@ pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
let plus = ctx.find_token_syntax_at_offset(T![+])?;
// Make sure we're in a `TypeBoundList`
- ast::TypeBoundList::cast(plus.parent()?)?;
+ let parent = ast::TypeBoundList::cast(plus.parent()?)?;
let (before, after) = (
- non_trivia_sibling(plus.clone().into(), Direction::Prev)?,
- non_trivia_sibling(plus.clone().into(), Direction::Next)?,
+ non_trivia_sibling(plus.clone().into(), Direction::Prev)?.into_node()?,
+ non_trivia_sibling(plus.clone().into(), Direction::Next)?.into_node()?,
);
let target = plus.text_range();
@@ -35,9 +35,11 @@ pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
AssistId("flip_trait_bound", AssistKind::RefactorRewrite),
"Flip trait bounds",
target,
- |edit| {
- edit.replace(before.text_range(), after.to_string());
- edit.replace(after.text_range(), before.to_string());
+ |builder| {
+ let mut editor = builder.make_editor(parent.syntax());
+ editor.replace(before.clone(), after.clone());
+ editor.replace(after, before);
+ builder.add_file_edits(ctx.file_id(), editor);
},
)
}
diff --git a/crates/ide-assists/src/handlers/generate_documentation_template.rs b/crates/ide-assists/src/handlers/generate_documentation_template.rs
index c5c70c9f8e..862be791d1 100644
--- a/crates/ide-assists/src/handlers/generate_documentation_template.rs
+++ b/crates/ide-assists/src/handlers/generate_documentation_template.rs
@@ -88,7 +88,7 @@ pub(crate) fn generate_documentation_template(
// /// # Examples
// ///
// /// ```
-// /// use test::add;
+// /// use ra_test_fixture::add;
// ///
// /// assert_eq!(add(a, b), );
// /// ```
@@ -596,7 +596,7 @@ pub fn noop_with_param(_a: i32) {}
/// # Examples
///
/// ```
-/// use test::noop_with_param;
+/// use ra_test_fixture::noop_with_param;
///
/// noop_with_param(_a);
/// ```
@@ -641,7 +641,7 @@ pub unsafe fn noop_unsafe() {}
/// # Examples
///
/// ```
-/// use test::noop_unsafe;
+/// use ra_test_fixture::noop_unsafe;
///
/// unsafe { noop_unsafe() };
/// ```
@@ -758,7 +758,7 @@ pub fn returns_a_value$0() -> i32 {
/// # Examples
///
/// ```
-/// use test::returns_a_value;
+/// use ra_test_fixture::returns_a_value;
///
/// assert_eq!(returns_a_value(), );
/// ```
@@ -807,7 +807,7 @@ pub fn modifies_a_value$0(a: &mut i32) {
/// # Examples
///
/// ```
-/// use test::modifies_a_value;
+/// use ra_test_fixture::modifies_a_value;
///
/// let mut a = ;
/// modifies_a_value(&mut a);
@@ -836,7 +836,7 @@ pub fn sum3$0(a: i32, b: i32, c: i32) -> i32 {
/// # Examples
///
/// ```
-/// use test::sum3;
+/// use ra_test_fixture::sum3;
///
/// let result = sum3(a, b, c);
/// assert_eq!(result, );
@@ -868,7 +868,7 @@ pub mod a {
/// # Examples
///
/// ```
- /// use test::a::b::noop;
+ /// use ra_test_fixture::a::b::noop;
///
/// noop();
/// ```
@@ -898,7 +898,7 @@ impl MyStruct {
/// # Examples
///
/// ```
- /// use test::MyStruct;
+ /// use ra_test_fixture::MyStruct;
///
/// MyStruct::noop();
/// ```
@@ -1169,7 +1169,7 @@ impl<T> MyGenericStruct<T> {
/// # Examples
///
/// ```
- /// use test::MyGenericStruct;
+ /// use ra_test_fixture::MyGenericStruct;
///
/// let my_generic_struct = ;
/// my_generic_struct.consume();
@@ -1199,7 +1199,7 @@ impl<T> MyGenericStruct<T> {
/// # Examples
///
/// ```
- /// use test::MyGenericStruct;
+ /// use ra_test_fixture::MyGenericStruct;
///
/// let mut my_generic_struct = ;
/// my_generic_struct.modify(new_value);
diff --git a/crates/ide-assists/src/handlers/generate_enum_variant.rs b/crates/ide-assists/src/handlers/generate_enum_variant.rs
index 5d58459121..bb08cb904e 100644
--- a/crates/ide-assists/src/handlers/generate_enum_variant.rs
+++ b/crates/ide-assists/src/handlers/generate_enum_variant.rs
@@ -1,7 +1,7 @@
use hir::{HasSource, HirDisplay, InRealFile};
use ide_db::assists::{AssistId, AssistKind};
use syntax::{
- ast::{self, make, HasArgList},
+ ast::{self, syntax_factory::SyntaxFactory, HasArgList},
match_ast, AstNode, SyntaxNode,
};
@@ -33,7 +33,7 @@ use crate::assist_context::{AssistContext, Assists};
// ```
pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let path: ast::Path = ctx.find_node_at_offset()?;
- let parent = path_parent(&path)?;
+ let parent = PathParent::new(&path)?;
if ctx.sema.resolve_path(&path).is_some() {
// No need to generate anything if the path resolves
@@ -46,14 +46,32 @@ pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>)
return None;
}
- if let Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e)))) =
+ let Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Enum(e)))) =
ctx.sema.resolve_path(&path.qualifier()?)
- {
- let target = path.syntax().text_range();
- return add_variant_to_accumulator(acc, ctx, target, e, &name_ref, parent);
- }
+ else {
+ return None;
+ };
- None
+ let target = path.syntax().text_range();
+ let name_ref: &ast::NameRef = &name_ref;
+ let db = ctx.db();
+ let InRealFile { file_id, value: enum_node } = e.source(db)?.original_ast_node_rooted(db)?;
+
+ acc.add(
+ AssistId("generate_enum_variant", AssistKind::Generate),
+ "Generate variant",
+ target,
+ |builder| {
+ let mut editor = builder.make_editor(enum_node.syntax());
+ let make = SyntaxFactory::new();
+ let field_list = parent.make_field_list(ctx, &make);
+ let variant = make.variant(None, make.name(&name_ref.text()), field_list, None);
+ if let Some(it) = enum_node.variant_list() {
+ it.add_variant(&mut editor, &variant);
+ }
+ builder.add_file_edits(file_id, editor);
+ },
+ )
}
#[derive(Debug)]
@@ -65,6 +83,20 @@ enum PathParent {
}
impl PathParent {
+ fn new(path: &ast::Path) -> Option<Self> {
+ let parent = path.syntax().parent()?;
+
+ match_ast! {
+ match parent {
+ ast::PathExpr(it) => Some(PathParent::PathExpr(it)),
+ ast::RecordExpr(it) => Some(PathParent::RecordExpr(it)),
+ ast::PathPat(it) => Some(PathParent::PathPat(it)),
+ ast::UseTree(it) => Some(PathParent::UseTree(it)),
+ _ => None
+ }
+ }
+ }
+
fn syntax(&self) -> &SyntaxNode {
match self {
PathParent::PathExpr(it) => it.syntax(),
@@ -74,97 +106,49 @@ impl PathParent {
}
}
- fn make_field_list(&self, ctx: &AssistContext<'_>) -> Option<ast::FieldList> {
+ fn make_field_list(
+ &self,
+ ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
+ ) -> Option<ast::FieldList> {
let scope = ctx.sema.scope(self.syntax())?;
match self {
PathParent::PathExpr(it) => {
- if let Some(call_expr) = it.syntax().parent().and_then(ast::CallExpr::cast) {
- make_tuple_field_list(call_expr, ctx, &scope)
- } else {
- None
- }
+ let call_expr = ast::CallExpr::cast(it.syntax().parent()?)?;
+ let args = call_expr.arg_list()?.args();
+ let tuple_fields = args.map(|arg| {
+ let ty =
+ expr_ty(ctx, make, arg, &scope).unwrap_or_else(|| make.ty_infer().into());
+ make.tuple_field(None, ty)
+ });
+ Some(make.tuple_field_list(tuple_fields).into())
+ }
+ PathParent::RecordExpr(it) => {
+ let fields = it.record_expr_field_list()?.fields();
+ let record_fields = fields.map(|field| {
+ let name = name_from_field(make, &field);
+
+ let ty = field
+ .expr()
+ .and_then(|it| expr_ty(ctx, make, it, &scope))
+ .unwrap_or_else(|| make.ty_infer().into());
+
+ make.record_field(None, name, ty)
+ });
+ Some(make.record_field_list(record_fields).into())
}
- PathParent::RecordExpr(it) => make_record_field_list(it, ctx, &scope),
PathParent::UseTree(_) | PathParent::PathPat(_) => None,
}
}
}
-fn path_parent(path: &ast::Path) -> Option<PathParent> {
- let parent = path.syntax().parent()?;
-
- match_ast! {
- match parent {
- ast::PathExpr(it) => Some(PathParent::PathExpr(it)),
- ast::RecordExpr(it) => Some(PathParent::RecordExpr(it)),
- ast::PathPat(it) => Some(PathParent::PathPat(it)),
- ast::UseTree(it) => Some(PathParent::UseTree(it)),
- _ => None
- }
- }
-}
-
-fn add_variant_to_accumulator(
- acc: &mut Assists,
- ctx: &AssistContext<'_>,
- target: syntax::TextRange,
- adt: hir::Enum,
- name_ref: &ast::NameRef,
- parent: PathParent,
-) -> Option<()> {
- let db = ctx.db();
- let InRealFile { file_id, value: enum_node } = adt.source(db)?.original_ast_node_rooted(db)?;
-
- acc.add(
- AssistId("generate_enum_variant", AssistKind::Generate),
- "Generate variant",
- target,
- |builder| {
- builder.edit_file(file_id.file_id());
- let node = builder.make_mut(enum_node);
- let variant = make_variant(ctx, name_ref, parent);
- if let Some(it) = node.variant_list() {
- it.add_variant(variant.clone_for_update())
- }
- },
- )
-}
-
-fn make_variant(
- ctx: &AssistContext<'_>,
- name_ref: &ast::NameRef,
- parent: PathParent,
-) -> ast::Variant {
- let field_list = parent.make_field_list(ctx);
- make::variant(make::name(&name_ref.text()), field_list)
-}
-
-fn make_record_field_list(
- record: &ast::RecordExpr,
- ctx: &AssistContext<'_>,
- scope: &hir::SemanticsScope<'_>,
-) -> Option<ast::FieldList> {
- let fields = record.record_expr_field_list()?.fields();
- let record_fields = fields.map(|field| {
- let name = name_from_field(&field);
-
- let ty = field
- .expr()
- .and_then(|it| expr_ty(ctx, it, scope))
- .unwrap_or_else(make::ty_placeholder);
-
- make::record_field(None, name, ty)
- });
- Some(make::record_field_list(record_fields).into())
-}
-
-fn name_from_field(field: &ast::RecordExprField) -> ast::Name {
+fn name_from_field(make: &SyntaxFactory, field: &ast::RecordExprField) -> ast::Name {
let text = match field.name_ref() {
Some(it) => it.to_string(),
None => name_from_field_shorthand(field).unwrap_or("unknown".to_owned()),
};
- make::name(&text)
+ make.name(&text)
}
fn name_from_field_shorthand(field: &ast::RecordExprField) -> Option<String> {
@@ -175,27 +159,15 @@ fn name_from_field_shorthand(field: &ast::RecordExprField) -> Option<String> {
Some(path.as_single_name_ref()?.to_string())
}
-fn make_tuple_field_list(
- call_expr: ast::CallExpr,
- ctx: &AssistContext<'_>,
- scope: &hir::SemanticsScope<'_>,
-) -> Option<ast::FieldList> {
- let args = call_expr.arg_list()?.args();
- let tuple_fields = args.map(|arg| {
- let ty = expr_ty(ctx, arg, scope).unwrap_or_else(make::ty_placeholder);
- make::tuple_field(None, ty)
- });
- Some(make::tuple_field_list(tuple_fields).into())
-}
-
fn expr_ty(
ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
arg: ast::Expr,
scope: &hir::SemanticsScope<'_>,
) -> Option<ast::Type> {
let ty = ctx.sema.type_of_expr(&arg).map(|it| it.adjusted())?;
let text = ty.display_source_code(ctx.db(), scope.module().into(), false).ok()?;
- Some(make::ty(&text))
+ Some(make.ty(&text))
}
#[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/introduce_named_generic.rs b/crates/ide-assists/src/handlers/introduce_named_generic.rs
index bf6ac1719f..8c276415bb 100644
--- a/crates/ide-assists/src/handlers/introduce_named_generic.rs
+++ b/crates/ide-assists/src/handlers/introduce_named_generic.rs
@@ -1,9 +1,6 @@
use ide_db::syntax_helpers::suggest_name;
use itertools::Itertools;
-use syntax::{
- ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams, HasName},
- ted,
-};
+use syntax::ast::{self, syntax_factory::SyntaxFactory, AstNode, HasGenericParams, HasName};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -25,42 +22,42 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>
let type_bound_list = impl_trait_type.type_bound_list()?;
+ let make = SyntaxFactory::new();
let target = fn_.syntax().text_range();
acc.add(
AssistId("introduce_named_generic", AssistKind::RefactorRewrite),
"Replace impl trait with generic",
target,
- |edit| {
- let impl_trait_type = edit.make_mut(impl_trait_type);
- let fn_ = edit.make_mut(fn_);
- let fn_generic_param_list = fn_.get_or_create_generic_param_list();
-
- let existing_names = fn_generic_param_list
- .generic_params()
- .flat_map(|param| match param {
- ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()),
- p => Some(p.to_string()),
- })
- .collect_vec();
+ |builder| {
+ let mut editor = builder.make_editor(fn_.syntax());
+
+ let existing_names = match fn_.generic_param_list() {
+ Some(generic_param_list) => generic_param_list
+ .generic_params()
+ .flat_map(|param| match param {
+ ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()),
+ p => Some(p.to_string()),
+ })
+ .collect_vec(),
+ None => Vec::new(),
+ };
let type_param_name = suggest_name::NameGenerator::new_with_names(
existing_names.iter().map(|s| s.as_str()),
)
.for_impl_trait_as_generic(&impl_trait_type);
- let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list))
- .clone_for_update();
- let new_ty = make::ty(&type_param_name).clone_for_update();
+ let type_param = make.type_param(make.name(&type_param_name), Some(type_bound_list));
+ let new_ty = make.ty(&type_param_name);
- ted::replace(impl_trait_type.syntax(), new_ty.syntax());
- fn_generic_param_list.add_generic_param(type_param.into());
+ editor.replace(impl_trait_type.syntax(), new_ty.syntax());
+ editor.add_generic_param(&fn_, type_param.clone().into());
if let Some(cap) = ctx.config.snippet_cap {
- if let Some(generic_param) =
- fn_.generic_param_list().and_then(|it| it.generic_params().last())
- {
- edit.add_tabstop_before(cap, generic_param);
- }
+ editor.add_annotation(type_param.syntax(), builder.make_tabstop_before(cap));
}
+
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.file_id(), editor);
},
)
}
diff --git a/crates/ide-assists/src/handlers/reorder_fields.rs b/crates/ide-assists/src/handlers/reorder_fields.rs
index 4d3e85ab1b..972303c2a0 100644
--- a/crates/ide-assists/src/handlers/reorder_fields.rs
+++ b/crates/ide-assists/src/handlers/reorder_fields.rs
@@ -92,10 +92,9 @@ fn replace<T: AstNode + PartialEq>(
fields: impl Iterator<Item = T>,
sorted_fields: impl IntoIterator<Item = T>,
) {
- fields.zip(sorted_fields).for_each(|(field, sorted_field)| {
- // FIXME: remove `clone_for_update` when `SyntaxEditor` handles it for us
- editor.replace(field.syntax(), sorted_field.syntax().clone_for_update())
- });
+ fields
+ .zip(sorted_fields)
+ .for_each(|(field, sorted_field)| editor.replace(field.syntax(), sorted_field.syntax()));
}
fn compute_fields_ranks(
diff --git a/crates/ide-assists/src/handlers/reorder_impl_items.rs b/crates/ide-assists/src/handlers/reorder_impl_items.rs
index d7fa882612..eb1d538f87 100644
--- a/crates/ide-assists/src/handlers/reorder_impl_items.rs
+++ b/crates/ide-assists/src/handlers/reorder_impl_items.rs
@@ -101,10 +101,10 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|builder| {
let mut editor = builder.make_editor(&parent_node);
- assoc_items.into_iter().zip(sorted).for_each(|(old, new)| {
- // FIXME: remove `clone_for_update` when `SyntaxEditor` handles it for us
- editor.replace(old.syntax(), new.clone_for_update().syntax())
- });
+ assoc_items
+ .into_iter()
+ .zip(sorted)
+ .for_each(|(old, new)| editor.replace(old.syntax(), new.syntax()));
builder.add_file_edits(ctx.file_id(), editor);
},
diff --git a/crates/ide-assists/src/handlers/sort_items.rs b/crates/ide-assists/src/handlers/sort_items.rs
index 64e30b1834..54e16d4d80 100644
--- a/crates/ide-assists/src/handlers/sort_items.rs
+++ b/crates/ide-assists/src/handlers/sort_items.rs
@@ -4,7 +4,7 @@ use itertools::Itertools;
use syntax::{
ast::{self, HasName},
- ted, AstNode, TextRange,
+ AstNode, SyntaxNode,
};
use crate::{utils::get_methods, AssistContext, AssistId, AssistKind, Assists};
@@ -114,7 +114,7 @@ trait AddRewrite {
label: &str,
old: Vec<T>,
new: Vec<T>,
- target: TextRange,
+ target: &SyntaxNode,
) -> Option<()>;
}
@@ -124,15 +124,22 @@ impl AddRewrite for Assists {
label: &str,
old: Vec<T>,
new: Vec<T>,
- target: TextRange,
+ target: &SyntaxNode,
) -> Option<()> {
- self.add(AssistId("sort_items", AssistKind::RefactorRewrite), label, target, |builder| {
- let mutable: Vec<T> = old.into_iter().map(|it| builder.make_mut(it)).collect();
- mutable
- .into_iter()
- .zip(new)
- .for_each(|(old, new)| ted::replace(old.syntax(), new.clone_for_update().syntax()));
- })
+ self.add(
+ AssistId("sort_items", AssistKind::RefactorRewrite),
+ label,
+ target.text_range(),
+ |builder| {
+ let mut editor = builder.make_editor(target);
+
+ old.into_iter()
+ .zip(new)
+ .for_each(|(old, new)| editor.replace(old.syntax(), new.syntax()));
+
+ builder.add_file_edits(builder.file_id, editor)
+ },
+ )
}
}
@@ -167,7 +174,7 @@ fn add_sort_methods_assist(
return None;
}
- acc.add_rewrite("Sort methods alphabetically", methods, sorted, item_list.syntax().text_range())
+ acc.add_rewrite("Sort methods alphabetically", methods, sorted, item_list.syntax())
}
fn add_sort_fields_assist(
@@ -182,12 +189,7 @@ fn add_sort_fields_assist(
return None;
}
- acc.add_rewrite(
- "Sort fields alphabetically",
- fields,
- sorted,
- record_field_list.syntax().text_range(),
- )
+ acc.add_rewrite("Sort fields alphabetically", fields, sorted, record_field_list.syntax())
}
fn add_sort_variants_assist(acc: &mut Assists, variant_list: ast::VariantList) -> Option<()> {
@@ -199,12 +201,7 @@ fn add_sort_variants_assist(acc: &mut Assists, variant_list: ast::VariantList) -
return None;
}
- acc.add_rewrite(
- "Sort variants alphabetically",
- variants,
- sorted,
- variant_list.syntax().text_range(),
- )
+ acc.add_rewrite("Sort variants alphabetically", variants, sorted, variant_list.syntax())
}
fn sort_by_name<T: HasName + Clone>(initial: &[T]) -> Vec<T> {
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index eef4da55e9..69ea200db1 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -1392,7 +1392,7 @@ pub fn add(a: i32, b: i32) -> i32 { a + b }
/// # Examples
///
/// ```
-/// use test::add;
+/// use ra_test_fixture::add;
///
/// assert_eq!(add(a, b), );
/// ```
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs
index d0b489c4e8..cf5427bae3 100644
--- a/crates/ide-completion/src/completions/attribute.rs
+++ b/crates/ide-completion/src/completions/attribute.rs
@@ -86,10 +86,21 @@ pub(crate) fn complete_attribute_path(
acc: &mut Completions,
ctx: &CompletionContext<'_>,
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
- &AttrCtx { kind, annotated_item_kind }: &AttrCtx,
+ &AttrCtx { kind, annotated_item_kind, ref derive_helpers }: &AttrCtx,
) {
let is_inner = kind == AttrKind::Inner;
+ for (derive_helper, derive_name) in derive_helpers {
+ let mut item = CompletionItem::new(
+ SymbolKind::Attribute,
+ ctx.source_range(),
+ derive_helper.as_str(),
+ ctx.edition,
+ );
+ item.detail(format!("derive helper of `{derive_name}`"));
+ item.add_to(acc, ctx.db);
+ }
+
match qualified {
Qualified::With {
resolution: Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))),
diff --git a/crates/ide-completion/src/completions/extern_abi.rs b/crates/ide-completion/src/completions/extern_abi.rs
index b0e417e6b3..847fa4cf88 100644
--- a/crates/ide-completion/src/completions/extern_abi.rs
+++ b/crates/ide-completion/src/completions/extern_abi.rs
@@ -38,7 +38,6 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[
"system-unwind",
"rust-intrinsic",
"rust-call",
- "platform-intrinsic",
"unadjusted",
];
diff --git a/crates/ide-completion/src/completions/lifetime.rs b/crates/ide-completion/src/completions/lifetime.rs
index 9efc52428e..0692446381 100644
--- a/crates/ide-completion/src/completions/lifetime.rs
+++ b/crates/ide-completion/src/completions/lifetime.rs
@@ -8,7 +8,6 @@
//! show up for normal completions, or they won't show completions other than lifetimes depending
//! on the fixture input.
use hir::{sym, Name, ScopeDef};
-use syntax::{ast, ToSmolStr, TokenText};
use crate::{
completions::Completions,
@@ -21,33 +20,24 @@ pub(crate) fn complete_lifetime(
ctx: &CompletionContext<'_>,
lifetime_ctx: &LifetimeContext,
) {
- let (lp, lifetime) = match lifetime_ctx {
- LifetimeContext { kind: LifetimeKind::Lifetime, lifetime } => (None, lifetime),
- LifetimeContext {
- kind: LifetimeKind::LifetimeParam { is_decl: false, param },
- lifetime,
- } => (Some(param), lifetime),
- _ => return,
- };
- let param_lifetime = match (lifetime, lp.and_then(|lp| lp.lifetime())) {
- (Some(lt), Some(lp)) if lp == lt.clone() => return,
- (Some(_), Some(lp)) => Some(lp),
- _ => None,
+ let &LifetimeContext { kind: LifetimeKind::Lifetime { in_lifetime_param_bound, def }, .. } =
+ lifetime_ctx
+ else {
+ return;
};
- let param_lifetime = param_lifetime.as_ref().map(ast::Lifetime::text);
- let param_lifetime = param_lifetime.as_ref().map(TokenText::as_str);
ctx.process_all_names_raw(&mut |name, res| {
- if matches!(
- res,
- ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_))
- if param_lifetime != Some(&*name.display_no_db(ctx.edition).to_smolstr())
- ) {
+ if matches!(res, ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_))) {
acc.add_lifetime(ctx, name);
}
});
- if param_lifetime.is_none() {
- acc.add_lifetime(ctx, Name::new_symbol_root(sym::tick_static.clone()));
+ acc.add_lifetime(ctx, Name::new_symbol_root(sym::tick_static.clone()));
+ if !in_lifetime_param_bound
+ && def.is_some_and(|def| {
+ !matches!(def, hir::GenericDef::Function(_) | hir::GenericDef::Impl(_))
+ })
+ {
+ acc.add_lifetime(ctx, Name::new_symbol_root(sym::tick_underscore.clone()));
}
}
@@ -222,6 +212,8 @@ fn foo<'footime, 'lifetime: 'a$0>() {}
"#,
expect![[r#"
lt 'footime
+ lt 'lifetime
+ lt 'static
"#]],
);
}
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index efbee39a2d..3a66170633 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -7,8 +7,8 @@ mod tests;
use std::{iter, ops::ControlFlow};
use hir::{
- HasAttrs, Local, ModuleSource, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type,
- TypeInfo,
+ HasAttrs, Local, ModuleSource, Name, PathResolution, ScopeDef, Semantics, SemanticsScope,
+ Symbol, Type, TypeInfo,
};
use ide_db::{
base_db::SourceDatabase, famous_defs::FamousDefs, helpers::is_editable_crate, FilePosition,
@@ -133,6 +133,7 @@ pub(crate) type ExistingDerives = FxHashSet<hir::Macro>;
pub(crate) struct AttrCtx {
pub(crate) kind: AttrKind,
pub(crate) annotated_item_kind: Option<SyntaxKind>,
+ pub(crate) derive_helpers: Vec<(Symbol, Symbol)>,
}
#[derive(Debug, PartialEq, Eq)]
@@ -289,15 +290,14 @@ pub(crate) struct ParamContext {
/// The state of the lifetime we are completing.
#[derive(Debug)]
pub(crate) struct LifetimeContext {
- pub(crate) lifetime: Option<ast::Lifetime>,
pub(crate) kind: LifetimeKind,
}
/// The kind of lifetime we are completing.
#[derive(Debug)]
pub(crate) enum LifetimeKind {
- LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
- Lifetime,
+ LifetimeParam,
+ Lifetime { in_lifetime_param_bound: bool, def: Option<hir::GenericDef> },
LabelRef,
LabelDef,
}
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 468ad81ad2..4a678963b9 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -562,7 +562,7 @@ fn expected_type_and_name(
}
fn classify_lifetime(
- _sema: &Semantics<'_, RootDatabase>,
+ sema: &Semantics<'_, RootDatabase>,
original_file: &SyntaxNode,
lifetime: ast::Lifetime,
) -> Option<LifetimeContext> {
@@ -571,21 +571,22 @@ fn classify_lifetime(
return None;
}
+ let lifetime =
+ find_node_at_offset::<ast::Lifetime>(original_file, lifetime.syntax().text_range().start());
let kind = match_ast! {
match parent {
- ast::LifetimeParam(param) => LifetimeKind::LifetimeParam {
- is_decl: param.lifetime().as_ref() == Some(&lifetime),
- param
- },
+ ast::LifetimeParam(_) => LifetimeKind::LifetimeParam,
ast::BreakExpr(_) => LifetimeKind::LabelRef,
ast::ContinueExpr(_) => LifetimeKind::LabelRef,
ast::Label(_) => LifetimeKind::LabelDef,
- _ => LifetimeKind::Lifetime,
+ _ => {
+ let def = lifetime.as_ref().and_then(|lt| sema.scope(lt.syntax())?.generic_def());
+ LifetimeKind::Lifetime { in_lifetime_param_bound: ast::TypeBound::can_cast(parent.kind()), def }
+ },
}
};
- let lifetime = find_node_at_offset(original_file, lifetime.syntax().text_range().start());
- Some(LifetimeContext { lifetime, kind })
+ Some(LifetimeContext { kind })
}
fn classify_name(
@@ -1129,7 +1130,22 @@ fn classify_name_ref(
let is_trailing_outer_attr = kind != AttrKind::Inner
&& non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none();
let annotated_item_kind = if is_trailing_outer_attr { None } else { Some(attached.kind()) };
- Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } })
+ let derive_helpers = annotated_item_kind
+ .filter(|kind| {
+ matches!(
+ kind,
+ SyntaxKind::STRUCT
+ | SyntaxKind::ENUM
+ | SyntaxKind::UNION
+ | SyntaxKind::VARIANT
+ | SyntaxKind::TUPLE_FIELD
+ | SyntaxKind::RECORD_FIELD
+ )
+ })
+ .and_then(|_| nameref.as_ref()?.syntax().ancestors().find_map(ast::Adt::cast))
+ .and_then(|adt| sema.derive_helpers_in_scope(&adt))
+ .unwrap_or_default();
+ Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind, derive_helpers } })
};
// Infer the path kind
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 52f6bedaaa..8878fbbea3 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -346,8 +346,7 @@ pub enum CompletionItemKind {
impl_from!(SymbolKind for CompletionItemKind);
impl CompletionItemKind {
- #[cfg(test)]
- pub(crate) fn tag(self) -> &'static str {
+ pub fn tag(self) -> &'static str {
match self {
CompletionItemKind::SymbolKind(kind) => match kind {
SymbolKind::Attribute => "at",
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index dfee01b187..14f42b4005 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -19,7 +19,7 @@ use ide_db::{
},
items_locator,
syntax_helpers::tree_diff::diff,
- FilePosition, RootDatabase,
+ FilePosition, FxHashSet, RootDatabase,
};
use crate::{
@@ -34,6 +34,7 @@ pub use crate::{
config::{CallableSnippets, CompletionConfig},
item::{
CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch,
+ CompletionRelevanceReturnType, CompletionRelevanceTypeMatch,
},
snippet::{Snippet, SnippetScope},
};
@@ -50,6 +51,18 @@ pub struct CompletionFieldsToResolve {
}
impl CompletionFieldsToResolve {
+ pub fn from_client_capabilities(client_capability_fields: &FxHashSet<&str>) -> Self {
+ Self {
+ resolve_label_details: client_capability_fields.contains("labelDetails"),
+ resolve_tags: client_capability_fields.contains("tags"),
+ resolve_detail: client_capability_fields.contains("detail"),
+ resolve_documentation: client_capability_fields.contains("documentation"),
+ resolve_filter_text: client_capability_fields.contains("filterText"),
+ resolve_text_edit: client_capability_fields.contains("textEdit"),
+ resolve_command: client_capability_fields.contains("command"),
+ }
+ }
+
pub const fn empty() -> Self {
Self {
resolve_label_details: false,
diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs
index 45679355b4..1443ebc6c0 100644
--- a/crates/ide-completion/src/tests/attribute.rs
+++ b/crates/ide-completion/src/tests/attribute.rs
@@ -9,6 +9,70 @@ fn check(ra_fixture: &str, expect: Expect) {
}
#[test]
+fn derive_helpers() {
+ check(
+ r#"
+//- /mac.rs crate:mac
+#![crate_type = "proc-macro"]
+
+#[proc_macro_derive(MyDerive, attributes(my_cool_helper_attribute))]
+pub fn my_derive() {}
+
+//- /lib.rs crate:lib deps:mac
+#[rustc_builtin_macro]
+pub macro derive($item:item) {}
+
+#[derive(mac::MyDerive)]
+pub struct Foo(#[m$0] i32);
+"#,
+ expect![[r#"
+ at allow(…)
+ at automatically_derived
+ at cfg(…)
+ at cfg_attr(…)
+ at cold
+ at deny(…)
+ at deprecated
+ at derive macro derive
+ at derive(…)
+ at doc = "…"
+ at doc(alias = "…")
+ at doc(hidden)
+ at expect(…)
+ at export_name = "…"
+ at forbid(…)
+ at global_allocator
+ at ignore = "…"
+ at inline
+ at link
+ at link_name = "…"
+ at link_section = "…"
+ at macro_export
+ at macro_use
+ at must_use
+ at my_cool_helper_attribute derive helper of `MyDerive`
+ at no_mangle
+ at non_exhaustive
+ at panic_handler
+ at path = "…"
+ at proc_macro
+ at proc_macro_attribute
+ at proc_macro_derive(…)
+ at repr(…)
+ at should_panic
+ at target_feature(enable = "…")
+ at test
+ at track_caller
+ at used
+ at warn(…)
+ md mac
+ kw crate::
+ kw self::
+ "#]],
+ )
+}
+
+#[test]
fn proc_macros() {
check(
r#"
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index fdac4dd2ef..932ca37302 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -733,6 +733,12 @@ impl NameRefClass {
}
None
},
+ ast::UseBoundGenericArgs(_) => {
+ sema.resolve_use_type_arg(name_ref)
+ .map(GenericParam::TypeParam)
+ .map(Definition::GenericParam)
+ .map(NameRefClass::Definition)
+ },
ast::ExternCrate(extern_crate_ast) => {
let extern_crate = sema.to_def(&extern_crate_ast)?;
let krate = extern_crate.resolved_crate(sema.db)?;
@@ -764,6 +770,7 @@ impl NameRefClass {
sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
}
SyntaxKind::LIFETIME_ARG
+ | SyntaxKind::USE_BOUND_GENERIC_ARGS
| SyntaxKind::SELF_PARAM
| SyntaxKind::TYPE_BOUND
| SyntaxKind::WHERE_PRED
@@ -772,16 +779,6 @@ impl NameRefClass {
.map(GenericParam::LifetimeParam)
.map(Definition::GenericParam)
.map(NameRefClass::Definition),
- // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
- // if our lifetime is in a LifetimeParam without being the constrained lifetime
- _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
- != Some(lifetime) =>
- {
- sema.resolve_lifetime_param(lifetime)
- .map(GenericParam::LifetimeParam)
- .map(Definition::GenericParam)
- .map(NameRefClass::Definition)
- }
_ => None,
}
}
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index 49b3ca290f..a508f2fedd 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -286,7 +286,8 @@ impl Ctx<'_> {
return None;
}
if path.segment().map_or(false, |s| {
- s.param_list().is_some() || (s.self_token().is_some() && path.parent_path().is_none())
+ s.parenthesized_arg_list().is_some()
+ || (s.self_token().is_some() && path.parent_path().is_none())
}) {
// don't try to qualify `Fn(Foo) -> Bar` paths, they are in prelude anyway
// don't try to qualify sole `self` either, they are usually locals, but are returned as modules due to namespace clashing
diff --git a/crates/ide-db/src/syntax_helpers/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs
index 2679cbef61..b3ecc26cb2 100644
--- a/crates/ide-db/src/syntax_helpers/suggest_name.rs
+++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs
@@ -29,7 +29,7 @@ const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"];
/// # Examples
/// `Option<Name>` -> `Name`
/// `Result<User, Error>` -> `User`
-const WRAPPER_TYPES: &[&str] = &["Box", "Option", "Result"];
+const WRAPPER_TYPES: &[&str] = &["Box", "Arc", "Rc", "Option", "Result"];
/// Prefixes to strip from methods names
///
@@ -859,6 +859,32 @@ fn foo() { $0(bar())$0; }
}
#[test]
+ fn arc_value() {
+ check(
+ r#"
+struct Arc<T>(*const T);
+struct Seed;
+fn bar() -> Arc<Seed> {}
+fn foo() { $0(bar())$0; }
+"#,
+ "seed",
+ );
+ }
+
+ #[test]
+ fn rc_value() {
+ check(
+ r#"
+struct Rc<T>(*const T);
+struct Seed;
+fn bar() -> Rc<Seed> {}
+fn foo() { $0(bar())$0; }
+"#,
+ "seed",
+ );
+ }
+
+ #[test]
fn ref_call() {
check(
r#"
diff --git a/crates/ide-diagnostics/src/handlers/expected_function.rs b/crates/ide-diagnostics/src/handlers/expected_function.rs
index 02299197b1..e3a1e12e02 100644
--- a/crates/ide-diagnostics/src/handlers/expected_function.rs
+++ b/crates/ide-diagnostics/src/handlers/expected_function.rs
@@ -37,4 +37,25 @@ fn foo() {
"#,
);
}
+
+ #[test]
+ fn no_error_for_async_fn_traits() {
+ check_diagnostics(
+ r#"
+//- minicore: async_fn
+async fn f(it: impl AsyncFn(u32) -> i32) {
+ let fut = it(0);
+ let _: i32 = fut.await;
+}
+async fn g(mut it: impl AsyncFnMut(u32) -> i32) {
+ let fut = it(0);
+ let _: i32 = fut.await;
+}
+async fn h(it: impl AsyncFnOnce(u32) -> i32) {
+ let fut = it(0);
+ let _: i32 = fut.await;
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
new file mode 100644
index 0000000000..a319a0bcf6
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
@@ -0,0 +1,442 @@
+use either::Either;
+use hir::GenericArgsProhibitedReason;
+use ide_db::assists::Assist;
+use ide_db::source_change::SourceChange;
+use ide_db::text_edit::TextEdit;
+use syntax::{ast, AstNode, TextRange};
+
+use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: generic-args-prohibited
+//
+// This diagnostic is shown when generic arguments are provided for a type that does not accept
+// generic arguments.
+pub(crate) fn generic_args_prohibited(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::GenericArgsProhibited,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0109"),
+ describe_reason(d.reason),
+ d.args.map(Into::into),
+ )
+ .with_fixes(fixes(ctx, d))
+}
+
+fn describe_reason(reason: GenericArgsProhibitedReason) -> String {
+ let kind = match reason {
+ GenericArgsProhibitedReason::Module => "modules",
+ GenericArgsProhibitedReason::TyParam => "type parameters",
+ GenericArgsProhibitedReason::SelfTy => "`Self`",
+ GenericArgsProhibitedReason::PrimitiveTy => "builtin types",
+ GenericArgsProhibitedReason::EnumVariant => {
+ return "you can specify generic arguments on either the enum or the variant, but not both"
+ .to_owned();
+ }
+ };
+ format!("generic arguments are not allowed on {kind}")
+}
+
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::GenericArgsProhibited) -> Option<Vec<Assist>> {
+ let file_id = d.args.file_id.file_id()?;
+ let syntax = d.args.to_node(ctx.sema.db);
+ let range = match &syntax {
+ Either::Left(_) => syntax.syntax().text_range(),
+ Either::Right(param_list) => {
+ let path_segment = ast::PathSegment::cast(param_list.syntax().parent()?)?;
+ let start = if let Some(coloncolon) = path_segment.coloncolon_token() {
+ coloncolon.text_range().start()
+ } else {
+ param_list.syntax().text_range().start()
+ };
+ let end = if let Some(ret_type) = path_segment.ret_type() {
+ ret_type.syntax().text_range().end()
+ } else {
+ param_list.syntax().text_range().end()
+ };
+ TextRange::new(start, end)
+ }
+ };
+ Some(vec![fix(
+ "remove_generic_args",
+ "Remove these generics",
+ SourceChange::from_text_edit(file_id, TextEdit::delete(range)),
+ syntax.syntax().text_range(),
+ )])
+}
+
+#[cfg(test)]
+mod tests {
+ // This diagnostic was the first to be emitted in ty lowering, so the tests here also test
+ // diagnostics in ty lowering in general (which is why there are so many of them).
+
+ use crate::tests::{check_diagnostics, check_fix};
+
+ #[test]
+ fn primitives() {
+ check_diagnostics(
+ r#"
+//- /core.rs crate:core library
+#![rustc_coherence_is_core]
+impl str {
+ pub fn trim() {}
+}
+
+//- /lib.rs crate:foo deps:core
+fn bar<T>() {}
+
+fn foo() {
+ let _: (bool<()>, ());
+ // ^^^^ 💡 error: generic arguments are not allowed on builtin types
+ let _ = <str<'_>>::trim;
+ // ^^^^ 💡 error: generic arguments are not allowed on builtin types
+ bar::<u32<{ const { 1 + 1 } }>>();
+ // ^^^^^^^^^^^^^^^^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn modules() {
+ check_diagnostics(
+ r#"
+pub mod foo {
+ pub mod bar {
+ pub struct Baz;
+
+ impl Baz {
+ pub fn qux() {}
+ }
+ }
+}
+
+fn foo() {
+ let _: foo::<'_>::bar::Baz;
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ let _ = <foo::bar<()>::Baz>::qux;
+ // ^^^^ 💡 error: generic arguments are not allowed on modules
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn type_parameters() {
+ check_diagnostics(
+ r#"
+fn foo<T, U>() {
+ let _: T<'a>;
+ // ^^^^ 💡 error: generic arguments are not allowed on type parameters
+ let _: U::<{ 1 + 2 }>;
+ // ^^^^^^^^^^^^^ 💡 error: generic arguments are not allowed on type parameters
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn fn_like_generic_args() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ let _: bool(bool, i32) -> ();
+ // ^^^^^^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn fn_signature() {
+ check_diagnostics(
+ r#"
+fn foo(
+ _a: bool<'_>,
+ // ^^^^ 💡 error: generic arguments are not allowed on builtin types
+ _b: i32::<i64>,
+ // ^^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ _c: &(&str<1>)
+ // ^^^ 💡 error: generic arguments are not allowed on builtin types
+) -> ((), i32<bool>) {
+ // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ ((), 0)
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn const_static_type() {
+ check_diagnostics(
+ r#"
+const A: i32<bool> = 0;
+ // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+static A: i32::<{ 1 + 3 }> = 0;
+ // ^^^^^^^^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ "#,
+ );
+ }
+
+ #[test]
+ fn fix() {
+ check_fix(
+ r#"
+fn foo() {
+ let _: bool<'_, (), { 1 + 1 }>$0;
+}"#,
+ r#"
+fn foo() {
+ let _: bool;
+}"#,
+ );
+ check_fix(
+ r#"
+fn foo() {
+ let _: bool::$0<'_, (), { 1 + 1 }>;
+}"#,
+ r#"
+fn foo() {
+ let _: bool;
+}"#,
+ );
+ check_fix(
+ r#"
+fn foo() {
+ let _: bool(i$032);
+}"#,
+ r#"
+fn foo() {
+ let _: bool;
+}"#,
+ );
+ check_fix(
+ r#"
+fn foo() {
+ let _: bool$0(i32) -> i64;
+}"#,
+ r#"
+fn foo() {
+ let _: bool;
+}"#,
+ );
+ check_fix(
+ r#"
+fn foo() {
+ let _: bool::(i$032) -> i64;
+}"#,
+ r#"
+fn foo() {
+ let _: bool;
+}"#,
+ );
+ check_fix(
+ r#"
+fn foo() {
+ let _: bool::(i32)$0;
+}"#,
+ r#"
+fn foo() {
+ let _: bool;
+}"#,
+ );
+ }
+
+ #[test]
+ fn in_fields() {
+ check_diagnostics(
+ r#"
+struct A(bool<i32>);
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+struct B { v: bool<(), 1> }
+ // ^^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+union C {
+ a: bool<i32>,
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ b: i32<bool>,
+ // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ }
+enum D {
+ A(bool<i32>),
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ B { v: i32<bool> },
+ // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn in_generics() {
+ check_diagnostics(
+ r#"
+mod foo {
+ pub trait Trait {}
+}
+
+struct A<A: foo::<()>::Trait>(A)
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ where bool<i32>: foo::Trait;
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+union B<A: foo::<()>::Trait>
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ where bool<i32>: foo::Trait
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+{ a: A }
+enum C<A: foo::<()>::Trait>
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ where bool<i32>: foo::Trait
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+{}
+
+fn f<A: foo::<()>::Trait>()
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ where bool<i32>: foo::Trait
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+{}
+
+type D<A: foo::<()>::Trait> = A
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ where bool<i32>: foo::Trait;
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+
+trait E<A: foo::<()>::Trait>
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ where bool<i32>: foo::Trait
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+{
+ fn f<B: foo::<()>::Trait>()
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ where bool<i32>: foo::Trait
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ {}
+
+ type D<B: foo::<()>::Trait> = A
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ where bool<i32>: foo::Trait;
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+}
+
+impl<A: foo::<()>::Trait> E for ()
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ where bool<i32>: foo::Trait
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+{
+ fn f<B: foo::<()>::Trait>()
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ where bool<i32>: foo::Trait
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ {}
+
+ type D<B: foo::<()>::Trait> = A
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ where bool<i32>: foo::Trait;
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn assoc_items() {
+ check_diagnostics(
+ r#"
+struct Foo;
+
+trait Trait {
+ fn f() -> bool<i32> { true }
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ type T = i32<bool>;
+ // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+}
+
+impl Trait for Foo {
+ fn f() -> bool<i32> { true }
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ type T = i32<bool>;
+ // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+}
+
+impl Foo {
+ fn f() -> bool<i32> { true }
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ type T = i32<bool>;
+ // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn const_param_ty() {
+ check_diagnostics(
+ r#"
+fn foo<
+ const A: bool<i32>,
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ B,
+ C,
+ const D: bool<i32>,
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ const E: bool<i32>,
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+>() {}
+ "#,
+ );
+ }
+
+ #[test]
+ fn generic_defaults() {
+ check_diagnostics(
+ r#"
+struct Foo<A = bool<i32>>(A);
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ "#,
+ );
+ }
+
+ #[test]
+ fn impl_self_ty() {
+ check_diagnostics(
+ r#"
+struct Foo<A>(A);
+trait Trait {}
+impl Foo<bool<i32>> {}
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+impl Trait for Foo<bool<i32>> {}
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+ "#,
+ );
+ }
+
+ #[test]
+ fn impl_trait() {
+ check_diagnostics(
+ r#"
+mod foo {
+ pub trait Trait {}
+}
+impl foo::<()>::Trait for () {}
+ // ^^^^^^ 💡 error: generic arguments are not allowed on modules
+ "#,
+ );
+ }
+
+ #[test]
+ fn type_alias() {
+ check_diagnostics(
+ r#"
+pub trait Trait {
+ type Assoc;
+}
+type T = bool<i32>;
+ // ^^^^^ 💡 error: generic arguments are not allowed on builtin types
+impl Trait for () {
+ type Assoc = i32<bool>;
+ // ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
+}
+ "#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index f39738f2fb..08e6e7dced 100644
--- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -1114,6 +1114,25 @@ fn test(x: Option<lib::PrivatelyUninhabited>) {
}
}
+ #[test]
+ fn non_exhaustive_may_be_empty() {
+ check_diagnostics_no_bails(
+ r"
+//- /main.rs crate:main deps:dep
+// In a different crate
+fn empty_match_on_empty_struct<T>(x: dep::UninhabitedStruct) -> T {
+ match x {}
+}
+//- /dep.rs crate:dep
+#[non_exhaustive]
+pub struct UninhabitedStruct {
+ pub never: !,
+ // other fields
+}
+",
+ );
+ }
+
mod false_negatives {
//! The implementation of match checking here is a work in progress. As we roll this out, we
//! prefer false negatives to false positives (ideally there would be no false positives). This
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index a630d3c7c3..2bfdda3565 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -1,5 +1,5 @@
use hir::db::ExpandDatabase;
-use hir::HirFileIdExt;
+use hir::{HirFileIdExt, UnsafetyReason};
use ide_db::text_edit::TextEdit;
use ide_db::{assists::Assist, source_change::SourceChange};
use syntax::{ast, SyntaxNode};
@@ -16,23 +16,35 @@ pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsaf
} else {
DiagnosticCode::RustcHardError("E0133")
};
+ let operation = display_unsafety_reason(d.reason);
Diagnostic::new_with_syntax_node_ptr(
ctx,
code,
- "this operation is unsafe and requires an unsafe function or block",
- d.expr.map(|it| it.into()),
+ format!("{operation} is unsafe and requires an unsafe function or block"),
+ d.node.map(|it| it.into()),
)
.with_fixes(fixes(ctx, d))
}
+fn display_unsafety_reason(reason: UnsafetyReason) -> &'static str {
+ match reason {
+ UnsafetyReason::UnionField => "access to union field",
+ UnsafetyReason::UnsafeFnCall => "call to unsafe function",
+ UnsafetyReason::InlineAsm => "use of inline assembly",
+ UnsafetyReason::RawPtrDeref => "dereference of raw pointer",
+ UnsafetyReason::MutableStatic => "use of mutable static",
+ UnsafetyReason::ExternStatic => "use of extern static",
+ }
+}
+
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option<Vec<Assist>> {
// The fixit will not work correctly for macro expansions, so we don't offer it in that case.
- if d.expr.file_id.is_macro() {
+ if d.node.file_id.is_macro() {
return None;
}
- let root = ctx.sema.db.parse_or_expand(d.expr.file_id);
- let node = d.expr.value.to_node(&root);
+ let root = ctx.sema.db.parse_or_expand(d.node.file_id);
+ let node = d.node.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)?;
@@ -40,7 +52,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option<Vec<Ass
let replacement = format!("unsafe {{ {} }}", node_to_add_unsafe_block.text());
let edit = TextEdit::replace(node_to_add_unsafe_block.text_range(), replacement);
let source_change =
- SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
+ SourceChange::from_text_edit(d.node.file_id.original_file(ctx.sema.db), edit);
Some(vec![fix("add_unsafe", "Add unsafe block", source_change, expr.syntax().text_range())])
}
@@ -110,7 +122,7 @@ fn main() {
let x = &5_usize as *const usize;
unsafe { let _y = *x; }
let _z = *x;
-} //^^💡 error: this operation is unsafe and requires an unsafe function or block
+} //^^💡 error: dereference of raw pointer is unsafe and requires an unsafe function or block
"#,
)
}
@@ -136,9 +148,9 @@ unsafe fn unsafe_fn() {
fn main() {
unsafe_fn();
- //^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
+ //^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
HasUnsafe.unsafe_fn();
- //^^^^^^^^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
+ //^^^^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
unsafe {
unsafe_fn();
HasUnsafe.unsafe_fn();
@@ -162,7 +174,7 @@ static mut STATIC_MUT: Ty = Ty { a: 0 };
fn main() {
let _x = STATIC_MUT.a;
- //^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
+ //^^^^^^^^^^💡 error: use of mutable static is unsafe and requires an unsafe function or block
unsafe {
let _x = STATIC_MUT.a;
}
@@ -184,9 +196,9 @@ extern "C" {
fn main() {
let _x = EXTERN;
- //^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
+ //^^^^^^💡 error: use of extern static is unsafe and requires an unsafe function or block
let _x = EXTERN_MUT;
- //^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
+ //^^^^^^^^^^💡 error: use of mutable static is unsafe and requires an unsafe function or block
unsafe {
let _x = EXTERN;
let _x = EXTERN_MUT;
@@ -234,7 +246,7 @@ extern "rust-intrinsic" {
fn main() {
let _ = bitreverse(12);
let _ = floorf32(12.0);
- //^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
+ //^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
}
"#,
);
@@ -567,7 +579,7 @@ unsafe fn not_safe() -> u8 {
fn main() {
ed2021::safe();
ed2024::not_safe();
- //^^^^^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
+ //^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
}
"#,
)
@@ -591,7 +603,7 @@ unsafe fn foo(p: *mut i32) {
#![warn(unsafe_op_in_unsafe_fn)]
unsafe fn foo(p: *mut i32) {
*p = 123;
- //^^💡 warn: this operation is unsafe and requires an unsafe function or block
+ //^^💡 warn: dereference of raw pointer is unsafe and requires an unsafe function or block
}
"#,
)
@@ -618,17 +630,119 @@ unsafe extern {
fn main() {
f();
g();
- //^^^💡 error: this operation is unsafe and requires an unsafe function or block
+ //^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
h();
- //^^^💡 error: this operation is unsafe and requires an unsafe function or block
+ //^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
let _ = S1;
let _ = S2;
- //^^💡 error: this operation is unsafe and requires an unsafe function or block
+ //^^💡 error: use of extern static is unsafe and requires an unsafe function or block
let _ = S3;
- //^^💡 error: this operation is unsafe and requires an unsafe function or block
+ //^^💡 error: use of extern static is unsafe and requires an unsafe function or block
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_unsafe_diagnostic_when_destructuring_union_with_wildcard() {
+ check_diagnostics(
+ r#"
+union Union { field: i32 }
+fn foo(v: &Union) {
+ let Union { field: _ } = v;
+ let Union { field: _ | _ } = v;
+ Union { field: _ } = *v;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn union_destructuring() {
+ check_diagnostics(
+ r#"
+union Union { field: u8 }
+fn foo(v @ Union { field: _field }: &Union) {
+ // ^^^^^^ error: access to union field is unsafe and requires an unsafe function or block
+ let Union { mut field } = v;
+ // ^^^^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
+ let Union { field: 0..=255 } = v;
+ // ^^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
+ let Union { field: 0
+ // ^💡 error: access to union field is unsafe and requires an unsafe function or block
+ | 1..=255 } = v;
+ // ^^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
+ Union { field } = *v;
+ // ^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
+ match v {
+ Union { field: _field } => {}
+ // ^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
+ }
+ if let Union { field: _field } = v {}
+ // ^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
+ (|&Union { field }| { _ = field; })(v);
+ // ^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn union_field_access() {
+ check_diagnostics(
+ r#"
+union Union { field: u8 }
+fn foo(v: &Union) {
+ v.field;
+ // ^^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
}
"#,
);
}
+
+ #[test]
+ fn inline_asm() {
+ check_diagnostics(
+ r#"
+//- minicore: asm
+fn foo() {
+ core::arch::asm!("");
+ // ^^^^ error: use of inline assembly is unsafe and requires an unsafe function or block
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unsafe_op_in_unsafe_fn_dismissed_in_signature() {
+ check_diagnostics(
+ r#"
+#![warn(unsafe_op_in_unsafe_fn)]
+union Union { field: u32 }
+unsafe fn foo(Union { field: _field }: Union) {}
+ "#,
+ )
+ }
+
+ #[test]
+ fn union_assignment_allowed() {
+ check_diagnostics(
+ r#"
+union Union { field: u32 }
+fn foo(mut v: Union) {
+ v.field = 123;
+ (v.field,) = (123,);
+ *&mut v.field = 123;
+ // ^^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
+}
+struct Struct { field: u32 }
+union Union2 { field: Struct }
+fn bar(mut v: Union2) {
+ v.field.field = 123;
+}
+
+ "#,
+ )
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 93fe9374a3..bfdda53740 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1,12 +1,16 @@
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 hir::{db::ExpandDatabase, CallableKind, ClosureStyle, HirDisplay, HirFileIdExt, InFile, Type};
+use ide_db::{
+ famous_defs::FamousDefs,
+ source_change::{SourceChange, SourceChangeBuilder},
+ text_edit::TextEdit,
+};
use syntax::{
ast::{
self,
edit::{AstNodeEdit, IndentLevel},
- BlockExpr, Expr, ExprStmt,
+ syntax_factory::SyntaxFactory,
+ BlockExpr, Expr, ExprStmt, HasArgList,
},
AstNode, AstPtr, TextSize,
};
@@ -63,6 +67,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assi
let expr_ptr = &InFile { file_id: d.expr_or_pat.file_id, value: expr_ptr };
add_reference(ctx, d, expr_ptr, &mut fixes);
add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes);
+ remove_unnecessary_wrapper(ctx, d, expr_ptr, &mut fixes);
remove_semicolon(ctx, d, expr_ptr, &mut fixes);
str_ref_to_owned(ctx, d, expr_ptr, &mut fixes);
}
@@ -184,6 +189,89 @@ fn add_missing_ok_or_some(
Some(())
}
+fn remove_unnecessary_wrapper(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::TypeMismatch,
+ expr_ptr: &InFile<AstPtr<ast::Expr>>,
+ acc: &mut Vec<Assist>,
+) -> Option<()> {
+ let db = ctx.sema.db;
+ let root = db.parse_or_expand(expr_ptr.file_id);
+ let expr = expr_ptr.value.to_node(&root);
+ let expr = ctx.sema.original_ast_node(expr.clone())?;
+
+ let Expr::CallExpr(call_expr) = expr else {
+ return None;
+ };
+
+ let callable = ctx.sema.resolve_expr_as_callable(&call_expr.expr()?)?;
+ let CallableKind::TupleEnumVariant(variant) = callable.kind() else {
+ return None;
+ };
+
+ let actual_enum = d.actual.as_adt()?.as_enum()?;
+ let famous_defs = FamousDefs(&ctx.sema, ctx.sema.scope(call_expr.syntax())?.krate());
+ let core_option = famous_defs.core_option_Option();
+ let core_result = famous_defs.core_result_Result();
+ if Some(actual_enum) != core_option && Some(actual_enum) != core_result {
+ return None;
+ }
+
+ let inner_type = variant.fields(db).first()?.ty_with_args(db, d.actual.type_arguments());
+ if !d.expected.could_unify_with(db, &inner_type) {
+ return None;
+ }
+
+ let inner_arg = call_expr.arg_list()?.args().next()?;
+
+ let file_id = expr_ptr.file_id.original_file(db);
+ let mut builder = SourceChangeBuilder::new(file_id);
+ let mut editor;
+ match inner_arg {
+ // We're returning `()`
+ Expr::TupleExpr(tup) if tup.fields().next().is_none() => {
+ let parent = call_expr
+ .syntax()
+ .parent()
+ .and_then(Either::<ast::ReturnExpr, ast::StmtList>::cast)?;
+
+ editor = builder.make_editor(parent.syntax());
+ let make = SyntaxFactory::new();
+
+ match parent {
+ Either::Left(ret_expr) => {
+ editor.replace(ret_expr.syntax(), make.expr_return(None).syntax());
+ }
+ Either::Right(stmt_list) => {
+ let new_block = if stmt_list.statements().next().is_none() {
+ make.expr_empty_block()
+ } else {
+ make.block_expr(stmt_list.statements(), None)
+ };
+
+ editor.replace(stmt_list.syntax().parent()?, new_block.syntax());
+ }
+ }
+
+ editor.add_mappings(make.finish_with_mappings());
+ }
+ _ => {
+ editor = builder.make_editor(call_expr.syntax());
+ editor.replace(call_expr.syntax(), inner_arg.syntax());
+ }
+ }
+
+ builder.add_file_edits(file_id, editor);
+ let name = format!("Remove unnecessary {}() wrapper", variant.name(db).as_str());
+ acc.push(fix(
+ "remove_unnecessary_wrapper",
+ &name,
+ builder.finish(),
+ call_expr.syntax().text_range(),
+ ));
+ Some(())
+}
+
fn remove_semicolon(
ctx: &DiagnosticsContext<'_>,
d: &hir::TypeMismatch,
@@ -243,7 +331,7 @@ fn str_ref_to_owned(
#[cfg(test)]
mod tests {
use crate::tests::{
- check_diagnostics, check_diagnostics_with_disabled, check_fix, check_no_fix,
+ check_diagnostics, check_diagnostics_with_disabled, check_fix, check_has_fix, check_no_fix,
};
#[test]
@@ -260,7 +348,7 @@ fn test(_arg: &i32) {}
}
#[test]
- fn test_add_reference_to_int() {
+ fn add_reference_to_int() {
check_fix(
r#"
fn main() {
@@ -278,7 +366,7 @@ fn test(_arg: &i32) {}
}
#[test]
- fn test_add_mutable_reference_to_int() {
+ fn add_mutable_reference_to_int() {
check_fix(
r#"
fn main() {
@@ -296,7 +384,7 @@ fn test(_arg: &mut i32) {}
}
#[test]
- fn test_add_reference_to_array() {
+ fn add_reference_to_array() {
check_fix(
r#"
//- minicore: coerce_unsized
@@ -315,7 +403,7 @@ fn test(_arg: &[i32]) {}
}
#[test]
- fn test_add_reference_with_autoderef() {
+ fn add_reference_with_autoderef() {
check_fix(
r#"
//- minicore: coerce_unsized, deref
@@ -348,7 +436,7 @@ fn test(_arg: &Bar) {}
}
#[test]
- fn test_add_reference_to_method_call() {
+ fn add_reference_to_method_call() {
check_fix(
r#"
fn main() {
@@ -372,7 +460,7 @@ impl Test {
}
#[test]
- fn test_add_reference_to_let_stmt() {
+ fn add_reference_to_let_stmt() {
check_fix(
r#"
fn main() {
@@ -388,7 +476,7 @@ fn main() {
}
#[test]
- fn test_add_reference_to_macro_call() {
+ fn add_reference_to_macro_call() {
check_fix(
r#"
macro_rules! thousand {
@@ -416,7 +504,7 @@ fn main() {
}
#[test]
- fn test_add_mutable_reference_to_let_stmt() {
+ fn add_mutable_reference_to_let_stmt() {
check_fix(
r#"
fn main() {
@@ -432,29 +520,6 @@ fn main() {
}
#[test]
- fn test_wrap_return_type_option() {
- check_fix(
- r#"
-//- minicore: option, result
-fn div(x: i32, y: i32) -> Option<i32> {
- if y == 0 {
- return None;
- }
- x / y$0
-}
-"#,
- r#"
-fn div(x: i32, y: i32) -> Option<i32> {
- if y == 0 {
- return None;
- }
- Some(x / y)
-}
-"#,
- );
- }
-
- #[test]
fn const_generic_type_mismatch() {
check_diagnostics(
r#"
@@ -487,59 +552,82 @@ fn div(x: i32, y: i32) -> Option<i32> {
}
#[test]
- fn test_wrap_return_type_option_tails() {
+ fn wrap_return_type() {
+ check_fix(
+ r#"
+//- minicore: option, result
+fn div(x: i32, y: i32) -> Result<i32, ()> {
+ if y == 0 {
+ return Err(());
+ }
+ x / y$0
+}
+"#,
+ r#"
+fn div(x: i32, y: i32) -> Result<i32, ()> {
+ if y == 0 {
+ return Err(());
+ }
+ Ok(x / y)
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn wrap_return_type_option() {
check_fix(
r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> Option<i32> {
if y == 0 {
- Some(0)
- } else if true {
- 100$0
- } else {
- None
+ return None;
}
+ x / y$0
}
"#,
r#"
fn div(x: i32, y: i32) -> Option<i32> {
if y == 0 {
- Some(0)
- } else if true {
- Some(100)
- } else {
- None
+ return None;
}
+ Some(x / y)
}
"#,
);
}
#[test]
- fn test_wrap_return_type() {
+ fn wrap_return_type_option_tails() {
check_fix(
r#"
//- minicore: option, result
-fn div(x: i32, y: i32) -> Result<i32, ()> {
+fn div(x: i32, y: i32) -> Option<i32> {
if y == 0 {
- return Err(());
+ Some(0)
+ } else if true {
+ 100$0
+ } else {
+ None
}
- x / y$0
}
"#,
r#"
-fn div(x: i32, y: i32) -> Result<i32, ()> {
+fn div(x: i32, y: i32) -> Option<i32> {
if y == 0 {
- return Err(());
+ Some(0)
+ } else if true {
+ Some(100)
+ } else {
+ None
}
- Ok(x / y)
}
"#,
);
}
#[test]
- fn test_wrap_return_type_handles_generic_functions() {
+ fn wrap_return_type_handles_generic_functions() {
check_fix(
r#"
//- minicore: option, result
@@ -562,7 +650,7 @@ fn div<T>(x: T) -> Result<T, i32> {
}
#[test]
- fn test_wrap_return_type_handles_type_aliases() {
+ fn wrap_return_type_handles_type_aliases() {
check_fix(
r#"
//- minicore: option, result
@@ -589,7 +677,7 @@ fn div(x: i32, y: i32) -> MyResult<i32> {
}
#[test]
- fn test_wrapped_unit_as_block_tail_expr() {
+ fn wrapped_unit_as_block_tail_expr() {
check_fix(
r#"
//- minicore: result
@@ -619,7 +707,7 @@ fn foo() -> Result<(), ()> {
}
#[test]
- fn test_wrapped_unit_as_return_expr() {
+ fn wrapped_unit_as_return_expr() {
check_fix(
r#"
//- minicore: result
@@ -642,7 +730,7 @@ fn foo(b: bool) -> Result<(), String> {
}
#[test]
- fn test_in_const_and_static() {
+ fn wrap_in_const_and_static() {
check_fix(
r#"
//- minicore: option, result
@@ -664,7 +752,7 @@ const _: Option<()> = {Some(())};
}
#[test]
- fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
+ fn wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
check_no_fix(
r#"
//- minicore: option, result
@@ -674,7 +762,7 @@ fn foo() -> Result<(), i32> { 0$0 }
}
#[test]
- fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
+ fn wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
check_no_fix(
r#"
//- minicore: option, result
@@ -686,6 +774,254 @@ fn foo() -> SomeOtherEnum { 0$0 }
}
#[test]
+ fn unwrap_return_type() {
+ check_fix(
+ r#"
+//- minicore: option, result
+fn div(x: i32, y: i32) -> i32 {
+ if y == 0 {
+ panic!();
+ }
+ Ok(x / y)$0
+}
+"#,
+ r#"
+fn div(x: i32, y: i32) -> i32 {
+ if y == 0 {
+ panic!();
+ }
+ x / y
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unwrap_return_type_option() {
+ check_fix(
+ r#"
+//- minicore: option, result
+fn div(x: i32, y: i32) -> i32 {
+ if y == 0 {
+ panic!();
+ }
+ Some(x / y)$0
+}
+"#,
+ r#"
+fn div(x: i32, y: i32) -> i32 {
+ if y == 0 {
+ panic!();
+ }
+ x / y
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unwrap_return_type_option_tails() {
+ check_fix(
+ r#"
+//- minicore: option, result
+fn div(x: i32, y: i32) -> i32 {
+ if y == 0 {
+ 42
+ } else if true {
+ Some(100)$0
+ } else {
+ 0
+ }
+}
+"#,
+ r#"
+fn div(x: i32, y: i32) -> i32 {
+ if y == 0 {
+ 42
+ } else if true {
+ 100
+ } else {
+ 0
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unwrap_return_type_option_tail_unit() {
+ check_fix(
+ r#"
+//- minicore: option, result
+fn div(x: i32, y: i32) {
+ if y == 0 {
+ panic!();
+ }
+
+ Ok(())$0
+}
+"#,
+ r#"
+fn div(x: i32, y: i32) {
+ if y == 0 {
+ panic!();
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unwrap_return_type_handles_generic_functions() {
+ check_fix(
+ r#"
+//- minicore: option, result
+fn div<T>(x: T) -> T {
+ if x == 0 {
+ panic!();
+ }
+ $0Ok(x)
+}
+"#,
+ r#"
+fn div<T>(x: T) -> T {
+ if x == 0 {
+ panic!();
+ }
+ x
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unwrap_return_type_handles_type_aliases() {
+ check_fix(
+ r#"
+//- minicore: option, result
+type MyResult<T> = T;
+
+fn div(x: i32, y: i32) -> MyResult<i32> {
+ if y == 0 {
+ panic!();
+ }
+ Ok(x $0/ y)
+}
+"#,
+ r#"
+type MyResult<T> = T;
+
+fn div(x: i32, y: i32) -> MyResult<i32> {
+ if y == 0 {
+ panic!();
+ }
+ x / y
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unwrap_tail_expr() {
+ check_fix(
+ r#"
+//- minicore: result
+fn foo() -> () {
+ println!("Hello, world!");
+ Ok(())$0
+}
+ "#,
+ r#"
+fn foo() -> () {
+ println!("Hello, world!");
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn unwrap_to_empty_block() {
+ check_fix(
+ r#"
+//- minicore: result
+fn foo() -> () {
+ Ok(())$0
+}
+ "#,
+ r#"
+fn foo() -> () {}
+ "#,
+ );
+ }
+
+ #[test]
+ fn unwrap_to_return_expr() {
+ check_has_fix(
+ r#"
+//- minicore: result
+fn foo(b: bool) -> () {
+ if b {
+ return $0Ok(());
+ }
+
+ panic!("oh dear");
+}"#,
+ r#"
+fn foo(b: bool) -> () {
+ if b {
+ return;
+ }
+
+ panic!("oh dear");
+}"#,
+ );
+ }
+
+ #[test]
+ fn unwrap_in_const_and_static() {
+ check_fix(
+ r#"
+//- minicore: option, result
+static A: () = {Some(($0))};
+ "#,
+ r#"
+static A: () = {};
+ "#,
+ );
+ check_fix(
+ r#"
+//- minicore: option, result
+const _: () = {Some(($0))};
+ "#,
+ r#"
+const _: () = {};
+ "#,
+ );
+ }
+
+ #[test]
+ fn unwrap_return_type_not_applicable_when_inner_type_does_not_match_return_type() {
+ check_no_fix(
+ r#"
+//- minicore: result
+fn foo() -> i32 { $0Ok(()) }
+"#,
+ );
+ }
+
+ #[test]
+ fn unwrap_return_type_not_applicable_when_wrapper_type_is_not_result_or_option() {
+ check_no_fix(
+ r#"
+//- minicore: option, result
+enum SomeOtherEnum { Ok(i32), Err(String) }
+
+fn foo() -> i32 { SomeOtherEnum::Ok($042) }
+"#,
+ );
+ }
+
+ #[test]
fn remove_semicolon() {
check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
}
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index 81cb452121..4ab649cc16 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -167,9 +167,9 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -
}
let method_name = call.name_ref()?;
- let assoc_func_call = format!("{receiver_type_adt_name}::{method_name}()");
+ let assoc_func_path = format!("{receiver_type_adt_name}::{method_name}");
- let assoc_func_call = make::expr_path(make::path_from_text(&assoc_func_call));
+ let assoc_func_path = make::expr_path(make::path_from_text(&assoc_func_path));
let args: Vec<_> = if need_to_take_receiver_as_first_arg {
std::iter::once(receiver).chain(call.arg_list()?.args()).collect()
@@ -178,7 +178,7 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -
};
let args = make::arg_list(args);
- let assoc_func_call_expr_string = make::expr_call(assoc_func_call, args).to_string();
+ let assoc_func_call_expr_string = make::expr_call(assoc_func_path, args).to_string();
let file_id = ctx.sema.original_range_opt(call.receiver()?.syntax())?.file_id;
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 1f1b6478d3..ad33956908 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -27,6 +27,7 @@ mod handlers {
pub(crate) mod await_outside_of_async;
pub(crate) mod break_outside_of_loop;
pub(crate) mod expected_function;
+ pub(crate) mod generic_args_prohibited;
pub(crate) mod inactive_code;
pub(crate) mod incoherent_impl;
pub(crate) mod incorrect_case;
@@ -468,6 +469,7 @@ pub fn semantic_diagnostics(
Some(it) => it,
None => continue,
},
+ AnyDiagnostic::GenericArgsProhibited(d) => handlers::generic_args_prohibited::generic_args_prohibited(&ctx, &d)
};
res.push(d)
}
@@ -542,7 +544,13 @@ fn handle_diag_from_macros(
sema.db.lookup_intern_syntax_context(span.ctx).outer_expn.is_some_and(|expansion| {
let macro_call =
sema.db.lookup_intern_macro_call(expansion.as_macro_file().macro_call_id);
+ // We don't want to show diagnostics for non-local macros at all, but proc macros authors
+ // seem to rely on being able to emit non-warning-free code, so we don't want to show warnings
+ // for them even when the proc macro comes from the same workspace (in rustc that's not a
+ // problem because it doesn't have the concept of workspaces, and proc macros always reside
+ // in a different crate).
!Crate::from(macro_call.def.krate).origin(sema.db).is_local()
+ || !macro_call.def.kind.is_declarative()
})
}) {
// Disable suggestions for external macros, they'll change library code and it's just bad.
diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs
index 6569f0f555..4edc3633fb 100644
--- a/crates/ide-ssr/src/matching.rs
+++ b/crates/ide-ssr/src/matching.rs
@@ -359,8 +359,8 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
)?;
self.attempt_match_opt(
phase,
- pattern_segment.param_list(),
- code_segment.param_list(),
+ pattern_segment.parenthesized_arg_list(),
+ code_segment.parenthesized_arg_list(),
)?;
}
if matches!(phase, Phase::Second(_)) {
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 0986d5542c..ea18b89c5c 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -288,7 +288,7 @@ m!(ab$0c);
*abc*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -301,7 +301,7 @@ m!(ab$0c);
---
```rust
- test::module
+ ra_test_fixture::module
```
```rust
@@ -327,7 +327,7 @@ fn main() {
"#,
expect![[r#"
*foo*
- test
+ ra_test_fixture
pub fn foo() -> u32
"#]],
@@ -487,7 +487,7 @@ fn main() {
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::S2",
+ mod_path: "ra_test_fixture::S2",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -500,7 +500,7 @@ fn main() {
},
},
HoverGotoTypeData {
- mod_path: "test::S",
+ mod_path: "ra_test_fixture::S",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -583,7 +583,7 @@ fn main() { let foo_test = fo$0o(); }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -605,7 +605,7 @@ fn main() { f$0oo(); }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -627,7 +627,7 @@ fn main() { m::f$0oo(); }
*foo*
```rust
- test::m
+ ra_test_fixture::m
```
```rust
@@ -649,7 +649,7 @@ fn main() { fo$0o(); }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -667,7 +667,7 @@ fn main() { fo$0o(); }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -691,7 +691,7 @@ fn main() { let foo_test = fo$0o(); }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -715,7 +715,7 @@ fn main() { }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -743,7 +743,7 @@ fn main() { }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -776,7 +776,7 @@ fn main() { }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -801,7 +801,7 @@ struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }
*field_a*
```rust
- test::Foo
+ ra_test_fixture::Foo
```
```rust
@@ -830,7 +830,7 @@ fn main() {
*field_a*
```rust
- test::Foo
+ ra_test_fixture::Foo
```
```rust
@@ -852,7 +852,7 @@ fn main() {
*field_a*
```rust
- test::Foo
+ ra_test_fixture::Foo
```
```rust
@@ -880,7 +880,7 @@ fn main() {
*0*
```rust
- test::Foo
+ ra_test_fixture::Foo
```
```rust
@@ -900,7 +900,7 @@ fn foo(foo: Foo) {
*0*
```rust
- test::Foo
+ ra_test_fixture::Foo
```
```rust
@@ -920,7 +920,7 @@ struct Foo$0(pub u32) where u32: Copy;
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -946,7 +946,7 @@ struct Foo$0 { field: u32 }
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -968,7 +968,7 @@ struct Foo$0 where u32: Copy { field: u32 }
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -998,7 +998,7 @@ fn hover_record_struct_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1023,7 +1023,7 @@ fn hover_record_struct_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1046,7 +1046,7 @@ fn hover_record_struct_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1072,7 +1072,7 @@ fn hover_record_struct_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1093,7 +1093,7 @@ fn hover_record_struct_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1116,7 +1116,7 @@ fn hover_record_struct_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1141,7 +1141,7 @@ fn hover_record_variant_limit() {
*A*
```rust
- test::Foo
+ ra_test_fixture::Foo
```
```rust
@@ -1162,7 +1162,7 @@ fn hover_record_variant_limit() {
*A*
```rust
- test::Foo
+ ra_test_fixture::Foo
```
```rust
@@ -1183,7 +1183,7 @@ fn hover_record_variant_limit() {
*A*
```rust
- test::Foo
+ ra_test_fixture::Foo
```
```rust
@@ -1204,7 +1204,7 @@ fn hover_record_variant_limit() {
*A*
```rust
- test::Foo
+ ra_test_fixture::Foo
```
```rust
@@ -1225,7 +1225,7 @@ fn hover_record_variant_limit() {
*A*
```rust
- test::Foo
+ ra_test_fixture::Foo
```
```rust
@@ -1248,7 +1248,7 @@ fn hover_enum_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1270,7 +1270,7 @@ fn hover_enum_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1292,7 +1292,7 @@ fn hover_enum_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1311,7 +1311,7 @@ fn hover_enum_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1339,7 +1339,7 @@ fn hover_enum_limit() {
*Enum*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1371,7 +1371,7 @@ fn hover_union_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1393,7 +1393,7 @@ fn hover_union_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1415,7 +1415,7 @@ fn hover_union_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1434,7 +1434,7 @@ fn hover_union_limit() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1458,7 +1458,7 @@ struct Foo$0 where u32: Copy;
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1484,7 +1484,7 @@ type Fo$0o: Trait = S where T: Trait;
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1504,7 +1504,7 @@ fn hover_const_static() {
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1522,7 +1522,7 @@ const foo$0: u32 = {
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1540,7 +1540,7 @@ const foo$0: u32 = {
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1555,7 +1555,7 @@ const foo$0: u32 = {
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1573,7 +1573,7 @@ const foo$0: u32 = {
*BAR*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1591,7 +1591,7 @@ fn hover_unsigned_max_const() {
*A*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1612,7 +1612,7 @@ fn hover_eval_complex_constants() {
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -1667,16 +1667,16 @@ use Option::Some;
fn main() { So$0me(12); }
"#,
expect![[r#"
- *Some*
+ *Some*
- ```rust
- test::Option
- ```
+ ```rust
+ ra_test_fixture::Option
+ ```
- ```rust
- Some(T)
- ```
- "#]],
+ ```rust
+ Some(T)
+ ```
+ "#]],
);
check(
@@ -1711,20 +1711,20 @@ enum Option<T> {
}
"#,
expect![[r#"
- *None*
+ *None*
- ```rust
- test::Option
- ```
+ ```rust
+ ra_test_fixture::Option
+ ```
- ```rust
- None
- ```
+ ```rust
+ None
+ ```
- ---
+ ---
- The None variant
- "#]],
+ The None variant
+ "#]],
);
check(
@@ -1738,20 +1738,20 @@ fn main() {
}
"#,
expect![[r#"
- *Some*
+ *Some*
- ```rust
- test::Option
- ```
+ ```rust
+ ra_test_fixture::Option
+ ```
- ```rust
- Some(T)
- ```
+ ```rust
+ Some(T)
+ ```
- ---
+ ---
- The Some variant
- "#]],
+ The Some variant
+ "#]],
);
}
@@ -1885,7 +1885,7 @@ fn main() { let foo_test = wrapper::Thing::new$0(); }
*new*
```rust
- test::wrapper::Thing
+ ra_test_fixture::wrapper::Thing
```
```rust
@@ -1916,7 +1916,7 @@ fn main() {
*C*
```rust
- test::X
+ ra_test_fixture::X
```
```rust
@@ -1936,18 +1936,18 @@ impl Thing {
}
"#,
expect![[r#"
- *Self*
+ *Self*
- ```rust
- test
- ```
+ ```rust
+ ra_test_fixture
+ ```
- ```rust
- struct Thing {
- x: u32,
- }
- ```
- "#]],
+ ```rust
+ struct Thing {
+ x: u32,
+ }
+ ```
+ "#]],
);
check_hover_fields_limit(
None,
@@ -1958,16 +1958,16 @@ impl Thing {
}
"#,
expect![[r#"
- *Self*
+ *Self*
- ```rust
- test
- ```
+ ```rust
+ ra_test_fixture
+ ```
- ```rust
- struct Thing
- ```
- "#]],
+ ```rust
+ struct Thing
+ ```
+ "#]],
);
check(
r#"
@@ -1977,18 +1977,18 @@ impl Thing {
}
"#,
expect![[r#"
- *Self*
+ *Self*
- ```rust
- test
- ```
+ ```rust
+ ra_test_fixture
+ ```
- ```rust
- struct Thing {
- x: u32,
- }
- ```
- "#]],
+ ```rust
+ struct Thing {
+ x: u32,
+ }
+ ```
+ "#]],
);
check(
r#"
@@ -2001,7 +2001,7 @@ impl Thing {
*Self*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2022,7 +2022,7 @@ impl Thing {
*Self*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2042,7 +2042,7 @@ impl usize {
*Self*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2060,7 +2060,7 @@ impl fn() -> usize {
*Self*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2103,7 +2103,7 @@ fn f() { fo$0o!(); }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2128,7 +2128,7 @@ fn f() { fo$0o!(); }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2172,7 +2172,7 @@ id! {
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2194,7 +2194,7 @@ fn foo$0() {}
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2252,7 +2252,7 @@ fn foo() { let a = id!([0u32, bar$0()] ); }
*bar*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2278,7 +2278,7 @@ fn foo() {
*bar*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2302,7 +2302,7 @@ fn foo(Foo { b$0ar }: &Foo) {}
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Bar",
+ mod_path: "ra_test_fixture::Bar",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -2334,7 +2334,7 @@ fn bar() { fo$0o(); }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2356,7 +2356,7 @@ fn test_hover_function_show_qualifiers() {
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2370,7 +2370,7 @@ fn test_hover_function_show_qualifiers() {
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2385,7 +2385,7 @@ fn test_hover_function_show_qualifiers() {
*foo*
```rust
- test::m
+ ra_test_fixture::m
```
```rust
@@ -2403,7 +2403,7 @@ fn test_hover_function_show_types() {
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2426,7 +2426,7 @@ fn main() { foo$0; }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2446,7 +2446,7 @@ fn main() { foo$0; }
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2464,7 +2464,7 @@ fn test_hover_function_pointer_show_identifiers() {
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2486,7 +2486,7 @@ fn test_hover_function_pointer_no_identifier() {
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2601,16 +2601,16 @@ mod my { pub struct Bar; }
fn my() {}
"#,
expect![[r#"
- *my*
+ *my*
- ```rust
- test
- ```
+ ```rust
+ ra_test_fixture
+ ```
- ```rust
- mod my
- ```
- "#]],
+ ```rust
+ mod my
+ ```
+ "#]],
);
}
@@ -2636,7 +2636,7 @@ fn foo() { let bar = Ba$0r; }
*Bar*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2672,7 +2672,7 @@ fn foo() { let bar = Ba$0r; }
*Bar*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2701,7 +2701,7 @@ fn foo() { let bar = Ba$0r; }
*Bar*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2729,7 +2729,7 @@ pub struct B$0ar
*Bar*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2760,7 +2760,7 @@ pub struct B$0ar
*Bar*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2809,7 +2809,7 @@ pub fn fo$0o() {}
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2853,7 +2853,7 @@ fn test_hover_layout_of_variant() {
*Variant1*
```rust
- test::Foo
+ ra_test_fixture::Foo
```
```rust
@@ -2878,7 +2878,7 @@ fn test_hover_layout_of_variant_generic() {
*None*
```rust
- test::Option
+ ra_test_fixture::Option
```
```rust
@@ -2899,7 +2899,7 @@ struct S$0<T>(core::marker::PhantomData<T>);
*S*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2924,7 +2924,7 @@ fn test_hover_layout_of_enum() {
*Foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -2949,7 +2949,7 @@ fn test_hover_no_memory_layout() {
*field_a*
```rust
- test::Foo
+ ra_test_fixture::Foo
```
```rust
@@ -3003,7 +3003,7 @@ fn foo() { let bar = Bar; bar.fo$0o(); }
*foo*
```rust
- test::Bar
+ ra_test_fixture::Bar
```
```rust
@@ -3041,7 +3041,7 @@ fn foo() { let bar = Bar; bar.fo$0o(); }
*foo*
```rust
- test::Bar
+ ra_test_fixture::Bar
```
```rust
@@ -3069,7 +3069,7 @@ fn main() { let foo_test = unsafe { fo$0o(1, 2, 3); } }
*foo*
```rust
- test::<extern>
+ ra_test_fixture::<extern>
```
```rust
@@ -3267,7 +3267,7 @@ fn main() { let s$0t = S{ f1:0 }; }
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::S",
+ mod_path: "ra_test_fixture::S",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3300,7 +3300,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; }
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Arg",
+ mod_path: "ra_test_fixture::Arg",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3313,7 +3313,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; }
},
},
HoverGotoTypeData {
- mod_path: "test::S",
+ mod_path: "ra_test_fixture::S",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3359,7 +3359,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Arg",
+ mod_path: "ra_test_fixture::Arg",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3372,7 +3372,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
},
},
HoverGotoTypeData {
- mod_path: "test::S",
+ mod_path: "ra_test_fixture::S",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3408,7 +3408,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::A",
+ mod_path: "ra_test_fixture::A",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3421,7 +3421,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
},
},
HoverGotoTypeData {
- mod_path: "test::B",
+ mod_path: "ra_test_fixture::B",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3434,7 +3434,7 @@ fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
},
},
HoverGotoTypeData {
- mod_path: "test::M::C",
+ mod_path: "ra_test_fixture::M::C",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3468,7 +3468,7 @@ fn main() { let s$0t = foo(); }
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3502,7 +3502,7 @@ fn main() { let s$0t = foo(); }
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3515,7 +3515,7 @@ fn main() { let s$0t = foo(); }
},
},
HoverGotoTypeData {
- mod_path: "test::S",
+ mod_path: "ra_test_fixture::S",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3549,7 +3549,7 @@ fn main() { let s$0t = foo(); }
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Bar",
+ mod_path: "ra_test_fixture::Bar",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3562,7 +3562,7 @@ fn main() { let s$0t = foo(); }
},
},
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3599,7 +3599,7 @@ fn main() { let s$0t = foo(); }
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Bar",
+ mod_path: "ra_test_fixture::Bar",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3612,7 +3612,7 @@ fn main() { let s$0t = foo(); }
},
},
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3625,7 +3625,7 @@ fn main() { let s$0t = foo(); }
},
},
HoverGotoTypeData {
- mod_path: "test::S1",
+ mod_path: "ra_test_fixture::S1",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3638,7 +3638,7 @@ fn main() { let s$0t = foo(); }
},
},
HoverGotoTypeData {
- mod_path: "test::S2",
+ mod_path: "ra_test_fixture::S2",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3669,7 +3669,7 @@ fn foo(ar$0g: &impl Foo) {}
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3703,7 +3703,7 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {}
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Bar",
+ mod_path: "ra_test_fixture::Bar",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3716,7 +3716,7 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {}
},
},
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3729,7 +3729,7 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {}
},
},
HoverGotoTypeData {
- mod_path: "test::S",
+ mod_path: "ra_test_fixture::S",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3816,7 +3816,7 @@ fn foo(ar$0g: &impl Foo<S>) {}
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3829,7 +3829,7 @@ fn foo(ar$0g: &impl Foo<S>) {}
},
},
HoverGotoTypeData {
- mod_path: "test::S",
+ mod_path: "ra_test_fixture::S",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3866,7 +3866,7 @@ fn main() { let s$0t = foo(); }
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::B",
+ mod_path: "ra_test_fixture::B",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3879,7 +3879,7 @@ fn main() { let s$0t = foo(); }
},
},
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3892,7 +3892,7 @@ fn main() { let s$0t = foo(); }
},
},
HoverGotoTypeData {
- mod_path: "test::S",
+ mod_path: "ra_test_fixture::S",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3923,7 +3923,7 @@ fn foo(ar$0g: &dyn Foo) {}
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3955,7 +3955,7 @@ fn foo(ar$0g: &dyn Foo<S>) {}
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -3968,7 +3968,7 @@ fn foo(ar$0g: &dyn Foo<S>) {}
},
},
HoverGotoTypeData {
- mod_path: "test::S",
+ mod_path: "ra_test_fixture::S",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -4003,7 +4003,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::B",
+ mod_path: "ra_test_fixture::B",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -4016,7 +4016,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
},
},
HoverGotoTypeData {
- mod_path: "test::DynTrait",
+ mod_path: "ra_test_fixture::DynTrait",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -4029,7 +4029,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
},
},
HoverGotoTypeData {
- mod_path: "test::ImplTrait",
+ mod_path: "ra_test_fixture::ImplTrait",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -4042,7 +4042,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
},
},
HoverGotoTypeData {
- mod_path: "test::S",
+ mod_path: "ra_test_fixture::S",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -4084,7 +4084,7 @@ fn main() { let s$0t = test().get(); }
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -4117,7 +4117,7 @@ impl<const BAR: Bar> Foo<BAR$0> {}
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Bar",
+ mod_path: "ra_test_fixture::Bar",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -4149,7 +4149,7 @@ fn foo<T: Foo>(t: T$0){}
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -4182,7 +4182,7 @@ impl Foo {
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -4257,7 +4257,7 @@ fn main() {
---
```rust
- test::S
+ ra_test_fixture::S
```
```rust
@@ -4282,7 +4282,7 @@ struct S$0T<const C: usize = 1, T = Foo>(T);
*ST*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -4307,7 +4307,7 @@ struct S$0T<const C: usize = {40 + 2}, T = Foo>(T);
*ST*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -4333,7 +4333,7 @@ struct S$0T<const C: usize = VAL, T = Foo>(T);
*ST*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -4527,21 +4527,21 @@ mod Foo$0 {
}
"#,
expect![[r#"
- *Foo*
+ *Foo*
- ```rust
- test
- ```
+ ```rust
+ ra_test_fixture
+ ```
- ```rust
- mod Foo
- ```
+ ```rust
+ mod Foo
+ ```
- ---
+ ---
- Be quick;
- time is mana
- "#]],
+ Be quick;
+ time is mana
+ "#]],
);
}
@@ -4558,21 +4558,21 @@ mod Foo$0 {
}
"#,
expect![[r#"
- *Foo*
+ *Foo*
- ```rust
- test
- ```
+ ```rust
+ ra_test_fixture
+ ```
- ```rust
- mod Foo
- ```
+ ```rust
+ mod Foo
+ ```
- ---
+ ---
- Be quick;
- time is mana
- "#]],
+ Be quick;
+ time is mana
+ "#]],
);
}
@@ -4592,7 +4592,7 @@ fn foo$0() {}
*foo*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -4902,7 +4902,7 @@ type Fo$0o2 = Foo<2>;
*Foo2*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -4948,7 +4948,7 @@ enum E {
*A*
```rust
- test::E
+ ra_test_fixture::E
```
```rust
@@ -4977,7 +4977,7 @@ enum E {
*A*
```rust
- test::E
+ ra_test_fixture::E
```
```rust
@@ -5007,7 +5007,7 @@ enum E {
*B*
```rust
- test::E
+ ra_test_fixture::E
```
```rust
@@ -5037,7 +5037,7 @@ enum E {
*B*
```rust
- test::E
+ ra_test_fixture::E
```
```rust
@@ -5074,7 +5074,7 @@ fn main() {
*B*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5111,7 +5111,7 @@ fn main() {
*AA*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5138,7 +5138,7 @@ fn main() {
*B*
```rust
- test::T
+ ra_test_fixture::T
```
```rust
@@ -5167,7 +5167,7 @@ fn main() {
*B*
```rust
- test::T
+ ra_test_fixture::T
```
```rust
@@ -5199,7 +5199,7 @@ fn main() {
*B*
```rust
- test::T
+ ra_test_fixture::T
```
```rust
@@ -5222,7 +5222,7 @@ const FOO$0: usize = 1 << 3;
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5243,7 +5243,7 @@ const FOO$0: usize = (1 << 3) + (1 << 2);
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5265,7 +5265,7 @@ const FOO$0: usize = 2 - 3;
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5287,7 +5287,7 @@ const FOO$0: i32 = 2 - 3;
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5308,7 +5308,7 @@ const FOO$0: &str = "bar";
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5330,7 +5330,7 @@ const FOO$0: char = 'a';
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5352,7 +5352,7 @@ const FOO$0: char = '\x61';
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5374,7 +5374,7 @@ const FOO$0: u8 = b'a';
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5396,7 +5396,7 @@ const FOO$0: u8 = b'\x61';
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5418,7 +5418,7 @@ const FOO$0: u8 = b'\x61';
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5440,7 +5440,7 @@ const FOO$0: f32 = 1f32;
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5462,7 +5462,7 @@ const FOO$0: &i32 = &2;
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5484,7 +5484,7 @@ const FOO$0: f64 = 1.0f64;
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5512,7 +5512,7 @@ const FOO$0: f64 = expf64(1.2);
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5530,7 +5530,7 @@ const FOO$0: f32 = 1.9999999403953552_f32;
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5552,7 +5552,7 @@ const FOO$0: f16 = -1.0f16;
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5573,7 +5573,7 @@ const FOO$0: f128 = -1.0f128;
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5604,7 +5604,7 @@ const FOO$0: Enum = VX;
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5621,7 +5621,7 @@ const FOO$0: Option<i32> = Some(2);
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5638,7 +5638,7 @@ const FOO$0: Option<&i32> = Some(2).as_ref();
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5661,7 +5661,7 @@ const FOO$0: &dyn Debug = &2i32;
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5682,7 +5682,7 @@ const FOO$0: &[i32] = &[1, 2, 3 + 4];
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5699,7 +5699,7 @@ const FOO$0: &[i32; 5] = &[12; 5];
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5720,7 +5720,7 @@ const FOO$0: (&i32, &[i32], &i32) = {
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5743,7 +5743,7 @@ const FOO$0: Tree = {
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5763,7 +5763,7 @@ const FOO$0: &S<[u8]> = core::mem::transmute::<&[u8], _>(&[1, 2, 3]);
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5783,7 +5783,7 @@ const FOO$0: &str = "foo";
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5806,7 +5806,7 @@ const FOO$0: X = X {
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5825,7 +5825,7 @@ const FOO$0: (&str, &str) = {
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5856,7 +5856,7 @@ fn test() {
*FOO*
```rust
- test::S
+ ra_test_fixture::S
```
```rust
@@ -5883,7 +5883,7 @@ fn foo() {
*FOO*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5912,7 +5912,7 @@ fn foo(e: E) {
*A*
```rust
- test::E
+ ra_test_fixture::E
```
```rust
@@ -5942,7 +5942,7 @@ pub fn the_function() -> AA {
*CONST*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -5984,20 +5984,20 @@ mod foo$0;
//! For the horde!
"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- test
- ```
+ ```rust
+ ra_test_fixture
+ ```
- ```rust
- mod foo
- ```
+ ```rust
+ mod foo
+ ```
- ---
+ ---
- For the horde!
- "#]],
+ For the horde!
+ "#]],
);
}
@@ -6013,20 +6013,20 @@ mod foo {
use foo::bar::{self$0};
"#,
expect![[r#"
- *self*
+ *self*
- ```rust
- test::foo
- ```
+ ```rust
+ ra_test_fixture::foo
+ ```
- ```rust
- mod bar
- ```
+ ```rust
+ mod bar
+ ```
- ---
+ ---
- But this should appear
- "#]],
+ But this should appear
+ "#]],
)
}
@@ -6166,7 +6166,7 @@ fn main() {
*bar*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -6200,7 +6200,7 @@ pub fn gimme() -> theitem::TheItem {
*[`TheItem`]*
```rust
- test::theitem
+ ra_test_fixture::theitem
```
```rust
@@ -6248,7 +6248,7 @@ impl T1 for Foo {
*Bar*
```rust
- test::t2::T2
+ ra_test_fixture::t2::T2
```
```rust
@@ -6270,7 +6270,7 @@ trait A {
*Assoc*
```rust
- test::A
+ ra_test_fixture::A
```
```rust
@@ -6291,7 +6291,7 @@ trait A {
*Assoc*
```rust
- test::A
+ ra_test_fixture::A
```
```rust
@@ -6310,7 +6310,7 @@ trait A where
*Assoc*
```rust
- test::A
+ ra_test_fixture::A
```
```rust
@@ -6572,12 +6572,12 @@ fn hover_rename() {
use self as foo$0;
"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- extern crate test
- ```
- "#]],
+ ```rust
+ extern crate ra_test_fixture
+ ```
+ "#]],
);
check(
r#"
@@ -6585,16 +6585,16 @@ mod bar {}
use bar::{self as foo$0};
"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- test
- ```
+ ```rust
+ ra_test_fixture
+ ```
- ```rust
- mod bar
- ```
- "#]],
+ ```rust
+ mod bar
+ ```
+ "#]],
);
check(
r#"
@@ -6603,24 +6603,24 @@ mod bar {
}
"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- extern crate test
- ```
- "#]],
+ ```rust
+ extern crate ra_test_fixture
+ ```
+ "#]],
);
check(
r#"
use crate as foo$0;
"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- extern crate test
- ```
- "#]],
+ ```rust
+ extern crate ra_test_fixture
+ ```
+ "#]],
);
}
@@ -6645,7 +6645,7 @@ identity!{
*Copy*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -6669,7 +6669,7 @@ struct Foo;
*Copy*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -6691,7 +6691,7 @@ struct Foo;
*Copy*
```rust
- test::foo
+ ra_test_fixture::foo
```
```rust
@@ -6910,7 +6910,7 @@ fn foo() {
GoToType(
[
HoverGotoTypeData {
- mod_path: "test::Foo",
+ mod_path: "ra_test_fixture::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -7110,7 +7110,7 @@ foo_macro!(
*[`Foo`]*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -7119,7 +7119,7 @@ foo_macro!(
---
- Doc comment for [`Foo`](https://docs.rs/test/*/test/struct.Foo.html)
+ Doc comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/struct.Foo.html)
"#]],
);
}
@@ -7135,7 +7135,7 @@ pub struct Foo(i32);
*[`Foo`]*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -7144,7 +7144,7 @@ pub struct Foo(i32);
---
- Doc comment for [`Foo`](https://docs.rs/test/*/test/struct.Foo.html)
+ Doc comment for [`Foo`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/struct.Foo.html)
"#]],
);
}
@@ -7160,7 +7160,7 @@ pub struct Foo<T>(T);
*[`Foo<T>`]*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -7169,7 +7169,7 @@ pub struct Foo<T>(T);
---
- Doc comment for [`Foo<T>`](https://docs.rs/test/*/test/struct.Foo.html)
+ Doc comment for [`Foo<T>`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/struct.Foo.html)
"#]],
);
}
@@ -7259,7 +7259,7 @@ enum Enum {
*RecordV*
```rust
- test::Enum
+ ra_test_fixture::Enum
```
```rust
@@ -7285,7 +7285,7 @@ enum Enum {
*field*
```rust
- test::RecordV
+ ra_test_fixture::RecordV
```
```rust
@@ -7315,7 +7315,7 @@ impl T for () {
*func*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -7341,7 +7341,7 @@ impl T$0 for () {}
*T*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -7360,7 +7360,7 @@ impl T$0 for () {}
*T*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -7383,7 +7383,7 @@ impl T$0 for () {}
*T*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -7406,7 +7406,7 @@ impl T$0 for () {}
*T*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -7433,7 +7433,7 @@ impl T$0 for () {}
*T*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -7460,7 +7460,7 @@ impl T$0 for () {}
*T*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -7529,7 +7529,7 @@ fn f() {
***
```rust
- test::Struct
+ ra_test_fixture::Struct
```
```rust
@@ -7558,7 +7558,7 @@ fn main() { $0V; }
*V*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -7585,7 +7585,7 @@ fn main() { $0V; }
*V*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -7765,7 +7765,7 @@ fn test() {
*foo*
```rust
- test::S
+ ra_test_fixture::S
```
```rust
@@ -7794,7 +7794,7 @@ fn test() {
*foo*
```rust
- test::S
+ ra_test_fixture::S
```
```rust
@@ -7824,7 +7824,7 @@ mod m {
*foo*
```rust
- test::S
+ ra_test_fixture::S
```
```rust
@@ -7854,7 +7854,7 @@ fn test() {
*A*
```rust
- test::S
+ ra_test_fixture::S
```
```rust
@@ -7883,7 +7883,7 @@ fn test() {
*A*
```rust
- test::S
+ ra_test_fixture::S
```
```rust
@@ -7913,7 +7913,7 @@ mod m {
*A*
```rust
- test::S
+ ra_test_fixture::S
```
```rust
@@ -7936,7 +7936,7 @@ fn test() {
*f*
```rust
- test::S
+ ra_test_fixture::S
```
```rust
@@ -8091,7 +8091,7 @@ fn main() {
*foo*
```rust
- test::S
+ ra_test_fixture::S
```
```rust
@@ -8573,7 +8573,7 @@ impl Iterator for S {
*S*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -8595,7 +8595,7 @@ extern "C" {
*STATIC*
```rust
- test::<extern>
+ ra_test_fixture::<extern>
```
```rust
@@ -8613,7 +8613,7 @@ extern "C" {
*fun*
```rust
- test::<extern>
+ ra_test_fixture::<extern>
```
```rust
@@ -8631,7 +8631,7 @@ extern "C" {
*Ty*
```rust
- test::<extern>
+ ra_test_fixture::<extern>
```
```rust
@@ -8731,7 +8731,7 @@ impl Iterator for S {
},
},
HoverGotoTypeData {
- mod_path: "test::Notable",
+ mod_path: "ra_test_fixture::Notable",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -8744,7 +8744,7 @@ impl Iterator for S {
},
},
HoverGotoTypeData {
- mod_path: "test::S2",
+ mod_path: "ra_test_fixture::S2",
nav: NavigationTarget {
file_id: FileId(
0,
@@ -8775,7 +8775,7 @@ struct Pedro$0<'a> {
*Pedro*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -8962,7 +8962,7 @@ fn test_hover_function_with_pat_param() {
*test_1*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -8978,7 +8978,7 @@ fn test_hover_function_with_pat_param() {
*test_2*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -8994,7 +8994,7 @@ fn test_hover_function_with_pat_param() {
*test_3*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9010,7 +9010,7 @@ fn test_hover_function_with_pat_param() {
*test_4*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9026,7 +9026,7 @@ fn test_hover_function_with_pat_param() {
*test_5*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9042,7 +9042,7 @@ fn test_hover_function_with_pat_param() {
*test_6*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9058,7 +9058,7 @@ fn test_hover_function_with_pat_param() {
*test_7*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9074,7 +9074,7 @@ fn test_hover_function_with_pat_param() {
*test_8*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9090,7 +9090,7 @@ fn test_hover_function_with_pat_param() {
*test_9*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9106,7 +9106,7 @@ fn test_hover_function_with_pat_param() {
*test_10*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9122,7 +9122,7 @@ fn test_hover_function_with_pat_param() {
*test_10*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9148,7 +9148,7 @@ mod m {
*C*
```rust
- test::m::m2
+ ra_test_fixture::m::m2
```
```rust
@@ -9173,17 +9173,17 @@ macro_rules! foo {
foo!(BAR_$0);
"#,
expect![[r#"
- *BAR_*
+ *BAR_*
- ```rust
- test
- ```
+ ```rust
+ ra_test_fixture
+ ```
- ```rust
- pub static BAR_: {error} = Foo::new(||{
- crate;
- })
- ```
+ ```rust
+ pub static BAR_: {error} = Foo::new(||{
+ crate;
+ })
+ ```
"#]],
);
}
@@ -9202,7 +9202,7 @@ type A$0 = B;
*A*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9235,7 +9235,7 @@ type A$0 = B;
*A*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9269,7 +9269,7 @@ type A$0 = B;
*A*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9301,7 +9301,7 @@ type A$0 = B;
*A*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9362,7 +9362,7 @@ trait Compat$0 {}
*Compat*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9384,7 +9384,7 @@ trait UnCompat$0 {
*UnCompat*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9407,7 +9407,7 @@ fn f<T: UnCompat$0>
*UnCompat*
```rust
- test
+ ra_test_fixture
```
```rust
@@ -9416,3 +9416,53 @@ fn f<T: UnCompat$0>
"#]],
);
}
+
+#[test]
+fn issue_18613() {
+ check(
+ r#"
+fn main() {
+ struct S<T, D = bool>();
+ let x$0 = S::<()>;
+}"#,
+ expect![[r#"
+ *x*
+
+ ```rust
+ let x: fn S<()>() -> S<()>
+ ```
+
+ ---
+
+ size = 0, align = 1
+ "#]],
+ );
+
+ check(
+ r#"
+pub struct Global;
+pub struct Box<T, A = Global>(T, A);
+
+impl<T> Box<T> {
+ pub fn new(x: T) -> Self { loop {} }
+}
+
+pub struct String;
+
+fn main() {
+ let box_value$0 = Box::<String>new();
+}
+"#,
+ expect![[r#"
+ *box_value*
+
+ ```rust
+ let box_value: fn Box<String>(String, Global) -> Box<String>
+ ```
+
+ ---
+
+ size = 0, align = 1
+ "#]],
+ );
+}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index c58ca0f01c..aa99ba49bc 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -8,8 +8,8 @@ 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 ide_db::{text_edit::TextEdit, FxHashSet};
use itertools::Itertools;
use smallvec::{smallvec, SmallVec};
use span::{Edition, EditionedFileId};
@@ -29,6 +29,7 @@ mod closing_brace;
mod closure_captures;
mod closure_ret;
mod discriminant;
+mod extern_block;
mod generic_param;
mod implicit_drop;
mod implicit_static;
@@ -116,6 +117,7 @@ pub(crate) fn inlay_hints(
#[derive(Default)]
struct InlayHintCtx {
lifetime_stacks: Vec<Vec<SmolStr>>,
+ extern_block_parent: Option<ast::ExternBlock>,
}
pub(crate) fn inlay_hints_resolve(
@@ -174,12 +176,18 @@ fn handle_event(ctx: &mut InlayHintCtx, node: WalkEvent<SyntaxNode>) -> Option<S
.unwrap_or_default();
ctx.lifetime_stacks.push(params);
}
+ if let Some(node) = ast::ExternBlock::cast(node.clone()) {
+ ctx.extern_block_parent = Some(node);
+ }
Some(node)
}
WalkEvent::Leave(n) => {
if ast::AnyHasGenericParams::can_cast(n.kind()) {
ctx.lifetime_stacks.pop();
}
+ if ast::ExternBlock::can_cast(n.kind()) {
+ ctx.extern_block_parent = None;
+ }
None
}
}
@@ -234,12 +242,20 @@ fn hints(
ast::Item(it) => match it {
ast::Item::Fn(it) => {
implicit_drop::hints(hints, famous_defs, config, file_id, &it);
+ if let Some(extern_block) = &ctx.extern_block_parent {
+ extern_block::fn_hints(hints, famous_defs, config, file_id, &it, extern_block);
+ }
lifetime::fn_hints(hints, ctx, famous_defs, config, file_id, it)
},
- // static type elisions
- ast::Item::Static(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Left(it)),
+ ast::Item::Static(it) => {
+ if let Some(extern_block) = &ctx.extern_block_parent {
+ extern_block::static_hints(hints, famous_defs, config, file_id, &it, extern_block);
+ }
+ implicit_static::hints(hints, famous_defs, config, file_id, Either::Left(it))
+ },
ast::Item::Const(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Right(it)),
ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it),
+ ast::Item::ExternBlock(it) => extern_block::extern_block_hints(hints, famous_defs, config, file_id, it),
_ => None,
},
// FIXME: trait object type elisions
@@ -289,6 +305,16 @@ pub struct InlayFieldsToResolve {
}
impl InlayFieldsToResolve {
+ pub fn from_client_capabilities(client_capability_fields: &FxHashSet<&str>) -> Self {
+ Self {
+ resolve_text_edits: client_capability_fields.contains("textEdits"),
+ resolve_hint_tooltip: client_capability_fields.contains("tooltip"),
+ resolve_label_tooltip: client_capability_fields.contains("label.tooltip"),
+ resolve_label_location: client_capability_fields.contains("label.location"),
+ resolve_label_command: client_capability_fields.contains("label.command"),
+ }
+ }
+
pub const fn empty() -> Self {
Self {
resolve_text_edits: false,
@@ -358,6 +384,7 @@ pub enum InlayKind {
Type,
Drop,
RangeExclusive,
+ ExternUnsafety,
}
#[derive(Debug, Hash)]
diff --git a/crates/ide/src/inlay_hints/extern_block.rs b/crates/ide/src/inlay_hints/extern_block.rs
new file mode 100644
index 0000000000..4cc4925cda
--- /dev/null
+++ b/crates/ide/src/inlay_hints/extern_block.rs
@@ -0,0 +1,138 @@
+//! Extern block hints
+use ide_db::{famous_defs::FamousDefs, text_edit::TextEdit};
+use span::EditionedFileId;
+use syntax::{ast, AstNode, SyntaxToken};
+
+use crate::{InlayHint, InlayHintsConfig};
+
+pub(super) fn extern_block_hints(
+ acc: &mut Vec<InlayHint>,
+ FamousDefs(_sema, _): &FamousDefs<'_, '_>,
+ _config: &InlayHintsConfig,
+ _file_id: EditionedFileId,
+ extern_block: ast::ExternBlock,
+) -> Option<()> {
+ if extern_block.unsafe_token().is_some() {
+ return None;
+ }
+ let abi = extern_block.abi()?;
+ acc.push(InlayHint {
+ range: abi.syntax().text_range(),
+ position: crate::InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: true,
+ kind: crate::InlayKind::ExternUnsafety,
+ label: crate::InlayHintLabel::from("unsafe"),
+ text_edit: Some(TextEdit::insert(abi.syntax().text_range().start(), "unsafe ".to_owned())),
+ resolve_parent: Some(extern_block.syntax().text_range()),
+ });
+ Some(())
+}
+
+pub(super) fn fn_hints(
+ acc: &mut Vec<InlayHint>,
+ FamousDefs(_sema, _): &FamousDefs<'_, '_>,
+ _config: &InlayHintsConfig,
+ _file_id: EditionedFileId,
+ fn_: &ast::Fn,
+ extern_block: &ast::ExternBlock,
+) -> Option<()> {
+ let implicit_unsafe = fn_.safe_token().is_none() && fn_.unsafe_token().is_none();
+ if !implicit_unsafe {
+ return None;
+ }
+ let fn_ = fn_.fn_token()?;
+ acc.push(item_hint(extern_block, fn_));
+ Some(())
+}
+
+pub(super) fn static_hints(
+ acc: &mut Vec<InlayHint>,
+ FamousDefs(_sema, _): &FamousDefs<'_, '_>,
+ _config: &InlayHintsConfig,
+ _file_id: EditionedFileId,
+ static_: &ast::Static,
+ extern_block: &ast::ExternBlock,
+) -> Option<()> {
+ let implicit_unsafe = static_.safe_token().is_none() && static_.unsafe_token().is_none();
+ if !implicit_unsafe {
+ return None;
+ }
+ let static_ = static_.static_token()?;
+ acc.push(item_hint(extern_block, static_));
+ Some(())
+}
+
+fn item_hint(extern_block: &ast::ExternBlock, token: SyntaxToken) -> InlayHint {
+ InlayHint {
+ range: token.text_range(),
+ position: crate::InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: true,
+ kind: crate::InlayKind::ExternUnsafety,
+ label: crate::InlayHintLabel::from("unsafe"),
+ text_edit: {
+ let mut builder = TextEdit::builder();
+ builder.insert(token.text_range().start(), "unsafe ".to_owned());
+ if extern_block.unsafe_token().is_none() {
+ if let Some(abi) = extern_block.abi() {
+ builder.insert(abi.syntax().text_range().start(), "unsafe ".to_owned());
+ }
+ }
+ Some(builder.finish())
+ },
+ resolve_parent: Some(extern_block.syntax().text_range()),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::inlay_hints::tests::{check_with_config, DISABLED_CONFIG};
+
+ #[test]
+ fn unadorned() {
+ check_with_config(
+ DISABLED_CONFIG,
+ r#"
+ extern "C" {
+//^^^^^^^^^^ unsafe
+ static FOO: ();
+ // ^^^^^^ unsafe
+ pub static FOO: ();
+ // ^^^^^^unsafe
+ unsafe static FOO: ();
+ safe static FOO: ();
+ fn foo();
+ // ^^ unsafe
+ pub fn foo();
+ // ^^ unsafe
+ unsafe fn foo();
+ safe fn foo();
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn adorned() {
+ check_with_config(
+ DISABLED_CONFIG,
+ r#"
+unsafe extern "C" {
+ static FOO: ();
+ // ^^^^^^ unsafe
+ pub static FOO: ();
+ // ^^^^^^unsafe
+ unsafe static FOO: ();
+ safe static FOO: ();
+ fn foo();
+ // ^^ unsafe
+ pub fn foo();
+ // ^^ unsafe
+ unsafe fn foo();
+ safe fn foo();
+}
+"#,
+ );
+ }
+}
diff --git a/crates/ide/src/inlay_hints/lifetime.rs b/crates/ide/src/inlay_hints/lifetime.rs
index 2163c959b1..1fdd698991 100644
--- a/crates/ide/src/inlay_hints/lifetime.rs
+++ b/crates/ide/src/inlay_hints/lifetime.rs
@@ -41,7 +41,15 @@ pub(super) fn fn_hints(
fd,
config,
file_id,
- param_list,
+ param_list.params().filter_map(|it| {
+ Some((
+ it.pat().and_then(|it| match it {
+ ast::Pat::IdentPat(p) => p.name(),
+ _ => None,
+ }),
+ it.ty()?,
+ ))
+ }),
generic_param_list,
ret_type,
self_param,
@@ -90,7 +98,15 @@ pub(super) fn fn_ptr_hints(
fd,
config,
file_id,
- param_list,
+ param_list.params().filter_map(|it| {
+ Some((
+ it.pat().and_then(|it| match it {
+ ast::Pat::IdentPat(p) => p.name(),
+ _ => None,
+ }),
+ it.ty()?,
+ ))
+ }),
generic_param_list,
ret_type,
None,
@@ -148,7 +164,7 @@ pub(super) fn fn_path_hints(
fd,
config,
file_id,
- param_list,
+ param_list.type_args().filter_map(|it| Some((None, it.ty()?))),
generic_param_list,
ret_type,
None,
@@ -177,8 +193,8 @@ pub(super) fn fn_path_hints(
)
}
-fn path_as_fn(path: &ast::Path) -> Option<(ast::ParamList, Option<ast::RetType>)> {
- path.segment().and_then(|it| it.param_list().zip(Some(it.ret_type())))
+fn path_as_fn(path: &ast::Path) -> Option<(ast::ParenthesizedArgList, Option<ast::RetType>)> {
+ path.segment().and_then(|it| it.parenthesized_arg_list().zip(Some(it.ret_type())))
}
fn hints_(
@@ -187,7 +203,7 @@ fn hints_(
FamousDefs(_, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
_file_id: EditionedFileId,
- param_list: ast::ParamList,
+ params: impl Iterator<Item = (Option<ast::Name>, ast::Type)>,
generic_param_list: Option<ast::GenericParamList>,
ret_type: Option<ast::RetType>,
self_param: Option<ast::SelfParam>,
@@ -217,45 +233,34 @@ fn hints_(
let is_elided = is_elided(&lifetime);
acc.push((None, self_param.amp_token(), lifetime, is_elided));
}
- param_list
- .params()
- .filter_map(|it| {
- Some((
- it.pat().and_then(|it| match it {
- ast::Pat::IdentPat(p) => p.name(),
- _ => None,
- }),
- it.ty()?,
- ))
- })
- .for_each(|(name, ty)| {
- // FIXME: check path types
- walk_ty(&ty, &mut |ty| match ty {
- ast::Type::RefType(r) => {
- let lifetime = r.lifetime();
- let is_elided = is_elided(&lifetime);
- acc.push((name.clone(), r.amp_token(), lifetime, is_elided));
- false
- }
- ast::Type::FnPtrType(_) => {
+ params.for_each(|(name, ty)| {
+ // FIXME: check path types
+ walk_ty(&ty, &mut |ty| match ty {
+ ast::Type::RefType(r) => {
+ let lifetime = r.lifetime();
+ let is_elided = is_elided(&lifetime);
+ acc.push((name.clone(), r.amp_token(), lifetime, is_elided));
+ false
+ }
+ ast::Type::FnPtrType(_) => {
+ is_trivial = false;
+ true
+ }
+ ast::Type::PathType(t) => {
+ if t.path()
+ .and_then(|it| it.segment())
+ .and_then(|it| it.parenthesized_arg_list())
+ .is_some()
+ {
is_trivial = false;
true
+ } else {
+ false
}
- ast::Type::PathType(t) => {
- if t.path()
- .and_then(|it| it.segment())
- .and_then(|it| it.param_list())
- .is_some()
- {
- is_trivial = false;
- true
- } else {
- false
- }
- }
- _ => false,
- })
- });
+ }
+ _ => false,
+ })
+ });
acc
};
@@ -339,7 +344,10 @@ fn hints_(
true
}
ast::Type::PathType(t) => {
- if t.path().and_then(|it| it.segment()).and_then(|it| it.param_list()).is_some()
+ if t.path()
+ .and_then(|it| it.segment())
+ .and_then(|it| it.parenthesized_arg_list())
+ .is_some()
{
is_trivial = false;
true
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index d4ef9570e1..c960b88a3e 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -402,6 +402,8 @@ impl Analysis {
self.with_db(|db| typing::on_enter(db, position))
}
+ pub const SUPPORTED_TRIGGER_CHARS: &'static str = typing::TRIGGER_CHARS;
+
/// Returns an edit which should be applied after a character was typed.
///
/// This is useful for some on-the-fly fixups, like adding `;` to `let =`
@@ -410,14 +412,16 @@ impl Analysis {
&self,
position: FilePosition,
char_typed: char,
- autoclose: bool,
+ chars_to_exclude: Option<String>,
) -> Cancellable<Option<SourceChange>> {
// Fast path to not even parse the file.
if !typing::TRIGGER_CHARS.contains(char_typed) {
return Ok(None);
}
- if char_typed == '<' && !autoclose {
- return Ok(None);
+ if let Some(chars_to_exclude) = chars_to_exclude {
+ if chars_to_exclude.contains(char_typed) {
+ return Ok(None);
+ }
}
self.with_db(|db| typing::on_char_typed(db, position, char_typed))
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index 665fc954d2..a9519c03b3 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -3105,4 +3105,73 @@ fn main() { let _: Baz; }
"#,
);
}
+
+ #[test]
+ fn rename_type_param_ref_in_use_bound() {
+ check(
+ "U",
+ r#"
+fn foo<T>() -> impl use<T$0> Trait {}
+"#,
+ r#"
+fn foo<U>() -> impl use<U> Trait {}
+"#,
+ );
+ }
+
+ #[test]
+ fn rename_type_param_in_use_bound() {
+ check(
+ "U",
+ r#"
+fn foo<T$0>() -> impl use<T> Trait {}
+"#,
+ r#"
+fn foo<U>() -> impl use<U> Trait {}
+"#,
+ );
+ }
+
+ #[test]
+ fn rename_lifetime_param_ref_in_use_bound() {
+ check(
+ "u",
+ r#"
+fn foo<'t>() -> impl use<'t$0> Trait {}
+"#,
+ r#"
+fn foo<'u>() -> impl use<'u> Trait {}
+"#,
+ );
+ }
+
+ #[test]
+ fn rename_lifetime_param_in_use_bound() {
+ check(
+ "u",
+ r#"
+fn foo<'t$0>() -> impl use<'t> Trait {}
+"#,
+ r#"
+fn foo<'u>() -> impl use<'u> Trait {}
+"#,
+ );
+ }
+
+ #[test]
+ fn rename_parent_type_param_in_use_bound() {
+ check(
+ "U",
+ r#"
+trait Trait<T> {
+ fn foo() -> impl use<T$0> Trait {}
+}
+"#,
+ r#"
+trait Trait<U> {
+ fn foo() -> impl use<U> Trait {}
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 96375937a1..3767a3917c 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -151,6 +151,14 @@ fn punctuation(
T!['['] | T![']'] => HlPunct::Bracket,
T!['{'] | T!['}'] => HlPunct::Brace,
T!['('] | T![')'] => HlPunct::Parenthesis,
+ T![>]
+ if parent
+ .as_ref()
+ .and_then(SyntaxNode::parent)
+ .map_or(false, |it| it.kind() == MACRO_RULES) =>
+ {
+ return HlOperator::Other.into()
+ }
T![<] | T![>] => HlPunct::Angle,
T![,] => HlPunct::Comma,
T![:] => HlPunct::Colon,
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
index 70f2d7203e..edd9639768 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
@@ -46,7 +46,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">foo</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span>foo<span class="colon">:</span>ident<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span>foo<span class="colon">:</span>ident<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
<span class="keyword">mod</span> y <span class="brace">{</span>
<span class="keyword">pub</span> <span class="keyword">struct</span> <span class="punctuation">$</span>foo<span class="semicolon">;</span>
<span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_const.html b/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
index a14f2cc88c..05289cfe3f 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
@@ -46,7 +46,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
<span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span>
<span class="brace">}</span><span class="semicolon">;</span>
<span class="brace">}</span>
@@ -79,7 +79,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">unsafe_deref</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
+ <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
<span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
<span class="brace">}</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 35650bbe87..aa9d23250c 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -147,11 +147,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="comment documentation">/// ```</span>
-<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">macro_rules</span><span class="macro_bang injected">!</span><span class="none injected"> </span><span class="macro declaration injected">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="angle injected">&gt;</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr </span><span class="brace injected">}</span><span class="brace injected">}</span>
+<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">macro_rules</span><span class="macro_bang injected">!</span><span class="none injected"> </span><span class="macro declaration injected">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="operator injected">&gt;</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr </span><span class="brace injected">}</span><span class="brace injected">}</span>
<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected macro">(</span><span class="numeric_literal injected macro">1</span><span class="parenthesis injected macro">)</span><span class="semicolon injected">;</span>
<span class="comment documentation">/// ```</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
<span class="punctuation">$</span>expr
<span class="brace">}</span>
<span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
index a790b38578..c2bf94fd9b 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
@@ -54,7 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span>
<span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
index 6dac066bfa..a30d16d532 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
@@ -54,7 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span>
<span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
index 6dac066bfa..a30d16d532 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
@@ -54,7 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span>
<span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
index 4ccc407990..b82a3f9f81 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
@@ -54,7 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span>
<span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
index 196552020a..06673d1a73 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -54,7 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace macro proc_macro">}</span> <span class="struct declaration macro proc_macro">Foo</span> <span class="keyword macro proc_macro">struct</span>
<span class="brace macro proc_macro">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">def_fn</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="brace">}</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="brace">}</span>
<span class="brace">}</span>
<span class="macro">def_fn</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
@@ -64,24 +64,24 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace macro">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">dont_color_me_braces</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="numeric_literal">0</span><span class="brace">}</span>
+ <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="numeric_literal">0</span><span class="brace">}</span>
<span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
<span class="punctuation">$</span>expr
<span class="brace">}</span>
<span class="brace">}</span>
<span class="comment documentation">/// textually shadow previous definition</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
<span class="punctuation">$</span>expr
<span class="brace">}</span>
<span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">keyword_frag</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="colon">:</span>ty<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="parenthesis">)</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="colon">:</span>ty<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="parenthesis">)</span>
<span class="brace">}</span>
<span class="keyword">macro</span> <span class="macro declaration">with_args</span><span class="parenthesis">(</span><span class="punctuation">$</span>i<span class="colon">:</span>ident<span class="parenthesis">)</span> <span class="brace">{</span>
@@ -95,7 +95,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
<span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span>
<span class="brace">}</span><span class="semicolon">;</span>
<span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 5594a36e73..1385ae0510 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -46,7 +46,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">println</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="brace">{</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="parenthesis">(</span><span class="brace">{</span>
<span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span><span class="parenthesis">)</span>
<span class="brace">}</span>
@@ -74,12 +74,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">reuse_twice</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="colon">:</span>literal<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">{</span>stringify<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="semicolon">;</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="colon">:</span>literal<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span><span class="brace">{</span>stringify<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="semicolon">;</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index be6176894b..4e69c82f3d 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -46,12 +46,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
<span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span>
<span class="brace">}</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">unsafe_deref</span> <span class="brace">{</span>
- <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
+ <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">&gt;</span> <span class="brace">{</span>
<span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
<span class="brace">}</span><span class="semicolon">;</span>
<span class="brace">}</span>
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index 9bb5de9f2e..3d9146cc4c 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -15,12 +15,14 @@
mod on_enter;
+use std::iter;
+
use ide_db::{base_db::SourceDatabase, FilePosition, RootDatabase};
-use span::EditionedFileId;
+use span::{Edition, EditionedFileId};
use syntax::{
algo::{ancestors_at_offset, find_node_at_offset},
ast::{self, edit::IndentLevel, AstToken},
- AstNode, Parse, SourceFile, SyntaxKind, TextRange, TextSize, T,
+ AstNode, Parse, SourceFile, SyntaxKind, TextRange, TextSize,
};
use ide_db::text_edit::TextEdit;
@@ -30,7 +32,7 @@ use crate::SourceChange;
pub(crate) use on_enter::on_enter;
// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
-pub(crate) const TRIGGER_CHARS: &str = ".=<>{(";
+pub(crate) const TRIGGER_CHARS: &str = ".=<>{(|";
struct ExtendedTextEdit {
edit: TextEdit,
@@ -47,6 +49,7 @@ struct ExtendedTextEdit {
// - typing `.` in a chain method call auto-indents
// - typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression
// - typing `{` in a use item adds a closing `}` in the right place
+// - typing `>` to complete a return type `->` will insert a whitespace after it
//
// VS Code::
//
@@ -66,55 +69,67 @@ pub(crate) fn on_char_typed(
if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
return None;
}
- let file = &db.parse(EditionedFileId::current_edition(position.file_id));
- if !stdx::always!(file.tree().syntax().text().char_at(position.offset) == Some(char_typed)) {
+ // FIXME: We need to figure out the edition of the file here, but that means hitting the
+ // database for more than just parsing the file which is bad.
+ // FIXME: We are hitting the database here, if we are unlucky this call might block momentarily
+ // causing the editor to feel sluggish!
+ let edition = Edition::CURRENT_FIXME;
+ let file = &db.parse(EditionedFileId::new(position.file_id, edition));
+ let char_matches_position =
+ file.tree().syntax().text().char_at(position.offset) == Some(char_typed);
+ if !stdx::always!(char_matches_position) {
return None;
}
- let edit = on_char_typed_inner(file, position.offset, char_typed)?;
+
+ let edit = on_char_typed_(file, position.offset, char_typed, edition)?;
+
let mut sc = SourceChange::from_text_edit(position.file_id, edit.edit);
sc.is_snippet = edit.is_snippet;
Some(sc)
}
-fn on_char_typed_inner(
+fn on_char_typed_(
file: &Parse<SourceFile>,
offset: TextSize,
char_typed: char,
+ edition: Edition,
) -> Option<ExtendedTextEdit> {
- if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
- return None;
- }
- let conv = |text_edit: Option<TextEdit>| {
- Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false })
- };
match char_typed {
- '.' => conv(on_dot_typed(&file.tree(), offset)),
- '=' => conv(on_eq_typed(&file.tree(), offset)),
- '<' => on_left_angle_typed(&file.tree(), offset),
- '>' => conv(on_right_angle_typed(&file.tree(), offset)),
- '{' => conv(on_opening_bracket_typed(file, offset, '{')),
- '(' => conv(on_opening_bracket_typed(file, offset, '(')),
+ '.' => on_dot_typed(&file.tree(), offset),
+ '=' => on_eq_typed(&file.tree(), offset),
+ '>' => on_right_angle_typed(&file.tree(), offset),
+ '{' | '(' | '<' => on_opening_delimiter_typed(file, offset, char_typed, edition),
+ '|' => on_pipe_typed(&file.tree(), offset),
_ => None,
}
+ .map(conv)
+}
+
+fn conv(edit: TextEdit) -> ExtendedTextEdit {
+ ExtendedTextEdit { edit, is_snippet: false }
}
-/// Inserts a closing bracket when the user types an opening bracket, wrapping an existing expression in a
+/// Inserts a closing delimiter when the user types an opening bracket, wrapping an existing expression in a
/// block, or a part of a `use` item (for `{`).
-fn on_opening_bracket_typed(
+fn on_opening_delimiter_typed(
file: &Parse<SourceFile>,
offset: TextSize,
opening_bracket: char,
+ edition: Edition,
) -> Option<TextEdit> {
- let (closing_bracket, expected_ast_bracket) = match opening_bracket {
- '{' => ('}', SyntaxKind::L_CURLY),
- '(' => (')', SyntaxKind::L_PAREN),
+ type FilterFn = fn(SyntaxKind) -> bool;
+ let (closing_bracket, expected_ast_bracket, allowed_kinds) = match opening_bracket {
+ '{' => ('}', SyntaxKind::L_CURLY, &[ast::Expr::can_cast as FilterFn] as &[FilterFn]),
+ '(' => (
+ ')',
+ SyntaxKind::L_PAREN,
+ &[ast::Expr::can_cast as FilterFn, ast::Pat::can_cast, ast::Type::can_cast]
+ as &[FilterFn],
+ ),
+ '<' => ('>', SyntaxKind::L_ANGLE, &[ast::Type::can_cast as FilterFn] as &[FilterFn]),
_ => return None,
};
- if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some(opening_bracket)) {
- return None;
- }
-
let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?;
if brace_token.kind() != expected_ast_bracket {
return None;
@@ -125,58 +140,53 @@ fn on_opening_bracket_typed(
if !stdx::always!(range.len() == TextSize::of(opening_bracket)) {
return None;
}
- // FIXME: Edition
- let file = file.reparse(range, "", span::Edition::CURRENT_FIXME);
+ let reparsed = file.reparse(range, "", edition).tree();
- if let Some(edit) = bracket_expr(&file.tree(), offset, opening_bracket, closing_bracket) {
+ if let Some(edit) =
+ on_delimited_node_typed(&reparsed, offset, opening_bracket, closing_bracket, allowed_kinds)
+ {
return Some(edit);
}
- if closing_bracket == '}' {
- if let Some(edit) = brace_use_path(&file.tree(), offset) {
- return Some(edit);
- }
+ match opening_bracket {
+ '{' => on_left_brace_typed(&reparsed, offset),
+ '<' => on_left_angle_typed(&file.tree(), &reparsed, offset),
+ _ => None,
}
+}
- return None;
-
- fn brace_use_path(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
- let segment: ast::PathSegment = find_node_at_offset(file.syntax(), offset)?;
- if segment.syntax().text_range().start() != offset {
- return None;
- }
-
- let tree: ast::UseTree = find_node_at_offset(file.syntax(), offset)?;
-
- Some(TextEdit::insert(tree.syntax().text_range().end() + TextSize::of("{"), "}".to_owned()))
+fn on_left_brace_typed(reparsed: &SourceFile, offset: TextSize) -> Option<TextEdit> {
+ let segment: ast::PathSegment = find_node_at_offset(reparsed.syntax(), offset)?;
+ if segment.syntax().text_range().start() != offset {
+ return None;
}
- fn bracket_expr(
- file: &SourceFile,
- offset: TextSize,
- opening_bracket: char,
- closing_bracket: char,
- ) -> Option<TextEdit> {
- let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?;
- if expr.syntax().text_range().start() != offset {
- return None;
- }
-
- // Enclose the outermost expression starting at `offset`
- while let Some(parent) = expr.syntax().parent() {
- if parent.text_range().start() != expr.syntax().text_range().start() {
- break;
- }
+ let tree: ast::UseTree = find_node_at_offset(reparsed.syntax(), offset)?;
- match ast::Expr::cast(parent) {
- Some(parent) => expr = parent,
- None => break,
- }
- }
+ Some(TextEdit::insert(tree.syntax().text_range().end() + TextSize::of("{"), "}".to_owned()))
+}
- if let Some(parent) = expr.syntax().parent().and_then(ast::Expr::cast) {
- let mut node = expr.syntax().clone();
- let all_prev_sib_attr = loop {
+fn on_delimited_node_typed(
+ reparsed: &SourceFile,
+ offset: TextSize,
+ opening_bracket: char,
+ closing_bracket: char,
+ kinds: &[fn(SyntaxKind) -> bool],
+) -> Option<TextEdit> {
+ let t = reparsed.syntax().token_at_offset(offset).right_biased()?;
+ let (filter, node) = t
+ .parent_ancestors()
+ .take_while(|n| n.text_range().start() == offset)
+ .find_map(|n| kinds.iter().find(|&kind_filter| kind_filter(n.kind())).zip(Some(n)))?;
+ let mut node = node
+ .ancestors()
+ .take_while(|n| n.text_range().start() == offset && filter(n.kind()))
+ .last()?;
+
+ if let Some(parent) = node.parent().filter(|it| filter(it.kind())) {
+ let all_prev_sib_attr = {
+ let mut node = node.clone();
+ loop {
match node.prev_sibling() {
Some(sib) if sib.kind().is_trivia() || sib.kind() == SyntaxKind::ATTR => {
node = sib
@@ -184,26 +194,32 @@ fn on_opening_bracket_typed(
Some(_) => break false,
None => break true,
};
- };
-
- if all_prev_sib_attr {
- expr = parent;
}
- }
+ };
- // Insert the closing bracket right after the expression.
- Some(TextEdit::insert(
- expr.syntax().text_range().end() + TextSize::of(opening_bracket),
- closing_bracket.to_string(),
- ))
+ if all_prev_sib_attr {
+ node = parent;
+ }
}
-}
+ // Insert the closing bracket right after the node.
+ Some(TextEdit::insert(
+ node.text_range().end() + TextSize::of(opening_bracket),
+ closing_bracket.to_string(),
+ ))
+}
/// Returns an edit which should be applied after `=` was typed. Primarily,
/// this works when adding `let =`.
// FIXME: use a snippet completion instead of this hack here.
fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
- if !stdx::always!(file.syntax().text().char_at(offset) == Some('=')) {
+ let text = file.syntax().text();
+ let has_newline = iter::successors(Some(offset), |&offset| Some(offset + TextSize::new(1)))
+ .filter_map(|offset| text.char_at(offset))
+ .find(|&c| !c.is_whitespace() || c == '\n')
+ == Some('n');
+ // don't attempt to add `;` if there is a newline after the `=`, the intent is likely to write
+ // out the expression afterwards!
+ if has_newline {
return None;
}
@@ -289,9 +305,6 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
- if !stdx::always!(file.syntax().text().char_at(offset) == Some('.')) {
- return None;
- }
let whitespace =
file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;
@@ -342,14 +355,15 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
}
/// Add closing `>` for generic arguments/parameters.
-fn on_left_angle_typed(file: &SourceFile, offset: TextSize) -> Option<ExtendedTextEdit> {
- let file_text = file.syntax().text();
- if !stdx::always!(file_text.char_at(offset) == Some('<')) {
- return None;
- }
+fn on_left_angle_typed(
+ file: &SourceFile,
+ reparsed: &SourceFile,
+ offset: TextSize,
+) -> Option<TextEdit> {
+ let file_text = reparsed.syntax().text();
- // Find the next non-whitespace char in the line.
- let mut next_offset = offset + TextSize::of('<');
+ // Find the next non-whitespace char in the line, check if its a `>`
+ let mut next_offset = offset;
while file_text.char_at(next_offset) == Some(' ') {
next_offset += TextSize::of(' ')
}
@@ -357,34 +371,36 @@ fn on_left_angle_typed(file: &SourceFile, offset: TextSize) -> Option<ExtendedTe
return None;
}
- let range = TextRange::at(offset, TextSize::of('<'));
- if let Some(t) = file.syntax().token_at_offset(offset).left_biased() {
- if T![impl] == t.kind() {
- return Some(ExtendedTextEdit {
- edit: TextEdit::replace(range, "<$0>".to_owned()),
- is_snippet: true,
- });
- }
- }
-
- if ancestors_at_offset(file.syntax(), offset).any(|n| {
- ast::GenericParamList::can_cast(n.kind()) || ast::GenericArgList::can_cast(n.kind())
- }) {
- Some(ExtendedTextEdit {
- edit: TextEdit::replace(range, "<$0>".to_owned()),
- is_snippet: true,
+ if ancestors_at_offset(file.syntax(), offset)
+ .take_while(|n| !ast::Item::can_cast(n.kind()))
+ .any(|n| {
+ ast::GenericParamList::can_cast(n.kind())
+ || ast::GenericArgList::can_cast(n.kind())
+ || ast::UseBoundGenericArgs::can_cast(n.kind())
})
+ {
+ // Insert the closing bracket right after
+ Some(TextEdit::insert(offset + TextSize::of('<'), '>'.to_string()))
} else {
None
}
}
+fn on_pipe_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
+ let pipe_token = file.syntax().token_at_offset(offset).right_biased()?;
+ if pipe_token.kind() != SyntaxKind::PIPE {
+ return None;
+ }
+ if pipe_token.parent().and_then(ast::ParamList::cast)?.r_paren_token().is_some() {
+ return None;
+ }
+ let after_lpipe = offset + TextSize::of('|');
+ Some(TextEdit::insert(after_lpipe, "|".to_owned()))
+}
+
/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
fn on_right_angle_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
let file_text = file.syntax().text();
- if !stdx::always!(file_text.char_at(offset) == Some('>')) {
- return None;
- }
let after_arrow = offset + TextSize::of('>');
if file_text.char_at(after_arrow) != Some('{') {
return None;
@@ -411,7 +427,7 @@ mod tests {
let edit = TextEdit::insert(offset, char_typed.to_string());
edit.apply(&mut before);
let parse = SourceFile::parse(&before, span::Edition::CURRENT_FIXME);
- on_char_typed_inner(&parse, offset, char_typed).map(|it| {
+ on_char_typed_(&parse, offset, char_typed, span::Edition::CURRENT_FIXME).map(|it| {
it.apply(&mut before);
before.to_string()
})
@@ -426,7 +442,7 @@ mod tests {
fn type_char_noop(char_typed: char, ra_fixture_before: &str) {
let file_change = do_type_char(char_typed, ra_fixture_before);
- assert!(file_change.is_none())
+ assert_eq!(file_change, None)
}
#[test]
@@ -471,6 +487,15 @@ fn foo() {
}
",
);
+ type_char_noop(
+ '=',
+ r"
+fn foo() {
+ let foo =$0
+ 1 + 1
+}
+",
+ );
}
#[test]
@@ -1067,6 +1092,81 @@ fn f() {
}
#[test]
+ fn adds_closing_parenthesis_for_pat() {
+ type_char(
+ '(',
+ r#"
+fn f() { match () { $0() => () } }
+"#,
+ r#"
+fn f() { match () { (()) => () } }
+"#,
+ );
+ type_char(
+ '(',
+ r#"
+fn f($0n: ()) {}
+"#,
+ r#"
+fn f((n): ()) {}
+"#,
+ );
+ }
+
+ #[test]
+ fn adds_closing_parenthesis_for_ty() {
+ type_char(
+ '(',
+ r#"
+fn f(n: $0()) {}
+"#,
+ r#"
+fn f(n: (())) {}
+"#,
+ );
+ type_char(
+ '(',
+ r#"
+fn f(n: $0a::b::<d>::c) {}
+"#,
+ r#"
+fn f(n: (a::b::<d>::c)) {}
+"#,
+ );
+ }
+
+ #[test]
+ fn adds_closing_angles_for_ty() {
+ type_char(
+ '<',
+ r#"
+fn f(n: $0()) {}
+"#,
+ r#"
+fn f(n: <()>) {}
+"#,
+ );
+ type_char(
+ '<',
+ r#"
+fn f(n: $0a::b::<d>::c) {}
+"#,
+ r#"
+fn f(n: <a::b::<d>::c>) {}
+"#,
+ );
+ type_char(
+ '<',
+ r#"
+fn f(n: a$0b::<d>::c) {}
+"#,
+ r#"
+fn f(n: a<>b::<d>::c) {}
+"#,
+ );
+ }
+
+ #[test]
fn parenthesis_noop_in_string_literal() {
// Regression test for #9351
type_char_noop(
@@ -1157,6 +1257,12 @@ use $0Thing as _;
use some::pa$0th::to::Item;
"#,
);
+ type_char_noop(
+ '<',
+ r#"
+use some::pa$0th::to::Item;
+ "#,
+ );
}
#[test]
@@ -1170,7 +1276,7 @@ fn foo() {
"#,
r#"
fn foo() {
- bar::<$0>
+ bar::<>
}
"#,
);
@@ -1184,7 +1290,7 @@ fn foo(bar: &[u64]) {
"#,
r#"
fn foo(bar: &[u64]) {
- bar.iter().collect::<$0>();
+ bar.iter().collect::<>();
}
"#,
);
@@ -1198,7 +1304,7 @@ fn foo(bar: &[u64]) {
fn foo$0() {}
"#,
r#"
-fn foo<$0>() {}
+fn foo<>() {}
"#,
);
type_char(
@@ -1207,7 +1313,7 @@ fn foo<$0>() {}
fn foo$0
"#,
r#"
-fn foo<$0>
+fn foo<>
"#,
);
type_char(
@@ -1216,7 +1322,7 @@ fn foo<$0>
struct Foo$0 {}
"#,
r#"
-struct Foo<$0> {}
+struct Foo<> {}
"#,
);
type_char(
@@ -1225,7 +1331,7 @@ struct Foo<$0> {}
struct Foo$0();
"#,
r#"
-struct Foo<$0>();
+struct Foo<>();
"#,
);
type_char(
@@ -1234,7 +1340,7 @@ struct Foo<$0>();
struct Foo$0
"#,
r#"
-struct Foo<$0>
+struct Foo<>
"#,
);
type_char(
@@ -1243,7 +1349,7 @@ struct Foo<$0>
enum Foo$0
"#,
r#"
-enum Foo<$0>
+enum Foo<>
"#,
);
type_char(
@@ -1252,7 +1358,7 @@ enum Foo<$0>
trait Foo$0
"#,
r#"
-trait Foo<$0>
+trait Foo<>
"#,
);
type_char(
@@ -1261,16 +1367,7 @@ trait Foo<$0>
type Foo$0 = Bar;
"#,
r#"
-type Foo<$0> = Bar;
- "#,
- );
- type_char(
- '<',
- r#"
-impl$0 Foo {}
- "#,
- r#"
-impl<$0> Foo {}
+type Foo<> = Bar;
"#,
);
type_char(
@@ -1279,7 +1376,7 @@ impl<$0> Foo {}
impl<T> Foo$0 {}
"#,
r#"
-impl<T> Foo<$0> {}
+impl<T> Foo<> {}
"#,
);
type_char(
@@ -1288,7 +1385,7 @@ impl<T> Foo<$0> {}
impl Foo$0 {}
"#,
r#"
-impl Foo<$0> {}
+impl Foo<> {}
"#,
);
}
@@ -1438,4 +1535,42 @@ fn foo() {
"#,
);
}
+
+ #[test]
+ fn completes_pipe_param_list() {
+ type_char(
+ '|',
+ r#"
+fn foo() {
+ $0
+}
+"#,
+ r#"
+fn foo() {
+ ||
+}
+"#,
+ );
+ type_char(
+ '|',
+ r#"
+fn foo() {
+ $0 a
+}
+"#,
+ r#"
+fn foo() {
+ || a
+}
+"#,
+ );
+ type_char_noop(
+ '|',
+ r#"
+fn foo() {
+ let $0
+}
+"#,
+ );
+ }
}
diff --git a/crates/intern/src/symbol.rs b/crates/intern/src/symbol.rs
index ef76192ba8..200b14027f 100644
--- a/crates/intern/src/symbol.rs
+++ b/crates/intern/src/symbol.rs
@@ -77,8 +77,12 @@ impl TaggedArcPtr {
}
/// Retrieves the tag.
+ ///
+ /// # Safety
+ ///
+ /// You can only drop the `Arc` if the instance is dropped.
#[inline]
- pub(crate) fn try_as_arc_owned(self) -> Option<ManuallyDrop<Arc<Box<str>>>> {
+ pub(crate) unsafe fn try_as_arc_owned(self) -> Option<ManuallyDrop<Arc<Box<str>>>> {
// Unpack the tag from the alignment niche
let tag = Strict::addr(self.packed.as_ptr()) & Self::BOOL_BITS;
if tag != 0 {
@@ -245,16 +249,14 @@ impl Symbol {
}
}
- ManuallyDrop::into_inner(
- match shard.raw_entry_mut().from_key_hashed_nocheck::<str>(hash, arc.as_ref()) {
- RawEntryMut::Occupied(occ) => occ.remove_entry(),
- RawEntryMut::Vacant(_) => unreachable!(),
- }
- .0
- .0
- .try_as_arc_owned()
- .unwrap(),
- );
+ let ptr = match shard.raw_entry_mut().from_key_hashed_nocheck::<str>(hash, arc.as_ref()) {
+ RawEntryMut::Occupied(occ) => occ.remove_entry(),
+ RawEntryMut::Vacant(_) => unreachable!(),
+ }
+ .0
+ .0;
+ // SAFETY: We're dropping, we have ownership.
+ ManuallyDrop::into_inner(unsafe { ptr.try_as_arc_owned().unwrap() });
debug_assert_eq!(Arc::count(arc), 1);
// Shrink the backing storage if the shard is less than 50% occupied.
@@ -267,7 +269,8 @@ impl Symbol {
impl Drop for Symbol {
#[inline]
fn drop(&mut self) {
- let Some(arc) = self.repr.try_as_arc_owned() else {
+ // SAFETY: We're dropping, we have ownership.
+ let Some(arc) = (unsafe { self.repr.try_as_arc_owned() }) else {
return;
};
// When the last `Ref` is dropped, remove the object from the global map.
@@ -288,7 +291,8 @@ impl Clone for Symbol {
}
fn increase_arc_refcount(repr: TaggedArcPtr) -> TaggedArcPtr {
- let Some(arc) = repr.try_as_arc_owned() else {
+ // SAFETY: We're not dropping the `Arc`.
+ let Some(arc) = (unsafe { repr.try_as_arc_owned() }) else {
return repr;
};
// increase the ref count
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 865518fe94..1120d3c7d6 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -80,6 +80,7 @@ define_symbols! {
self_ = "self",
Self_ = "Self",
tick_static = "'static",
+ tick_underscore = "'_",
dollar_crate = "$crate",
MISSING_NAME = "[missing name]",
fn_ = "fn",
@@ -99,7 +100,6 @@ define_symbols! {
cdecl_dash_unwind = "cdecl-unwind",
fastcall_dash_unwind = "fastcall-unwind",
msp430_dash_interrupt = "msp430-interrupt",
- platform_dash_intrinsic = "platform-intrinsic",
ptx_dash_kernel = "ptx-kernel",
riscv_dash_interrupt_dash_m = "riscv-interrupt-m",
riscv_dash_interrupt_dash_s = "riscv-interrupt-s",
@@ -150,6 +150,9 @@ define_symbols! {
C,
call_mut,
call_once,
+ async_call_once,
+ async_call_mut,
+ async_call,
call,
cdecl,
Center,
@@ -221,6 +224,9 @@ define_symbols! {
fn_mut,
fn_once_output,
fn_once,
+ async_fn_once,
+ async_fn_mut,
+ async_fn,
fn_ptr_addr,
fn_ptr_trait,
format_alignment,
@@ -334,6 +340,8 @@ define_symbols! {
Option,
Ord,
Output,
+ CallRefFuture,
+ CallOnceFuture,
owned_box,
packed,
panic_2015,
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index 0e1606a699..cf26845b11 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -123,7 +123,7 @@ pub fn load_workspace(
.collect()
};
- let project_folders = ProjectFolders::new(std::slice::from_ref(&ws), &[]);
+ let project_folders = ProjectFolders::new(std::slice::from_ref(&ws), &[], None);
loader.set_config(vfs::loader::Config {
load: project_folders.load,
watch: vec![],
@@ -153,7 +153,11 @@ pub struct ProjectFolders {
}
impl ProjectFolders {
- pub fn new(workspaces: &[ProjectWorkspace], global_excludes: &[AbsPathBuf]) -> ProjectFolders {
+ pub fn new(
+ workspaces: &[ProjectWorkspace],
+ global_excludes: &[AbsPathBuf],
+ user_config_dir_path: Option<&AbsPath>,
+ ) -> ProjectFolders {
let mut res = ProjectFolders::default();
let mut fsc = FileSetConfig::builder();
let mut local_filesets = vec![];
@@ -291,6 +295,22 @@ impl ProjectFolders {
}
}
+ if let Some(user_config_path) = user_config_dir_path {
+ let ratoml_path = {
+ let mut p = user_config_path.to_path_buf();
+ p.push("rust-analyzer.toml");
+ p
+ };
+
+ let file_set_roots = vec![VfsPath::from(ratoml_path.to_owned())];
+ let entry = vfs::loader::Entry::Files(vec![ratoml_path.to_owned()]);
+
+ res.watch.push(res.load.len());
+ res.load.push(entry);
+ local_filesets.push(fsc.len() as u64);
+ fsc.add_file_set(file_set_roots)
+ }
+
let fsc = fsc.build();
res.source_root_config = SourceRootConfig { fsc, local_filesets };
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index a50a2182a7..fe6b904bd8 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -242,7 +242,7 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
// struct MyStruct(pub ());
if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
p.bump(T!['(']);
- paths::use_path(p);
+ paths::vis_path(p);
p.expect(T![')']);
}
}
@@ -252,7 +252,7 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
T![in] => {
p.bump(T!['(']);
p.bump(T![in]);
- paths::use_path(p);
+ paths::vis_path(p);
p.expect(T![')']);
}
_ => {}
@@ -307,13 +307,49 @@ fn name(p: &mut Parser<'_>) {
name_r(p, TokenSet::EMPTY);
}
-fn name_ref(p: &mut Parser<'_>) {
- if p.at(IDENT) {
+fn name_ref_or_self(p: &mut Parser<'_>) {
+ if matches!(p.current(), T![ident] | T![self]) {
let m = p.start();
- p.bump(IDENT);
+ p.bump_any();
+ m.complete(p, NAME_REF);
+ } else {
+ p.err_and_bump("expected identifier or `self`");
+ }
+}
+
+fn name_ref_or_upper_self(p: &mut Parser<'_>) {
+ if matches!(p.current(), T![ident] | T![Self]) {
+ let m = p.start();
+ p.bump_any();
+ m.complete(p, NAME_REF);
+ } else {
+ p.err_and_bump("expected identifier or `Self`");
+ }
+}
+
+const PATH_NAME_REF_KINDS: TokenSet =
+ TokenSet::new(&[IDENT, T![self], T![super], T![crate], T![Self]]);
+
+fn name_ref_mod_path(p: &mut Parser<'_>) {
+ if p.at_ts(PATH_NAME_REF_KINDS) {
+ let m = p.start();
+ p.bump_any();
+ m.complete(p, NAME_REF);
+ } else {
+ p.err_and_bump("expected identifier, `self`, `super`, `crate`, or `Self`");
+ }
+}
+
+const PATH_NAME_REF_OR_INDEX_KINDS: TokenSet =
+ PATH_NAME_REF_KINDS.union(TokenSet::new(&[INT_NUMBER]));
+
+fn name_ref_mod_path_or_index(p: &mut Parser<'_>) {
+ if p.at_ts(PATH_NAME_REF_OR_INDEX_KINDS) {
+ let m = p.start();
+ p.bump_any();
m.complete(p, NAME_REF);
} else {
- p.err_and_bump("expected identifier");
+ p.err_and_bump("expected integer, identifier, `self`, `super`, `crate`, or `Self`");
}
}
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs
index 82e4d66148..ccb556b2cc 100644
--- a/crates/parser/src/grammar/attributes.rs
+++ b/crates/parser/src/grammar/attributes.rs
@@ -36,6 +36,14 @@ fn attr(p: &mut Parser<'_>, inner: bool) {
attr.complete(p, ATTR);
}
+// test_err meta_recovery
+// #![]
+// #![p = ]
+// #![p::]
+// #![p:: =]
+// #![unsafe]
+// #![unsafe =]
+
// test metas
// #![simple_ident]
// #![simple::path]
@@ -63,7 +71,7 @@ pub(super) fn meta(p: &mut Parser<'_>) {
if is_unsafe {
p.expect(T!['(']);
}
- paths::use_path(p);
+ paths::attr_path(p);
match p.current() {
T![=] => {
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index e565874a42..3b3f11be13 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -449,7 +449,9 @@ fn postfix_dot_expr<const FLOAT_RECOVERY: bool>(
let nth1 = if FLOAT_RECOVERY { 0 } else { 1 };
let nth2 = if FLOAT_RECOVERY { 1 } else { 2 };
- if p.nth(nth1) == IDENT && (p.nth(nth2) == T!['('] || p.nth_at(nth2, T![::])) {
+ if PATH_NAME_REF_KINDS.contains(p.nth(nth1))
+ && (p.nth(nth2) == T!['('] || p.nth_at(nth2, T![::]))
+ {
return Ok(method_call_expr::<FLOAT_RECOVERY>(p, lhs));
}
@@ -510,22 +512,27 @@ fn index_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker {
// y.bar::<T>(1, 2,);
// x.0.0.call();
// x.0. call();
+// x.0()
// }
fn method_call_expr<const FLOAT_RECOVERY: bool>(
p: &mut Parser<'_>,
lhs: CompletedMarker,
) -> CompletedMarker {
if FLOAT_RECOVERY {
- assert!(p.nth(0) == IDENT && (p.nth(1) == T!['('] || p.nth_at(1, T![::])));
+ assert!(p.at_ts(PATH_NAME_REF_KINDS) && (p.nth(1) == T!['('] || p.nth_at(1, T![::])));
} else {
- assert!(p.at(T![.]) && p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth_at(2, T![::])));
+ assert!(
+ p.at(T![.])
+ && PATH_NAME_REF_KINDS.contains(p.nth(1))
+ && (p.nth(2) == T!['('] || p.nth_at(2, T![::]))
+ );
}
let m = lhs.precede(p);
if !FLOAT_RECOVERY {
p.bump(T![.]);
}
- name_ref(p);
- generic_args::opt_generic_arg_list(p, true);
+ name_ref_mod_path(p);
+ generic_args::opt_generic_arg_list_expr(p);
if p.at(T!['(']) {
arg_list(p);
} else {
@@ -543,6 +550,8 @@ fn method_call_expr<const FLOAT_RECOVERY: bool>(
// test field_expr
// fn foo() {
+// x.self;
+// x.Self;
// x.foo;
// x.0.bar;
// x.0.1;
@@ -560,8 +569,8 @@ fn field_expr<const FLOAT_RECOVERY: bool>(
if !FLOAT_RECOVERY {
p.bump(T![.]);
}
- if p.at(IDENT) || p.at(INT_NUMBER) {
- name_ref_or_index(p);
+ if p.at_ts(PATH_NAME_REF_OR_INDEX_KINDS) {
+ name_ref_mod_path_or_index(p);
} else if p.at(FLOAT_NUMBER) {
return match p.split_float(m) {
(true, m) => {
@@ -679,34 +688,37 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) {
IDENT | INT_NUMBER if p.nth_at(1, T![::]) => {
// test_err record_literal_missing_ellipsis_recovery
// fn main() {
- // S { S::default() }
+ // S { S::default() };
+ // S { 0::default() };
// }
m.abandon(p);
p.expect(T![..]);
expr(p);
}
+ IDENT | INT_NUMBER if p.nth_at(1, T![..]) => {
+ // test_err record_literal_before_ellipsis_recovery
+ // fn main() {
+ // S { field ..S::default() }
+ // S { 0 ..S::default() }
+ // }
+ name_ref_or_index(p);
+ p.error("expected `:`");
+ m.complete(p, RECORD_EXPR_FIELD);
+ }
IDENT | INT_NUMBER => {
- if p.nth_at(1, T![..]) {
- // test_err record_literal_before_ellipsis_recovery
- // fn main() {
- // S { field ..S::default() }
- // }
+ // test_err record_literal_field_eq_recovery
+ // fn main() {
+ // S { field = foo }
+ // S { 0 = foo }
+ // }
+ if p.nth_at(1, T![:]) {
name_ref_or_index(p);
- p.error("expected `:`");
- } else {
- // test_err record_literal_field_eq_recovery
- // fn main() {
- // S { field = foo }
- // }
- if p.nth_at(1, T![:]) {
- name_ref_or_index(p);
- p.bump(T![:]);
- } else if p.nth_at(1, T![=]) {
- name_ref_or_index(p);
- p.err_and_bump("expected `:`");
- }
- expr(p);
+ p.bump(T![:]);
+ } else if p.nth_at(1, T![=]) {
+ name_ref_or_index(p);
+ p.err_and_bump("expected `:`");
}
+ expr(p);
m.complete(p, RECORD_EXPR_FIELD);
}
T![.] if p.at(T![..]) => {
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 97e0392ce1..407320e1d0 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -259,13 +259,7 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
type_(p);
p.expect(T![,]);
while !p.at(EOF) && !p.at(T![')']) {
- if p.at(IDENT) || p.at(INT_NUMBER) {
- name_ref_or_index(p);
- // } else if p.at(FLOAT_NUMBER) {
- // FIXME: needs float hack
- } else {
- p.err_and_bump("expected field name or number");
- }
+ name_ref_mod_path_or_index(p);
if !p.at(T![')']) {
p.expect(T![.]);
}
@@ -351,10 +345,7 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
name(p);
p.bump(T![=]);
allow_templates = false;
- true
- } else {
- false
- };
+ }
let op = p.start();
let dir_spec = p.start();
@@ -405,6 +396,19 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
op.abandon(p);
op_n.abandon(p);
p.err_and_bump("expected asm operand");
+
+ // improves error recovery and handles err_and_bump recovering from `{` which gets
+ // the parser stuck here
+ if p.at(T!['{']) {
+ // test_err bad_asm_expr
+ // fn foo() {
+ // builtin#asm(
+ // label crashy = { return; }
+ // );
+ // }
+ expr(p);
+ }
+
if p.at(T!['}']) {
break;
}
@@ -465,9 +469,9 @@ fn parse_clobber_abi(p: &mut Parser<'_>) {
fn parse_reg(p: &mut Parser<'_>) {
p.expect(T!['(']);
- if p.at(T![ident]) {
+ if p.at_ts(PATH_NAME_REF_KINDS) {
let m = p.start();
- name_ref(p);
+ name_ref_mod_path(p);
m.complete(p, ASM_REG_SPEC);
} else if p.at(T![string]) {
let m = p.start();
diff --git a/crates/parser/src/grammar/generic_args.rs b/crates/parser/src/grammar/generic_args.rs
index c62c8a9d3f..b9d5bff663 100644
--- a/crates/parser/src/grammar/generic_args.rs
+++ b/crates/parser/src/grammar/generic_args.rs
@@ -1,14 +1,13 @@
use super::*;
-// test_err generic_arg_list_recover
-// type T = T<0, ,T>;
-pub(super) fn opt_generic_arg_list(p: &mut Parser<'_>, colon_colon_required: bool) {
+// test_err generic_arg_list_recover_expr
+// const _: () = T::<0, ,T>;
+// const _: () = T::<0, ,T>();
+pub(super) fn opt_generic_arg_list_expr(p: &mut Parser<'_>) {
let m;
if p.at(T![::]) && p.nth(2) == T![<] {
m = p.start();
p.bump(T![::]);
- } else if !colon_colon_required && p.at(T![<]) && p.nth(1) != T![=] {
- m = p.start();
} else {
return;
}
@@ -25,7 +24,7 @@ pub(super) fn opt_generic_arg_list(p: &mut Parser<'_>, colon_colon_required: boo
m.complete(p, GENERIC_ARG_LIST);
}
-const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[
+pub(crate) const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[
LIFETIME_IDENT,
IDENT,
T!['{'],
@@ -46,21 +45,27 @@ const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[
const GENERIC_ARG_RECOVERY_SET: TokenSet = TokenSet::new(&[T![>], T![,]]);
// test generic_arg
-// type T = S<i32>;
-fn generic_arg(p: &mut Parser<'_>) -> bool {
+// type T = S<i32, dyn T, fn()>;
+pub(crate) fn generic_arg(p: &mut Parser<'_>) -> bool {
match p.current() {
LIFETIME_IDENT if !p.nth_at(1, T![+]) => lifetime_arg(p),
T!['{'] | T![true] | T![false] | T![-] => const_arg(p),
k if k.is_literal() => const_arg(p),
- // test associated_type_bounds
- // fn print_all<T: Iterator<Item, Item::Item, Item::<true>, Item: Display, Item<'a> = Item>>(printables: T) {}
+ // test generic_arg_bounds
+ // type Plain = Foo<Item, Item::Item, Item: Bound, Item = Item>;
+ // type GenericArgs = Foo<Item<T>, Item::<T>, Item<T>: Bound, Item::<T>: Bound, Item<T> = Item, Item::<T> = Item>;
+ // type ParenthesizedArgs = Foo<Item(T), Item::(T), Item(T): Bound, Item::(T): Bound, Item(T) = Item, Item::(T) = Item>;
+ // type RTN = Foo<Item(..), Item(..), Item(..): Bound, Item(..): Bound, Item(..) = Item, Item(..) = Item>;
+ // test edition_2015_dyn_prefix_inside_generic_arg 2015
+ // type A = Foo<dyn T>;
+ T![ident] if !p.edition().at_least_2018() && types::is_dyn_weak(p) => type_arg(p),
// test macro_inside_generic_arg
// type A = Foo<syn::Token![_]>;
- IDENT if [T![<], T![=], T![:]].contains(&p.nth(1)) && !p.nth_at(1, T![::]) => {
+ k if PATH_NAME_REF_KINDS.contains(k) => {
let m = p.start();
- name_ref(p);
- opt_generic_arg_list(p, false);
+ name_ref_mod_path(p);
+ paths::opt_path_type_args(p);
match p.current() {
T![=] => {
p.bump_any();
@@ -88,45 +93,26 @@ fn generic_arg(p: &mut Parser<'_>) -> bool {
}
// test assoc_type_bound
// type T = StreamingIterator<Item<'a>: Clone>;
+ // type T = StreamingIterator<Item(T): Clone>;
T![:] if !p.at(T![::]) => {
generic_params::bounds(p);
m.complete(p, ASSOC_TYPE_ARG);
}
+ // Turned out to be just a normal path type (mirror `path_or_macro_type`)
_ => {
let m = m.complete(p, PATH_SEGMENT).precede(p).complete(p, PATH);
let m = paths::type_path_for_qualifier(p, m);
- m.precede(p).complete(p, PATH_TYPE).precede(p).complete(p, TYPE_ARG);
+ let m = if p.at(T![!]) && !p.at(T![!=]) {
+ let m = m.precede(p);
+ items::macro_call_after_excl(p);
+ m.complete(p, MACRO_CALL).precede(p).complete(p, MACRO_TYPE)
+ } else {
+ m.precede(p).complete(p, PATH_TYPE)
+ };
+ types::opt_type_bounds_as_dyn_trait_type(p, m).precede(p).complete(p, TYPE_ARG);
}
}
}
- IDENT if p.nth_at(1, T!['(']) => {
- let m = p.start();
- name_ref(p);
- if p.nth_at(1, T![..]) {
- let rtn = p.start();
- p.bump(T!['(']);
- p.bump(T![..]);
- p.expect(T![')']);
- rtn.complete(p, RETURN_TYPE_SYNTAX);
- // test return_type_syntax_assoc_type_bound
- // fn foo<T: Trait<method(..): Send>>() {}
- generic_params::bounds(p);
- m.complete(p, ASSOC_TYPE_ARG);
- } else {
- params::param_list_fn_trait(p);
- // test bare_dyn_types_with_paren_as_generic_args
- // type A = S<Fn(i32)>;
- // type A = S<Fn(i32) + Send>;
- // type B = S<Fn(i32) -> i32>;
- // type C = S<Fn(i32) -> i32 + Send>;
- opt_ret_type(p);
- let m = m.complete(p, PATH_SEGMENT).precede(p).complete(p, PATH);
- let m = paths::type_path_for_qualifier(p, m);
- let m = m.precede(p).complete(p, PATH_TYPE);
- let m = types::opt_type_bounds_as_dyn_trait_type(p, m);
- m.precede(p).complete(p, TYPE_ARG);
- }
- }
_ if p.at_ts(types::TYPE_FIRST) => type_arg(p),
_ => return false,
}
@@ -168,10 +154,10 @@ pub(super) fn const_arg_expr(p: &mut Parser<'_>) {
expressions::literal(p);
lm.complete(p, PREFIX_EXPR);
}
- _ if paths::is_use_path_start(p) => {
+ _ if paths::is_path_start(p) => {
// This shouldn't be hit by `const_arg`
let lm = p.start();
- paths::use_path(p);
+ paths::expr_path(p);
lm.complete(p, PATH_EXPR);
}
_ => {
@@ -190,7 +176,7 @@ pub(super) fn const_arg(p: &mut Parser<'_>) {
m.complete(p, CONST_ARG);
}
-fn type_arg(p: &mut Parser<'_>) {
+pub(crate) fn type_arg(p: &mut Parser<'_>) {
let m = p.start();
types::type_(p);
m.complete(p, TYPE_ARG);
diff --git a/crates/parser/src/grammar/generic_params.rs b/crates/parser/src/grammar/generic_params.rs
index 92311238c2..9d4fdbfaf2 100644
--- a/crates/parser/src/grammar/generic_params.rs
+++ b/crates/parser/src/grammar/generic_params.rs
@@ -56,7 +56,7 @@ fn generic_param(p: &mut Parser<'_>, m: Marker) -> bool {
fn lifetime_param(p: &mut Parser<'_>, m: Marker) {
assert!(p.at(LIFETIME_IDENT));
lifetime(p);
- if p.at(T![:]) {
+ if p.eat(T![:]) {
lifetime_bounds(p);
}
m.complete(p, LIFETIME_PARAM);
@@ -106,14 +106,19 @@ fn const_param(p: &mut Parser<'_>, m: Marker) {
}
fn lifetime_bounds(p: &mut Parser<'_>) {
- assert!(p.at(T![:]));
- p.bump(T![:]);
- while p.at(LIFETIME_IDENT) {
- lifetime(p);
+ let marker = p.start();
+ while {
+ if !matches!(p.current(), LIFETIME_IDENT | T![>] | T![,]) {
+ p.error("expected lifetime");
+ }
+
+ type_bound(p)
+ } {
if !p.eat(T![+]) {
break;
}
}
+ marker.complete(p, TYPE_BOUND_LIST);
}
// test type_param_bounds
@@ -145,6 +150,9 @@ fn type_bound(p: &mut Parser<'_>) -> bool {
T![for] => types::for_type(p, false),
// test precise_capturing
// fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T, Self> {}
+
+ // test_err precise_capturing_invalid
+ // type T = impl use<self, 1>;
T![use] if p.nth_at(1, T![<]) => {
p.bump_any();
let m = p.start();
@@ -156,14 +164,10 @@ fn type_bound(p: &mut Parser<'_>) -> bool {
|| "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) {
+ if p.at(LIFETIME_IDENT) {
lifetime(p);
} else {
- name_ref(p);
+ name_ref_or_upper_self(p);
}
true
},
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index 7d98499008..8ece5af527 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -254,22 +254,16 @@ fn opt_item_without_modifiers(p: &mut Parser<'_>, m: Marker) -> Result<(), Marke
// test extern_crate
// extern crate foo;
+// extern crate self;
fn extern_crate(p: &mut Parser<'_>, m: Marker) {
p.bump(T![extern]);
p.bump(T![crate]);
- if p.at(T![self]) {
- // test extern_crate_self
- // extern crate self;
- let m = p.start();
- p.bump(T![self]);
- m.complete(p, NAME_REF);
- } else {
- name_ref(p);
- }
+ name_ref_or_self(p);
// test extern_crate_rename
// extern crate foo as bar;
+ // extern crate self as bar;
opt_rename(p);
p.expect(T![;]);
m.complete(p, EXTERN_CRATE);
diff --git a/crates/parser/src/grammar/params.rs b/crates/parser/src/grammar/params.rs
index c535267c16..51ffcd0706 100644
--- a/crates/parser/src/grammar/params.rs
+++ b/crates/parser/src/grammar/params.rs
@@ -14,12 +14,6 @@ pub(super) fn param_list_fn_def(p: &mut Parser<'_>) {
list_(p, Flavor::FnDef);
}
-// test param_list_opt_patterns
-// fn foo<F: FnMut(&mut Foo<'a>)>(){}
-pub(super) fn param_list_fn_trait(p: &mut Parser<'_>) {
- list_(p, Flavor::FnTrait);
-}
-
pub(super) fn param_list_fn_ptr(p: &mut Parser<'_>) {
list_(p, Flavor::FnPointer);
}
@@ -28,10 +22,9 @@ pub(super) fn param_list_closure(p: &mut Parser<'_>) {
list_(p, Flavor::Closure);
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Flavor {
- FnDef, // Includes trait fn params; omitted param idents are not supported
- FnTrait, // Params for `Fn(...)`/`FnMut(...)`/`FnOnce(...)` annotations
+ FnDef, // Includes trait fn params; omitted param idents are not supported
FnPointer,
Closure,
}
@@ -41,7 +34,7 @@ fn list_(p: &mut Parser<'_>, flavor: Flavor) {
let (bra, ket) = match flavor {
Closure => (T![|], T![|]),
- FnDef | FnTrait | FnPointer => (T!['('], T![')']),
+ FnDef | FnPointer => (T!['('], T![')']),
};
let list_marker = p.start();
@@ -119,11 +112,6 @@ fn param(p: &mut Parser<'_>, m: Marker, flavor: Flavor) {
}
}
}
- // test value_parameters_no_patterns
- // type F = Box<Fn(i32, &i32, &i32, ())>;
- Flavor::FnTrait => {
- types::type_(p);
- }
// test fn_pointer_param_ident_path
// type Foo = fn(Bar::Baz);
// type Qux = fn(baz: Bar::Baz);
diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs
index 09db921803..3410505cd4 100644
--- a/crates/parser/src/grammar/paths.rs
+++ b/crates/parser/src/grammar/paths.rs
@@ -19,6 +19,14 @@ pub(super) fn use_path(p: &mut Parser<'_>) {
path(p, Mode::Use);
}
+pub(super) fn vis_path(p: &mut Parser<'_>) {
+ path(p, Mode::Vis);
+}
+
+pub(super) fn attr_path(p: &mut Parser<'_>) {
+ path(p, Mode::Attr);
+}
+
pub(crate) fn type_path(p: &mut Parser<'_>) {
path(p, Mode::Type);
}
@@ -37,15 +45,20 @@ pub(crate) fn type_path_for_qualifier(
#[derive(Clone, Copy, Eq, PartialEq)]
enum Mode {
Use,
+ Attr,
Type,
Expr,
+ Vis,
}
-fn path(p: &mut Parser<'_>, mode: Mode) {
+fn path(p: &mut Parser<'_>, mode: Mode) -> Option<CompletedMarker> {
let path = p.start();
- path_segment(p, mode, true);
+ if path_segment(p, mode, true).is_none() {
+ path.abandon(p);
+ return None;
+ }
let qual = path.complete(p, PATH);
- path_for_qualifier(p, mode, qual);
+ Some(path_for_qualifier(p, mode, qual))
}
fn path_for_qualifier(
@@ -71,7 +84,7 @@ const EXPR_PATH_SEGMENT_RECOVERY_SET: TokenSet =
items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![')'], T![,], T![let]]));
const TYPE_PATH_SEGMENT_RECOVERY_SET: TokenSet = types::TYPE_RECOVERY_SET;
-fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
+fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) -> Option<CompletedMarker> {
let m = p.start();
// test qual_paths
// type X = <A as B>::Output;
@@ -93,75 +106,105 @@ fn path_segment(p: &mut Parser<'_>, mode: Mode, first: bool) {
p.error("expected `::`");
}
} else {
- let empty = if first {
- p.eat(T![::]);
- false
- } else {
- true
- };
- match p.current() {
- IDENT => {
- name_ref(p);
- opt_path_type_args(p, mode);
- }
+ let mut empty = if first { !p.eat(T![::]) } else { true };
+ if p.at_ts(PATH_NAME_REF_KINDS) {
// test crate_path
// use crate::foo;
- T![self] | T![super] | T![crate] | T![Self] => {
- let m = p.start();
- p.bump_any();
- m.complete(p, NAME_REF);
- }
- _ => {
- let recover_set = match mode {
- Mode::Use => items::ITEM_RECOVERY_SET,
- Mode::Type => TYPE_PATH_SEGMENT_RECOVERY_SET,
- Mode::Expr => EXPR_PATH_SEGMENT_RECOVERY_SET,
- };
- p.err_recover("expected identifier", recover_set);
- if empty {
- // test_err empty_segment
- // use crate::;
- m.abandon(p);
- return;
+ name_ref_mod_path(p);
+ opt_path_args(p, mode);
+ } else {
+ let recover_set = match mode {
+ Mode::Use => items::ITEM_RECOVERY_SET,
+ Mode::Attr => {
+ items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![']'], T![=], T![#]]))
}
+ Mode::Vis => items::ITEM_RECOVERY_SET.union(TokenSet::new(&[T![')']])),
+ Mode::Type => TYPE_PATH_SEGMENT_RECOVERY_SET,
+ Mode::Expr => EXPR_PATH_SEGMENT_RECOVERY_SET,
+ };
+ empty &= p.err_recover(
+ "expected identifier, `self`, `super`, `crate`, or `Self`",
+ recover_set,
+ );
+ if empty {
+ // test_err empty_segment
+ // use crate::;
+ m.abandon(p);
+ return None;
}
- };
+ }
}
- m.complete(p, PATH_SEGMENT);
+ Some(m.complete(p, PATH_SEGMENT))
}
-fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) {
+pub(crate) fn opt_path_type_args(p: &mut Parser<'_>) {
+ // test typepathfn_with_coloncolon
+ // type F = Start::(Middle) -> (Middle)::End;
+ // type GenericArg = S<Start(Middle)::End>;
+ let m;
+ if p.at(T![::]) && matches!(p.nth(2), T![<] | T!['(']) {
+ m = p.start();
+ p.bump(T![::]);
+ } else if (p.current() == T![<] && p.nth(1) != T![=]) || p.current() == T!['('] {
+ m = p.start();
+ } else {
+ return;
+ }
+ let current = p.current();
+ if current == T![<] {
+ // test_err generic_arg_list_recover
+ // type T = T<0, ,T>;
+ // type T = T::<0, ,T>;
+ delimited(
+ p,
+ T![<],
+ T![>],
+ T![,],
+ || "expected generic argument".into(),
+ generic_args::GENERIC_ARG_FIRST,
+ generic_args::generic_arg,
+ );
+ m.complete(p, GENERIC_ARG_LIST);
+ } else if p.nth_at(1, T![..]) {
+ // test return_type_syntax_in_path
+ // fn foo<T>()
+ // where
+ // T::method(..): Send,
+ // method(..): Send,
+ // method::(..): Send,
+ // {}
+ p.bump(T!['(']);
+ p.bump(T![..]);
+ p.expect(T![')']);
+ m.complete(p, RETURN_TYPE_SYNTAX);
+ } else {
+ // test path_fn_trait_args
+ // type F = Box<Fn(i32) -> ()>;
+ // type F = Box<::Fn(i32) -> ()>;
+ // type F = Box<Fn::(i32) -> ()>;
+ // type F = Box<::Fn::(i32) -> ()>;
+ delimited(
+ p,
+ T!['('],
+ T![')'],
+ T![,],
+ || "expected type".into(),
+ types::TYPE_FIRST,
+ |p| {
+ let progress = types::TYPE_FIRST.contains(p.current());
+ generic_args::type_arg(p);
+ progress
+ },
+ );
+ m.complete(p, PARENTHESIZED_ARG_LIST);
+ opt_ret_type(p);
+ }
+}
+
+fn opt_path_args(p: &mut Parser<'_>, mode: Mode) {
match mode {
- Mode::Use => {}
- Mode::Type => {
- // test typepathfn_with_coloncolon
- // type F = Start::(Middle) -> (Middle)::End;
- // type GenericArg = S<Start(Middle)::End>;
- if p.at(T![::]) && p.nth_at(2, T!['(']) {
- p.bump(T![::]);
- }
- if p.at(T!['(']) {
- if p.nth_at(1, T![..]) {
- // test return_type_syntax_in_path
- // fn foo<T>()
- // where
- // T::method(..): Send,
- // {}
- let rtn = p.start();
- p.bump(T!['(']);
- p.bump(T![..]);
- p.expect(T![')']);
- rtn.complete(p, RETURN_TYPE_SYNTAX);
- } else {
- // test path_fn_trait_args
- // type F = Box<Fn(i32) -> ()>;
- params::param_list_fn_trait(p);
- opt_ret_type(p);
- }
- } else {
- generic_args::opt_generic_arg_list(p, false);
- }
- }
- Mode::Expr => generic_args::opt_generic_arg_list(p, true),
+ Mode::Use | Mode::Attr | Mode::Vis => {}
+ Mode::Type => opt_path_type_args(p),
+ Mode::Expr => generic_args::opt_generic_arg_list_expr(p),
}
}
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs
index f4e57d3d6f..0133b7d5d8 100644
--- a/crates/parser/src/grammar/types.rs
+++ b/crates/parser/src/grammar/types.rs
@@ -50,7 +50,7 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) {
// Some path types are not allowed to have bounds (no plus)
T![<] => path_type_bounds(p, allow_bounds),
T![ident] if !p.edition().at_least_2018() && is_dyn_weak(p) => dyn_trait_type_weak(p),
- _ if paths::is_path_start(p) => path_or_macro_type_(p, allow_bounds),
+ _ if paths::is_path_start(p) => path_or_macro_type(p, allow_bounds),
LIFETIME_IDENT if p.nth_at(1, T![+]) => bare_dyn_trait_type(p),
_ => {
p.err_recover("expected type", TYPE_RECOVERY_SET);
@@ -58,7 +58,7 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) {
}
}
-fn is_dyn_weak(p: &Parser<'_>) -> bool {
+pub(crate) fn is_dyn_weak(p: &Parser<'_>) -> bool {
const WEAK_DYN_PATH_FIRST: TokenSet = TokenSet::new(&[
IDENT,
T![self],
@@ -337,7 +337,7 @@ pub(super) fn path_type(p: &mut Parser<'_>) {
// test macro_call_type
// type A = foo!();
// type B = crate::foo!();
-fn path_or_macro_type_(p: &mut Parser<'_>, allow_bounds: bool) {
+fn path_or_macro_type(p: &mut Parser<'_>, allow_bounds: bool) {
assert!(paths::is_path_start(p));
let r = p.start();
let m = p.start();
diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs
index f6b3783d1c..75a75f601c 100644
--- a/crates/parser/src/parser.rs
+++ b/crates/parser/src/parser.rs
@@ -258,22 +258,25 @@ impl<'t> Parser<'t> {
self.err_recover(message, TokenSet::EMPTY);
}
- /// Create an error node and consume the next token.
- pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) {
+ /// Create an error node and consume the next token unless it is in the recovery set.
+ ///
+ /// Returns true if recovery kicked in.
+ pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) -> bool {
if matches!(self.current(), T!['{'] | T!['}']) {
self.error(message);
- return;
+ return true;
}
if self.at_ts(recovery) {
self.error(message);
- return;
+ return true;
}
let m = self.start();
self.error(message);
self.bump_any();
m.complete(self, ERROR);
+ false
}
fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
@@ -324,10 +327,10 @@ impl Marker {
self.bomb.defuse();
let idx = self.pos as usize;
if idx == p.events.len() - 1 {
- match p.events.pop() {
- Some(Event::Start { kind: TOMBSTONE, forward_parent: None }) => (),
- _ => unreachable!(),
- }
+ assert!(matches!(
+ p.events.pop(),
+ Some(Event::Start { kind: TOMBSTONE, forward_parent: None })
+ ));
}
}
}
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 21730244a3..0c9c6ffd71 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -256,6 +256,7 @@ pub enum SyntaxKind {
OR_PAT,
PARAM,
PARAM_LIST,
+ PARENTHESIZED_ARG_LIST,
PAREN_EXPR,
PAREN_PAT,
PAREN_TYPE,
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs
index 62b381b668..003b7fda94 100644
--- a/crates/parser/test_data/generated/runner.rs
+++ b/crates/parser/test_data/generated/runner.rs
@@ -39,10 +39,6 @@ mod ok {
#[test]
fn assoc_type_eq() { run_and_expect_no_errors("test_data/parser/inline/ok/assoc_type_eq.rs"); }
#[test]
- fn associated_type_bounds() {
- run_and_expect_no_errors("test_data/parser/inline/ok/associated_type_bounds.rs");
- }
- #[test]
fn async_trait_bound() {
run_and_expect_no_errors("test_data/parser/inline/ok/async_trait_bound.rs");
}
@@ -59,12 +55,6 @@ mod ok {
);
}
#[test]
- fn bare_dyn_types_with_paren_as_generic_args() {
- run_and_expect_no_errors(
- "test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rs",
- );
- }
- #[test]
fn become_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/become_expr.rs"); }
#[test]
fn bind_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/bind_pat.rs"); }
@@ -200,6 +190,13 @@ mod ok {
);
}
#[test]
+ fn edition_2015_dyn_prefix_inside_generic_arg() {
+ run_and_expect_no_errors_with_edition(
+ "test_data/parser/inline/ok/edition_2015_dyn_prefix_inside_generic_arg.rs",
+ crate::Edition::Edition2015,
+ );
+ }
+ #[test]
fn effect_blocks() { run_and_expect_no_errors("test_data/parser/inline/ok/effect_blocks.rs"); }
#[test]
fn exclusive_range_pat() {
@@ -220,10 +217,6 @@ mod ok {
run_and_expect_no_errors("test_data/parser/inline/ok/extern_crate_rename.rs");
}
#[test]
- fn extern_crate_self() {
- run_and_expect_no_errors("test_data/parser/inline/ok/extern_crate_self.rs");
- }
- #[test]
fn field_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/field_expr.rs"); }
#[test]
fn fn_() { run_and_expect_no_errors("test_data/parser/inline/ok/fn_.rs"); }
@@ -281,6 +274,10 @@ mod ok {
#[test]
fn generic_arg() { run_and_expect_no_errors("test_data/parser/inline/ok/generic_arg.rs"); }
#[test]
+ fn generic_arg_bounds() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/generic_arg_bounds.rs");
+ }
+ #[test]
fn generic_param_attribute() {
run_and_expect_no_errors("test_data/parser/inline/ok/generic_param_attribute.rs");
}
@@ -423,10 +420,6 @@ mod ok {
#[test]
fn param_list() { run_and_expect_no_errors("test_data/parser/inline/ok/param_list.rs"); }
#[test]
- fn param_list_opt_patterns() {
- run_and_expect_no_errors("test_data/parser/inline/ok/param_list_opt_patterns.rs");
- }
- #[test]
fn param_list_vararg() {
run_and_expect_no_errors("test_data/parser/inline/ok/param_list_vararg.rs");
}
@@ -521,12 +514,6 @@ mod ok {
#[test]
fn return_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/return_expr.rs"); }
#[test]
- fn return_type_syntax_assoc_type_bound() {
- run_and_expect_no_errors(
- "test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rs",
- );
- }
- #[test]
fn return_type_syntax_in_path() {
run_and_expect_no_errors("test_data/parser/inline/ok/return_type_syntax_in_path.rs");
}
@@ -685,10 +672,6 @@ mod ok {
#[test]
fn use_tree_star() { run_and_expect_no_errors("test_data/parser/inline/ok/use_tree_star.rs"); }
#[test]
- fn value_parameters_no_patterns() {
- run_and_expect_no_errors("test_data/parser/inline/ok/value_parameters_no_patterns.rs");
- }
- #[test]
fn variant_discriminant() {
run_and_expect_no_errors("test_data/parser/inline/ok/variant_discriminant.rs");
}
@@ -728,6 +711,8 @@ mod err {
run_and_expect_errors("test_data/parser/inline/err/async_without_semicolon.rs");
}
#[test]
+ fn bad_asm_expr() { run_and_expect_errors("test_data/parser/inline/err/bad_asm_expr.rs"); }
+ #[test]
fn comma_after_functional_update_syntax() {
run_and_expect_errors(
"test_data/parser/inline/err/comma_after_functional_update_syntax.rs",
@@ -754,6 +739,10 @@ mod err {
run_and_expect_errors("test_data/parser/inline/err/generic_arg_list_recover.rs");
}
#[test]
+ fn generic_arg_list_recover_expr() {
+ run_and_expect_errors("test_data/parser/inline/err/generic_arg_list_recover_expr.rs");
+ }
+ #[test]
fn generic_param_list_recover() {
run_and_expect_errors("test_data/parser/inline/err/generic_param_list_recover.rs");
}
@@ -772,6 +761,8 @@ mod err {
run_and_expect_errors("test_data/parser/inline/err/match_arms_recovery.rs");
}
#[test]
+ fn meta_recovery() { run_and_expect_errors("test_data/parser/inline/err/meta_recovery.rs"); }
+ #[test]
fn method_call_missing_argument_list() {
run_and_expect_errors("test_data/parser/inline/err/method_call_missing_argument_list.rs");
}
@@ -788,6 +779,10 @@ mod err {
run_and_expect_errors("test_data/parser/inline/err/pointer_type_no_mutability.rs");
}
#[test]
+ fn precise_capturing_invalid() {
+ run_and_expect_errors("test_data/parser/inline/err/precise_capturing_invalid.rs");
+ }
+ #[test]
fn pub_expr() { run_and_expect_errors("test_data/parser/inline/err/pub_expr.rs"); }
#[test]
fn record_literal_before_ellipsis_recovery() {
diff --git a/crates/parser/test_data/parser/err/0004_use_path_bad_segment.rast b/crates/parser/test_data/parser/err/0004_use_path_bad_segment.rast
index 44e192a5fc..7273c98456 100644
--- a/crates/parser/test_data/parser/err/0004_use_path_bad_segment.rast
+++ b/crates/parser/test_data/parser/err/0004_use_path_bad_segment.rast
@@ -9,7 +9,8 @@ SOURCE_FILE
NAME_REF
IDENT "foo"
COLON2 "::"
- ERROR
- INT_NUMBER "92"
+ PATH_SEGMENT
+ ERROR
+ INT_NUMBER "92"
SEMICOLON ";"
-error 9: expected identifier
+error 9: expected identifier, `self`, `super`, `crate`, or `Self`
diff --git a/crates/parser/test_data/parser/err/0048_double_fish.rast b/crates/parser/test_data/parser/err/0048_double_fish.rast
index 207a5c24df..47daaf7b3b 100644
--- a/crates/parser/test_data/parser/err/0048_double_fish.rast
+++ b/crates/parser/test_data/parser/err/0048_double_fish.rast
@@ -39,8 +39,9 @@ SOURCE_FILE
IDENT "lol"
R_ANGLE ">"
COLON2 "::"
- ERROR
- L_ANGLE "<"
+ PATH_SEGMENT
+ ERROR
+ L_ANGLE "<"
TYPE_ARG
PATH_TYPE
PATH
@@ -91,8 +92,9 @@ SOURCE_FILE
IDENT "lol"
R_ANGLE ">"
COLON2 "::"
- ERROR
- L_ANGLE "<"
+ PATH_SEGMENT
+ ERROR
+ L_ANGLE "<"
EXPR_STMT
BIN_EXPR
PATH_EXPR
@@ -113,10 +115,10 @@ SOURCE_FILE
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
-error 30: expected identifier
+error 30: expected identifier, `self`, `super`, `crate`, or `Self`
error 31: expected COMMA
error 37: expected expression
-error 75: expected identifier
+error 75: expected identifier, `self`, `super`, `crate`, or `Self`
error 76: expected SEMICOLON
error 82: expected expression
error 83: expected SEMICOLON
diff --git a/crates/parser/test_data/parser/inline/err/arg_list_recovery.rast b/crates/parser/test_data/parser/inline/err/arg_list_recovery.rast
index cd5aa680c6..755b20bb27 100644
--- a/crates/parser/test_data/parser/inline/err/arg_list_recovery.rast
+++ b/crates/parser/test_data/parser/inline/err/arg_list_recovery.rast
@@ -98,7 +98,7 @@ SOURCE_FILE
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
-error 25: expected identifier
+error 25: expected identifier, `self`, `super`, `crate`, or `Self`
error 39: expected COMMA
error 39: expected expression
error 55: expected expression
diff --git a/crates/parser/test_data/parser/inline/err/bad_asm_expr.rast b/crates/parser/test_data/parser/inline/err/bad_asm_expr.rast
new file mode 100644
index 0000000000..306446e64d
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/bad_asm_expr.rast
@@ -0,0 +1,50 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ ASM_EXPR
+ BUILTIN_KW "builtin"
+ POUND "#"
+ ASM_KW "asm"
+ L_PAREN "("
+ WHITESPACE "\n "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "label"
+ WHITESPACE " "
+ NAME
+ IDENT "crashy"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ EXPR_STMT
+ RETURN_EXPR
+ RETURN_KW "return"
+ SEMICOLON ";"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE "\n "
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
+error 41: expected COMMA
+error 50: expected asm operand
diff --git a/crates/parser/test_data/parser/inline/err/bad_asm_expr.rs b/crates/parser/test_data/parser/inline/err/bad_asm_expr.rs
new file mode 100644
index 0000000000..6056f925e3
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/bad_asm_expr.rs
@@ -0,0 +1,5 @@
+fn foo() {
+ builtin#asm(
+ label crashy = { return; }
+ );
+}
diff --git a/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast b/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast
index 0fe4ca42d7..172bc099b5 100644
--- a/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast
+++ b/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast
@@ -3,10 +3,7 @@ SOURCE_FILE
VISIBILITY
PUB_KW "pub"
L_PAREN "("
- PATH
- PATH_SEGMENT
- ERROR
- R_PAREN ")"
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
@@ -14,5 +11,4 @@ SOURCE_FILE
IDENT "S"
SEMICOLON ";"
WHITESPACE "\n"
-error 4: expected identifier
-error 5: expected R_PAREN
+error 4: expected identifier, `self`, `super`, `crate`, or `Self`
diff --git a/crates/parser/test_data/parser/inline/err/empty_segment.rast b/crates/parser/test_data/parser/inline/err/empty_segment.rast
index b03f5ad9f7..7f256218ef 100644
--- a/crates/parser/test_data/parser/inline/err/empty_segment.rast
+++ b/crates/parser/test_data/parser/inline/err/empty_segment.rast
@@ -11,4 +11,4 @@ SOURCE_FILE
COLON2 "::"
SEMICOLON ";"
WHITESPACE "\n"
-error 11: expected identifier
+error 11: expected identifier, `self`, `super`, `crate`, or `Self`
diff --git a/crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rast b/crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rast
index 4cf5a3386b..16183ce9ef 100644
--- a/crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rast
+++ b/crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rast
@@ -30,4 +30,37 @@ SOURCE_FILE
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "T"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ GENERIC_ARG_LIST
+ COLON2 "::"
+ L_ANGLE "<"
+ CONST_ARG
+ LITERAL
+ INT_NUMBER "0"
+ COMMA ","
+ WHITESPACE " "
+ ERROR
+ COMMA ","
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
error 14: expected generic argument
+error 35: expected generic argument
diff --git a/crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rs b/crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rs
index 7d849aa1be..aa65a0673a 100644
--- a/crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rs
+++ b/crates/parser/test_data/parser/inline/err/generic_arg_list_recover.rs
@@ -1 +1,2 @@
type T = T<0, ,T>;
+type T = T::<0, ,T>;
diff --git a/crates/parser/test_data/parser/inline/err/generic_arg_list_recover_expr.rast b/crates/parser/test_data/parser/inline/err/generic_arg_list_recover_expr.rast
new file mode 100644
index 0000000000..de403d3049
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/generic_arg_list_recover_expr.rast
@@ -0,0 +1,79 @@
+SOURCE_FILE
+ CONST
+ CONST_KW "const"
+ WHITESPACE " "
+ UNDERSCORE "_"
+ COLON ":"
+ WHITESPACE " "
+ TUPLE_TYPE
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ GENERIC_ARG_LIST
+ COLON2 "::"
+ L_ANGLE "<"
+ CONST_ARG
+ LITERAL
+ INT_NUMBER "0"
+ COMMA ","
+ WHITESPACE " "
+ ERROR
+ COMMA ","
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ CONST
+ CONST_KW "const"
+ WHITESPACE " "
+ UNDERSCORE "_"
+ COLON ":"
+ WHITESPACE " "
+ TUPLE_TYPE
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ GENERIC_ARG_LIST
+ COLON2 "::"
+ L_ANGLE "<"
+ CONST_ARG
+ LITERAL
+ INT_NUMBER "0"
+ COMMA ","
+ WHITESPACE " "
+ ERROR
+ COMMA ","
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_ANGLE ">"
+ ARG_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 21: expected generic argument
+error 47: expected generic argument
diff --git a/crates/parser/test_data/parser/inline/err/generic_arg_list_recover_expr.rs b/crates/parser/test_data/parser/inline/err/generic_arg_list_recover_expr.rs
new file mode 100644
index 0000000000..74cc63ac4a
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/generic_arg_list_recover_expr.rs
@@ -0,0 +1,2 @@
+const _: () = T::<0, ,T>;
+const _: () = T::<0, ,T>();
diff --git a/crates/parser/test_data/parser/inline/err/meta_recovery.rast b/crates/parser/test_data/parser/inline/err/meta_recovery.rast
new file mode 100644
index 0000000000..b5c16e0798
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/meta_recovery.rast
@@ -0,0 +1,83 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "p"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "p"
+ COLON2 "::"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "p"
+ COLON2 "::"
+ WHITESPACE " "
+ EQ "="
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ UNSAFE_KW "unsafe"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ META
+ UNSAFE_KW "unsafe"
+ WHITESPACE " "
+ EQ "="
+ R_BRACK "]"
+ WHITESPACE "\n"
+error 3: expected identifier, `self`, `super`, `crate`, or `Self`
+error 11: expected expression
+error 11: expected expression
+error 20: expected identifier, `self`, `super`, `crate`, or `Self`
+error 28: expected identifier, `self`, `super`, `crate`, or `Self`
+error 30: expected expression
+error 30: expected expression
+error 41: expected L_PAREN
+error 41: expected identifier, `self`, `super`, `crate`, or `Self`
+error 41: expected R_PAREN
+error 52: expected L_PAREN
+error 52: expected identifier, `self`, `super`, `crate`, or `Self`
+error 54: expected expression
+error 54: expected expression
+error 54: expected R_PAREN
diff --git a/crates/parser/test_data/parser/inline/err/meta_recovery.rs b/crates/parser/test_data/parser/inline/err/meta_recovery.rs
new file mode 100644
index 0000000000..51d30adf8b
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/meta_recovery.rs
@@ -0,0 +1,6 @@
+#![]
+#![p = ]
+#![p::]
+#![p:: =]
+#![unsafe]
+#![unsafe =]
diff --git a/crates/parser/test_data/parser/inline/err/precise_capturing_invalid.rast b/crates/parser/test_data/parser/inline/err/precise_capturing_invalid.rast
new file mode 100644
index 0000000000..5ae184c5fe
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/precise_capturing_invalid.rast
@@ -0,0 +1,28 @@
+SOURCE_FILE
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "T"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ IMPL_TRAIT_TYPE
+ IMPL_KW "impl"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ USE_KW "use"
+ USE_BOUND_GENERIC_ARGS
+ L_ANGLE "<"
+ ERROR
+ SELF_KW "self"
+ COMMA ","
+ WHITESPACE " "
+ ERROR
+ INT_NUMBER "1"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 18: expected identifier or `Self`
+error 24: expected identifier or `Self`
diff --git a/crates/parser/test_data/parser/inline/err/precise_capturing_invalid.rs b/crates/parser/test_data/parser/inline/err/precise_capturing_invalid.rs
new file mode 100644
index 0000000000..3180338d33
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/precise_capturing_invalid.rs
@@ -0,0 +1 @@
+type T = impl use<self, 1>;
diff --git a/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast b/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
index 741b7845e7..08ae906421 100644
--- a/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
+++ b/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast
@@ -12,6 +12,38 @@ SOURCE_FILE
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
+ EXPR_STMT
+ RECORD_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ WHITESPACE " "
+ RECORD_EXPR_FIELD_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ RECORD_EXPR_FIELD
+ NAME_REF
+ IDENT "field"
+ WHITESPACE " "
+ DOT2 ".."
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "default"
+ ARG_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE "\n "
RECORD_EXPR
PATH
PATH_SEGMENT
@@ -23,7 +55,7 @@ SOURCE_FILE
WHITESPACE " "
RECORD_EXPR_FIELD
NAME_REF
- IDENT "field"
+ INT_NUMBER "0"
WHITESPACE " "
DOT2 ".."
CALL_EXPR
@@ -47,3 +79,6 @@ SOURCE_FILE
WHITESPACE "\n"
error 25: expected `:`
error 25: expected COMMA
+error 42: expected SEMICOLON
+error 52: expected `:`
+error 52: expected COMMA
diff --git a/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs b/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
index a4e5b2f693..65398ccb88 100644
--- a/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
+++ b/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs
@@ -1,3 +1,4 @@
fn main() {
S { field ..S::default() }
+ S { 0 ..S::default() }
}
diff --git a/crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rast b/crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rast
index ad4deeb0b6..ad3b6f208e 100644
--- a/crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rast
+++ b/crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rast
@@ -12,6 +12,31 @@ SOURCE_FILE
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
+ EXPR_STMT
+ RECORD_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ WHITESPACE " "
+ RECORD_EXPR_FIELD_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ RECORD_EXPR_FIELD
+ NAME_REF
+ IDENT "field"
+ WHITESPACE " "
+ ERROR
+ EQ "="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "foo"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE "\n "
RECORD_EXPR
PATH
PATH_SEGMENT
@@ -23,7 +48,7 @@ SOURCE_FILE
WHITESPACE " "
RECORD_EXPR_FIELD
NAME_REF
- IDENT "field"
+ INT_NUMBER "0"
WHITESPACE " "
ERROR
EQ "="
@@ -39,3 +64,5 @@ SOURCE_FILE
R_CURLY "}"
WHITESPACE "\n"
error 26: expected `:`
+error 33: expected SEMICOLON
+error 44: expected `:`
diff --git a/crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rs b/crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rs
index 1eb1aa9b92..9ddc46e0da 100644
--- a/crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rs
+++ b/crates/parser/test_data/parser/inline/err/record_literal_field_eq_recovery.rs
@@ -1,3 +1,4 @@
fn main() {
S { field = foo }
+ S { 0 = foo }
}
diff --git a/crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rast b/crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rast
index 0c5b618e6f..9cd07d2ea3 100644
--- a/crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rast
+++ b/crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rast
@@ -12,32 +12,70 @@ SOURCE_FILE
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
- RECORD_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "S"
- WHITESPACE " "
- RECORD_EXPR_FIELD_LIST
- L_CURLY "{"
+ EXPR_STMT
+ RECORD_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
WHITESPACE " "
- CALL_EXPR
- PATH_EXPR
- PATH
+ RECORD_EXPR_FIELD_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ CALL_EXPR
+ PATH_EXPR
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "S"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "default"
- ARG_LIST
- L_PAREN "("
- R_PAREN ")"
+ IDENT "default"
+ ARG_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ RECORD_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
WHITESPACE " "
- R_CURLY "}"
+ RECORD_EXPR_FIELD_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "0"
+ ERROR
+ COLON ":"
+ ERROR
+ COLON ":"
+ RECORD_EXPR_FIELD
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "default"
+ ARG_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ R_CURLY "}"
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
error 19: expected DOT2
+error 43: expected DOT2
+error 45: expected COMMA
+error 45: expected identifier
+error 46: expected COMMA
+error 46: expected identifier
+error 47: expected COMMA
diff --git a/crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rs b/crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rs
index 1b594e8ab9..a63c3c9e7c 100644
--- a/crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rs
+++ b/crates/parser/test_data/parser/inline/err/record_literal_missing_ellipsis_recovery.rs
@@ -1,3 +1,4 @@
fn main() {
- S { S::default() }
+ S { S::default() };
+ S { 0::default() };
}
diff --git a/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rast b/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rast
index f2e4e01069..0f62e1dd18 100644
--- a/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rast
+++ b/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rast
@@ -35,3 +35,42 @@ SOURCE_FILE
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "T"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "StreamingIterator"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ PARENTHESIZED_ARG_LIST
+ L_PAREN "("
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Clone"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rs b/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rs
index daae97e4fd..83fea1c6c8 100644
--- a/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rs
+++ b/crates/parser/test_data/parser/inline/ok/assoc_type_bound.rs
@@ -1 +1,2 @@
type T = StreamingIterator<Item<'a>: Clone>;
+type T = StreamingIterator<Item(T): Clone>;
diff --git a/crates/parser/test_data/parser/inline/ok/associated_type_bounds.rast b/crates/parser/test_data/parser/inline/ok/associated_type_bounds.rast
deleted file mode 100644
index 8cbc98c51c..0000000000
--- a/crates/parser/test_data/parser/inline/ok/associated_type_bounds.rast
+++ /dev/null
@@ -1,111 +0,0 @@
-SOURCE_FILE
- FN
- FN_KW "fn"
- WHITESPACE " "
- NAME
- IDENT "print_all"
- GENERIC_PARAM_LIST
- L_ANGLE "<"
- TYPE_PARAM
- NAME
- IDENT "T"
- COLON ":"
- WHITESPACE " "
- TYPE_BOUND_LIST
- TYPE_BOUND
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Iterator"
- GENERIC_ARG_LIST
- L_ANGLE "<"
- TYPE_ARG
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Item"
- COMMA ","
- WHITESPACE " "
- TYPE_ARG
- PATH_TYPE
- PATH
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Item"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "Item"
- COMMA ","
- WHITESPACE " "
- TYPE_ARG
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Item"
- GENERIC_ARG_LIST
- COLON2 "::"
- L_ANGLE "<"
- CONST_ARG
- LITERAL
- TRUE_KW "true"
- R_ANGLE ">"
- COMMA ","
- WHITESPACE " "
- ASSOC_TYPE_ARG
- NAME_REF
- IDENT "Item"
- COLON ":"
- WHITESPACE " "
- TYPE_BOUND_LIST
- TYPE_BOUND
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Display"
- COMMA ","
- WHITESPACE " "
- ASSOC_TYPE_ARG
- NAME_REF
- IDENT "Item"
- GENERIC_ARG_LIST
- L_ANGLE "<"
- LIFETIME_ARG
- LIFETIME
- LIFETIME_IDENT "'a"
- R_ANGLE ">"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Item"
- R_ANGLE ">"
- R_ANGLE ">"
- PARAM_LIST
- L_PAREN "("
- PARAM
- IDENT_PAT
- NAME
- IDENT "printables"
- COLON ":"
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "T"
- R_PAREN ")"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- R_CURLY "}"
- WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/associated_type_bounds.rs b/crates/parser/test_data/parser/inline/ok/associated_type_bounds.rs
deleted file mode 100644
index 0f7a2d1608..0000000000
--- a/crates/parser/test_data/parser/inline/ok/associated_type_bounds.rs
+++ /dev/null
@@ -1 +0,0 @@
-fn print_all<T: Iterator<Item, Item::Item, Item::<true>, Item: Display, Item<'a> = Item>>(printables: T) {}
diff --git a/crates/parser/test_data/parser/inline/ok/async_trait_bound.rast b/crates/parser/test_data/parser/inline/ok/async_trait_bound.rast
index ebf758286a..df0ba55d03 100644
--- a/crates/parser/test_data/parser/inline/ok/async_trait_bound.rast
+++ b/crates/parser/test_data/parser/inline/ok/async_trait_bound.rast
@@ -23,9 +23,9 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "Fn"
- PARAM_LIST
+ PARENTHESIZED_ARG_LIST
L_PAREN "("
- PARAM
+ TYPE_ARG
REF_TYPE
AMP "&"
PATH_TYPE
diff --git a/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rast b/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rast
deleted file mode 100644
index d5f97bad89..0000000000
--- a/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rast
+++ /dev/null
@@ -1,175 +0,0 @@
-SOURCE_FILE
- TYPE_ALIAS
- TYPE_KW "type"
- WHITESPACE " "
- NAME
- IDENT "A"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "S"
- GENERIC_ARG_LIST
- L_ANGLE "<"
- TYPE_ARG
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Fn"
- PARAM_LIST
- L_PAREN "("
- PARAM
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "i32"
- R_PAREN ")"
- R_ANGLE ">"
- SEMICOLON ";"
- WHITESPACE "\n"
- TYPE_ALIAS
- TYPE_KW "type"
- WHITESPACE " "
- NAME
- IDENT "A"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "S"
- GENERIC_ARG_LIST
- L_ANGLE "<"
- TYPE_ARG
- DYN_TRAIT_TYPE
- TYPE_BOUND_LIST
- TYPE_BOUND
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Fn"
- PARAM_LIST
- L_PAREN "("
- PARAM
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "i32"
- R_PAREN ")"
- WHITESPACE " "
- PLUS "+"
- WHITESPACE " "
- TYPE_BOUND
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Send"
- R_ANGLE ">"
- SEMICOLON ";"
- WHITESPACE "\n"
- TYPE_ALIAS
- TYPE_KW "type"
- WHITESPACE " "
- NAME
- IDENT "B"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "S"
- GENERIC_ARG_LIST
- L_ANGLE "<"
- TYPE_ARG
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Fn"
- PARAM_LIST
- L_PAREN "("
- PARAM
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "i32"
- R_PAREN ")"
- WHITESPACE " "
- RET_TYPE
- THIN_ARROW "->"
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "i32"
- R_ANGLE ">"
- SEMICOLON ";"
- WHITESPACE "\n"
- TYPE_ALIAS
- TYPE_KW "type"
- WHITESPACE " "
- NAME
- IDENT "C"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "S"
- GENERIC_ARG_LIST
- L_ANGLE "<"
- TYPE_ARG
- DYN_TRAIT_TYPE
- TYPE_BOUND_LIST
- TYPE_BOUND
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Fn"
- PARAM_LIST
- L_PAREN "("
- PARAM
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "i32"
- R_PAREN ")"
- WHITESPACE " "
- RET_TYPE
- THIN_ARROW "->"
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "i32"
- WHITESPACE " "
- PLUS "+"
- WHITESPACE " "
- TYPE_BOUND
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Send"
- R_ANGLE ">"
- SEMICOLON ";"
- WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rs b/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rs
deleted file mode 100644
index 800002b1b8..0000000000
--- a/crates/parser/test_data/parser/inline/ok/bare_dyn_types_with_paren_as_generic_args.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-type A = S<Fn(i32)>;
-type A = S<Fn(i32) + Send>;
-type B = S<Fn(i32) -> i32>;
-type C = S<Fn(i32) -> i32 + Send>;
diff --git a/crates/parser/test_data/parser/inline/ok/edition_2015_dyn_prefix_inside_generic_arg.rast b/crates/parser/test_data/parser/inline/ok/edition_2015_dyn_prefix_inside_generic_arg.rast
new file mode 100644
index 0000000000..24e671b343
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/edition_2015_dyn_prefix_inside_generic_arg.rast
@@ -0,0 +1,32 @@
+SOURCE_FILE
+ TYPE_ALIAS
+ COMMENT "// 2015"
+ WHITESPACE "\n"
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "A"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Foo"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ DYN_TRAIT_TYPE
+ DYN_KW "dyn"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/edition_2015_dyn_prefix_inside_generic_arg.rs b/crates/parser/test_data/parser/inline/ok/edition_2015_dyn_prefix_inside_generic_arg.rs
new file mode 100644
index 0000000000..84cece5748
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/edition_2015_dyn_prefix_inside_generic_arg.rs
@@ -0,0 +1,2 @@
+// 2015
+type A = Foo<dyn T>;
diff --git a/crates/parser/test_data/parser/inline/ok/extern_crate.rast b/crates/parser/test_data/parser/inline/ok/extern_crate.rast
index 0a660957d1..aa555ed229 100644
--- a/crates/parser/test_data/parser/inline/ok/extern_crate.rast
+++ b/crates/parser/test_data/parser/inline/ok/extern_crate.rast
@@ -8,3 +8,12 @@ SOURCE_FILE
IDENT "foo"
SEMICOLON ";"
WHITESPACE "\n"
+ EXTERN_CRATE
+ EXTERN_KW "extern"
+ WHITESPACE " "
+ CRATE_KW "crate"
+ WHITESPACE " "
+ NAME_REF
+ SELF_KW "self"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/extern_crate.rs b/crates/parser/test_data/parser/inline/ok/extern_crate.rs
index 49af74e1b7..3c498c8738 100644
--- a/crates/parser/test_data/parser/inline/ok/extern_crate.rs
+++ b/crates/parser/test_data/parser/inline/ok/extern_crate.rs
@@ -1 +1,2 @@
extern crate foo;
+extern crate self;
diff --git a/crates/parser/test_data/parser/inline/ok/extern_crate_rename.rast b/crates/parser/test_data/parser/inline/ok/extern_crate_rename.rast
index 5a5aca96f9..5f0a5b5bb0 100644
--- a/crates/parser/test_data/parser/inline/ok/extern_crate_rename.rast
+++ b/crates/parser/test_data/parser/inline/ok/extern_crate_rename.rast
@@ -14,3 +14,18 @@ SOURCE_FILE
IDENT "bar"
SEMICOLON ";"
WHITESPACE "\n"
+ EXTERN_CRATE
+ EXTERN_KW "extern"
+ WHITESPACE " "
+ CRATE_KW "crate"
+ WHITESPACE " "
+ NAME_REF
+ SELF_KW "self"
+ WHITESPACE " "
+ RENAME
+ AS_KW "as"
+ WHITESPACE " "
+ NAME
+ IDENT "bar"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/extern_crate_rename.rs b/crates/parser/test_data/parser/inline/ok/extern_crate_rename.rs
index fc76e17dda..6d1873d659 100644
--- a/crates/parser/test_data/parser/inline/ok/extern_crate_rename.rs
+++ b/crates/parser/test_data/parser/inline/ok/extern_crate_rename.rs
@@ -1 +1,2 @@
extern crate foo as bar;
+extern crate self as bar;
diff --git a/crates/parser/test_data/parser/inline/ok/extern_crate_self.rast b/crates/parser/test_data/parser/inline/ok/extern_crate_self.rast
deleted file mode 100644
index edea4245f2..0000000000
--- a/crates/parser/test_data/parser/inline/ok/extern_crate_self.rast
+++ /dev/null
@@ -1,10 +0,0 @@
-SOURCE_FILE
- EXTERN_CRATE
- EXTERN_KW "extern"
- WHITESPACE " "
- CRATE_KW "crate"
- WHITESPACE " "
- NAME_REF
- SELF_KW "self"
- SEMICOLON ";"
- WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/extern_crate_self.rs b/crates/parser/test_data/parser/inline/ok/extern_crate_self.rs
deleted file mode 100644
index c969ed1093..0000000000
--- a/crates/parser/test_data/parser/inline/ok/extern_crate_self.rs
+++ /dev/null
@@ -1 +0,0 @@
-extern crate self;
diff --git a/crates/parser/test_data/parser/inline/ok/field_expr.rast b/crates/parser/test_data/parser/inline/ok/field_expr.rast
index dd27dc4896..3bac226d34 100644
--- a/crates/parser/test_data/parser/inline/ok/field_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/field_expr.rast
@@ -21,6 +21,30 @@ SOURCE_FILE
IDENT "x"
DOT "."
NAME_REF
+ SELF_KW "self"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ FIELD_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "x"
+ DOT "."
+ NAME_REF
+ SELF_TYPE_KW "Self"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ FIELD_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "x"
+ DOT "."
+ NAME_REF
IDENT "foo"
SEMICOLON ";"
WHITESPACE "\n "
diff --git a/crates/parser/test_data/parser/inline/ok/field_expr.rs b/crates/parser/test_data/parser/inline/ok/field_expr.rs
index 98dbe45a7e..b156d6f7d8 100644
--- a/crates/parser/test_data/parser/inline/ok/field_expr.rs
+++ b/crates/parser/test_data/parser/inline/ok/field_expr.rs
@@ -1,4 +1,6 @@
fn foo() {
+ x.self;
+ x.Self;
x.foo;
x.0.bar;
x.0.1;
diff --git a/crates/parser/test_data/parser/inline/ok/generic_arg.rast b/crates/parser/test_data/parser/inline/ok/generic_arg.rast
index 5a01f154ba..e32cf08565 100644
--- a/crates/parser/test_data/parser/inline/ok/generic_arg.rast
+++ b/crates/parser/test_data/parser/inline/ok/generic_arg.rast
@@ -20,6 +20,27 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "i32"
+ COMMA ","
+ WHITESPACE " "
+ TYPE_ARG
+ DYN_TRAIT_TYPE
+ DYN_KW "dyn"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ COMMA ","
+ WHITESPACE " "
+ TYPE_ARG
+ FN_PTR_TYPE
+ FN_KW "fn"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/generic_arg.rs b/crates/parser/test_data/parser/inline/ok/generic_arg.rs
index f2ccc558bb..cf991b5b36 100644
--- a/crates/parser/test_data/parser/inline/ok/generic_arg.rs
+++ b/crates/parser/test_data/parser/inline/ok/generic_arg.rs
@@ -1 +1 @@
-type T = S<i32>;
+type T = S<i32, dyn T, fn()>;
diff --git a/crates/parser/test_data/parser/inline/ok/generic_arg_bounds.rast b/crates/parser/test_data/parser/inline/ok/generic_arg_bounds.rast
new file mode 100644
index 0000000000..fee5913aca
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/generic_arg_bounds.rast
@@ -0,0 +1,461 @@
+SOURCE_FILE
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "Plain"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Foo"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ COMMA ","
+ WHITESPACE " "
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Bound"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "GenericArgs"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Foo"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_ANGLE ">"
+ COMMA ","
+ WHITESPACE " "
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ GENERIC_ARG_LIST
+ COLON2 "::"
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_ANGLE ">"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_ANGLE ">"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Bound"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ GENERIC_ARG_LIST
+ COLON2 "::"
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_ANGLE ">"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Bound"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_ANGLE ">"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ GENERIC_ARG_LIST
+ COLON2 "::"
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_ANGLE ">"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "ParenthesizedArgs"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Foo"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ PARENTHESIZED_ARG_LIST
+ L_PAREN "("
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE " "
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ PARENTHESIZED_ARG_LIST
+ COLON2 "::"
+ L_PAREN "("
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ PARENTHESIZED_ARG_LIST
+ L_PAREN "("
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Bound"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ PARENTHESIZED_ARG_LIST
+ COLON2 "::"
+ L_PAREN "("
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Bound"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ PARENTHESIZED_ARG_LIST
+ L_PAREN "("
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ PARENTHESIZED_ARG_LIST
+ COLON2 "::"
+ L_PAREN "("
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "RTN"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Foo"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ RETURN_TYPE_SYNTAX
+ L_PAREN "("
+ DOT2 ".."
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE " "
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ RETURN_TYPE_SYNTAX
+ L_PAREN "("
+ DOT2 ".."
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ RETURN_TYPE_SYNTAX
+ L_PAREN "("
+ DOT2 ".."
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Bound"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ RETURN_TYPE_SYNTAX
+ L_PAREN "("
+ DOT2 ".."
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Bound"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ RETURN_TYPE_SYNTAX
+ L_PAREN "("
+ DOT2 ".."
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "Item"
+ RETURN_TYPE_SYNTAX
+ L_PAREN "("
+ DOT2 ".."
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Item"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/generic_arg_bounds.rs b/crates/parser/test_data/parser/inline/ok/generic_arg_bounds.rs
new file mode 100644
index 0000000000..1abd0aeb59
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/generic_arg_bounds.rs
@@ -0,0 +1,4 @@
+type Plain = Foo<Item, Item::Item, Item: Bound, Item = Item>;
+type GenericArgs = Foo<Item<T>, Item::<T>, Item<T>: Bound, Item::<T>: Bound, Item<T> = Item, Item::<T> = Item>;
+type ParenthesizedArgs = Foo<Item(T), Item::(T), Item(T): Bound, Item::(T): Bound, Item(T) = Item, Item::(T) = Item>;
+type RTN = Foo<Item(..), Item(..), Item(..): Bound, Item(..): Bound, Item(..) = Item, Item(..) = Item>;
diff --git a/crates/parser/test_data/parser/inline/ok/lifetime_param.rast b/crates/parser/test_data/parser/inline/ok/lifetime_param.rast
index c595031f35..315200aca2 100644
--- a/crates/parser/test_data/parser/inline/ok/lifetime_param.rast
+++ b/crates/parser/test_data/parser/inline/ok/lifetime_param.rast
@@ -11,8 +11,10 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
R_ANGLE ">"
PARAM_LIST
L_PAREN "("
diff --git a/crates/parser/test_data/parser/inline/ok/method_call_expr.rast b/crates/parser/test_data/parser/inline/ok/method_call_expr.rast
index b28b8eb673..3245042cf2 100644
--- a/crates/parser/test_data/parser/inline/ok/method_call_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/method_call_expr.rast
@@ -101,6 +101,20 @@ SOURCE_FILE
L_PAREN "("
R_PAREN ")"
SEMICOLON ";"
+ WHITESPACE "\n "
+ CALL_EXPR
+ FIELD_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "x"
+ DOT "."
+ NAME_REF
+ INT_NUMBER "0"
+ ARG_LIST
+ L_PAREN "("
+ R_PAREN ")"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/method_call_expr.rs b/crates/parser/test_data/parser/inline/ok/method_call_expr.rs
index 48bb6381e8..bb54d75c19 100644
--- a/crates/parser/test_data/parser/inline/ok/method_call_expr.rs
+++ b/crates/parser/test_data/parser/inline/ok/method_call_expr.rs
@@ -3,4 +3,5 @@ fn foo() {
y.bar::<T>(1, 2,);
x.0.0.call();
x.0. call();
+ x.0()
}
diff --git a/crates/parser/test_data/parser/inline/ok/param_list_opt_patterns.rast b/crates/parser/test_data/parser/inline/ok/param_list_opt_patterns.rast
deleted file mode 100644
index e9d93a0d0a..0000000000
--- a/crates/parser/test_data/parser/inline/ok/param_list_opt_patterns.rast
+++ /dev/null
@@ -1,48 +0,0 @@
-SOURCE_FILE
- FN
- FN_KW "fn"
- WHITESPACE " "
- NAME
- IDENT "foo"
- GENERIC_PARAM_LIST
- L_ANGLE "<"
- TYPE_PARAM
- NAME
- IDENT "F"
- COLON ":"
- WHITESPACE " "
- TYPE_BOUND_LIST
- TYPE_BOUND
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "FnMut"
- PARAM_LIST
- L_PAREN "("
- PARAM
- REF_TYPE
- AMP "&"
- MUT_KW "mut"
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Foo"
- GENERIC_ARG_LIST
- L_ANGLE "<"
- LIFETIME_ARG
- LIFETIME
- LIFETIME_IDENT "'a"
- R_ANGLE ">"
- R_PAREN ")"
- R_ANGLE ">"
- PARAM_LIST
- L_PAREN "("
- R_PAREN ")"
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- R_CURLY "}"
- WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/param_list_opt_patterns.rs b/crates/parser/test_data/parser/inline/ok/param_list_opt_patterns.rs
deleted file mode 100644
index 9b93442c0f..0000000000
--- a/crates/parser/test_data/parser/inline/ok/param_list_opt_patterns.rs
+++ /dev/null
@@ -1 +0,0 @@
-fn foo<F: FnMut(&mut Foo<'a>)>(){}
diff --git a/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rast b/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rast
index fd83daf841..924f7ba2c9 100644
--- a/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rast
+++ b/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rast
@@ -20,9 +20,133 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "Fn"
- PARAM_LIST
+ PARENTHESIZED_ARG_LIST
L_PAREN "("
- PARAM
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ WHITESPACE " "
+ RET_TYPE
+ THIN_ARROW "->"
+ WHITESPACE " "
+ TUPLE_TYPE
+ L_PAREN "("
+ R_PAREN ")"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "F"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Box"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ COLON2 "::"
+ NAME_REF
+ IDENT "Fn"
+ PARENTHESIZED_ARG_LIST
+ L_PAREN "("
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ WHITESPACE " "
+ RET_TYPE
+ THIN_ARROW "->"
+ WHITESPACE " "
+ TUPLE_TYPE
+ L_PAREN "("
+ R_PAREN ")"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "F"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Box"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Fn"
+ PARENTHESIZED_ARG_LIST
+ COLON2 "::"
+ L_PAREN "("
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ WHITESPACE " "
+ RET_TYPE
+ THIN_ARROW "->"
+ WHITESPACE " "
+ TUPLE_TYPE
+ L_PAREN "("
+ R_PAREN ")"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "F"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Box"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ COLON2 "::"
+ NAME_REF
+ IDENT "Fn"
+ PARENTHESIZED_ARG_LIST
+ COLON2 "::"
+ L_PAREN "("
+ TYPE_ARG
PATH_TYPE
PATH
PATH_SEGMENT
diff --git a/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rs b/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rs
index 17ed20e5b1..7aa655d7ea 100644
--- a/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rs
+++ b/crates/parser/test_data/parser/inline/ok/path_fn_trait_args.rs
@@ -1 +1,4 @@
type F = Box<Fn(i32) -> ()>;
+type F = Box<::Fn(i32) -> ()>;
+type F = Box<Fn::(i32) -> ()>;
+type F = Box<::Fn::(i32) -> ()>;
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 f9c0a245af..5a67cc2176 100644
--- a/crates/parser/test_data/parser/inline/ok/precise_capturing.rast
+++ b/crates/parser/test_data/parser/inline/ok/precise_capturing.rast
@@ -11,8 +11,10 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'a"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'a"
COMMA ","
WHITESPACE " "
LIFETIME_PARAM
@@ -20,8 +22,10 @@ SOURCE_FILE
LIFETIME_IDENT "'b"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
COMMA ","
WHITESPACE " "
TYPE_PARAM
diff --git a/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rast b/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rast
deleted file mode 100644
index 30e0e73bbd..0000000000
--- a/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rast
+++ /dev/null
@@ -1,49 +0,0 @@
-SOURCE_FILE
- FN
- FN_KW "fn"
- WHITESPACE " "
- NAME
- IDENT "foo"
- GENERIC_PARAM_LIST
- L_ANGLE "<"
- TYPE_PARAM
- NAME
- IDENT "T"
- COLON ":"
- WHITESPACE " "
- TYPE_BOUND_LIST
- TYPE_BOUND
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Trait"
- GENERIC_ARG_LIST
- L_ANGLE "<"
- ASSOC_TYPE_ARG
- NAME_REF
- IDENT "method"
- RETURN_TYPE_SYNTAX
- L_PAREN "("
- DOT2 ".."
- R_PAREN ")"
- COLON ":"
- WHITESPACE " "
- TYPE_BOUND_LIST
- TYPE_BOUND
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Send"
- R_ANGLE ">"
- R_ANGLE ">"
- PARAM_LIST
- L_PAREN "("
- R_PAREN ")"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- R_CURLY "}"
- WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rs b/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rs
deleted file mode 100644
index 8a4cf4c3a0..0000000000
--- a/crates/parser/test_data/parser/inline/ok/return_type_syntax_assoc_type_bound.rs
+++ /dev/null
@@ -1 +0,0 @@
-fn foo<T: Trait<method(..): Send>>() {}
diff --git a/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rast b/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rast
index 501dccd79d..15a0558b53 100644
--- a/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rast
+++ b/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rast
@@ -42,6 +42,49 @@ SOURCE_FILE
NAME_REF
IDENT "Send"
COMMA ","
+ WHITESPACE "\n "
+ WHERE_PRED
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "method"
+ RETURN_TYPE_SYNTAX
+ L_PAREN "("
+ DOT2 ".."
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Send"
+ COMMA ","
+ WHITESPACE "\n "
+ WHERE_PRED
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "method"
+ RETURN_TYPE_SYNTAX
+ COLON2 "::"
+ L_PAREN "("
+ DOT2 ".."
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Send"
+ COMMA ","
WHITESPACE "\n"
BLOCK_EXPR
STMT_LIST
diff --git a/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rs b/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rs
index a9b63fb01c..64b48c1638 100644
--- a/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rs
+++ b/crates/parser/test_data/parser/inline/ok/return_type_syntax_in_path.rs
@@ -1,4 +1,6 @@
fn foo<T>()
where
T::method(..): Send,
+ method(..): Send,
+ method::(..): Send,
{}
diff --git a/crates/parser/test_data/parser/inline/ok/typepathfn_with_coloncolon.rast b/crates/parser/test_data/parser/inline/ok/typepathfn_with_coloncolon.rast
index 67277d0639..fb442bfb73 100644
--- a/crates/parser/test_data/parser/inline/ok/typepathfn_with_coloncolon.rast
+++ b/crates/parser/test_data/parser/inline/ok/typepathfn_with_coloncolon.rast
@@ -13,10 +13,10 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "Start"
- COLON2 "::"
- PARAM_LIST
+ PARENTHESIZED_ARG_LIST
+ COLON2 "::"
L_PAREN "("
- PARAM
+ TYPE_ARG
PATH_TYPE
PATH
PATH_SEGMENT
@@ -63,9 +63,9 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "Start"
- PARAM_LIST
+ PARENTHESIZED_ARG_LIST
L_PAREN "("
- PARAM
+ TYPE_ARG
PATH_TYPE
PATH
PATH_SEGMENT
diff --git a/crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rast b/crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rast
deleted file mode 100644
index 902b06484c..0000000000
--- a/crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rast
+++ /dev/null
@@ -1,60 +0,0 @@
-SOURCE_FILE
- TYPE_ALIAS
- TYPE_KW "type"
- WHITESPACE " "
- NAME
- IDENT "F"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Box"
- GENERIC_ARG_LIST
- L_ANGLE "<"
- TYPE_ARG
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Fn"
- PARAM_LIST
- L_PAREN "("
- PARAM
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "i32"
- COMMA ","
- WHITESPACE " "
- PARAM
- REF_TYPE
- AMP "&"
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "i32"
- COMMA ","
- WHITESPACE " "
- PARAM
- REF_TYPE
- AMP "&"
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "i32"
- COMMA ","
- WHITESPACE " "
- PARAM
- TUPLE_TYPE
- L_PAREN "("
- R_PAREN ")"
- R_PAREN ")"
- R_ANGLE ">"
- SEMICOLON ";"
- WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rs b/crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rs
deleted file mode 100644
index 93636e926e..0000000000
--- a/crates/parser/test_data/parser/inline/ok/value_parameters_no_patterns.rs
+++ /dev/null
@@ -1 +0,0 @@
-type F = Box<Fn(i32, &i32, &i32, ())>;
diff --git a/crates/parser/test_data/parser/inline/ok/where_pred_for.rast b/crates/parser/test_data/parser/inline/ok/where_pred_for.rast
index 8407e99f61..0cc365efbe 100644
--- a/crates/parser/test_data/parser/inline/ok/where_pred_for.rast
+++ b/crates/parser/test_data/parser/inline/ok/where_pred_for.rast
@@ -40,9 +40,9 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "Fn"
- PARAM_LIST
+ PARENTHESIZED_ARG_LIST
L_PAREN "("
- PARAM
+ TYPE_ARG
REF_TYPE
AMP "&"
LIFETIME
diff --git a/crates/parser/test_data/parser/ok/0018_struct_type_params.rast b/crates/parser/test_data/parser/ok/0018_struct_type_params.rast
index 11ebc7efb9..1e4eb15609 100644
--- a/crates/parser/test_data/parser/ok/0018_struct_type_params.rast
+++ b/crates/parser/test_data/parser/ok/0018_struct_type_params.rast
@@ -96,6 +96,7 @@ SOURCE_FILE
LIFETIME
LIFETIME_IDENT "'a"
COLON ":"
+ TYPE_BOUND_LIST
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
@@ -111,8 +112,10 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
@@ -128,10 +131,12 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
- WHITESPACE " "
- PLUS "+"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
+ WHITESPACE " "
+ PLUS "+"
WHITESPACE " "
R_ANGLE ">"
SEMICOLON ";"
@@ -148,13 +153,16 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
- WHITESPACE " "
- PLUS "+"
- WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'c"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'c"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
@@ -202,9 +210,11 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
- PLUS "+"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
+ PLUS "+"
COMMA ","
WHITESPACE " "
LIFETIME_PARAM
@@ -212,8 +222,10 @@ SOURCE_FILE
LIFETIME_IDENT "'b"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'c"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'c"
COMMA ","
R_ANGLE ">"
SEMICOLON ";"
diff --git a/crates/parser/test_data/parser/ok/0020_type_param_bounds.rast b/crates/parser/test_data/parser/ok/0020_type_param_bounds.rast
index 043a966ff9..448cf49446 100644
--- a/crates/parser/test_data/parser/ok/0020_type_param_bounds.rast
+++ b/crates/parser/test_data/parser/ok/0020_type_param_bounds.rast
@@ -237,8 +237,10 @@ SOURCE_FILE
LIFETIME_IDENT "'a"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'d"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'d"
COMMA ","
WHITESPACE " "
LIFETIME_PARAM
@@ -246,13 +248,16 @@ SOURCE_FILE
LIFETIME_IDENT "'d"
COLON ":"
WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'a"
- WHITESPACE " "
- PLUS "+"
- WHITESPACE " "
- LIFETIME
- LIFETIME_IDENT "'b"
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'a"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'b"
COMMA ","
WHITESPACE " "
TYPE_PARAM
diff --git a/crates/parser/test_data/parser/ok/0045_block_attrs.rast b/crates/parser/test_data/parser/ok/0045_block_attrs.rast
index fad574a476..c22d99f1ae 100644
--- a/crates/parser/test_data/parser/ok/0045_block_attrs.rast
+++ b/crates/parser/test_data/parser/ok/0045_block_attrs.rast
@@ -180,7 +180,7 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "Fn"
- PARAM_LIST
+ PARENTHESIZED_ARG_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
diff --git a/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast b/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast
index f8b11e7782..eafee90db4 100644
--- a/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast
+++ b/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast
@@ -138,63 +138,6 @@ SOURCE_FILE
WHITESPACE " "
R_CURLY "}"
WHITESPACE "\n\n"
- FN
- FN_KW "fn"
- WHITESPACE " "
- NAME
- IDENT "foo"
- GENERIC_PARAM_LIST
- L_ANGLE "<"
- TYPE_PARAM
- NAME
- IDENT "F"
- COLON ":"
- WHITESPACE " "
- TYPE_BOUND_LIST
- TYPE_BOUND
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "FnMut"
- PARAM_LIST
- L_PAREN "("
- PARAM
- ATTR
- POUND "#"
- L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "attr"
- R_BRACK "]"
- WHITESPACE " "
- REF_TYPE
- AMP "&"
- MUT_KW "mut"
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Foo"
- GENERIC_ARG_LIST
- L_ANGLE "<"
- LIFETIME_ARG
- LIFETIME
- LIFETIME_IDENT "'a"
- R_ANGLE ">"
- R_PAREN ")"
- R_ANGLE ">"
- PARAM_LIST
- L_PAREN "("
- R_PAREN ")"
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- R_CURLY "}"
- WHITESPACE "\n\n"
TRAIT
TRAIT_KW "trait"
WHITESPACE " "
diff --git a/crates/parser/test_data/parser/ok/0051_parameter_attrs.rs b/crates/parser/test_data/parser/ok/0051_parameter_attrs.rs
index de350d8587..0a0100e5d0 100644
--- a/crates/parser/test_data/parser/ok/0051_parameter_attrs.rs
+++ b/crates/parser/test_data/parser/ok/0051_parameter_attrs.rs
@@ -3,8 +3,6 @@ fn g2(#[attr1] x: u8) {}
extern "C" { fn printf(format: *const i8, #[attr] ...) -> i32; }
-fn foo<F: FnMut(#[attr] &mut Foo<'a>)>(){}
-
trait Foo {
fn bar(#[attr] _: u64, # [attr] mut x: i32);
}
diff --git a/crates/parser/test_data/parser/ok/0054_qual_path_in_type_arg.rast b/crates/parser/test_data/parser/ok/0054_qual_path_in_type_arg.rast
index 4e1e31f376..31cca601ca 100644
--- a/crates/parser/test_data/parser/ok/0054_qual_path_in_type_arg.rast
+++ b/crates/parser/test_data/parser/ok/0054_qual_path_in_type_arg.rast
@@ -58,9 +58,9 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "FnMut"
- PARAM_LIST
+ PARENTHESIZED_ARG_LIST
L_PAREN "("
- PARAM
+ TYPE_ARG
PATH_TYPE
PATH
PATH
@@ -101,9 +101,9 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "FnMut"
- PARAM_LIST
+ PARENTHESIZED_ARG_LIST
L_PAREN "("
- PARAM
+ TYPE_ARG
REF_TYPE
AMP "&"
PATH_TYPE
diff --git a/crates/parser/test_data/parser/ok/0065_plus_after_fn_trait_bound.rast b/crates/parser/test_data/parser/ok/0065_plus_after_fn_trait_bound.rast
index ba7b6042a9..d7ee11077c 100644
--- a/crates/parser/test_data/parser/ok/0065_plus_after_fn_trait_bound.rast
+++ b/crates/parser/test_data/parser/ok/0065_plus_after_fn_trait_bound.rast
@@ -32,7 +32,7 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "Fn"
- PARAM_LIST
+ PARENTHESIZED_ARG_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
diff --git a/crates/parser/test_data/parser/ok/0067_where_for_pred.rast b/crates/parser/test_data/parser/ok/0067_where_for_pred.rast
index 136fce93d7..cd3b21ae94 100644
--- a/crates/parser/test_data/parser/ok/0067_where_for_pred.rast
+++ b/crates/parser/test_data/parser/ok/0067_where_for_pred.rast
@@ -40,9 +40,9 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "Fn"
- PARAM_LIST
+ PARENTHESIZED_ARG_LIST
L_PAREN "("
- PARAM
+ TYPE_ARG
REF_TYPE
AMP "&"
LIFETIME
@@ -165,9 +165,9 @@ SOURCE_FILE
PATH_SEGMENT
NAME_REF
IDENT "Fn"
- PARAM_LIST
+ PARENTHESIZED_ARG_LIST
L_PAREN "("
- PARAM
+ TYPE_ARG
REF_TYPE
AMP "&"
LIFETIME
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index f5ba71fcd0..988eff9be4 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -747,17 +747,14 @@ impl ProjectWorkspace {
let _p = tracing::info_span!("ProjectWorkspace::to_crate_graph").entered();
let Self { kind, sysroot, cfg_overrides, rustc_cfg, .. } = self;
- let ((mut crate_graph, proc_macros), sysroot) = match kind {
- ProjectWorkspaceKind::Json(project) => (
- project_json_to_crate_graph(
- rustc_cfg.clone(),
- load,
- project,
- sysroot,
- extra_env,
- cfg_overrides,
- ),
+ let (crate_graph, proc_macros) = match kind {
+ ProjectWorkspaceKind::Json(project) => project_json_to_crate_graph(
+ rustc_cfg.clone(),
+ load,
+ project,
sysroot,
+ extra_env,
+ cfg_overrides,
),
ProjectWorkspaceKind::Cargo {
cargo,
@@ -766,20 +763,17 @@ impl ProjectWorkspace {
cargo_config_extra_env: _,
error: _,
set_test,
- } => (
- cargo_to_crate_graph(
- load,
- rustc.as_ref().map(|a| a.as_ref()).ok(),
- cargo,
- sysroot,
- rustc_cfg.clone(),
- cfg_overrides,
- build_scripts,
- *set_test,
- ),
+ } => cargo_to_crate_graph(
+ load,
+ rustc.as_ref().map(|a| a.as_ref()).ok(),
+ cargo,
sysroot,
+ rustc_cfg.clone(),
+ cfg_overrides,
+ build_scripts,
+ *set_test,
),
- ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, set_test, .. } => (
+ ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, set_test, .. } => {
if let Some((cargo, build_scripts, _)) = cargo_script {
cargo_to_crate_graph(
&mut |path| load(path),
@@ -800,16 +794,10 @@ impl ProjectWorkspace {
cfg_overrides,
*set_test,
)
- },
- sysroot,
- ),
+ }
+ }
};
- if matches!(sysroot.mode(), SysrootMode::Stitched(_)) && crate_graph.patch_cfg_if() {
- debug!("Patched std to depend on cfg-if")
- } else {
- debug!("Did not patch std to depend on cfg-if")
- }
(crate_graph, proc_macros)
}
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 2dd2f2242a..7c8610280b 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -21,9 +21,11 @@ path = "src/bin/main.rs"
[dependencies]
anyhow.workspace = true
+base64 = "0.22"
crossbeam-channel.workspace = true
dirs = "5.0.1"
dissimilar.workspace = true
+ide-completion.workspace = true
itertools.workspace = true
scip = "0.5.1"
lsp-types = { version = "=0.95.0", features = ["proposed"] }
@@ -34,6 +36,7 @@ rayon.workspace = true
rustc-hash.workspace = true
serde_json = { workspace = true, features = ["preserve_order"] }
serde.workspace = true
+tenthash = "0.4.0"
num_cpus = "1.15.0"
mimalloc = { version = "0.1.30", default-features = false, optional = true }
lsp-server.workspace = true
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index e872585c57..eac33be566 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -20,7 +20,6 @@ use rust_analyzer::{
config::{Config, ConfigChange, ConfigErrors},
from_json,
};
-use semver::Version;
use tracing_subscriber::fmt::writer::BoxMakeWriter;
use vfs::AbsPathBuf;
@@ -204,18 +203,12 @@ fn run_server() -> anyhow::Result<()> {
}
};
- let mut visual_studio_code_version = None;
- if let Some(client_info) = client_info {
+ if let Some(client_info) = &client_info {
tracing::info!(
"Client '{}' {}",
client_info.name,
client_info.version.as_deref().unwrap_or_default()
);
- visual_studio_code_version = client_info
- .name
- .starts_with("Visual Studio Code")
- .then(|| client_info.version.as_deref().map(Version::parse).and_then(Result::ok))
- .flatten();
}
let workspace_roots = workspace_folders
@@ -230,8 +223,7 @@ fn run_server() -> anyhow::Result<()> {
})
.filter(|workspaces| !workspaces.is_empty())
.unwrap_or_else(|| vec![root_path.clone()]);
- let mut config =
- Config::new(root_path, capabilities, workspace_roots, visual_studio_code_version);
+ let mut config = Config::new(root_path, capabilities, workspace_roots, client_info);
if let Some(json) = initialization_options {
let mut change = ConfigChange::default();
change.change_client_config(json);
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 802d0c69a4..e3ea441f3a 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -13,8 +13,9 @@ use hir::{
ModuleDef, Name,
};
use hir_def::{
- body::{BodySourceMap, SyntheticSyntax},
+ body::BodySourceMap,
hir::{ExprId, PatId},
+ SyntheticSyntax,
};
use hir_ty::{Interner, Substitution, TyExt, TypeFlags};
use ide::{
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 37d45255e2..bf7aca42fa 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -3,11 +3,7 @@
//! Of particular interest is the `feature_flags` hash map: while other fields
//! configure the server itself, feature flags are passed into analysis, and
//! tweak things like automatic insertion of `()` in completions.
-use std::{
- env, fmt, iter,
- ops::Not,
- sync::{LazyLock, OnceLock},
-};
+use std::{env, fmt, iter, ops::Not, sync::OnceLock};
use cfg::{CfgAtom, CfgDiff};
use hir::Symbol;
@@ -308,8 +304,8 @@ config_data! {
/// Show documentation.
signatureInfo_documentation_enable: bool = true,
- /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.
- typing_autoClosingAngleBrackets_enable: bool = false,
+ /// Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`.
+ typing_excludeChars: Option<String> = Some("|<".to_owned()),
/// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
@@ -730,6 +726,12 @@ enum RatomlFile {
Crate(LocalConfigInput),
}
+#[derive(Clone, Debug)]
+struct ClientInfo {
+ name: String,
+ version: Option<Version>,
+}
+
#[derive(Clone)]
pub struct Config {
/// Projects that have a Cargo.toml or a rust-project.json in a
@@ -744,7 +746,7 @@ pub struct Config {
caps: ClientCapabilities,
root_path: AbsPathBuf,
snippets: Vec<Snippet>,
- visual_studio_code_version: Option<Version>,
+ client_info: Option<ClientInfo>,
default_config: &'static DefaultConfigData,
/// Config node that obtains its initial value during the server initialization and
@@ -777,7 +779,7 @@ impl fmt::Debug for Config {
.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_info", &self.client_info)
.field("client_config", &self.client_config)
.field("user_config", &self.user_config)
.field("ratoml_file", &self.ratoml_file)
@@ -798,25 +800,14 @@ impl std::ops::Deref for Config {
}
impl Config {
- /// Path to the root configuration file. This can be seen as a generic way to define what would be `$XDG_CONFIG_HOME/rust-analyzer/rust-analyzer.toml` in Linux.
- /// This path is equal to:
- ///
- /// |Platform | Value | Example |
- /// | ------- | ------------------------------------- | ---------------------------------------- |
- /// | Linux | `$XDG_CONFIG_HOME` or `$HOME`/.config | /home/alice/.config |
- /// | macOS | `$HOME`/Library/Application Support | /Users/Alice/Library/Application Support |
- /// | Windows | `{FOLDERID_RoamingAppData}` | C:\Users\Alice\AppData\Roaming |
- pub fn user_config_path() -> Option<&'static AbsPath> {
- static USER_CONFIG_PATH: LazyLock<Option<AbsPathBuf>> = LazyLock::new(|| {
- let user_config_path = if let Some(path) = env::var_os("__TEST_RA_USER_CONFIG_DIR") {
- std::path::PathBuf::from(path)
- } else {
- dirs::config_dir()?.join("rust-analyzer")
- }
- .join("rust-analyzer.toml");
- Some(AbsPathBuf::assert_utf8(user_config_path))
- });
- USER_CONFIG_PATH.as_deref()
+ /// Path to the user configuration dir. This can be seen as a generic way to define what would be `$XDG_CONFIG_HOME/rust-analyzer` in Linux.
+ pub fn user_config_dir_path() -> Option<AbsPathBuf> {
+ let user_config_path = if let Some(path) = env::var_os("__TEST_RA_USER_CONFIG_DIR") {
+ std::path::PathBuf::from(path)
+ } else {
+ dirs::config_dir()?.join("rust-analyzer")
+ };
+ Some(AbsPathBuf::assert_utf8(user_config_path))
}
pub fn same_source_root_parent_map(
@@ -1256,7 +1247,7 @@ pub struct NotificationsConfig {
pub cargo_toml_not_found: bool,
}
-#[derive(Deserialize, Serialize, Debug, Clone)]
+#[derive(Debug, Clone)]
pub enum RustfmtConfig {
Rustfmt { extra_args: Vec<String>, enable_range_formatting: bool },
CustomCommand { command: String, args: Vec<String> },
@@ -1335,7 +1326,7 @@ impl Config {
root_path: AbsPathBuf,
caps: lsp_types::ClientCapabilities,
workspace_roots: Vec<AbsPathBuf>,
- visual_studio_code_version: Option<Version>,
+ client_info: Option<lsp_types::ClientInfo>,
) -> Self {
static DEFAULT_CONFIG_DATA: OnceLock<&'static DefaultConfigData> = OnceLock::new();
@@ -1346,7 +1337,10 @@ impl Config {
root_path,
snippets: Default::default(),
workspace_roots,
- visual_studio_code_version,
+ client_info: client_info.map(|it| ClientInfo {
+ name: it.name,
+ version: it.version.as_deref().map(Version::parse).and_then(Result::ok),
+ }),
client_config: (FullConfigInput::default(), ConfigErrors(vec![])),
default_config: DEFAULT_CONFIG_DATA.get_or_init(|| Box::leak(Box::default())),
source_root_parent_map: Arc::new(FxHashMap::default()),
@@ -1446,14 +1440,10 @@ impl Config {
limit: self.completion_limit(source_root).to_owned(),
enable_term_search: self.completion_termSearch_enable(source_root).to_owned(),
term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64,
- fields_to_resolve: CompletionFieldsToResolve {
- resolve_label_details: client_capability_fields.contains("labelDetails"),
- resolve_tags: client_capability_fields.contains("tags"),
- resolve_detail: client_capability_fields.contains("detail"),
- resolve_documentation: client_capability_fields.contains("documentation"),
- resolve_filter_text: client_capability_fields.contains("filterText"),
- resolve_text_edit: client_capability_fields.contains("textEdit"),
- resolve_command: client_capability_fields.contains("command"),
+ fields_to_resolve: if self.client_is_neovim() {
+ CompletionFieldsToResolve::empty()
+ } else {
+ CompletionFieldsToResolve::from_client_capabilities(&client_capability_fields)
},
}
}
@@ -1614,13 +1604,9 @@ impl Config {
} else {
None
},
- fields_to_resolve: InlayFieldsToResolve {
- resolve_text_edits: client_capability_fields.contains("textEdits"),
- resolve_hint_tooltip: client_capability_fields.contains("tooltip"),
- resolve_label_tooltip: client_capability_fields.contains("label.tooltip"),
- resolve_label_location: client_capability_fields.contains("label.location"),
- resolve_label_command: client_capability_fields.contains("label.command"),
- },
+ fields_to_resolve: InlayFieldsToResolve::from_client_capabilities(
+ &client_capability_fields,
+ ),
implicit_drop_hints: self.inlayHints_implicitDrops_enable().to_owned(),
range_exclusive_hints: self.inlayHints_rangeExclusiveHints_enable().to_owned(),
}
@@ -2166,14 +2152,25 @@ impl Config {
}
}
- pub fn typing_autoclose_angle(&self) -> bool {
- *self.typing_autoClosingAngleBrackets_enable()
+ pub fn typing_exclude_chars(&self) -> Option<String> {
+ self.typing_excludeChars().clone()
}
// VSCode is our reference implementation, so we allow ourselves to work around issues by
// special casing certain versions
pub fn visual_studio_code_version(&self) -> Option<&Version> {
- self.visual_studio_code_version.as_ref()
+ self.client_info
+ .as_ref()
+ .filter(|it| it.name.starts_with("Visual Studio Code"))
+ .and_then(|it| it.version.as_ref())
+ }
+
+ pub fn client_is_helix(&self) -> bool {
+ self.client_info.as_ref().map(|it| it.name == "helix").unwrap_or_default()
+ }
+
+ pub fn client_is_neovim(&self) -> bool {
+ self.client_info.as_ref().map(|it| it.name == "Neovim").unwrap_or_default()
}
}
// Deserialization definitions
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 7fbeaa4e3e..5f83570284 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -392,7 +392,14 @@ impl GlobalState {
|| !self.config.same_source_root_parent_map(&self.local_roots_parent_map)
{
let config_change = {
- let user_config_path = Config::user_config_path();
+ let user_config_path = (|| {
+ let mut p = Config::user_config_dir_path()?;
+ p.push("rust-analyzer.toml");
+ Some(p)
+ })();
+
+ let user_config_abs_path = user_config_path.as_deref();
+
let mut change = ConfigChange::default();
let db = self.analysis_host.raw_database();
@@ -411,7 +418,7 @@ impl GlobalState {
.collect_vec();
for (file_id, (_change_kind, vfs_path)) in modified_ratoml_files {
- if vfs_path.as_path() == user_config_path {
+ if vfs_path.as_path() == user_config_abs_path {
change.change_user_config(Some(db.file_text(file_id)));
continue;
}
diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs
index bb03eb3c89..5e7262b14c 100644
--- a/crates/rust-analyzer/src/handlers/notification.rs
+++ b/crates/rust-analyzer/src/handlers/notification.rs
@@ -379,9 +379,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
for (id, package) in workspace_ids.clone() {
if id == flycheck.id() {
updated = true;
- match package.filter(|_| {
- !world.config.flycheck_workspace(source_root_id) && target.is_some()
- }) {
+ match package.filter(|_| !world.config.flycheck_workspace(source_root_id)) {
Some(package) => flycheck
.restart_for_package(package, target.clone().map(TupleExt::head)),
None => flycheck.restart_workspace(saved_file.clone()),
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 4975467ece..fa78be5cb6 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -10,6 +10,7 @@ use std::{
use anyhow::Context;
+use base64::{prelude::BASE64_STANDARD, Engine};
use ide::{
AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve,
FilePosition, FileRange, HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query,
@@ -36,6 +37,7 @@ use triomphe::Arc;
use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
use crate::{
+ completion_item_hash,
config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
diagnostics::convert_diagnostic,
global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
@@ -459,9 +461,9 @@ pub(crate) fn handle_on_type_formatting(
if char_typed == '>' {
return Ok(None);
}
+ let chars_to_exclude = snap.config.typing_exclude_chars();
- let edit =
- snap.analysis.on_char_typed(position, char_typed, snap.config.typing_autoclose_angle())?;
+ let edit = snap.analysis.on_char_typed(position, char_typed, chars_to_exclude)?;
let edit = match edit {
Some(it) => it,
None => return Ok(None),
@@ -1127,7 +1129,7 @@ pub(crate) fn handle_completion_resolve(
forced_resolve_completions_config.fields_to_resolve = CompletionFieldsToResolve::empty();
let position = FilePosition { file_id, offset };
- let Some(resolved_completions) = snap.analysis.completions(
+ let Some(completions) = snap.analysis.completions(
&forced_resolve_completions_config,
position,
resolve_data.trigger_character,
@@ -1135,6 +1137,19 @@ pub(crate) fn handle_completion_resolve(
else {
return Ok(original_completion);
};
+ let Ok(resolve_data_hash) = BASE64_STANDARD.decode(resolve_data.hash) else {
+ return Ok(original_completion);
+ };
+
+ let Some(corresponding_completion) = completions.into_iter().find(|completion_item| {
+ // Avoid computing hashes for items that obviously do not match
+ // r-a might append a detail-based suffix to the label, so we cannot check for equality
+ original_completion.label.starts_with(completion_item.label.as_str())
+ && resolve_data_hash == completion_item_hash(completion_item, resolve_data.for_ref)
+ }) else {
+ return Ok(original_completion);
+ };
+
let mut resolved_completions = to_proto::completion_items(
&snap.config,
&forced_resolve_completions_config.fields_to_resolve,
@@ -1142,15 +1157,11 @@ pub(crate) fn handle_completion_resolve(
snap.file_version(position.file_id),
resolve_data.position,
resolve_data.trigger_character,
- resolved_completions,
+ vec![corresponding_completion],
);
-
- let mut resolved_completion =
- if resolved_completions.get(resolve_data.completion_item_index).is_some() {
- resolved_completions.swap_remove(resolve_data.completion_item_index)
- } else {
- return Ok(original_completion);
- };
+ let Some(mut resolved_completion) = resolved_completions.pop() else {
+ return Ok(original_completion);
+ };
if !resolve_data.imports.is_empty() {
let additional_edits = snap
@@ -2341,6 +2352,10 @@ fn run_rustfmt(
);
Ok(None)
}
+ // rustfmt panicked at lexing/parsing the file
+ Some(101) if !rustfmt_not_installed && captured_stderr.starts_with("error[") => {
+ Ok(None)
+ }
_ => {
// Something else happened - e.g. `rustfmt` is missing or caught a signal
Err(LspError::new(
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index 234204695c..15d60c873f 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -47,7 +47,9 @@ use self::lsp::ext as lsp_ext;
#[cfg(test)]
mod integrated_benchmarks;
+use ide::{CompletionItem, CompletionRelevance};
use serde::de::DeserializeOwned;
+use tenthash::TentHasher;
pub use crate::{
lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph,
@@ -61,3 +63,79 @@ pub fn from_json<T: DeserializeOwned>(
serde_json::from_value(json.clone())
.map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}"))
}
+
+fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] {
+ fn hash_completion_relevance(hasher: &mut TentHasher, relevance: &CompletionRelevance) {
+ use ide_completion::{
+ CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
+ CompletionRelevanceTypeMatch,
+ };
+
+ hasher.update([
+ u8::from(relevance.exact_name_match),
+ u8::from(relevance.is_local),
+ u8::from(relevance.is_name_already_imported),
+ u8::from(relevance.requires_import),
+ u8::from(relevance.is_private_editable),
+ ]);
+ if let Some(type_match) = &relevance.type_match {
+ let label = match type_match {
+ CompletionRelevanceTypeMatch::CouldUnify => "could_unify",
+ CompletionRelevanceTypeMatch::Exact => "exact",
+ };
+ hasher.update(label);
+ }
+ if let Some(trait_) = &relevance.trait_ {
+ hasher.update([u8::from(trait_.is_op_method), u8::from(trait_.notable_trait)]);
+ }
+ if let Some(postfix_match) = &relevance.postfix_match {
+ let label = match postfix_match {
+ CompletionRelevancePostfixMatch::NonExact => "non_exact",
+ CompletionRelevancePostfixMatch::Exact => "exact",
+ };
+ hasher.update(label);
+ }
+ if let Some(function) = &relevance.function {
+ hasher.update([u8::from(function.has_params), u8::from(function.has_self_param)]);
+ let label = match function.return_type {
+ CompletionRelevanceReturnType::Other => "other",
+ CompletionRelevanceReturnType::DirectConstructor => "direct_constructor",
+ CompletionRelevanceReturnType::Constructor => "constructor",
+ CompletionRelevanceReturnType::Builder => "builder",
+ };
+ hasher.update(label);
+ }
+ }
+
+ let mut hasher = TentHasher::new();
+ hasher.update([
+ u8::from(is_ref_completion),
+ u8::from(item.is_snippet),
+ u8::from(item.deprecated),
+ u8::from(item.trigger_call_info),
+ ]);
+ hasher.update(&item.label);
+ if let Some(label_detail) = &item.label_detail {
+ hasher.update(label_detail);
+ }
+ // NB: do not hash edits or source range, as those may change between the time the client sends the resolve request
+ // and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different.
+ //
+ // Documentation hashing is skipped too, as it's a large blob to process,
+ // while not really making completion properties more unique as they are already.
+ hasher.update(item.kind.tag());
+ hasher.update(&item.lookup);
+ if let Some(detail) = &item.detail {
+ hasher.update(detail);
+ }
+ hash_completion_relevance(&mut hasher, &item.relevance);
+ if let Some((mutability, text_size)) = &item.ref_match {
+ hasher.update(mutability.as_keyword_for_ref());
+ hasher.update(u32::from(*text_size).to_le_bytes());
+ }
+ for (import_path, import_name) in &item.import_to_add {
+ hasher.update(import_path);
+ hasher.update(import_name);
+ }
+ hasher.finalize()
+}
diff --git a/crates/rust-analyzer/src/lsp/capabilities.rs b/crates/rust-analyzer/src/lsp/capabilities.rs
index 1db616898e..b1136dbbda 100644
--- a/crates/rust-analyzer/src/lsp/capabilities.rs
+++ b/crates/rust-analyzer/src/lsp/capabilities.rs
@@ -1,4 +1,5 @@
//! Advertises the capabilities of the LSP Server.
+use ide::{CompletionFieldsToResolve, InlayFieldsToResolve};
use ide_db::{line_index::WideEncoding, FxHashSet};
use lsp_types::{
CallHierarchyServerCapability, CodeActionKind, CodeActionOptions, CodeActionProviderCapability,
@@ -40,7 +41,11 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
})),
hover_provider: Some(HoverProviderCapability::Simple(true)),
completion_provider: Some(CompletionOptions {
- resolve_provider: config.caps().completions_resolve_provider(),
+ resolve_provider: if config.client_is_neovim() {
+ config.completion_item_edit_resolve().then_some(true)
+ } else {
+ Some(config.caps().completions_resolve_provider())
+ },
trigger_characters: Some(vec![
":".to_owned(),
".".to_owned(),
@@ -71,9 +76,12 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)),
_ => Some(OneOf::Left(false)),
},
- document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
- first_trigger_character: "=".to_owned(),
- more_trigger_character: Some(more_trigger_character(config)),
+ document_on_type_formatting_provider: Some({
+ let mut chars = ide::Analysis::SUPPORTED_TRIGGER_CHARS.chars();
+ DocumentOnTypeFormattingOptions {
+ first_trigger_character: chars.next().unwrap().to_string(),
+ more_trigger_character: Some(chars.map(|c| c.to_string()).collect()),
+ }
}),
selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
@@ -136,7 +144,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
inlay_hint_provider: Some(OneOf::Right(InlayHintServerCapabilities::Options(
InlayHintOptions {
work_done_progress_options: Default::default(),
- resolve_provider: Some(true),
+ resolve_provider: Some(config.caps().inlay_hints_resolve_provider()),
},
))),
inline_value_provider: None,
@@ -176,8 +184,18 @@ impl ClientCapabilities {
Self(caps)
}
- fn completions_resolve_provider(&self) -> Option<bool> {
- self.completion_item_edit_resolve().then_some(true)
+ fn completions_resolve_provider(&self) -> bool {
+ let client_capabilities = self.completion_resolve_support_properties();
+ let fields_to_resolve =
+ CompletionFieldsToResolve::from_client_capabilities(&client_capabilities);
+ fields_to_resolve != CompletionFieldsToResolve::empty()
+ }
+
+ fn inlay_hints_resolve_provider(&self) -> bool {
+ let client_capabilities = self.inlay_hint_resolve_support_properties();
+ let fields_to_resolve =
+ InlayFieldsToResolve::from_client_capabilities(&client_capabilities);
+ fields_to_resolve != InlayFieldsToResolve::empty()
}
fn experimental_bool(&self, index: &'static str) -> bool {
@@ -517,11 +535,3 @@ impl ClientCapabilities {
.unwrap_or_default()
}
}
-
-fn more_trigger_character(config: &Config) -> Vec<String> {
- let mut res = vec![".".to_owned(), ">".to_owned(), "{".to_owned(), "(".to_owned()];
- if config.snippet_cap().is_some() {
- res.push("<".to_owned());
- }
- res
-}
diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs
index 6ddfe118d5..df06270a8b 100644
--- a/crates/rust-analyzer/src/lsp/ext.rs
+++ b/crates/rust-analyzer/src/lsp/ext.rs
@@ -826,7 +826,8 @@ pub struct CompletionResolveData {
pub imports: Vec<CompletionImport>,
pub version: Option<i32>,
pub trigger_character: Option<char>,
- pub completion_item_index: usize,
+ pub for_ref: bool,
+ pub hash: String,
}
#[derive(Debug, Serialize, Deserialize)]
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index d444f90a13..612cb547b4 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -5,6 +5,7 @@ use std::{
sync::atomic::{AtomicU32, Ordering},
};
+use base64::{prelude::BASE64_STANDARD, Engine};
use ide::{
Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionFieldsToResolve,
CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange,
@@ -21,6 +22,7 @@ use serde_json::to_value;
use vfs::AbsPath;
use crate::{
+ completion_item_hash,
config::{CallInfoConfig, Config},
global_state::GlobalStateSnapshot,
line_index::{LineEndings, LineIndex, PositionEncoding},
@@ -295,7 +297,7 @@ fn completion_item(
// non-trivial mapping here.
let mut text_edit = None;
let source_range = item.source_range;
- for indel in item.text_edit {
+ for indel in &item.text_edit {
if indel.delete.contains_range(source_range) {
// Extract this indel as the main edit
text_edit = Some(if indel.delete == source_range {
@@ -347,7 +349,7 @@ fn completion_item(
something_to_resolve |= item.documentation.is_some();
None
} else {
- item.documentation.map(documentation)
+ item.documentation.clone().map(documentation)
};
let mut lsp_item = lsp_types::CompletionItem {
@@ -371,10 +373,10 @@ fn completion_item(
} else {
lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails {
detail: item.label_detail.as_ref().map(ToString::to_string),
- description: item.detail,
+ description: item.detail.clone(),
});
}
- } else if let Some(label_detail) = item.label_detail {
+ } else if let Some(label_detail) = &item.label_detail {
lsp_item.label.push_str(label_detail.as_str());
}
@@ -383,6 +385,7 @@ fn completion_item(
let imports =
if config.completion(None).enable_imports_on_the_fly && !item.import_to_add.is_empty() {
item.import_to_add
+ .clone()
.into_iter()
.map(|(import_path, import_name)| lsp_ext::CompletionImport {
full_import_path: import_path,
@@ -393,16 +396,15 @@ fn completion_item(
Vec::new()
};
let (ref_resolve_data, resolve_data) = if something_to_resolve || !imports.is_empty() {
- let mut item_index = acc.len();
let ref_resolve_data = if ref_match.is_some() {
let ref_resolve_data = lsp_ext::CompletionResolveData {
position: tdpp.clone(),
imports: Vec::new(),
version,
trigger_character: completion_trigger_character,
- completion_item_index: item_index,
+ for_ref: true,
+ hash: BASE64_STANDARD.encode(completion_item_hash(&item, true)),
};
- item_index += 1;
Some(to_value(ref_resolve_data).unwrap())
} else {
None
@@ -412,7 +414,8 @@ fn completion_item(
imports,
version,
trigger_character: completion_trigger_character,
- completion_item_index: item_index,
+ for_ref: false,
+ hash: BASE64_STANDARD.encode(completion_item_hash(&item, false)),
};
(ref_resolve_data, Some(to_value(resolve_data).unwrap()))
} else {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 73fce42437..a34f0a3c92 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -809,7 +809,7 @@ impl GlobalState {
}
}
vfs::loader::Message::Progress { n_total, n_done, dir, config_version } => {
- let _p = span!(Level::INFO, "GlobalState::handle_vfs_mgs/progress").entered();
+ let _p = span!(Level::INFO, "GlobalState::handle_vfs_msg/progress").entered();
always!(config_version <= self.vfs_config_version);
let (n_done, state) = match n_done {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index bc85afa0e4..4549735fef 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -590,7 +590,7 @@ impl GlobalState {
}
watchers.extend(
- iter::once(Config::user_config_path())
+ iter::once(Config::user_config_dir_path().as_deref())
.chain(self.workspaces.iter().map(|ws| ws.manifest().map(ManifestPath::as_ref)))
.flatten()
.map(|glob_pattern| lsp_types::FileSystemWatcher {
@@ -613,7 +613,11 @@ impl GlobalState {
}
let files_config = self.config.files();
- let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude);
+ let project_folders = ProjectFolders::new(
+ &self.workspaces,
+ &files_config.exclude,
+ Config::user_config_dir_path().as_deref(),
+ );
if (self.proc_macro_clients.is_empty() || !same_workspaces)
&& self.config.expand_proc_macros()
diff --git a/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/crates/rust-analyzer/tests/slow-tests/ratoml.rs
index a857e0c296..5dfaf0d365 100644
--- a/crates/rust-analyzer/tests/slow-tests/ratoml.rs
+++ b/crates/rust-analyzer/tests/slow-tests/ratoml.rs
@@ -46,7 +46,7 @@ impl RatomlTest {
project = project.with_config(client_config);
}
- let server = project.server().wait_until_workspace_is_loaded();
+ let server = project.server_with_lock(true).wait_until_workspace_is_loaded();
let mut case = Self { urls: vec![], server, tmp_path };
let urls = fixtures.iter().map(|fixture| case.fixture_path(fixture)).collect::<Vec<_>>();
@@ -72,7 +72,7 @@ impl RatomlTest {
let mut spl = spl.into_iter();
if let Some(first) = spl.next() {
if first == "$$CONFIG_DIR$$" {
- path = Config::user_config_path().unwrap().to_path_buf().into();
+ path = Config::user_config_dir_path().unwrap().into();
} else {
path = path.join(first);
}
@@ -285,7 +285,6 @@ enum Value {
// }
#[test]
-#[ignore = "the user config is currently not being watched on startup, fix this"]
fn ratoml_user_config_detected() {
if skip_slow_tests() {
return;
@@ -294,7 +293,7 @@ fn ratoml_user_config_detected() {
let server = RatomlTest::new(
vec![
r#"
-//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml
+//- /$$CONFIG_DIR$$/rust-analyzer.toml
assist.emitMustUse = true
"#,
r#"
@@ -322,7 +321,6 @@ enum Value {
}
#[test]
-#[ignore = "the user config is currently not being watched on startup, fix this"]
fn ratoml_create_user_config() {
if skip_slow_tests() {
return;
@@ -353,10 +351,7 @@ enum Value {
1,
InternalTestingFetchConfigResponse::AssistEmitMustUse(false),
);
- server.create(
- "//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml",
- RatomlTest::EMIT_MUST_USE.to_owned(),
- );
+ server.create("//- /$$CONFIG_DIR$$/rust-analyzer.toml", RatomlTest::EMIT_MUST_USE.to_owned());
server.query(
InternalTestingFetchConfigOption::AssistEmitMustUse,
1,
@@ -365,7 +360,6 @@ enum Value {
}
#[test]
-#[ignore = "the user config is currently not being watched on startup, fix this"]
fn ratoml_modify_user_config() {
if skip_slow_tests() {
return;
@@ -386,7 +380,7 @@ enum Value {
Text(String),
}"#,
r#"
-//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml
+//- /$$CONFIG_DIR$$/rust-analyzer.toml
assist.emitMustUse = true"#,
],
vec!["p1"],
@@ -407,7 +401,6 @@ assist.emitMustUse = true"#,
}
#[test]
-#[ignore = "the user config is currently not being watched on startup, fix this"]
fn ratoml_delete_user_config() {
if skip_slow_tests() {
return;
@@ -428,7 +421,7 @@ enum Value {
Text(String),
}"#,
r#"
-//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml
+//- /$$CONFIG_DIR$$/rust-analyzer.toml
assist.emitMustUse = true"#,
],
vec!["p1"],
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index 78572e37a9..5a88a5515c 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -1,7 +1,7 @@
use std::{
cell::{Cell, RefCell},
- fs,
- sync::Once,
+ env, fs,
+ sync::{Once, OnceLock},
time::Duration,
};
@@ -127,7 +127,53 @@ impl Project<'_> {
}
pub(crate) fn server(self) -> Server {
- static CONFIG_DIR_LOCK: Mutex<()> = Mutex::new(());
+ Project::server_with_lock(self, false)
+ }
+
+ /// `prelock` : Forcefully acquire a lock that will maintain the path to the config dir throughout the whole test.
+ ///
+ /// When testing we set the user config dir by setting an envvar `__TEST_RA_USER_CONFIG_DIR`.
+ /// This value must be maintained until the end of a test case. When tests run in parallel
+ /// this value may change thus making the tests flaky. As such, we use a `MutexGuard` that locks
+ /// the process until `Server` is dropped. To optimize parallelization we use a lock only when it is
+ /// needed, that is when a test uses config directory to do stuff. Our naive approach is to use a lock
+ /// if there is a path to config dir in the test fixture. However, in certain cases we create a
+ /// file in the config dir after server is run, something where our naive approach comes short.
+ /// Using a `prelock` allows us to force a lock when we know we need it.
+ pub(crate) fn server_with_lock(self, config_lock: bool) -> Server {
+ static CONFIG_DIR_LOCK: OnceLock<(Utf8PathBuf, Mutex<()>)> = OnceLock::new();
+
+ let config_dir_guard = if config_lock {
+ Some({
+ let (path, mutex) = CONFIG_DIR_LOCK.get_or_init(|| {
+ let value = TestDir::new().keep().path().to_owned();
+ env::set_var("__TEST_RA_USER_CONFIG_DIR", &value);
+ (value, Mutex::new(()))
+ });
+ #[allow(dyn_drop)]
+ (mutex.lock(), {
+ Box::new({
+ struct Dropper(Utf8PathBuf);
+ impl Drop for Dropper {
+ fn drop(&mut self) {
+ for entry in fs::read_dir(&self.0).unwrap() {
+ let path = entry.unwrap().path();
+ if path.is_file() {
+ fs::remove_file(path).unwrap();
+ } else if path.is_dir() {
+ fs::remove_dir_all(path).unwrap();
+ }
+ }
+ }
+ }
+ Dropper(path.clone())
+ }) as Box<dyn Drop>
+ })
+ })
+ } else {
+ None
+ };
+
let tmp_dir = self.tmp_dir.unwrap_or_else(|| {
if self.root_dir_contains_symlink {
TestDir::new_symlink()
@@ -160,13 +206,9 @@ impl Project<'_> {
assert!(mini_core.is_none());
assert!(toolchain.is_none());
- let mut config_dir_guard = None;
for entry in fixture {
if let Some(pth) = entry.path.strip_prefix("/$$CONFIG_DIR$$") {
- if config_dir_guard.is_none() {
- config_dir_guard = Some(CONFIG_DIR_LOCK.lock());
- }
- let path = Config::user_config_path().unwrap().join(&pth['/'.len_utf8()..]);
+ let path = Config::user_config_dir_path().unwrap().join(&pth['/'.len_utf8()..]);
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
} else {
@@ -269,12 +311,14 @@ pub(crate) struct Server {
client: Connection,
/// XXX: remove the tempdir last
dir: TestDir,
- _config_dir_guard: Option<MutexGuard<'static, ()>>,
+ #[allow(dyn_drop)]
+ _config_dir_guard: Option<(MutexGuard<'static, ()>, Box<dyn Drop>)>,
}
impl Server {
+ #[allow(dyn_drop)]
fn new(
- config_dir_guard: Option<MutexGuard<'static, ()>>,
+ config_dir_guard: Option<(MutexGuard<'static, ()>, Box<dyn Drop>)>,
dir: TestDir,
config: Config,
) -> Server {
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 02c59646a9..30428329dd 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -37,7 +37,7 @@ Path =
PathSegment =
'::'? NameRef
| NameRef GenericArgList?
-| NameRef ParamList RetType?
+| NameRef ParenthesizedArgList RetType?
| NameRef ReturnTypeSyntax
| '<' Type ('as' PathType)? '>'
@@ -49,6 +49,9 @@ ReturnTypeSyntax =
// Generics //
//*************************//
+ParenthesizedArgList =
+ '::'? '(' (TypeArg (',' TypeArg)* ','?)? ')'
+
GenericArgList =
'::'? '<' (GenericArg (',' GenericArg)* ','?)? '>'
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index f1286e7aa2..ffe9f16cfd 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -909,30 +909,6 @@ fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken {
}
}
-impl ast::VariantList {
- pub fn add_variant(&self, variant: ast::Variant) {
- let (indent, position) = match self.variants().last() {
- Some(last_item) => (
- IndentLevel::from_node(last_item.syntax()),
- Position::after(get_or_insert_comma_after(last_item.syntax())),
- ),
- None => match self.l_curly_token() {
- Some(l_curly) => {
- normalize_ws_between_braces(self.syntax());
- (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly))
- }
- None => (IndentLevel::single(), Position::last_child_of(self.syntax())),
- },
- };
- let elements: Vec<SyntaxElement> = vec![
- make::tokens::whitespace(&format!("{}{indent}", "\n")).into(),
- variant.syntax().clone().into(),
- ast::make::token(T![,]).into(),
- ];
- ted::insert_all(position, elements);
- }
-}
-
fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
let l = node
.children_with_tokens()
@@ -1055,8 +1031,6 @@ mod tests {
use std::fmt;
use parser::Edition;
- use stdx::trim_indent;
- use test_utils::assert_eq_text;
use crate::SourceFile;
@@ -1170,102 +1144,4 @@ mod tests {
check("let a: u8 = 3;", "let a = 3;", None);
check("let a: = 3;", "let a = 3;", None);
}
-
- #[test]
- fn add_variant_to_empty_enum() {
- let variant = make::variant(make::name("Bar"), None).clone_for_update();
-
- check_add_variant(
- r#"
-enum Foo {}
-"#,
- r#"
-enum Foo {
- Bar,
-}
-"#,
- variant,
- );
- }
-
- #[test]
- fn add_variant_to_non_empty_enum() {
- let variant = make::variant(make::name("Baz"), None).clone_for_update();
-
- check_add_variant(
- r#"
-enum Foo {
- Bar,
-}
-"#,
- r#"
-enum Foo {
- Bar,
- Baz,
-}
-"#,
- variant,
- );
- }
-
- #[test]
- fn add_variant_with_tuple_field_list() {
- let variant = make::variant(
- make::name("Baz"),
- Some(ast::FieldList::TupleFieldList(make::tuple_field_list(std::iter::once(
- make::tuple_field(None, make::ty("bool")),
- )))),
- )
- .clone_for_update();
-
- check_add_variant(
- r#"
-enum Foo {
- Bar,
-}
-"#,
- r#"
-enum Foo {
- Bar,
- Baz(bool),
-}
-"#,
- variant,
- );
- }
-
- #[test]
- fn add_variant_with_record_field_list() {
- let variant = make::variant(
- make::name("Baz"),
- Some(ast::FieldList::RecordFieldList(make::record_field_list(std::iter::once(
- make::record_field(None, make::name("x"), make::ty("bool")),
- )))),
- )
- .clone_for_update();
-
- check_add_variant(
- r#"
-enum Foo {
- Bar,
-}
-"#,
- r#"
-enum Foo {
- Bar,
- Baz { x: bool },
-}
-"#,
- variant,
- );
- }
-
- fn check_add_variant(before: &str, expected: &str, variant: ast::Variant) {
- let enum_ = ast_mut_from_text::<ast::Enum>(before);
- if let Some(it) = enum_.variant_list() {
- it.add_variant(variant)
- }
- let after = enum_.to_string();
- assert_eq_text!(&trim_indent(expected.trim()), &trim_indent(after.trim()));
- }
}
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 23d2b355a9..01dcb646b3 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -1363,6 +1363,21 @@ impl ParenType {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ParenthesizedArgList {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ParenthesizedArgList {
+ #[inline]
+ pub fn type_args(&self) -> AstChildren<TypeArg> { support::children(&self.syntax) }
+ #[inline]
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ #[inline]
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+ #[inline]
+ pub fn coloncolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![::]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Path {
pub(crate) syntax: SyntaxNode,
}
@@ -1403,7 +1418,9 @@ impl PathSegment {
#[inline]
pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
#[inline]
- pub fn param_list(&self) -> Option<ParamList> { support::child(&self.syntax) }
+ pub fn parenthesized_arg_list(&self) -> Option<ParenthesizedArgList> {
+ support::child(&self.syntax)
+ }
#[inline]
pub fn path_type(&self) -> Option<PathType> { support::child(&self.syntax) }
#[inline]
@@ -3760,6 +3777,20 @@ impl AstNode for ParenType {
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for ParenthesizedArgList {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == PARENTHESIZED_ARG_LIST }
+ #[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 Path {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == PATH }
@@ -7097,6 +7128,11 @@ impl std::fmt::Display for ParenType {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for ParenthesizedArgList {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Path {
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/make.rs b/crates/syntax/src/ast/make.rs
index 2ec83d23b2..05c2a8354d 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -402,7 +402,7 @@ pub fn join_paths(paths: impl IntoIterator<Item = ast::Path>) -> ast::Path {
// FIXME: should not be pub
pub fn path_from_text(text: &str) -> ast::Path {
- ast_from_text(&format!("fn main() {{ let test = {text}; }}"))
+ ast_from_text(&format!("fn main() {{ let test: {text}; }}"))
}
pub fn use_tree_glob() -> ast::UseTree {
@@ -1053,7 +1053,17 @@ pub fn variant_list(variants: impl IntoIterator<Item = ast::Variant>) -> ast::Va
ast_from_text(&format!("enum f {{ {variants} }}"))
}
-pub fn variant(name: ast::Name, field_list: Option<ast::FieldList>) -> ast::Variant {
+pub fn variant(
+ visibility: Option<ast::Visibility>,
+ name: ast::Name,
+ field_list: Option<ast::FieldList>,
+ discriminant: Option<ast::Expr>,
+) -> ast::Variant {
+ let visibility = match visibility {
+ None => String::new(),
+ Some(it) => format!("{it} "),
+ };
+
let field_list = match field_list {
None => String::new(),
Some(it) => match it {
@@ -1061,7 +1071,12 @@ pub fn variant(name: ast::Name, field_list: Option<ast::FieldList>) -> ast::Vari
ast::FieldList::TupleFieldList(tuple) => format!("{tuple}"),
},
};
- ast_from_text(&format!("enum f {{ {name}{field_list} }}"))
+
+ let discriminant = match discriminant {
+ Some(it) => format!(" = {it}"),
+ None => String::new(),
+ };
+ ast_from_text(&format!("enum f {{ {visibility}{name}{field_list}{discriminant} }}"))
}
pub fn fn_(
@@ -1122,6 +1137,8 @@ pub fn struct_(
pub fn enum_(
visibility: Option<ast::Visibility>,
enum_name: ast::Name,
+ generic_param_list: Option<ast::GenericParamList>,
+ where_clause: Option<ast::WhereClause>,
variant_list: ast::VariantList,
) -> ast::Enum {
let visibility = match visibility {
@@ -1129,7 +1146,12 @@ pub fn enum_(
Some(it) => format!("{it} "),
};
- ast_from_text(&format!("{visibility}enum {enum_name} {variant_list}"))
+ let generic_params = generic_param_list.map(|it| it.to_string()).unwrap_or_default();
+ let where_clause = where_clause.map(|it| format!(" {it}")).unwrap_or_default();
+
+ ast_from_text(&format!(
+ "{visibility}enum {enum_name}{generic_params}{where_clause} {variant_list}"
+ ))
}
pub fn attr_outer(meta: ast::Meta) -> ast::Attr {
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs
index 9f88add0f7..e86c291f76 100644
--- a/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -2,9 +2,9 @@
use itertools::Itertools;
use crate::{
- ast::{self, make, HasName},
+ ast::{self, make, HasGenericParams, HasName, HasTypeBounds, HasVisibility},
syntax_editor::SyntaxMappingBuilder,
- AstNode,
+ AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken,
};
use super::SyntaxFactory;
@@ -14,6 +14,40 @@ impl SyntaxFactory {
make::name(name).clone_for_update()
}
+ pub fn ty(&self, text: &str) -> ast::Type {
+ make::ty(text).clone_for_update()
+ }
+
+ pub fn ty_infer(&self) -> ast::InferType {
+ let ast::Type::InferType(ast) = make::ty_placeholder().clone_for_update() else {
+ unreachable!()
+ };
+
+ ast
+ }
+
+ pub fn type_param(
+ &self,
+ name: ast::Name,
+ bounds: Option<ast::TypeBoundList>,
+ ) -> ast::TypeParam {
+ let ast = make::type_param(name.clone(), bounds.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone());
+ if let Some(input) = bounds {
+ builder.map_node(
+ input.syntax().clone(),
+ ast.type_bound_list().unwrap().syntax().clone(),
+ );
+ }
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn ident_pat(&self, ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat {
let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update();
@@ -32,22 +66,52 @@ impl SyntaxFactory {
tail_expr: Option<ast::Expr>,
) -> ast::BlockExpr {
let stmts = stmts.into_iter().collect_vec();
- let input = stmts.iter().map(|it| it.syntax().clone()).collect_vec();
+ let mut input = stmts.iter().map(|it| it.syntax().clone()).collect_vec();
let ast = make::block_expr(stmts, tail_expr.clone()).clone_for_update();
- if let Some((mut mapping, stmt_list)) = self.mappings().zip(ast.stmt_list()) {
+ if let Some(mut mapping) = self.mappings() {
+ let stmt_list = ast.stmt_list().unwrap();
let mut builder = SyntaxMappingBuilder::new(stmt_list.syntax().clone());
+ if let Some(input) = tail_expr {
+ builder.map_node(
+ input.syntax().clone(),
+ stmt_list.tail_expr().unwrap().syntax().clone(),
+ );
+ } else if let Some(ast_tail) = stmt_list.tail_expr() {
+ // The parser interpreted the last statement (probably a statement with a block) as an Expr
+ let last_stmt = input.pop().unwrap();
+
+ builder.map_node(last_stmt, ast_tail.syntax().clone());
+ }
+
builder.map_children(
input.into_iter(),
stmt_list.statements().map(|it| it.syntax().clone()),
);
- if let Some((input, output)) = tail_expr.zip(stmt_list.tail_expr()) {
- builder.map_node(input.syntax().clone(), output.syntax().clone());
- }
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn expr_empty_block(&self) -> ast::BlockExpr {
+ ast::BlockExpr { syntax: make::expr_empty_block().syntax().clone_for_update() }
+ }
+ pub fn expr_bin(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::BinExpr {
+ let ast::Expr::BinExpr(ast) =
+ make::expr_bin_op(lhs.clone(), op, rhs.clone()).clone_for_update()
+ else {
+ unreachable!()
+ };
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(lhs.syntax().clone(), ast.lhs().unwrap().syntax().clone());
+ builder.map_node(rhs.syntax().clone(), ast.rhs().unwrap().syntax().clone());
builder.finish(&mut mapping);
}
@@ -83,6 +147,22 @@ impl SyntaxFactory {
ast.into()
}
+ pub fn expr_return(&self, expr: Option<ast::Expr>) -> ast::ReturnExpr {
+ let ast::Expr::ReturnExpr(ast) = make::expr_return(expr.clone()).clone_for_update() else {
+ unreachable!()
+ };
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ if let Some(input) = expr {
+ builder.map_node(input.syntax().clone(), ast.expr().unwrap().syntax().clone());
+ }
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn let_stmt(
&self,
pattern: ast::Pat,
@@ -107,4 +187,261 @@ impl SyntaxFactory {
ast
}
+
+ pub fn turbofish_generic_arg_list(
+ &self,
+ args: impl IntoIterator<Item = ast::GenericArg> + Clone,
+ ) -> ast::GenericArgList {
+ let ast = make::turbofish_generic_arg_list(args.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_children(
+ args.into_iter().map(|arg| arg.syntax().clone()),
+ ast.generic_args().map(|arg| arg.syntax().clone()),
+ );
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn record_field_list(
+ &self,
+ fields: impl IntoIterator<Item = ast::RecordField>,
+ ) -> ast::RecordFieldList {
+ let fields: Vec<ast::RecordField> = fields.into_iter().collect();
+ let input: Vec<_> = fields.iter().map(|it| it.syntax().clone()).collect();
+ let ast = make::record_field_list(fields).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+
+ builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone()));
+
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn record_field(
+ &self,
+ visibility: Option<ast::Visibility>,
+ name: ast::Name,
+ ty: ast::Type,
+ ) -> ast::RecordField {
+ let ast =
+ make::record_field(visibility.clone(), name.clone(), ty.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ if let Some(visibility) = visibility {
+ builder.map_node(
+ visibility.syntax().clone(),
+ ast.visibility().unwrap().syntax().clone(),
+ );
+ }
+
+ builder.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone());
+ builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone());
+
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn tuple_field_list(
+ &self,
+ fields: impl IntoIterator<Item = ast::TupleField>,
+ ) -> ast::TupleFieldList {
+ let fields: Vec<ast::TupleField> = fields.into_iter().collect();
+ let input: Vec<_> = fields.iter().map(|it| it.syntax().clone()).collect();
+ let ast = make::tuple_field_list(fields).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+
+ builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone()));
+
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn tuple_field(
+ &self,
+ visibility: Option<ast::Visibility>,
+ ty: ast::Type,
+ ) -> ast::TupleField {
+ let ast = make::tuple_field(visibility.clone(), ty.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ if let Some(visibility) = visibility {
+ builder.map_node(
+ visibility.syntax().clone(),
+ ast.visibility().unwrap().syntax().clone(),
+ );
+ }
+
+ builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone());
+
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn item_enum(
+ &self,
+ visibility: Option<ast::Visibility>,
+ name: ast::Name,
+ generic_param_list: Option<ast::GenericParamList>,
+ where_clause: Option<ast::WhereClause>,
+ variant_list: ast::VariantList,
+ ) -> ast::Enum {
+ let ast = make::enum_(
+ visibility.clone(),
+ name.clone(),
+ generic_param_list.clone(),
+ where_clause.clone(),
+ variant_list.clone(),
+ )
+ .clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ if let Some(visibility) = visibility {
+ builder.map_node(
+ visibility.syntax().clone(),
+ ast.visibility().unwrap().syntax().clone(),
+ );
+ }
+
+ builder.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone());
+
+ if let Some(generic_param_list) = generic_param_list {
+ builder.map_node(
+ generic_param_list.syntax().clone(),
+ ast.generic_param_list().unwrap().syntax().clone(),
+ );
+ }
+
+ if let Some(where_clause) = where_clause {
+ builder.map_node(
+ where_clause.syntax().clone(),
+ ast.where_clause().unwrap().syntax().clone(),
+ );
+ }
+
+ builder.map_node(
+ variant_list.syntax().clone(),
+ ast.variant_list().unwrap().syntax().clone(),
+ );
+
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn variant_list(
+ &self,
+ variants: impl IntoIterator<Item = ast::Variant>,
+ ) -> ast::VariantList {
+ let variants: Vec<ast::Variant> = variants.into_iter().collect();
+ let input: Vec<_> = variants.iter().map(|it| it.syntax().clone()).collect();
+ let ast = make::variant_list(variants).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+
+ builder.map_children(input.into_iter(), ast.variants().map(|it| it.syntax().clone()));
+
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn variant(
+ &self,
+ visibility: Option<ast::Visibility>,
+ name: ast::Name,
+ field_list: Option<ast::FieldList>,
+ discriminant: Option<ast::Expr>,
+ ) -> ast::Variant {
+ let ast = make::variant(
+ visibility.clone(),
+ name.clone(),
+ field_list.clone(),
+ discriminant.clone(),
+ )
+ .clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ if let Some(visibility) = visibility {
+ builder.map_node(
+ visibility.syntax().clone(),
+ ast.visibility().unwrap().syntax().clone(),
+ );
+ }
+
+ builder.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone());
+
+ if let Some(field_list) = field_list {
+ builder.map_node(
+ field_list.syntax().clone(),
+ ast.field_list().unwrap().syntax().clone(),
+ );
+ }
+
+ if let Some(discriminant) = discriminant {
+ builder
+ .map_node(discriminant.syntax().clone(), ast.expr().unwrap().syntax().clone());
+ }
+
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn token_tree(
+ &self,
+ delimiter: SyntaxKind,
+ tt: Vec<NodeOrToken<ast::TokenTree, SyntaxToken>>,
+ ) -> ast::TokenTree {
+ let tt: Vec<_> = tt.into_iter().collect();
+ let input: Vec<_> = tt.iter().cloned().filter_map(only_nodes).collect();
+
+ let ast = make::token_tree(delimiter, tt).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_children(
+ input.into_iter(),
+ ast.token_trees_and_tokens().filter_map(only_nodes),
+ );
+ builder.finish(&mut mapping);
+ }
+
+ return ast;
+
+ fn only_nodes(element: NodeOrToken<ast::TokenTree, SyntaxToken>) -> Option<SyntaxNode> {
+ element.as_node().map(|it| it.syntax().clone())
+ }
+ }
+
+ pub fn token(&self, kind: SyntaxKind) -> SyntaxToken {
+ make::token(kind)
+ }
+
+ pub fn whitespace(&self, text: &str) -> ast::SyntaxToken {
+ make::tokens::whitespace(text)
+ }
}
diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs
index 714f5a9911..992a847663 100644
--- a/crates/syntax/src/syntax_editor.rs
+++ b/crates/syntax/src/syntax_editor.rs
@@ -16,6 +16,7 @@ use rustc_hash::FxHashMap;
use crate::{SyntaxElement, SyntaxNode, SyntaxToken};
mod edit_algo;
+mod edits;
mod mapping;
pub use mapping::{SyntaxMapping, SyntaxMappingBuilder};
@@ -326,7 +327,7 @@ mod tests {
use crate::{
ast::{self, make, syntax_factory::SyntaxFactory},
- AstNode,
+ AstNode, SyntaxKind,
};
use super::*;
@@ -539,4 +540,50 @@ mod tests {
}"#]];
expect.assert_eq(&edit.new_root.to_string());
}
+
+ #[test]
+ fn test_replace_token_in_parent() {
+ let parent_fn = make::fn_(
+ None,
+ make::name("it"),
+ None,
+ None,
+ make::param_list(None, []),
+ make::block_expr([], Some(make::expr_unit())),
+ Some(make::ret_type(make::ty_unit())),
+ false,
+ false,
+ false,
+ false,
+ );
+
+ let mut editor = SyntaxEditor::new(parent_fn.syntax().clone());
+
+ if let Some(ret_ty) = parent_fn.ret_type() {
+ editor.delete(ret_ty.syntax().clone());
+
+ if let Some(SyntaxElement::Token(token)) = ret_ty.syntax().next_sibling_or_token() {
+ if token.kind().is_trivia() {
+ editor.delete(token);
+ }
+ }
+ }
+
+ if let Some(tail) = parent_fn.body().unwrap().tail_expr() {
+ // FIXME: We do this because `xtask tidy` will not allow us to have trailing whitespace in the expect string.
+ if let Some(SyntaxElement::Token(token)) = tail.syntax().prev_sibling_or_token() {
+ if let SyntaxKind::WHITESPACE = token.kind() {
+ editor.delete(token);
+ }
+ }
+ editor.delete(tail.syntax().clone());
+ }
+
+ let edit = editor.finish();
+
+ let expect = expect![[r#"
+fn it() {
+}"#]];
+ expect.assert_eq(&edit.new_root.to_string());
+ }
}
diff --git a/crates/syntax/src/syntax_editor/edit_algo.rs b/crates/syntax/src/syntax_editor/edit_algo.rs
index b769c94110..57ecbe5701 100644
--- a/crates/syntax/src/syntax_editor/edit_algo.rs
+++ b/crates/syntax/src/syntax_editor/edit_algo.rs
@@ -73,7 +73,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
})
.all(|(l, r)| {
get_node_depth(l.target_parent()) != get_node_depth(r.target_parent())
- || l.target_range().intersect(r.target_range()).is_none()
+ || (l.target_range().end() <= r.target_range().start())
});
if stdx::never!(
@@ -128,13 +128,14 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
// Add to changed ancestors, if applicable
match change {
- Change::Insert(_, _) | Change::InsertAll(_, _) => {}
- Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => {
+ Change::Replace(SyntaxElement::Node(target), _)
+ | Change::ReplaceWithMany(SyntaxElement::Node(target), _) => {
changed_ancestors.push_back(ChangedAncestor::single(target, change_index))
}
Change::ReplaceAll(range, _) => {
changed_ancestors.push_back(ChangedAncestor::multiple(range, change_index))
}
+ _ => (),
}
}
@@ -154,6 +155,12 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
}
};
}
+ Change::Replace(SyntaxElement::Node(target), Some(SyntaxElement::Node(new_target))) => {
+ *target = tree_mutator.make_syntax_mut(target);
+ if new_target.ancestors().any(|node| node == tree_mutator.immutable) {
+ *new_target = new_target.clone_for_update();
+ }
+ }
Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => {
*target = tree_mutator.make_element_mut(target);
}
@@ -304,13 +311,8 @@ enum ChangedAncestorKind {
}
impl ChangedAncestor {
- fn single(element: &SyntaxElement, change_index: usize) -> Self {
- let kind = match element {
- SyntaxElement::Node(node) => ChangedAncestorKind::Single { node: node.clone() },
- SyntaxElement::Token(token) => {
- ChangedAncestorKind::Single { node: token.parent().unwrap() }
- }
- };
+ fn single(node: &SyntaxNode, change_index: usize) -> Self {
+ let kind = ChangedAncestorKind::Single { node: node.clone() };
Self { kind, change_index }
}
diff --git a/crates/syntax/src/syntax_editor/edits.rs b/crates/syntax/src/syntax_editor/edits.rs
new file mode 100644
index 0000000000..8069fdd06f
--- /dev/null
+++ b/crates/syntax/src/syntax_editor/edits.rs
@@ -0,0 +1,274 @@
+//! Structural editing for ast using `SyntaxEditor`
+
+use crate::{
+ ast::{
+ self, edit::IndentLevel, make, syntax_factory::SyntaxFactory, AstNode, Fn, GenericParam,
+ HasGenericParams, HasName,
+ },
+ syntax_editor::{Position, SyntaxEditor},
+ Direction, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T,
+};
+
+impl SyntaxEditor {
+ /// Adds a new generic param to the function using `SyntaxEditor`
+ pub fn add_generic_param(&mut self, function: &Fn, new_param: GenericParam) {
+ match function.generic_param_list() {
+ Some(generic_param_list) => match generic_param_list.generic_params().last() {
+ Some(last_param) => {
+ // There exists a generic param list and it's not empty
+ let position = generic_param_list.r_angle_token().map_or_else(
+ || Position::last_child_of(function.syntax()),
+ Position::before,
+ );
+
+ if last_param
+ .syntax()
+ .next_sibling_or_token()
+ .map_or(false, |it| it.kind() == SyntaxKind::COMMA)
+ {
+ self.insert(
+ Position::after(last_param.syntax()),
+ new_param.syntax().clone(),
+ );
+ self.insert(
+ Position::after(last_param.syntax()),
+ make::token(SyntaxKind::WHITESPACE),
+ );
+ self.insert(
+ Position::after(last_param.syntax()),
+ make::token(SyntaxKind::COMMA),
+ );
+ } else {
+ let elements = vec![
+ make::token(SyntaxKind::COMMA).into(),
+ make::token(SyntaxKind::WHITESPACE).into(),
+ new_param.syntax().clone().into(),
+ ];
+ self.insert_all(position, elements);
+ }
+ }
+ None => {
+ // There exists a generic param list but it's empty
+ let position = Position::after(generic_param_list.l_angle_token().unwrap());
+ self.insert(position, new_param.syntax());
+ }
+ },
+ None => {
+ // There was no generic param list
+ let position = if let Some(name) = function.name() {
+ Position::after(name.syntax)
+ } else if let Some(fn_token) = function.fn_token() {
+ Position::after(fn_token)
+ } else if let Some(param_list) = function.param_list() {
+ Position::before(param_list.syntax)
+ } else {
+ Position::last_child_of(function.syntax())
+ };
+ let elements = vec![
+ make::token(SyntaxKind::L_ANGLE).into(),
+ new_param.syntax().clone().into(),
+ make::token(SyntaxKind::R_ANGLE).into(),
+ ];
+ self.insert_all(position, elements);
+ }
+ }
+ }
+}
+
+fn get_or_insert_comma_after(editor: &mut SyntaxEditor, syntax: &SyntaxNode) -> SyntaxToken {
+ let make = SyntaxFactory::without_mappings();
+ match syntax
+ .siblings_with_tokens(Direction::Next)
+ .filter_map(|it| it.into_token())
+ .find(|it| it.kind() == T![,])
+ {
+ Some(it) => it,
+ None => {
+ let comma = make.token(T![,]);
+ editor.insert(Position::after(syntax), &comma);
+ comma
+ }
+ }
+}
+
+impl ast::VariantList {
+ pub fn add_variant(&self, editor: &mut SyntaxEditor, variant: &ast::Variant) {
+ let make = SyntaxFactory::without_mappings();
+ let (indent, position) = match self.variants().last() {
+ Some(last_item) => (
+ IndentLevel::from_node(last_item.syntax()),
+ Position::after(get_or_insert_comma_after(editor, last_item.syntax())),
+ ),
+ None => match self.l_curly_token() {
+ Some(l_curly) => {
+ normalize_ws_between_braces(editor, self.syntax());
+ (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly))
+ }
+ None => (IndentLevel::single(), Position::last_child_of(self.syntax())),
+ },
+ };
+ let elements: Vec<SyntaxElement> = vec![
+ make.whitespace(&format!("{}{indent}", "\n")).into(),
+ variant.syntax().clone().into(),
+ make.token(T![,]).into(),
+ ];
+ editor.insert_all(position, elements);
+ }
+}
+
+fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) -> Option<()> {
+ let make = SyntaxFactory::without_mappings();
+ let l = node
+ .children_with_tokens()
+ .filter_map(|it| it.into_token())
+ .find(|it| it.kind() == T!['{'])?;
+ let r = node
+ .children_with_tokens()
+ .filter_map(|it| it.into_token())
+ .find(|it| it.kind() == T!['}'])?;
+
+ let indent = IndentLevel::from_node(node);
+
+ match l.next_sibling_or_token() {
+ Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => {
+ if ws.next_sibling_or_token()?.into_token()? == r {
+ editor.replace(ws, make.whitespace(&format!("\n{indent}")));
+ }
+ }
+ Some(ws) if ws.kind() == T!['}'] => {
+ editor.insert(Position::after(l), make.whitespace(&format!("\n{indent}")));
+ }
+ _ => (),
+ }
+ Some(())
+}
+
+#[cfg(test)]
+mod tests {
+ use parser::Edition;
+ use stdx::trim_indent;
+ use test_utils::assert_eq_text;
+
+ use crate::SourceFile;
+
+ use super::*;
+
+ fn ast_from_text<N: AstNode>(text: &str) -> N {
+ let parse = SourceFile::parse(text, Edition::CURRENT);
+ let node = match parse.tree().syntax().descendants().find_map(N::cast) {
+ Some(it) => it,
+ None => {
+ let node = std::any::type_name::<N>();
+ panic!("Failed to make ast node `{node}` from text {text}")
+ }
+ };
+ let node = node.clone_subtree();
+ assert_eq!(node.syntax().text_range().start(), 0.into());
+ node
+ }
+
+ #[test]
+ fn add_variant_to_empty_enum() {
+ let make = SyntaxFactory::without_mappings();
+ let variant = make.variant(None, make.name("Bar"), None, None);
+
+ check_add_variant(
+ r#"
+enum Foo {}
+"#,
+ r#"
+enum Foo {
+ Bar,
+}
+"#,
+ variant,
+ );
+ }
+
+ #[test]
+ fn add_variant_to_non_empty_enum() {
+ let make = SyntaxFactory::without_mappings();
+ let variant = make.variant(None, make.name("Baz"), None, None);
+
+ check_add_variant(
+ r#"
+enum Foo {
+ Bar,
+}
+"#,
+ r#"
+enum Foo {
+ Bar,
+ Baz,
+}
+"#,
+ variant,
+ );
+ }
+
+ #[test]
+ fn add_variant_with_tuple_field_list() {
+ let make = SyntaxFactory::without_mappings();
+ let variant = make.variant(
+ None,
+ make.name("Baz"),
+ Some(make.tuple_field_list([make.tuple_field(None, make.ty("bool"))]).into()),
+ None,
+ );
+
+ check_add_variant(
+ r#"
+enum Foo {
+ Bar,
+}
+"#,
+ r#"
+enum Foo {
+ Bar,
+ Baz(bool),
+}
+"#,
+ variant,
+ );
+ }
+
+ #[test]
+ fn add_variant_with_record_field_list() {
+ let make = SyntaxFactory::without_mappings();
+ let variant = make.variant(
+ None,
+ make.name("Baz"),
+ Some(
+ make.record_field_list([make.record_field(None, make.name("x"), make.ty("bool"))])
+ .into(),
+ ),
+ None,
+ );
+
+ check_add_variant(
+ r#"
+enum Foo {
+ Bar,
+}
+"#,
+ r#"
+enum Foo {
+ Bar,
+ Baz { x: bool },
+}
+"#,
+ variant,
+ );
+ }
+
+ fn check_add_variant(before: &str, expected: &str, variant: ast::Variant) {
+ let enum_ = ast_from_text::<ast::Enum>(before);
+ let mut editor = SyntaxEditor::new(enum_.syntax().clone());
+ if let Some(it) = enum_.variant_list() {
+ it.add_variant(&mut editor, &variant)
+ }
+ let edit = editor.finish();
+ let after = edit.new_root.to_string();
+ assert_eq_text!(&trim_indent(expected.trim()), &trim_indent(after.trim()));
+ }
+}
diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs
index 593e31c2fb..889a7d10ad 100644
--- a/crates/test-fixture/src/lib.rs
+++ b/crates/test-fixture/src/lib.rs
@@ -236,7 +236,7 @@ impl ChangeFixture {
crate_graph.add_crate_root(
crate_root,
Edition::CURRENT,
- Some(CrateName::new("test").unwrap().into()),
+ Some(CrateName::new("ra_test_fixture").unwrap().into()),
None,
From::from(default_cfg.clone()),
Some(From::from(default_cfg)),
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 07767d5ae9..99dfabe174 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -12,6 +12,7 @@
//! asm:
//! assert:
//! as_ref: sized
+//! async_fn: fn, tuple, future, copy
//! bool_impl: option, fn
//! builtin_impls:
//! cell: copy, drop
@@ -29,7 +30,7 @@
//! eq: sized
//! error: fmt
//! fmt: option, result, transmute, coerce_unsized, copy, clone, derive
-//! fn:
+//! fn: tuple
//! from: sized
//! future: pin
//! coroutine: pin
@@ -60,6 +61,7 @@
//! sync: sized
//! transmute:
//! try: infallible
+//! tuple:
//! unpin: sized
//! unsize: sized
//! todo: panic
@@ -138,10 +140,10 @@ pub mod marker {
}
// endregion:copy
- // region:fn
+ // region:tuple
#[lang = "tuple_trait"]
pub trait Tuple {}
- // endregion:fn
+ // endregion:tuple
// region:phantom_data
#[lang = "phantom_data"]
@@ -682,6 +684,112 @@ pub mod ops {
}
pub use self::function::{Fn, FnMut, FnOnce};
// endregion:fn
+
+ // region:async_fn
+ mod async_function {
+ use crate::{future::Future, marker::Tuple};
+
+ #[lang = "async_fn"]
+ #[fundamental]
+ pub trait AsyncFn<Args: Tuple>: AsyncFnMut<Args> {
+ extern "rust-call" fn async_call(&self, args: Args) -> Self::CallRefFuture<'_>;
+ }
+
+ #[lang = "async_fn_mut"]
+ #[fundamental]
+ pub trait AsyncFnMut<Args: Tuple>: AsyncFnOnce<Args> {
+ #[lang = "call_ref_future"]
+ type CallRefFuture<'a>: Future<Output = Self::Output>
+ where
+ Self: 'a;
+ extern "rust-call" fn async_call_mut(&mut self, args: Args) -> Self::CallRefFuture<'_>;
+ }
+
+ #[lang = "async_fn_once"]
+ #[fundamental]
+ pub trait AsyncFnOnce<Args: Tuple> {
+ #[lang = "async_fn_once_output"]
+ type Output;
+ #[lang = "call_once_future"]
+ type CallOnceFuture: Future<Output = Self::Output>;
+ extern "rust-call" fn async_call_once(self, args: Args) -> Self::CallOnceFuture;
+ }
+
+ mod impls {
+ use super::{AsyncFn, AsyncFnMut, AsyncFnOnce};
+ use crate::marker::Tuple;
+
+ impl<A: Tuple, F: ?Sized> AsyncFn<A> for &F
+ where
+ F: AsyncFn<A>,
+ {
+ extern "rust-call" fn async_call(&self, args: A) -> Self::CallRefFuture<'_> {
+ F::async_call(*self, args)
+ }
+ }
+
+ impl<A: Tuple, F: ?Sized> AsyncFnMut<A> for &F
+ where
+ F: AsyncFn<A>,
+ {
+ type CallRefFuture<'a>
+ = F::CallRefFuture<'a>
+ where
+ Self: 'a;
+
+ extern "rust-call" fn async_call_mut(
+ &mut self,
+ args: A,
+ ) -> Self::CallRefFuture<'_> {
+ F::async_call(*self, args)
+ }
+ }
+
+ impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce<A> for &'a F
+ where
+ F: AsyncFn<A>,
+ {
+ type Output = F::Output;
+ type CallOnceFuture = F::CallRefFuture<'a>;
+
+ extern "rust-call" fn async_call_once(self, args: A) -> Self::CallOnceFuture {
+ F::async_call(self, args)
+ }
+ }
+
+ impl<A: Tuple, F: ?Sized> AsyncFnMut<A> for &mut F
+ where
+ F: AsyncFnMut<A>,
+ {
+ type CallRefFuture<'a>
+ = F::CallRefFuture<'a>
+ where
+ Self: 'a;
+
+ extern "rust-call" fn async_call_mut(
+ &mut self,
+ args: A,
+ ) -> Self::CallRefFuture<'_> {
+ F::async_call_mut(*self, args)
+ }
+ }
+
+ impl<'a, A: Tuple, F: ?Sized> AsyncFnOnce<A> for &'a mut F
+ where
+ F: AsyncFnMut<A>,
+ {
+ type Output = F::Output;
+ type CallOnceFuture = F::CallRefFuture<'a>;
+
+ extern "rust-call" fn async_call_once(self, args: A) -> Self::CallOnceFuture {
+ F::async_call_mut(self, args)
+ }
+ }
+ }
+ }
+ pub use self::async_function::{AsyncFn, AsyncFnMut, AsyncFnOnce};
+ // endregion:async_fn
+
// region:try
mod try_ {
use crate::convert::Infallible;
@@ -1684,6 +1792,7 @@ pub mod prelude {
marker::Sync, // :sync
mem::drop, // :drop
ops::Drop, // :drop
+ ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}, // :async_fn
ops::{Fn, FnMut, FnOnce}, // :fn
option::Option::{self, None, Some}, // :option
panic, // :panic
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index b7c536e027..2aad2cfa36 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
<!---
-lsp/ext.rs hash: 96f88b7a5d0080c6
+lsp/ext.rs hash: 14b7fb1309f5bb00
If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue:
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 052d0a2a41..1195a85cf7 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -992,10 +992,10 @@ Show full signature of the callable. Only shows parameters if disabled.
--
Show documentation.
--
-[[rust-analyzer.typing.autoClosingAngleBrackets.enable]]rust-analyzer.typing.autoClosingAngleBrackets.enable (default: `false`)::
+[[rust-analyzer.typing.excludeChars]]rust-analyzer.typing.excludeChars (default: `"|<"`)::
+
--
-Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.
+Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`.
--
[[rust-analyzer.workspace.discoverConfig]]rust-analyzer.workspace.discoverConfig (default: `null`)::
+
diff --git a/editors/code/package.json b/editors/code/package.json
index 82c43b76fd..469c1b458d 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -425,6 +425,41 @@
],
"default": "openLogs",
"markdownDescription": "Action to run when clicking the extension status bar item."
+ },
+ "rust-analyzer.statusBar.documentSelector": {
+ "type": [
+ "array",
+ "null"
+ ],
+ "items": {
+ "type": "object",
+ "properties": {
+ "language": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "pattern": {
+ "type": [
+ "string",
+ "null"
+ ]
+ }
+ }
+ },
+ "default": [
+ {
+ "language": "rust"
+ },
+ {
+ "pattern": "**/Cargo.toml"
+ },
+ {
+ "pattern": "**/Cargo.lock"
+ }
+ ],
+ "markdownDescription": "Determines when to show the extension status bar item based on the currently open file. Use `{ \"pattern\": \"**\" }` to always show. Use `null` to never show."
}
}
},
@@ -2570,10 +2605,13 @@
{
"title": "typing",
"properties": {
- "rust-analyzer.typing.autoClosingAngleBrackets.enable": {
- "markdownDescription": "Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list.",
- "default": false,
- "type": "boolean"
+ "rust-analyzer.typing.excludeChars": {
+ "markdownDescription": "Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`.",
+ "default": "|<",
+ "type": [
+ "null",
+ "string"
+ ]
}
}
},
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index eac7b849fd..4ce19f5c66 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -324,7 +324,7 @@ class ExperimentalFeatures implements lc.StaticFeature {
}
fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
capabilities.experimental = {
- snippetTextEdit: true,
+ snippetTextEdit: false,
codeActionGroup: true,
hoverActions: true,
serverStatusNotification: true,
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 67bc72f1e1..f7ef80df2b 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -261,9 +261,9 @@ export class Config {
return this.get<boolean | undefined>("testExplorer");
}
- get runnablesExtraEnv() {
+ runnablesExtraEnv(label: string): Record<string, string> | undefined {
const item = this.get<any>("runnables.extraEnv") ?? this.get<any>("runnableEnv");
- if (!item) return item;
+ if (!item) return undefined;
const fixRecord = (r: Record<string, any>) => {
for (const key in r) {
if (typeof r[key] !== "string") {
@@ -271,11 +271,28 @@ export class Config {
}
}
};
+
+ const platform = process.platform;
+ const checkPlatform = (it: RunnableEnvCfgItem) => {
+ if (it.platform) {
+ const platforms = Array.isArray(it.platform) ? it.platform : [it.platform];
+ return platforms.indexOf(platform) >= 0;
+ }
+ return true;
+ };
+
if (item instanceof Array) {
- item.forEach((x) => fixRecord(x.env));
- } else {
- fixRecord(item);
+ const env = {};
+ for (const it of item) {
+ const masked = !it.mask || new RegExp(it.mask).test(label);
+ if (masked && checkPlatform(it)) {
+ Object.assign(env, it.env);
+ }
+ }
+ fixRecord(env);
+ return env;
}
+ fixRecord(item);
return item;
}
@@ -331,6 +348,10 @@ export class Config {
return this.get<string>("statusBar.clickAction");
}
+ get statusBarDocumentSelector() {
+ return this.get<vscode.DocumentSelector>("statusBar.documentSelector");
+ }
+
get initializeStopped() {
return this.get<boolean>("initializeStopped");
}
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 234fe6ab02..4a3f66b00d 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -88,6 +88,7 @@ export class Ctx implements RustAnalyzerExtensionApi {
private _treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId> | undefined;
private lastStatus: ServerStatusParams | { health: "stopped" } = { health: "stopped" };
private _serverVersion: string;
+ private statusBarActiveEditorListener: Disposable;
get serverPath(): string | undefined {
return this._serverPath;
@@ -119,6 +120,10 @@ export class Ctx implements RustAnalyzerExtensionApi {
this._serverVersion = "<not running>";
this.config = new Config(extCtx.subscriptions);
this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
+ this.updateStatusBarVisibility(vscode.window.activeTextEditor);
+ this.statusBarActiveEditorListener = vscode.window.onDidChangeActiveTextEditor((editor) =>
+ this.updateStatusBarVisibility(editor),
+ );
if (this.config.testExplorer) {
this.testController = vscode.tests.createTestController(
"rustAnalyzerTestController",
@@ -141,6 +146,7 @@ export class Ctx implements RustAnalyzerExtensionApi {
dispose() {
this.config.dispose();
this.statusBar.dispose();
+ this.statusBarActiveEditorListener.dispose();
this.testController?.dispose();
void this.disposeClient();
this.commandDisposables.forEach((disposable) => disposable.dispose());
@@ -404,7 +410,6 @@ export class Ctx implements RustAnalyzerExtensionApi {
let icon = "";
const status = this.lastStatus;
const statusBar = this.statusBar;
- statusBar.show();
statusBar.tooltip = new vscode.MarkdownString("", true);
statusBar.tooltip.isTrusted = true;
switch (status.health) {
@@ -472,6 +477,17 @@ export class Ctx implements RustAnalyzerExtensionApi {
statusBar.text = `${icon}rust-analyzer`;
}
+ private updateStatusBarVisibility(editor: vscode.TextEditor | undefined) {
+ const documentSelector = this.config.statusBarDocumentSelector;
+ if (documentSelector != null) {
+ if (editor != null && vscode.languages.match(documentSelector, editor.document) > 0) {
+ this.statusBar.show();
+ return;
+ }
+ }
+ this.statusBar.hide();
+ }
+
pushExtCleanup(d: Disposable) {
this.extCtx.subscriptions.push(d);
}
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index 9e2e3d2185..f21ca2e8f9 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -148,8 +148,16 @@ async function getDebugConfiguration(
return path.normalize(p).replace(wsFolder, `\${workspaceFolder${workspaceQualifier}}`);
}
- const env = prepareEnv(inheritEnv, runnable.label, runnableArgs, config.runnablesExtraEnv);
- const executable = await getDebugExecutable(runnableArgs, env);
+ const executable = await getDebugExecutable(
+ runnableArgs,
+ prepareEnv(true, {}, config.runnablesExtraEnv(runnable.label)),
+ );
+
+ const env = prepareEnv(
+ inheritEnv,
+ runnableArgs.environment,
+ config.runnablesExtraEnv(runnable.label),
+ );
let sourceFileMap = debugOptions.sourceFileMap;
if (sourceFileMap === "auto") {
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts
index 8a82a5a58c..f71ab7ffbd 100644
--- a/editors/code/src/run.ts
+++ b/editors/code/src/run.ts
@@ -5,7 +5,7 @@ import * as tasks from "./tasks";
import type { CtxInit } from "./ctx";
import { makeDebugConfig } from "./debug";
-import type { Config, RunnableEnvCfg, RunnableEnvCfgItem } from "./config";
+import type { Config } from "./config";
import type { LanguageClient } from "vscode-languageclient/node";
import { unwrapUndefinable, type RustEditor } from "./util";
@@ -81,32 +81,13 @@ export function prepareBaseEnv(
export function prepareEnv(
inheritEnv: boolean,
- label: string,
- runnableArgs: ra.CargoRunnableArgs,
- runnableEnvCfg?: RunnableEnvCfg,
+ runnableEnv?: Record<string, string>,
+ runnableEnvCfg?: Record<string, string>,
): Record<string, string> {
- const env = prepareBaseEnv(inheritEnv, runnableArgs.environment);
- const platform = process.platform;
-
- const checkPlatform = (it: RunnableEnvCfgItem) => {
- if (it.platform) {
- const platforms = Array.isArray(it.platform) ? it.platform : [it.platform];
- return platforms.indexOf(platform) >= 0;
- }
- return true;
- };
+ const env = prepareBaseEnv(inheritEnv, runnableEnv);
if (runnableEnvCfg) {
- if (Array.isArray(runnableEnvCfg)) {
- for (const it of runnableEnvCfg) {
- const masked = !it.mask || new RegExp(it.mask).test(label);
- if (masked && checkPlatform(it)) {
- Object.assign(env, it.env);
- }
- }
- } else {
- Object.assign(env, runnableEnvCfg);
- }
+ Object.assign(env, runnableEnvCfg);
}
return env;
@@ -140,7 +121,11 @@ export async function createTaskFromRunnable(
};
options = {
cwd: runnableArgs.workspaceRoot || ".",
- env: prepareEnv(true, runnable.label, runnableArgs, config.runnablesExtraEnv),
+ env: prepareEnv(
+ true,
+ runnableArgs.environment,
+ config.runnablesExtraEnv(runnable.label),
+ ),
};
} else {
const runnableArgs = runnable.args;
diff --git a/editors/code/tests/unit/runnable_env.test.ts b/editors/code/tests/unit/runnable_env.test.ts
deleted file mode 100644
index f0a62a3cce..0000000000
--- a/editors/code/tests/unit/runnable_env.test.ts
+++ /dev/null
@@ -1,122 +0,0 @@
-import * as assert from "assert";
-import { prepareEnv } from "../../src/run";
-import type { RunnableEnvCfg } from "../../src/config";
-import type { Context } from ".";
-import type * as ra from "../../src/lsp_ext";
-
-function makeRunnable(label: string): ra.Runnable {
- return {
- label,
- kind: "cargo",
- args: {
- cargoArgs: [],
- cwd: ".",
- executableArgs: [],
- },
- };
-}
-
-function fakePrepareEnv(runnableName: string, config?: RunnableEnvCfg): Record<string, string> {
- const runnable = makeRunnable(runnableName);
- const runnableArgs = runnable.args as ra.CargoRunnableArgs;
- return prepareEnv(false, runnable.label, runnableArgs, config);
-}
-
-export async function getTests(ctx: Context) {
- await ctx.suite("Runnable env", (suite) => {
- suite.addTest("Global config works", async () => {
- const binEnv = fakePrepareEnv("run project_name", { GLOBAL: "g" });
- assert.strictEqual(binEnv["GLOBAL"], "g");
-
- const testEnv = fakePrepareEnv("test some::mod::test_name", { GLOBAL: "g" });
- assert.strictEqual(testEnv["GLOBAL"], "g");
- });
-
- suite.addTest("null mask works", async () => {
- const config = [
- {
- env: { DATA: "data" },
- },
- ];
- const binEnv = fakePrepareEnv("run project_name", config);
- assert.strictEqual(binEnv["DATA"], "data");
-
- const testEnv = fakePrepareEnv("test some::mod::test_name", config);
- assert.strictEqual(testEnv["DATA"], "data");
- });
-
- suite.addTest("order works", async () => {
- const config = [
- {
- env: { DATA: "data" },
- },
- {
- env: { DATA: "newdata" },
- },
- ];
- const binEnv = fakePrepareEnv("run project_name", config);
- assert.strictEqual(binEnv["DATA"], "newdata");
-
- const testEnv = fakePrepareEnv("test some::mod::test_name", config);
- assert.strictEqual(testEnv["DATA"], "newdata");
- });
-
- suite.addTest("mask works", async () => {
- const config = [
- {
- env: { DATA: "data" },
- },
- {
- mask: "^run",
- env: { DATA: "rundata" },
- },
- {
- mask: "special_test$",
- env: { DATA: "special_test" },
- },
- ];
- const binEnv = fakePrepareEnv("run project_name", config);
- assert.strictEqual(binEnv["DATA"], "rundata");
-
- const testEnv = fakePrepareEnv("test some::mod::test_name", config);
- assert.strictEqual(testEnv["DATA"], "data");
-
- const specialTestEnv = fakePrepareEnv("test some::mod::special_test", config);
- assert.strictEqual(specialTestEnv["DATA"], "special_test");
- });
-
- suite.addTest("exact test name works", async () => {
- const config = [
- {
- env: { DATA: "data" },
- },
- {
- mask: "some::mod::test_name",
- env: { DATA: "test special" },
- },
- ];
- const testEnv = fakePrepareEnv("test some::mod::test_name", config);
- assert.strictEqual(testEnv["DATA"], "test special");
-
- const specialTestEnv = fakePrepareEnv("test some::mod::another_test", config);
- assert.strictEqual(specialTestEnv["DATA"], "data");
- });
-
- suite.addTest("test mod name works", async () => {
- const config = [
- {
- env: { DATA: "data" },
- },
- {
- mask: "some::mod",
- env: { DATA: "mod special" },
- },
- ];
- const testEnv = fakePrepareEnv("test some::mod::test_name", config);
- assert.strictEqual(testEnv["DATA"], "mod special");
-
- const specialTestEnv = fakePrepareEnv("test some::mod::another_test", config);
- assert.strictEqual(specialTestEnv["DATA"], "mod special");
- });
- });
-}
diff --git a/rust-version b/rust-version
index 8f41ed9e14..7d60fa6cb7 100644
--- a/rust-version
+++ b/rust-version
@@ -1 +1 @@
-f005c7437def424a1c43cbc380352a58d8ac920b
+5a6036a1802262f8cf02192b02026688d396f1d7