Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--Cargo.lock102
-rw-r--r--Cargo.toml24
-rw-r--r--crates/base-db/src/lib.rs2
-rw-r--r--crates/edition/src/lib.rs3
-rw-r--r--crates/hir-def/src/attr.rs5
-rw-r--r--crates/hir-def/src/body.rs15
-rw-r--r--crates/hir-def/src/body/lower.rs44
-rw-r--r--crates/hir-def/src/body/scope.rs4
-rw-r--r--crates/hir-def/src/body/tests.rs23
-rw-r--r--crates/hir-def/src/data.rs9
-rw-r--r--crates/hir-def/src/expander.rs4
-rw-r--r--crates/hir-def/src/find_path.rs26
-rw-r--r--crates/hir-def/src/import_map.rs9
-rw-r--r--crates/hir-def/src/item_scope.rs19
-rw-r--r--crates/hir-def/src/item_tree/tests.rs4
-rw-r--r--crates/hir-def/src/lang_item.rs2
-rw-r--r--crates/hir-def/src/lib.rs2
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe.rs30
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mod.rs5
-rw-r--r--crates/hir-def/src/macro_expansion_tests/proc_macros.rs6
-rw-r--r--crates/hir-def/src/nameres/collector.rs6
-rw-r--r--crates/hir-def/src/nameres/tests.rs6
-rw-r--r--crates/hir-def/src/resolver.rs60
-rw-r--r--crates/hir-def/src/visibility.rs4
-rw-r--r--crates/hir-expand/src/builtin/derive_macro.rs105
-rw-r--r--crates/hir-expand/src/builtin/fn_macro.rs10
-rw-r--r--crates/hir-expand/src/builtin/quote.rs9
-rw-r--r--crates/hir-expand/src/db.rs37
-rw-r--r--crates/hir-expand/src/declarative.rs11
-rw-r--r--crates/hir-expand/src/files.rs11
-rw-r--r--crates/hir-expand/src/fixup.rs9
-rw-r--r--crates/hir-expand/src/hygiene.rs46
-rw-r--r--crates/hir-expand/src/lib.rs6
-rw-r--r--crates/hir-expand/src/mod_path.rs21
-rw-r--r--crates/hir-expand/src/name.rs49
-rw-r--r--crates/hir-ty/src/chalk_db.rs2
-rw-r--r--crates/hir-ty/src/consteval/tests.rs14
-rw-r--r--crates/hir-ty/src/display.rs49
-rw-r--r--crates/hir-ty/src/dyn_compatibility/tests.rs2
-rw-r--r--crates/hir-ty/src/layout/tests.rs26
-rw-r--r--crates/hir-ty/src/lower.rs33
-rw-r--r--crates/hir-ty/src/method_resolution.rs26
-rw-r--r--crates/hir-ty/src/mir/eval/tests.rs10
-rw-r--r--crates/hir-ty/src/tests.rs23
-rw-r--r--crates/hir-ty/src/tests/closure_captures.rs2
-rw-r--r--crates/hir-ty/src/tests/simple.rs12
-rw-r--r--crates/hir-ty/src/variance.rs2
-rw-r--r--crates/hir/Cargo.toml1
-rw-r--r--crates/hir/src/attrs.rs15
-rw-r--r--crates/hir/src/display.rs33
-rw-r--r--crates/hir/src/lib.rs118
-rw-r--r--crates/hir/src/semantics.rs66
-rw-r--r--crates/hir/src/semantics/child_by_source.rs2
-rw-r--r--crates/hir/src/semantics/source_to_def.rs32
-rw-r--r--crates/hir/src/source_analyzer.rs74
-rw-r--r--crates/hir/src/symbols.rs253
-rw-r--r--crates/ide-assists/src/assist_context.rs4
-rw-r--r--crates/ide-assists/src/handlers/add_missing_match_arms.rs12
-rw-r--r--crates/ide-assists/src/handlers/add_turbo_fish.rs2
-rw-r--r--crates/ide-assists/src/handlers/apply_demorgan.rs2
-rw-r--r--crates/ide-assists/src/handlers/bool_to_enum.rs1
-rw-r--r--crates/ide-assists/src/handlers/convert_closure_to_fn.rs2
-rw-r--r--crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs2
-rw-r--r--crates/ide-assists/src/handlers/convert_while_to_loop.rs2
-rw-r--r--crates/ide-assists/src/handlers/destructure_tuple_binding.rs10
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs36
-rw-r--r--crates/ide-assists/src/handlers/extract_variable.rs18
-rw-r--r--crates/ide-assists/src/handlers/generate_getter_or_setter.rs2
-rw-r--r--crates/ide-assists/src/handlers/inline_macro.rs18
-rw-r--r--crates/ide-assists/src/handlers/move_guard.rs2
-rw-r--r--crates/ide-assists/src/handlers/remove_dbg.rs9
-rw-r--r--crates/ide-assists/src/handlers/replace_if_let_with_match.rs98
-rw-r--r--crates/ide-assists/src/handlers/replace_let_with_if_let.rs27
-rw-r--r--crates/ide-assists/src/handlers/replace_try_expr_with_match.rs8
-rw-r--r--crates/ide-assists/src/handlers/unmerge_match_arm.rs10
-rw-r--r--crates/ide-assists/src/handlers/unwrap_block.rs2
-rw-r--r--crates/ide-assists/src/handlers/unwrap_return_type.rs177
-rw-r--r--crates/ide-assists/src/handlers/wrap_return_type.rs119
-rw-r--r--crates/ide-assists/src/tests.rs45
-rw-r--r--crates/ide-assists/src/utils.rs4
-rw-r--r--crates/ide-assists/src/utils/gen_trait_fn_body.rs32
-rw-r--r--crates/ide-assists/src/utils/ref_field_expr.rs2
-rw-r--r--crates/ide-completion/src/completions.rs6
-rw-r--r--crates/ide-completion/src/completions/dot.rs298
-rw-r--r--crates/ide-completion/src/completions/env_vars.rs57
-rw-r--r--crates/ide-completion/src/completions/expr.rs26
-rw-r--r--crates/ide-completion/src/completions/extern_abi.rs15
-rw-r--r--crates/ide-completion/src/completions/flyimport.rs9
-rw-r--r--crates/ide-completion/src/completions/format_string.rs13
-rw-r--r--crates/ide-completion/src/completions/item_list/trait_impl.rs41
-rw-r--r--crates/ide-completion/src/completions/keyword.rs9
-rw-r--r--crates/ide-completion/src/completions/lifetime.rs9
-rw-r--r--crates/ide-completion/src/completions/mod_.rs9
-rw-r--r--crates/ide-completion/src/completions/postfix.rs11
-rw-r--r--crates/ide-completion/src/context.rs5
-rw-r--r--crates/ide-completion/src/context/analysis.rs7
-rw-r--r--crates/ide-completion/src/context/tests.rs2
-rw-r--r--crates/ide-completion/src/item.rs46
-rw-r--r--crates/ide-completion/src/lib.rs5
-rw-r--r--crates/ide-completion/src/render.rs140
-rw-r--r--crates/ide-completion/src/render/function.rs20
-rw-r--r--crates/ide-completion/src/tests.rs55
-rw-r--r--crates/ide-completion/src/tests/attribute.rs51
-rw-r--r--crates/ide-completion/src/tests/expression.rs202
-rw-r--r--crates/ide-completion/src/tests/flyimport.rs14
-rw-r--r--crates/ide-completion/src/tests/fn_param.rs16
-rw-r--r--crates/ide-completion/src/tests/item.rs41
-rw-r--r--crates/ide-completion/src/tests/item_list.rs53
-rw-r--r--crates/ide-completion/src/tests/pattern.rs93
-rw-r--r--crates/ide-completion/src/tests/predicate.rs25
-rw-r--r--crates/ide-completion/src/tests/proc_macros.rs9
-rw-r--r--crates/ide-completion/src/tests/raw_identifiers.rs2
-rw-r--r--crates/ide-completion/src/tests/record.rs9
-rw-r--r--crates/ide-completion/src/tests/special.rs24
-rw-r--r--crates/ide-completion/src/tests/type_pos.rs99
-rw-r--r--crates/ide-completion/src/tests/use_tree.rs9
-rw-r--r--crates/ide-completion/src/tests/visibility.rs16
-rw-r--r--crates/ide-db/src/active_parameter.rs6
-rw-r--r--crates/ide-db/src/defs.rs39
-rw-r--r--crates/ide-db/src/documentation.rs2
-rw-r--r--crates/ide-db/src/famous_defs.rs4
-rw-r--r--crates/ide-db/src/imports/import_assets.rs43
-rw-r--r--crates/ide-db/src/imports/insert_use/tests.rs34
-rw-r--r--crates/ide-db/src/items_locator.rs19
-rw-r--r--crates/ide-db/src/lib.rs32
-rw-r--r--crates/ide-db/src/rename.rs1
-rw-r--r--crates/ide-db/src/search.rs11
-rw-r--r--crates/ide-db/src/symbol_index.rs53
-rw-r--r--crates/ide-db/src/syntax_helpers/suggest_name.rs2
-rw-r--r--crates/ide-db/src/test_data/test_symbol_index_collection.txt87
-rw-r--r--crates/ide-db/src/traits.rs8
-rw-r--r--crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs19
-rw-r--r--crates/ide-diagnostics/src/handlers/inactive_code.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/macro_error.rs32
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_match_arms.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/no_such_field.rs34
-rw-r--r--crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs31
-rw-r--r--crates/ide-diagnostics/src/tests.rs55
-rw-r--r--crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs2726
-rw-r--r--crates/ide/src/annotations.rs8
-rw-r--r--crates/ide/src/call_hierarchy.rs2
-rw-r--r--crates/ide/src/doc_links.rs2
-rw-r--r--crates/ide/src/doc_links/tests.rs6
-rw-r--r--crates/ide/src/expand_macro.rs2
-rw-r--r--crates/ide/src/file_structure.rs2
-rw-r--r--crates/ide/src/fixture.rs20
-rwxr-xr-xcrates/ide/src/folding_ranges.rs2
-rw-r--r--crates/ide/src/goto_declaration.rs2
-rw-r--r--crates/ide/src/goto_definition.rs168
-rw-r--r--crates/ide/src/goto_implementation.rs2
-rw-r--r--crates/ide/src/goto_type_definition.rs7
-rw-r--r--crates/ide/src/highlight_related.rs7
-rw-r--r--crates/ide/src/hover.rs51
-rw-r--r--crates/ide/src/hover/render.rs78
-rw-r--r--crates/ide/src/hover/tests.rs193
-rw-r--r--crates/ide/src/inlay_hints.rs159
-rw-r--r--crates/ide/src/inlay_hints/adjustment.rs24
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs95
-rw-r--r--crates/ide/src/inlay_hints/binding_mode.rs56
-rw-r--r--crates/ide/src/inlay_hints/bounds.rs152
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs15
-rw-r--r--crates/ide/src/inlay_hints/closure_ret.rs3
-rw-r--r--crates/ide/src/inlay_hints/discriminant.rs52
-rw-r--r--crates/ide/src/inlay_hints/extern_block.rs26
-rw-r--r--crates/ide/src/inlay_hints/generic_param.rs4
-rw-r--r--crates/ide/src/inlay_hints/implicit_drop.rs2
-rw-r--r--crates/ide/src/inlay_hints/implicit_static.rs4
-rw-r--r--crates/ide/src/inlay_hints/param_name.rs2
-rw-r--r--crates/ide/src/join_lines.rs10
-rw-r--r--crates/ide/src/lib.rs26
-rw-r--r--crates/ide/src/moniker.rs18
-rw-r--r--crates/ide/src/move_item.rs6
-rw-r--r--crates/ide/src/navigation_target.rs16
-rw-r--r--crates/ide/src/parent_module.rs2
-rw-r--r--crates/ide/src/references.rs8
-rw-r--r--crates/ide/src/rename.rs20
-rw-r--r--crates/ide/src/runnables.rs4
-rw-r--r--crates/ide/src/signature_help.rs6
-rw-r--r--crates/ide/src/ssr.rs5
-rw-r--r--crates/ide/src/static_index.rs11
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs3
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs13
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_const.html6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html2
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html4
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html16
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html16
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html16
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html16
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_macros.html20
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html110
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html12
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs8
-rw-r--r--crates/ide/src/syntax_tree.rs338
-rw-r--r--crates/ide/src/typing.rs12
-rw-r--r--crates/ide/src/typing/on_enter.rs5
-rw-r--r--crates/ide/src/view_memory_layout.rs4
-rw-r--r--crates/ide/src/view_syntax_tree.rs226
-rw-r--r--crates/intern/src/symbol/symbols.rs5
-rw-r--r--crates/load-cargo/src/lib.rs5
-rw-r--r--crates/mbe/src/expander/transcriber.rs10
-rw-r--r--crates/mbe/src/lib.rs3
-rw-r--r--crates/mbe/src/tests.rs177
-rw-r--r--crates/parser/src/event.rs2
-rw-r--r--crates/parser/src/grammar/expressions.rs23
-rw-r--r--crates/parser/src/parser.rs25
-rw-r--r--crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast79
-rw-r--r--crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs8
-rw-r--r--crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast42
-rw-r--r--crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs5
-rw-r--r--crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast90
-rw-r--r--crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs5
-rw-r--r--crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast40
-rw-r--r--crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs5
-rw-r--r--crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast55
-rw-r--r--crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs5
-rw-r--r--crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast38
-rw-r--r--crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs5
-rw-r--r--crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast90
-rw-r--r--crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs7
-rw-r--r--crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast40
-rw-r--r--crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs5
-rw-r--r--crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast38
-rw-r--r--crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs5
-rw-r--r--crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast45
-rw-r--r--crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs5
-rw-r--r--crates/parser/test_data/parser/inline/ok/ref_expr.rast19
-rw-r--r--crates/parser/test_data/parser/inline/ok/ref_expr.rs1
-rw-r--r--crates/proc-macro-api/src/legacy_protocol/msg.rs20
-rw-r--r--crates/proc-macro-srv/Cargo.toml1
-rw-r--r--crates/proc-macro-srv/src/dylib.rs11
-rw-r--r--crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs4
-rw-r--r--crates/proc-macro-srv/src/tests/mod.rs130
-rw-r--r--crates/proc-macro-srv/src/tests/utils.rs13
-rw-r--r--crates/profile/Cargo.toml5
-rw-r--r--crates/project-model/src/cargo_workspace.rs2
-rw-r--r--crates/project-model/src/tests.rs3
-rw-r--r--crates/project-model/src/toolchain_info/rustc_cfg.rs2
-rw-r--r--crates/project-model/src/workspace.rs45
-rw-r--r--crates/rust-analyzer/Cargo.toml5
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs1
-rw-r--r--crates/rust-analyzer/src/cli/rustc_tests.rs1
-rw-r--r--crates/rust-analyzer/src/cli/scip.rs4
-rw-r--r--crates/rust-analyzer/src/config.rs45
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs27
-rw-r--r--crates/rust-analyzer/src/lib.rs12
-rw-r--r--crates/rust-analyzer/src/lsp/ext.rs11
-rw-r--r--crates/rust-analyzer/src/lsp/to_proto.rs25
-rw-r--r--crates/rust-analyzer/src/main_loop.rs2
-rw-r--r--crates/rust-analyzer/src/test_runner.rs6
-rw-r--r--crates/span/src/hygiene.rs29
-rw-r--r--crates/span/src/map.rs2
-rw-r--r--crates/stdx/Cargo.toml2
-rw-r--r--crates/syntax-bridge/src/lib.rs17
-rw-r--r--crates/syntax-bridge/src/to_parser_input.rs44
-rw-r--r--crates/syntax/src/ast/edit.rs4
-rw-r--r--crates/syntax/src/ast/expr_ext.rs23
-rw-r--r--crates/syntax/src/ast/make.rs51
-rw-r--r--crates/syntax/src/ast/node_ext.rs8
-rw-r--r--crates/syntax/src/ast/syntax_factory/constructors.rs364
-rw-r--r--crates/syntax/src/syntax_editor.rs7
-rw-r--r--crates/test-fixture/Cargo.toml2
-rw-r--r--crates/test-fixture/src/lib.rs96
-rw-r--r--crates/test-utils/src/fixture.rs2
-rw-r--r--crates/test-utils/src/minicore.rs58
-rw-r--r--crates/vfs-notify/Cargo.toml2
-rw-r--r--docs/dev/README.md16
-rw-r--r--docs/dev/lsp-extensions.md19
-rw-r--r--docs/user/generated_config.adoc32
-rw-r--r--editors/code/package.json147
-rw-r--r--editors/code/src/ast_inspector.ts216
-rw-r--r--editors/code/src/commands.ts167
-rw-r--r--editors/code/src/config.ts4
-rw-r--r--editors/code/src/ctx.ts93
-rw-r--r--editors/code/src/lsp_ext.ts4
-rw-r--r--editors/code/src/main.ts5
-rw-r--r--editors/code/src/syntax_tree_provider.ts301
-rw-r--r--lib/lsp-server/src/msg.rs8
-rw-r--r--lib/lsp-server/src/socket.rs23
-rw-r--r--lib/lsp-server/src/stdio.rs23
-rw-r--r--rust-version2
286 files changed, 9088 insertions, 2920 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 48b5f3aabf..f92668a6a9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -98,9 +98,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.6.0"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be"
[[package]]
name = "borsh"
@@ -194,9 +194,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chalk-derive"
-version = "0.98.0"
+version = "0.99.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9426c8fd0fe61c3da880b801d3b510524df17843a8f9ec1f5b9cec24fb7412df"
+checksum = "572583d9b97f9d277e5c7607f8239a30e2e04d3ed3b47c87d1cb2152ae724073"
dependencies = [
"proc-macro2",
"quote",
@@ -206,19 +206,19 @@ dependencies = [
[[package]]
name = "chalk-ir"
-version = "0.98.0"
+version = "0.99.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5f2eb1cd6054da221bd1ac0197fb2fe5e2caf3dcb93619398fc1433f8f09093"
+checksum = "e60e0ef9c81dce1336a9ed3c76f08775f5b623151d96d85ba45f7b10de76d1c7"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"chalk-derive",
]
[[package]]
name = "chalk-recursive"
-version = "0.98.0"
+version = "0.99.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "129dc03458f71cfb9c3cd621c9c68166a94e87b85b16ccd29af015d7ff9a1c61"
+checksum = "5a06350d614e22b03a69b8105e3541614450a7ea48bc58ecc6c6bd92731a3995"
dependencies = [
"chalk-derive",
"chalk-ir",
@@ -229,9 +229,9 @@ dependencies = [
[[package]]
name = "chalk-solve"
-version = "0.98.0"
+version = "0.99.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7e8a8c1e928f98cdf227b868416ef21dcd8cc3c61b347576d783713444d41c8"
+checksum = "0e428761e9b55bee516bfe2457caed8b6d1b86353f92ae825bbe438a36ce91e8"
dependencies = [
"chalk-derive",
"chalk-ir",
@@ -523,6 +523,7 @@ dependencies = [
"hir-def",
"hir-expand",
"hir-ty",
+ "indexmap",
"intern",
"itertools",
"rustc-hash 2.0.0",
@@ -544,7 +545,7 @@ version = "0.0.0"
dependencies = [
"arrayvec",
"base-db",
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"cfg",
"cov-mark",
"dashmap",
@@ -610,7 +611,7 @@ version = "0.0.0"
dependencies = [
"arrayvec",
"base-db",
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"chalk-derive",
"chalk-ir",
"chalk-recursive",
@@ -734,7 +735,7 @@ version = "0.0.0"
dependencies = [
"arrayvec",
"base-db",
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"cov-mark",
"crossbeam-channel",
"either",
@@ -820,11 +821,11 @@ dependencies = [
[[package]]
name = "inotify"
-version = "0.9.6"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff"
+checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3"
dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.7.0",
"inotify-sys",
"libc",
]
@@ -908,9 +909,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
-version = "0.2.155"
+version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libloading"
@@ -938,7 +939,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"libc",
"redox_syscall",
]
@@ -1117,14 +1118,14 @@ dependencies = [
[[package]]
name = "mio"
-version = "0.8.11"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"log",
"wasi",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -1142,7 +1143,7 @@ version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"cfg-if",
"cfg_aliases 0.1.1",
"libc",
@@ -1156,12 +1157,11 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
name = "notify"
-version = "6.1.1"
+version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
+checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943"
dependencies = [
- "bitflags 2.6.0",
- "crossbeam-channel",
+ "bitflags 2.7.0",
"filetime",
"fsevent-sys",
"inotify",
@@ -1169,11 +1169,18 @@ dependencies = [
"libc",
"log",
"mio",
+ "notify-types",
"walkdir",
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
]
[[package]]
+name = "notify-types"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
+
+[[package]]
name = "nu-ansi-term"
version = "0.50.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1371,6 +1378,7 @@ version = "0.0.0"
dependencies = [
"expect-test",
"intern",
+ "libc",
"libloading",
"memmap2",
"object 0.33.0",
@@ -1428,7 +1436,7 @@ dependencies = [
"libc",
"perf-event",
"tikv-jemalloc-ctl",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -1482,7 +1490,7 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"memchr",
"unicase",
]
@@ -1507,20 +1515,20 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_abi"
-version = "0.87.0"
+version = "0.91.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28b782af0a7a8df16ddf43cd70da9f17bc3b1ce712c9e4992b6edb16f5f53632"
+checksum = "d5246e9e1f450333a990877eabbc36fe0567e7cedd56d5365db319e14079cf2a"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
"ra-ap-rustc_index",
"tracing",
]
[[package]]
name = "ra-ap-rustc_index"
-version = "0.87.0"
+version = "0.91.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce5742f134960482f543b35ecebec3cacc6d79a9a685713518b4d8d70c5f9aa8"
+checksum = "59fd8e4f5b34c434ec111efb0e0614954db048b9307d3b2e4cc3c915da9d2160"
dependencies = [
"ra-ap-rustc_index_macros",
"smallvec",
@@ -1528,9 +1536,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_index_macros"
-version = "0.87.0"
+version = "0.91.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7ea011fcf68309a8835ad01d91c032cb18444617b00e2cab21d45b208164441"
+checksum = "2d34973fe081392bd1edb022e865e9952fcaa093f9cdae183edce64472e5e889"
dependencies = [
"proc-macro2",
"quote",
@@ -1539,9 +1547,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_lexer"
-version = "0.87.0"
+version = "0.91.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb76f0a4d4c20859e41f0a23bff0f37ab9ca9171c214a6c7dd72ea69434865dc"
+checksum = "52fa42c582e21b35e8f61a5afe3c63a9c722d995826762eb19b18beeccf5157f"
dependencies = [
"unicode-properties",
"unicode-xid",
@@ -1549,9 +1557,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_parse_format"
-version = "0.87.0"
+version = "0.91.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06080bd35078305421a62da77f3c128482d8d44441b6da8ce9d146d1cd9cdb5b"
+checksum = "740383328d7033393e5385f4a6073b880d5811b0fc0fd2559e481f905940f2f8"
dependencies = [
"ra-ap-rustc_index",
"ra-ap-rustc_lexer",
@@ -1559,9 +1567,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_pattern_analysis"
-version = "0.87.0"
+version = "0.91.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68a3154fe4c20c177d7b3c678a2d3a97aba0cca156ddef88959915041889daf0"
+checksum = "c39f544728f32cebffb1a8b92ba3c1f3dcb4144081438d192137ed197d479a9d"
dependencies = [
"ra-ap-rustc_index",
"rustc-hash 2.0.0",
@@ -1626,7 +1634,7 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
dependencies = [
- "bitflags 2.6.0",
+ "bitflags 2.7.0",
]
[[package]]
@@ -1713,7 +1721,7 @@ dependencies = [
"vfs",
"vfs-notify",
"walkdir",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
"xflags",
"xshell",
]
@@ -1936,7 +1944,7 @@ dependencies = [
"jod-thread",
"libc",
"miow",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 9440123de7..1029844cd3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
resolver = "2"
[workspace.package]
-rust-version = "1.82"
+rust-version = "1.83"
edition = "2021"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer team"]
@@ -79,6 +79,7 @@ span = { path = "./crates/span", version = "0.0.0" }
stdx = { path = "./crates/stdx", version = "0.0.0" }
syntax = { path = "./crates/syntax", version = "0.0.0" }
syntax-bridge = { path = "./crates/syntax-bridge", version = "0.0.0" }
+test-fixture = { path = "./crates/test-fixture", version = "0.0.0" }
test-utils = { path = "./crates/test-utils", version = "0.0.0" }
toolchain = { path = "./crates/toolchain", version = "0.0.0" }
tt = { path = "./crates/tt", version = "0.0.0" }
@@ -86,16 +87,15 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
vfs = { path = "./crates/vfs", version = "0.0.0" }
edition = { path = "./crates/edition", version = "0.0.0" }
-ra-ap-rustc_lexer = { version = "0.87", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.87", default-features = false }
-ra-ap-rustc_index = { version = "0.87", default-features = false }
-ra-ap-rustc_abi = { version = "0.87", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.87", default-features = false }
+ra-ap-rustc_lexer = { version = "0.91", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.91", default-features = false }
+ra-ap-rustc_index = { version = "0.91", default-features = false }
+ra-ap-rustc_abi = { version = "0.91", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.91", default-features = false }
# local crates that aren't published to crates.io. These should not have versions.
-test-fixture = { path = "./crates/test-fixture" }
-# In-tree crates that are published separately and follow semver. See lib/README.md
+# in-tree crates that are published separately and follow semver. See lib/README.md
line-index = { version = "0.1.2" }
la-arena = { version = "0.3.1" }
lsp-server = { version = "0.7.6" }
@@ -106,10 +106,10 @@ arrayvec = "0.7.4"
bitflags = "2.4.1"
cargo_metadata = "0.18.1"
camino = "1.1.6"
-chalk-solve = { version = "0.98.0", default-features = false }
-chalk-ir = "0.98.0"
-chalk-recursive = { version = "0.98.0", default-features = false }
-chalk-derive = "0.98.0"
+chalk-solve = { version = "0.99.0", default-features = false }
+chalk-ir = "0.99.0"
+chalk-recursive = { version = "0.99.0", default-features = false }
+chalk-derive = "0.99.0"
crossbeam-channel = "0.5.8"
dissimilar = "1.0.7"
dot = "0.1.4"
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index 0a9e83bc3b..c7e4168f6b 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -136,7 +136,7 @@ pub trait SourceRootDatabase: SourceDatabase {
#[ra_salsa::input]
fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
- /// Crates whose root fool is in `id`.
+ /// Crates whose root file is in `id`.
fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>;
}
diff --git a/crates/edition/src/lib.rs b/crates/edition/src/lib.rs
index c25d5b9557..7e9c94af40 100644
--- a/crates/edition/src/lib.rs
+++ b/crates/edition/src/lib.rs
@@ -5,7 +5,8 @@ use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Edition {
- Edition2015,
+ // The syntax context stuff needs the discriminants to start from 0 and be consecutive.
+ Edition2015 = 0,
Edition2018,
Edition2021,
Edition2024,
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index 37e2a99e60..710bffcefe 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -122,6 +122,11 @@ impl Attrs {
AttrQuery { attrs: self, key }
}
+ pub fn rust_analyzer_tool(&self) -> impl Iterator<Item = &Attr> {
+ self.iter()
+ .filter(|&attr| attr.path.segments().first().is_some_and(|s| *s == sym::rust_analyzer))
+ }
+
pub fn cfg(&self) -> Option<CfgExpr> {
let mut cfgs = self.by_key(&sym::cfg).tt_values().map(CfgExpr::parse);
let first = cfgs.next()?;
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index 433a956ff9..de43924930 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/body.rs
@@ -15,7 +15,7 @@ use hir_expand::{name::Name, ExpandError, InFile};
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
-use span::{Edition, MacroFileId};
+use span::{Edition, MacroFileId, SyntaxContextData};
use syntax::{ast, AstPtr, SyntaxNodePtr};
use triomphe::Arc;
use tt::TextRange;
@@ -37,15 +37,22 @@ use crate::{
/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct HygieneId(pub(crate) span::SyntaxContextId);
+pub struct HygieneId(span::SyntaxContextId);
impl HygieneId {
- pub const ROOT: Self = Self(span::SyntaxContextId::ROOT);
+ // The edition doesn't matter here, we only use this for comparisons and to lookup the macro.
+ pub const ROOT: Self = Self(span::SyntaxContextId::root(Edition::Edition2015));
- pub fn new(ctx: span::SyntaxContextId) -> Self {
+ pub fn new(mut ctx: span::SyntaxContextId) -> Self {
+ // See `Name` for why we're doing that.
+ ctx.remove_root_edition();
Self(ctx)
}
+ pub(crate) fn lookup(self, db: &dyn DefDatabase) -> SyntaxContextData {
+ db.lookup_intern_syntax_context(self.0)
+ }
+
pub(crate) fn is_root(self) -> bool {
self.0.is_root()
}
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 10b84d041b..1327bb3ab5 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -8,6 +8,7 @@ use std::mem;
use base_db::CrateId;
use either::Either;
use hir_expand::{
+ mod_path::tool_path,
name::{AsName, Name},
span_map::{ExpansionSpanMap, SpanMap},
InFile, MacroDefId,
@@ -27,6 +28,7 @@ use text_size::TextSize;
use triomphe::Arc;
use crate::{
+ attr::Attrs,
body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr},
builtin_type::BuiltinUint,
data::adt::StructKind,
@@ -212,6 +214,43 @@ impl ExprCollector<'_> {
body: Option<ast::Expr>,
is_async_fn: bool,
) -> (Body, BodySourceMap) {
+ let skip_body = match self.owner {
+ DefWithBodyId::FunctionId(it) => self.db.attrs(it.into()),
+ DefWithBodyId::StaticId(it) => self.db.attrs(it.into()),
+ DefWithBodyId::ConstId(it) => self.db.attrs(it.into()),
+ DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY,
+ DefWithBodyId::VariantId(it) => self.db.attrs(it.into()),
+ }
+ .rust_analyzer_tool()
+ .any(|attr| *attr.path() == tool_path![skip]);
+ // If #[rust_analyzer::skip] annotated, only construct enough information for the signature
+ // and skip the body.
+ if skip_body {
+ self.body.body_expr = self.missing_expr();
+ if let Some((param_list, mut attr_enabled)) = param_list {
+ if let Some(self_param) =
+ param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
+ {
+ let is_mutable =
+ self_param.mut_token().is_some() && self_param.amp_token().is_none();
+ let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
+ Name::new_symbol_root(sym::self_.clone()),
+ BindingAnnotation::new(is_mutable, false),
+ );
+ self.body.self_param = Some(binding_id);
+ self.source_map.self_param =
+ Some(self.expander.in_file(AstPtr::new(&self_param)));
+ }
+ self.body.params = param_list
+ .params()
+ .zip(attr_enabled)
+ .filter(|(_, enabled)| *enabled)
+ .map(|_| self.missing_pat())
+ .collect();
+ };
+ return (self.body, self.source_map);
+ }
+
self.awaitable_context.replace(if is_async_fn {
Awaitable::Yes
} else {
@@ -542,10 +581,7 @@ impl ExprCollector<'_> {
let mutability = if raw_tok {
if e.mut_token().is_some() {
Mutability::Mut
- } else if e.const_token().is_some() {
- Mutability::Shared
} else {
- never!("parser only remaps to raw_token() if matching mutability token follows");
Mutability::Shared
}
} else {
@@ -2460,7 +2496,7 @@ impl ExprCollector<'_> {
None => HygieneId::ROOT,
Some(span_map) => {
let ctx = span_map.span_at(span_start).ctx;
- HygieneId(self.db.lookup_intern_syntax_context(ctx).opaque_and_semitransparent)
+ HygieneId::new(self.db.lookup_intern_syntax_context(ctx).opaque_and_semitransparent)
}
}
}
diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs
index 63a7a9af20..08af470b96 100644
--- a/crates/hir-def/src/body/scope.rs
+++ b/crates/hir-def/src/body/scope.rs
@@ -345,7 +345,7 @@ mod tests {
}
}
- fn do_check(ra_fixture: &str, expected: &[&str]) {
+ fn do_check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: &[&str]) {
let (offset, code) = extract_offset(ra_fixture);
let code = {
let mut buf = String::new();
@@ -509,7 +509,7 @@ fn foo() {
);
}
- fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
+ fn do_check_local_name(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected_offset: u32) {
let (db, position) = TestDB::with_position(ra_fixture);
let file_id = position.file_id;
let offset = position.offset;
diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs
index 7e15a9f2d6..edc7c4c1f2 100644
--- a/crates/hir-def/src/body/tests.rs
+++ b/crates/hir-def/src/body/tests.rs
@@ -7,7 +7,7 @@ use crate::{test_db::TestDB, ModuleDefId};
use super::*;
-fn lower(ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
+fn lower(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
let db = TestDB::with_files(ra_fixture);
let krate = db.fetch_test_crate();
@@ -27,14 +27,14 @@ fn lower(ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
(db, body, fn_def)
}
-fn def_map_at(ra_fixture: &str) -> String {
+fn def_map_at(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
let (db, position) = TestDB::with_position(ra_fixture);
let module = db.module_at_position(position);
module.def_map(&db).dump(&db)
}
-fn check_block_scopes_at(ra_fixture: &str, expect: Expect) {
+fn check_block_scopes_at(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (db, position) = TestDB::with_position(ra_fixture);
let module = db.module_at_position(position);
@@ -42,7 +42,7 @@ fn check_block_scopes_at(ra_fixture: &str, expect: Expect) {
expect.assert_eq(&actual);
}
-fn check_at(ra_fixture: &str, expect: Expect) {
+fn check_at(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let actual = def_map_at(ra_fixture);
expect.assert_eq(&actual);
}
@@ -444,3 +444,18 @@ fn foo() {
}"#
);
}
+
+#[test]
+fn skip_skips_body() {
+ let (db, body, owner) = lower(
+ r#"
+#[rust_analyzer::skip]
+async fn foo(a: (), b: i32) -> u32 {
+ 0 + 1 + b()
+}
+"#,
+ );
+ let printed = body.pretty_print(&db, owner, Edition::CURRENT);
+ expect!["fn foo(�: (), �: i32) -> impl ::core::future::Future::<Output = u32> �"]
+ .assert_eq(&printed);
+}
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index d85bc9a432..12f5f6ad79 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -244,7 +244,7 @@ bitflags::bitflags! {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TraitData {
pub name: Name,
- pub items: Vec<(Name, AssocItemId)>,
+ pub items: Box<[(Name, AssocItemId)]>,
pub flags: TraitFlags,
pub visibility: RawVisibility,
// box it as the vec is usually empty anyways
@@ -360,7 +360,7 @@ impl TraitAliasData {
pub struct ImplData {
pub target_trait: Option<TraitRef>,
pub self_ty: TypeRefId,
- pub items: Box<[AssocItemId]>,
+ pub items: Box<[(Name, AssocItemId)]>,
pub is_negative: bool,
pub is_unsafe: bool,
// box it as the vec is usually empty anyways
@@ -393,7 +393,6 @@ impl ImplData {
collector.collect(&item_tree, tree_id.tree_id(), &impl_def.items);
let (items, macro_calls, diagnostics) = collector.finish();
- let items = items.into_iter().map(|(_, item)| item).collect();
(
Arc::new(ImplData {
@@ -648,12 +647,12 @@ impl<'a> AssocItemCollector<'a> {
fn finish(
self,
) -> (
- Vec<(Name, AssocItemId)>,
+ Box<[(Name, AssocItemId)]>,
Option<Box<Vec<(AstId<ast::Item>, MacroCallId)>>>,
Vec<DefDiagnostic>,
) {
(
- self.items,
+ self.items.into_boxed_slice(),
if self.macro_calls.is_empty() { None } else { Some(Box::new(self.macro_calls)) },
self.diagnostics,
)
diff --git a/crates/hir-def/src/expander.rs b/crates/hir-def/src/expander.rs
index 5315c1c6fb..108258d5a1 100644
--- a/crates/hir-def/src/expander.rs
+++ b/crates/hir-def/src/expander.rs
@@ -10,7 +10,7 @@ use hir_expand::{
ExpandResult, HirFileId, InFile, Lookup, MacroCallId,
};
use limit::Limit;
-use span::SyntaxContextId;
+use span::{Edition, SyntaxContextId};
use syntax::{ast, Parse};
use triomphe::Arc;
@@ -60,7 +60,7 @@ impl Expander {
pub fn syntax_context(&self) -> SyntaxContextId {
// FIXME:
- SyntaxContextId::ROOT
+ SyntaxContextId::root(Edition::CURRENT)
}
pub fn enter_expand<T: ast::AstNode>(
diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs
index a615abd1bb..5d67902c8a 100644
--- a/crates/hir-def/src/find_path.rs
+++ b/crates/hir-def/src/find_path.rs
@@ -665,7 +665,7 @@ mod tests {
/// module the cursor is in.
#[track_caller]
fn check_found_path_(
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
path: &str,
prefer_prelude: bool,
prefer_absolute: bool,
@@ -727,19 +727,35 @@ mod tests {
expect.assert_eq(&res);
}
- fn check_found_path(ra_fixture: &str, path: &str, expect: Expect) {
+ fn check_found_path(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ path: &str,
+ expect: Expect,
+ ) {
check_found_path_(ra_fixture, path, false, false, false, expect);
}
- fn check_found_path_prelude(ra_fixture: &str, path: &str, expect: Expect) {
+ fn check_found_path_prelude(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ path: &str,
+ expect: Expect,
+ ) {
check_found_path_(ra_fixture, path, true, false, false, expect);
}
- fn check_found_path_absolute(ra_fixture: &str, path: &str, expect: Expect) {
+ fn check_found_path_absolute(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ path: &str,
+ expect: Expect,
+ ) {
check_found_path_(ra_fixture, path, false, true, false, expect);
}
- fn check_found_path_prefer_no_std(ra_fixture: &str, path: &str, expect: Expect) {
+ fn check_found_path_prefer_no_std(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ path: &str,
+ expect: Expect,
+ ) {
check_found_path_(ra_fixture, path, false, false, true, expect);
}
diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs
index 9574e5d9cd..ac262950f1 100644
--- a/crates/hir-def/src/import_map.rs
+++ b/crates/hir-def/src/import_map.rs
@@ -509,7 +509,12 @@ mod tests {
}
}
- fn check_search(ra_fixture: &str, crate_name: &str, query: Query, expect: Expect) {
+ fn check_search(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ crate_name: &str,
+ query: Query,
+ expect: Expect,
+ ) {
let db = TestDB::with_files(ra_fixture);
let crate_graph = db.crate_graph();
let krate = crate_graph
@@ -587,7 +592,7 @@ mod tests {
))
}
- fn check(ra_fixture: &str, expect: Expect) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let db = TestDB::with_files(ra_fixture);
let crate_graph = db.crate_graph();
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs
index 2c3eb5c8e5..0fec767410 100644
--- a/crates/hir-def/src/item_scope.rs
+++ b/crates/hir-def/src/item_scope.rs
@@ -162,6 +162,20 @@ impl ItemScope {
.map(move |name| (name, self.get(name)))
}
+ pub fn values(&self) -> impl Iterator<Item = (&Name, Item<ModuleDefId, ImportId>)> + '_ {
+ self.values.iter().map(|(n, &i)| (n, i))
+ }
+
+ pub fn types(
+ &self,
+ ) -> impl Iterator<Item = (&Name, Item<ModuleDefId, ImportOrExternCrate>)> + '_ {
+ self.types.iter().map(|(n, &i)| (n, i))
+ }
+
+ pub fn macros(&self) -> impl Iterator<Item = (&Name, Item<MacroId, ImportId>)> + '_ {
+ self.macros.iter().map(|(n, &i)| (n, i))
+ }
+
pub fn imports(&self) -> impl Iterator<Item = ImportId> + '_ {
self.use_imports_types
.keys()
@@ -263,11 +277,6 @@ impl ItemScope {
self.unnamed_consts.iter().copied()
}
- /// Iterate over all module scoped macros
- pub(crate) fn macros(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ {
- self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_)))
- }
-
/// Iterate over all legacy textual scoped macros visible at the end of the module
pub fn legacy_macros(&self) -> impl Iterator<Item = (&Name, &[MacroId])> + '_ {
self.legacy_macros.iter().map(|(name, def)| (name, &**def))
diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs
index 0f53969d6c..80b699649f 100644
--- a/crates/hir-def/src/item_tree/tests.rs
+++ b/crates/hir-def/src/item_tree/tests.rs
@@ -4,7 +4,7 @@ use test_fixture::WithFixture;
use crate::{db::DefDatabase, test_db::TestDB};
-fn check(ra_fixture: &str, expect: Expect) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (db, file_id) = TestDB::with_single_file(ra_fixture);
let item_tree = db.file_item_tree(file_id.into());
let pretty = item_tree.pretty_print(&db, Edition::CURRENT);
@@ -270,7 +270,7 @@ m!();
// AstId: 2
pub macro m2 { ... }
- // AstId: 3, SyntaxContext: 0, ExpandTo: Items
+ // AstId: 3, SyntaxContext: 2, ExpandTo: Items
m!(...);
"#]],
);
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index 0629d87e54..afdc49a2dc 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -107,7 +107,7 @@ impl LangItems {
for (_, module_data) in crate_def_map.modules() {
for impl_def in module_data.scope.impls() {
lang_items.collect_lang_item(db, impl_def, LangItemTarget::ImplDef);
- for assoc in db.impl_data(impl_def).items.iter().copied() {
+ for &(_, assoc) in db.impl_data(impl_def).items.iter() {
match assoc {
AssocItemId::FunctionId(f) => {
lang_items.collect_lang_item(db, f, LangItemTarget::Function)
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 8af27513eb..84c105a0a3 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -502,7 +502,7 @@ impl ModuleId {
}
/// Whether this module represents the crate root module
- fn is_crate_root(&self) -> bool {
+ pub fn is_crate_root(&self) -> bool {
self.local_id == DefMap::ROOT && self.block.is_none()
}
}
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 511626b5ed..8c5bd3b6d3 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -35,9 +35,9 @@ macro_rules! f {
};
}
-struct#0:[email protected]#1# MyTraitMap2#0:[email protected]#0# {#0:[email protected]#1#
+struct#0:[email protected]#4# MyTraitMap2#0:[email protected]#2# {#0:[email protected]#4#
"#]],
);
}
@@ -75,12 +75,12 @@ macro_rules! f {
};
}
"#]],
@@ -171,7 +171,7 @@ fn main(foo: ()) {
}
fn main(foo: ()) {
- /* error: unresolved macro unresolved */"helloworld!"#0:[email protected]#0#;
+ /* error: unresolved macro unresolved */"helloworld!"#0:[email protected]#2#;
}
}
@@ -197,7 +197,7 @@ macro_rules! mk_struct {
#[macro_use]
mod foo;
"#]],
);
}
@@ -423,10 +423,10 @@ m! { foo, bar }
macro_rules! m {
($($i:ident),*) => ( impl Bar { $(fn $i() {})* } );
}
-impl#\1# Bar#\1# {#\1#
- fn#\1# foo#\0#(#\1#)#\1# {#\1#}#\1#
- fn#\1# bar#\0#(#\1#)#\1# {#\1#}#\1#
-}#\1#
+impl#\4# Bar#\4# {#\4#
+ fn#\4# foo#\2#(#\4#)#\4# {#\4#}#\4#
+ fn#\4# bar#\2#(#\4#)#\4# {#\4#}#\4#
+}#\4#
"#]],
);
}
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index 5b9ffdf37b..408d03ff71 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -47,7 +47,7 @@ use crate::{
};
#[track_caller]
-fn check_errors(ra_fixture: &str, expect: Expect) {
+fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let db = TestDB::with_files(ra_fixture);
let krate = db.fetch_test_crate();
let def_map = db.crate_def_map(krate);
@@ -77,7 +77,7 @@ fn check_errors(ra_fixture: &str, expect: Expect) {
}
#[track_caller]
-fn check(ra_fixture: &str, mut expect: Expect) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, mut expect: Expect) {
let extra_proc_macros = vec![(
r#"
#[proc_macro_attribute]
@@ -358,6 +358,7 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
let (parse, _) = syntax_bridge::token_tree_to_syntax_node(
subtree,
syntax_bridge::TopEntryPoint::MacroItems,
+ &mut |_| span::Edition::CURRENT,
span::Edition::CURRENT,
);
if parse.errors().is_empty() {
diff --git a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index c0178adc9a..70e3e1ed4e 100644
--- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -181,9 +181,9 @@ fn foo(&self) {
self.0. 1;
}
-}#0:[email protected]#0#"#]],
+}#0:[email protected]#2#"#]],
);
}
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 8beeda82bc..1e4b42dff5 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -74,7 +74,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
let proc_macros = if krate.is_proc_macro {
db.proc_macros()
- .for_crate(def_map.krate, db.syntax_context(tree_id.file_id()))
+ .for_crate(def_map.krate, db.syntax_context(tree_id.file_id(), krate.edition))
.unwrap_or_default()
} else {
Default::default()
@@ -717,8 +717,8 @@ impl DefCollector<'_> {
}
}
None => {
- for (name, def) in root_scope.macros() {
- self.def_map.macro_use_prelude.insert(name.clone(), (def, extern_crate));
+ for (name, it) in root_scope.macros() {
+ self.def_map.macro_use_prelude.insert(name.clone(), (it.def, extern_crate));
}
}
}
diff --git a/crates/hir-def/src/nameres/tests.rs b/crates/hir-def/src/nameres/tests.rs
index 32c158415b..318aee04f7 100644
--- a/crates/hir-def/src/nameres/tests.rs
+++ b/crates/hir-def/src/nameres/tests.rs
@@ -11,19 +11,19 @@ use triomphe::Arc;
use crate::{db::DefDatabase, nameres::DefMap, test_db::TestDB};
-fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> {
+fn compute_crate_def_map(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Arc<DefMap> {
let db = TestDB::with_files(ra_fixture);
let krate = db.fetch_test_crate();
db.crate_def_map(krate)
}
-fn render_crate_def_map(ra_fixture: &str) -> String {
+fn render_crate_def_map(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
let db = TestDB::with_files(ra_fixture);
let krate = db.fetch_test_crate();
db.crate_def_map(krate).dump(&db)
}
-fn check(ra_fixture: &str, expect: Expect) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let actual = render_crate_def_map(ra_fixture);
expect.assert_eq(&actual);
}
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 82da57a9bb..0b9b6da8d5 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -166,6 +166,17 @@ impl Resolver {
db: &dyn DefDatabase,
path: &Path,
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
+ self.resolve_path_in_type_ns_with_prefix_info(db, path).map(
+ |(resolution, remaining_segments, import, _)| (resolution, remaining_segments, import),
+ )
+ }
+
+ pub fn resolve_path_in_type_ns_with_prefix_info(
+ &self,
+ db: &dyn DefDatabase,
+ path: &Path,
+ ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)>
+ {
let path = match path {
Path::BarePath(mod_path) => mod_path,
Path::Normal(it) => it.mod_path(),
@@ -181,7 +192,12 @@ impl Resolver {
| LangItemTarget::ImplDef(_)
| LangItemTarget::Static(_) => return None,
};
- return Some((type_ns, seg.as_ref().map(|_| 1), None));
+ return Some((
+ type_ns,
+ seg.as_ref().map(|_| 1),
+ None,
+ ResolvePathResultPrefixInfo::default(),
+ ));
}
};
let first_name = path.segments().first()?;
@@ -197,17 +213,32 @@ impl Resolver {
Scope::ExprScope(_) | Scope::MacroDefScope(_) => continue,
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_type_by_name(first_name, *def) {
- return Some((TypeNs::GenericParam(id), remaining_idx(), None));
+ return Some((
+ TypeNs::GenericParam(id),
+ remaining_idx(),
+ None,
+ ResolvePathResultPrefixInfo::default(),
+ ));
}
}
&Scope::ImplDefScope(impl_) => {
if *first_name == sym::Self_.clone() {
- return Some((TypeNs::SelfType(impl_), remaining_idx(), None));
+ return Some((
+ TypeNs::SelfType(impl_),
+ remaining_idx(),
+ None,
+ ResolvePathResultPrefixInfo::default(),
+ ));
}
}
&Scope::AdtScope(adt) => {
if *first_name == sym::Self_.clone() {
- return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None));
+ return Some((
+ TypeNs::AdtSelfType(adt),
+ remaining_idx(),
+ None,
+ ResolvePathResultPrefixInfo::default(),
+ ));
}
}
Scope::BlockScope(m) => {
@@ -220,18 +251,6 @@ impl Resolver {
self.module_scope.resolve_path_in_type_ns(db, path)
}
- pub fn resolve_path_in_type_ns_fully_with_imports(
- &self,
- db: &dyn DefDatabase,
- path: &Path,
- ) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
- let (res, unresolved, imp) = self.resolve_path_in_type_ns(db, path)?;
- if unresolved.is_some() {
- return None;
- }
- Some((res, imp))
- }
-
pub fn resolve_path_in_type_ns_fully(
&self,
db: &dyn DefDatabase,
@@ -324,7 +343,7 @@ impl Resolver {
if n_segments <= 1 {
let mut hygiene_info = if !hygiene_id.is_root() {
- let ctx = db.lookup_intern_syntax_context(hygiene_id.0);
+ let ctx = hygiene_id.lookup(db);
ctx.outer_expn.map(|expansion| {
let expansion = db.lookup_intern_macro_call(expansion);
(ctx.parent, expansion.def)
@@ -986,11 +1005,12 @@ impl ModuleItemMap {
&self,
db: &dyn DefDatabase,
path: &ModPath,
- ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
- let (module_def, idx, _) =
+ ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>, ResolvePathResultPrefixInfo)>
+ {
+ let (module_def, idx, prefix_info) =
self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
let (res, import) = to_type_ns(module_def)?;
- Some((res, idx, import))
+ Some((res, idx, import, prefix_info))
}
}
diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs
index 4edb683592..c4473e454a 100644
--- a/crates/hir-def/src/visibility.rs
+++ b/crates/hir-def/src/visibility.rs
@@ -240,12 +240,12 @@ impl Visibility {
if a_ancestors.any(|m| m == mod_b.local_id) {
// B is above A
- return Some(Visibility::Module(mod_a, expl_b));
+ return Some(Visibility::Module(mod_a, expl_a));
}
if b_ancestors.any(|m| m == mod_a.local_id) {
// A is above B
- return Some(Visibility::Module(mod_b, expl_a));
+ return Some(Visibility::Module(mod_b, expl_b));
}
None
diff --git a/crates/hir-expand/src/builtin/derive_macro.rs b/crates/hir-expand/src/builtin/derive_macro.rs
index 4510a593af..28b6812139 100644
--- a/crates/hir-expand/src/builtin/derive_macro.rs
+++ b/crates/hir-expand/src/builtin/derive_macro.rs
@@ -4,7 +4,7 @@ use intern::sym;
use itertools::{izip, Itertools};
use parser::SyntaxKind;
use rustc_hash::FxHashSet;
-use span::{MacroCallId, Span, SyntaxContextId};
+use span::{Edition, MacroCallId, Span, SyntaxContextId};
use stdx::never;
use syntax_bridge::DocCommentDesugarMode;
use tracing::debug;
@@ -33,7 +33,7 @@ macro_rules! register_builtin {
}
impl BuiltinDeriveExpander {
- pub fn expander(&self) -> fn(Span, &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+ pub fn expander(&self) -> fn(&dyn ExpandDatabase, Span, &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
match *self {
$( BuiltinDeriveExpander::$trait => $expand, )*
}
@@ -58,8 +58,8 @@ impl BuiltinDeriveExpander {
tt: &tt::TopSubtree,
span: Span,
) -> ExpandResult<tt::TopSubtree> {
- let span = span_with_def_site_ctxt(db, span, id);
- self.expander()(span, tt)
+ let span = span_with_def_site_ctxt(db, span, id, Edition::CURRENT);
+ self.expander()(db, span, tt)
}
}
@@ -226,8 +226,12 @@ struct AdtParam {
}
// FIXME: This whole thing needs a refactor. Each derive requires its special values, and the result is a mess.
-fn parse_adt(tt: &tt::TopSubtree, call_site: Span) -> Result<BasicAdtInfo, ExpandError> {
- let (adt, tm) = to_adt_syntax(tt, call_site)?;
+fn parse_adt(
+ db: &dyn ExpandDatabase,
+ tt: &tt::TopSubtree,
+ call_site: Span,
+) -> Result<BasicAdtInfo, ExpandError> {
+ let (adt, tm) = to_adt_syntax(db, tt, call_site)?;
parse_adt_from_syntax(&adt, &tm, call_site)
}
@@ -382,12 +386,14 @@ fn parse_adt_from_syntax(
}
fn to_adt_syntax(
+ db: &dyn ExpandDatabase,
tt: &tt::TopSubtree,
call_site: Span,
) -> Result<(ast::Adt, span::SpanMap<SyntaxContextId>), ExpandError> {
- let (parsed, tm) = syntax_bridge::token_tree_to_syntax_node(
+ let (parsed, tm) = crate::db::token_tree_to_syntax_node(
+ db,
tt,
- syntax_bridge::TopEntryPoint::MacroItems,
+ crate::ExpandTo::Items,
parser::Edition::CURRENT_FIXME,
);
let macro_items = ast::MacroItems::cast(parsed.syntax_node())
@@ -446,12 +452,13 @@ fn name_to_token(
/// where B1, ..., BN are the bounds given by `bounds_paths`. Z is a phantom type, and
/// therefore does not get bound by the derived trait.
fn expand_simple_derive(
+ db: &dyn ExpandDatabase,
invoc_span: Span,
tt: &tt::TopSubtree,
trait_path: tt::TopSubtree,
make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::TopSubtree,
) -> ExpandResult<tt::TopSubtree> {
- let info = match parse_adt(tt, invoc_span) {
+ let info = match parse_adt(db, tt, invoc_span) {
Ok(info) => info,
Err(e) => {
return ExpandResult::new(
@@ -520,14 +527,22 @@ fn expand_simple_derive_with_parsed(
}
}
-fn copy_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn copy_expand(
+ db: &dyn ExpandDatabase,
+ span: Span,
+ tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
let krate = dollar_crate(span);
- expand_simple_derive(span, tt, quote! {span => #krate::marker::Copy }, |_| quote! {span =>})
+ expand_simple_derive(db, span, tt, quote! {span => #krate::marker::Copy }, |_| quote! {span =>})
}
-fn clone_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn clone_expand(
+ db: &dyn ExpandDatabase,
+ span: Span,
+ tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
let krate = dollar_crate(span);
- expand_simple_derive(span, tt, quote! {span => #krate::clone::Clone }, |adt| {
+ expand_simple_derive(db, span, tt, quote! {span => #krate::clone::Clone }, |adt| {
if matches!(adt.shape, AdtShape::Union) {
let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span };
return quote! {span =>
@@ -576,9 +591,13 @@ fn and_and(span: Span) -> tt::TopSubtree {
quote! {span => #and& }
}
-fn default_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn default_expand(
+ db: &dyn ExpandDatabase,
+ span: Span,
+ tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
let krate = &dollar_crate(span);
- expand_simple_derive(span, tt, quote! {span => #krate::default::Default }, |adt| {
+ expand_simple_derive(db, span, tt, quote! {span => #krate::default::Default }, |adt| {
let body = match &adt.shape {
AdtShape::Struct(fields) => {
let name = &adt.name;
@@ -615,9 +634,13 @@ fn default_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtre
})
}
-fn debug_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn debug_expand(
+ db: &dyn ExpandDatabase,
+ span: Span,
+ tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
let krate = &dollar_crate(span);
- expand_simple_derive(span, tt, quote! {span => #krate::fmt::Debug }, |adt| {
+ expand_simple_derive(db, span, tt, quote! {span => #krate::fmt::Debug }, |adt| {
let for_variant = |name: String, v: &VariantShape| match v {
VariantShape::Struct(fields) => {
let for_fields = fields.iter().map(|it| {
@@ -687,9 +710,13 @@ fn debug_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree>
})
}
-fn hash_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn hash_expand(
+ db: &dyn ExpandDatabase,
+ span: Span,
+ tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
let krate = &dollar_crate(span);
- expand_simple_derive(span, tt, quote! {span => #krate::hash::Hash }, |adt| {
+ expand_simple_derive(db, span, tt, quote! {span => #krate::hash::Hash }, |adt| {
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote! {span =>};
@@ -734,14 +761,22 @@ fn hash_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree>
})
}
-fn eq_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn eq_expand(
+ db: &dyn ExpandDatabase,
+ span: Span,
+ tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
let krate = dollar_crate(span);
- expand_simple_derive(span, tt, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>})
+ expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>})
}
-fn partial_eq_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn partial_eq_expand(
+ db: &dyn ExpandDatabase,
+ span: Span,
+ tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
let krate = dollar_crate(span);
- expand_simple_derive(span, tt, quote! {span => #krate::cmp::PartialEq }, |adt| {
+ expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialEq }, |adt| {
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote! {span =>};
@@ -811,9 +846,13 @@ fn self_and_other_patterns(
(self_patterns, other_patterns)
}
-fn ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn ord_expand(
+ db: &dyn ExpandDatabase,
+ span: Span,
+ tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
let krate = &dollar_crate(span);
- expand_simple_derive(span, tt, quote! {span => #krate::cmp::Ord }, |adt| {
+ expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Ord }, |adt| {
fn compare(
krate: &tt::Ident,
left: tt::TopSubtree,
@@ -869,9 +908,13 @@ fn ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
})
}
-fn partial_ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
+fn partial_ord_expand(
+ db: &dyn ExpandDatabase,
+ span: Span,
+ tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
let krate = &dollar_crate(span);
- expand_simple_derive(span, tt, quote! {span => #krate::cmp::PartialOrd }, |adt| {
+ expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialOrd }, |adt| {
fn compare(
krate: &tt::Ident,
left: tt::TopSubtree,
@@ -932,8 +975,12 @@ fn partial_ord_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSu
})
}
-fn coerce_pointee_expand(span: Span, tt: &tt::TopSubtree) -> ExpandResult<tt::TopSubtree> {
- let (adt, _span_map) = match to_adt_syntax(tt, span) {
+fn coerce_pointee_expand(
+ db: &dyn ExpandDatabase,
+ span: Span,
+ tt: &tt::TopSubtree,
+) -> ExpandResult<tt::TopSubtree> {
+ let (adt, _span_map) = match to_adt_syntax(db, tt, span) {
Ok(it) => it,
Err(err) => {
return ExpandResult::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), err);
diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs
index 5b06de9875..310ddaaf9e 100644
--- a/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/crates/hir-expand/src/builtin/fn_macro.rs
@@ -69,7 +69,7 @@ impl BuiltinFnLikeExpander {
tt: &tt::TopSubtree,
span: Span,
) -> ExpandResult<tt::TopSubtree> {
- let span = span_with_def_site_ctxt(db, span, id);
+ let span = span_with_def_site_ctxt(db, span, id, Edition::CURRENT);
self.expander()(db, id, tt, span)
}
@@ -86,7 +86,7 @@ impl EagerExpander {
tt: &tt::TopSubtree,
span: Span,
) -> ExpandResult<tt::TopSubtree> {
- let span = span_with_def_site_ctxt(db, span, id);
+ let span = span_with_def_site_ctxt(db, span, id, Edition::CURRENT);
self.expander()(db, id, tt, span)
}
@@ -221,7 +221,7 @@ fn assert_expand(
tt: &tt::TopSubtree,
span: Span,
) -> ExpandResult<tt::TopSubtree> {
- let call_site_span = span_with_call_site_ctxt(db, span, id);
+ let call_site_span = span_with_call_site_ctxt(db, span, id, Edition::CURRENT);
let mut iter = tt.iter();
@@ -342,7 +342,7 @@ fn panic_expand(
span: Span,
) -> ExpandResult<tt::TopSubtree> {
let dollar_crate = dollar_crate(span);
- let call_site_span = span_with_call_site_ctxt(db, span, id);
+ let call_site_span = span_with_call_site_ctxt(db, span, id, Edition::CURRENT);
let mac = if use_panic_2021(db, call_site_span) {
sym::panic_2021.clone()
@@ -373,7 +373,7 @@ fn unreachable_expand(
span: Span,
) -> ExpandResult<tt::TopSubtree> {
let dollar_crate = dollar_crate(span);
- let call_site_span = span_with_call_site_ctxt(db, span, id);
+ let call_site_span = span_with_call_site_ctxt(db, span, id, Edition::CURRENT);
let mac = if use_panic_2021(db, call_site_span) {
sym::unreachable_2021.clone()
diff --git a/crates/hir-expand/src/builtin/quote.rs b/crates/hir-expand/src/builtin/quote.rs
index 6c1abc2620..9b637fc768 100644
--- a/crates/hir-expand/src/builtin/quote.rs
+++ b/crates/hir-expand/src/builtin/quote.rs
@@ -102,6 +102,7 @@ macro_rules! quote_impl__ {
($span:ident $builder:ident # ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '#')};
($span:ident $builder:ident $ ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '$')};
($span:ident $builder:ident * ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '*')};
+ ($span:ident $builder:ident = ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '=')};
($span:ident $builder:ident $first:tt $($tail:tt)+ ) => {{
$crate::builtin::quote::__quote!($span $builder $first);
@@ -225,7 +226,7 @@ mod tests {
use ::tt::IdentIsRaw;
use expect_test::expect;
use intern::Symbol;
- use span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
+ use span::{Edition, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
use syntax::{TextRange, TextSize};
use super::quote;
@@ -239,7 +240,7 @@ mod tests {
),
ast_id: ROOT_ERASED_FILE_AST_ID,
},
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
};
#[test]
@@ -276,8 +277,8 @@ mod tests {
assert_eq!(quoted.to_string(), "hello");
let t = format!("{quoted:#?}");
expect![[r#"
- SUBTREE $$ 937550:[email protected]#0 937550:[email protected]#0
- IDENT hello 937550:[email protected]#0"#]]
+ SUBTREE $$ 937550:[email protected]#2 937550:[email protected]#2
+ IDENT hello 937550:[email protected]#2"#]]
.assert_eq(&t);
}
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index f4e80ef9e2..b7804f888a 100644
--- a/crates/hir-expand/src/db.rs
+++ b/crates/hir-expand/src/db.rs
@@ -5,7 +5,7 @@ use either::Either;
use limit::Limit;
use mbe::MatchedArmIndex;
use rustc_hash::FxHashSet;
-use span::{AstIdMap, EditionedFileId, Span, SyntaxContextData, SyntaxContextId};
+use span::{AstIdMap, Edition, EditionedFileId, Span, SyntaxContextData, SyntaxContextId};
use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T};
use syntax_bridge::{syntax_node_to_token_tree, DocCommentDesugarMode};
use triomphe::Arc;
@@ -136,12 +136,12 @@ pub trait ExpandDatabase: SourceDatabase {
macro_call: MacroCallId,
) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>>;
#[ra_salsa::transparent]
- fn syntax_context(&self, file: HirFileId) -> SyntaxContextId;
+ fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContextId;
}
-fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId) -> SyntaxContextId {
+fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> SyntaxContextId {
match file.repr() {
- HirFileIdRepr::FileId(_) => SyntaxContextId::ROOT,
+ HirFileIdRepr::FileId(_) => SyntaxContextId::root(edition),
HirFileIdRepr::MacroFile(m) => {
db.macro_arg_considering_derives(m.macro_call_id, &m.macro_call_id.lookup(db).kind)
.2
@@ -273,9 +273,9 @@ pub fn expand_speculative(
loc.krate,
&tt,
attr_arg.as_ref(),
- span_with_def_site_ctxt(db, span, actual_macro_call),
- span_with_call_site_ctxt(db, span, actual_macro_call),
- span_with_mixed_site_ctxt(db, span, actual_macro_call),
+ span_with_def_site_ctxt(db, span, actual_macro_call, loc.def.edition),
+ span_with_call_site_ctxt(db, span, actual_macro_call, loc.def.edition),
+ span_with_mixed_site_ctxt(db, span, actual_macro_call, loc.def.edition),
)
}
MacroDefKind::BuiltInAttr(_, it) if it.is_derive() => {
@@ -300,7 +300,7 @@ pub fn expand_speculative(
fixup::reverse_fixups(&mut speculative_expansion.value, &undo_info);
let (node, rev_tmap) =
- token_tree_to_syntax_node(&speculative_expansion.value, expand_to, loc.def.edition);
+ token_tree_to_syntax_node(db, &speculative_expansion.value, expand_to, loc.def.edition);
let syntax_node = node.syntax_node();
let token = rev_tmap
@@ -346,6 +346,7 @@ fn parse_macro_expansion(
macro_expand(db, macro_file.macro_call_id, loc);
let (parse, mut rev_token_map) = token_tree_to_syntax_node(
+ db,
match &tt {
CowArc::Arc(it) => it,
CowArc::Owned(it) => it,
@@ -699,9 +700,9 @@ fn expand_proc_macro(
loc.krate,
&macro_arg,
attr_arg,
- span_with_def_site_ctxt(db, span, id),
- span_with_call_site_ctxt(db, span, id),
- span_with_mixed_site_ctxt(db, span, id),
+ span_with_def_site_ctxt(db, span, id, loc.def.edition),
+ span_with_call_site_ctxt(db, span, id, loc.def.edition),
+ span_with_mixed_site_ctxt(db, span, id, loc.def.edition),
)
};
@@ -715,7 +716,8 @@ fn expand_proc_macro(
ExpandResult { value: Arc::new(tt), err }
}
-fn token_tree_to_syntax_node(
+pub(crate) fn token_tree_to_syntax_node(
+ db: &dyn ExpandDatabase,
tt: &tt::TopSubtree,
expand_to: ExpandTo,
edition: parser::Edition,
@@ -727,7 +729,12 @@ fn token_tree_to_syntax_node(
ExpandTo::Type => syntax_bridge::TopEntryPoint::Type,
ExpandTo::Expr => syntax_bridge::TopEntryPoint::Expr,
};
- syntax_bridge::token_tree_to_syntax_node(tt, entry_point, edition)
+ syntax_bridge::token_tree_to_syntax_node(
+ tt,
+ entry_point,
+ &mut |ctx| ctx.lookup(db).edition,
+ edition,
+ )
}
fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> {
@@ -751,5 +758,7 @@ fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> {
}
fn setup_syntax_context_root(db: &dyn ExpandDatabase) {
- db.intern_syntax_context(SyntaxContextData::root());
+ for edition in Edition::iter() {
+ db.intern_syntax_context(SyntaxContextData::root(edition));
+ }
}
diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs
index d1c39f32ca..fef77acb7b 100644
--- a/crates/hir-expand/src/declarative.rs
+++ b/crates/hir-expand/src/declarative.rs
@@ -2,7 +2,7 @@
use base_db::CrateId;
use intern::sym;
-use span::{Edition, MacroCallId, Span, SyntaxContextId};
+use span::{Edition, HirFileIdRepr, MacroCallId, Span, SyntaxContextId};
use stdx::TupleExt;
use syntax::{ast, AstNode};
use syntax_bridge::DocCommentDesugarMode;
@@ -20,6 +20,7 @@ use crate::{
pub struct DeclarativeMacroExpander {
pub mac: mbe::DeclarativeMacro,
pub transparency: Transparency,
+ edition: Edition,
}
impl DeclarativeMacroExpander {
@@ -40,7 +41,7 @@ impl DeclarativeMacroExpander {
.mac
.expand(
&tt,
- |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency),
+ |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency, self.edition),
span,
loc.def.edition,
)
@@ -159,6 +160,10 @@ impl DeclarativeMacroExpander {
transparency(&macro_def).unwrap_or(Transparency::Opaque),
),
};
- Arc::new(DeclarativeMacroExpander { mac, transparency })
+ let edition = ctx_edition(match id.file_id.repr() {
+ HirFileIdRepr::MacroFile(macro_file) => macro_file.macro_call_id.lookup(db).ctxt,
+ HirFileIdRepr::FileId(file) => SyntaxContextId::root(file.edition()),
+ });
+ Arc::new(DeclarativeMacroExpander { mac, transparency, edition })
}
}
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index 8c04d05402..13ddb0d4ac 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -380,14 +380,14 @@ impl InFile<TextRange> {
) -> (FileRange, SyntaxContextId) {
match self.file_id.repr() {
HirFileIdRepr::FileId(file_id) => {
- (FileRange { file_id, range: self.value }, SyntaxContextId::ROOT)
+ (FileRange { file_id, range: self.value }, SyntaxContextId::root(file_id.edition()))
}
HirFileIdRepr::MacroFile(mac_file) => {
match map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) {
Some(it) => it,
None => {
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
- (loc.kind.original_call_range(db), SyntaxContextId::ROOT)
+ (loc.kind.original_call_range(db), SyntaxContextId::root(loc.def.edition))
}
}
}
@@ -432,9 +432,10 @@ impl InFile<TextRange> {
db: &dyn db::ExpandDatabase,
) -> Option<(FileRange, SyntaxContextId)> {
match self.file_id.repr() {
- HirFileIdRepr::FileId(file_id) => {
- Some((FileRange { file_id, range: self.value }, SyntaxContextId::ROOT))
- }
+ HirFileIdRepr::FileId(file_id) => Some((
+ FileRange { file_id, range: self.value },
+ SyntaxContextId::root(file_id.edition()),
+ )),
HirFileIdRepr::MacroFile(mac_file) => {
map_node_range_up(db, &db.expansion_span_map(mac_file), self.value)
}
diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs
index 3d2d52a0af..eb43017739 100644
--- a/crates/hir-expand/src/fixup.rs
+++ b/crates/hir-expand/src/fixup.rs
@@ -380,7 +380,7 @@ pub(crate) fn reverse_fixups(tt: &mut TopSubtree, undo_info: &SyntaxFixupUndoInf
let span = |file_id| Span {
range: TextRange::empty(TextSize::new(0)),
anchor: SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(span::Edition::Edition2015),
};
delimiter.open = span(delimiter.open.anchor.file_id);
delimiter.close = span(delimiter.close.anchor.file_id);
@@ -441,8 +441,8 @@ fn transform_tt<'a, 'b>(
};
let len_diff = replacement.len() as i64 - old_len as i64;
tt.splice(i..i + old_len, replacement.flat_tokens().iter().cloned());
- // `+1` for the loop.
- i = i.checked_add_signed(len_diff as isize + 1).unwrap();
+ // Skip the newly inserted replacement, we don't want to visit it.
+ i += replacement.len();
for &subtree_idx in &subtrees_stack {
let tt::TokenTree::Subtree(subtree) = &mut tt[subtree_idx] else {
@@ -532,7 +532,7 @@ mod tests {
}
#[track_caller]
- fn check(ra_fixture: &str, mut expect: Expect) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, mut expect: Expect) {
let parsed = syntax::SourceFile::parse(ra_fixture, span::Edition::CURRENT);
let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(EditionedFileId::new(
FileId::from_raw(0),
@@ -562,6 +562,7 @@ mod tests {
let (parse, _) = syntax_bridge::token_tree_to_syntax_node(
&tt,
syntax_bridge::TopEntryPoint::MacroItems,
+ &mut |_| parser::Edition::CURRENT,
parser::Edition::CURRENT,
);
assert!(
diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs
index f48de807c2..fe05af0ac9 100644
--- a/crates/hir-expand/src/hygiene.rs
+++ b/crates/hir-expand/src/hygiene.rs
@@ -24,26 +24,37 @@
use std::iter;
-use span::{MacroCallId, Span, SyntaxContextData, SyntaxContextId};
+use span::{Edition, MacroCallId, Span, SyntaxContextData, SyntaxContextId};
use crate::db::{ExpandDatabase, InternSyntaxContextQuery};
pub use span::Transparency;
-pub fn span_with_def_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span {
- span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque)
+pub fn span_with_def_site_ctxt(
+ db: &dyn ExpandDatabase,
+ span: Span,
+ expn_id: MacroCallId,
+ edition: Edition,
+) -> Span {
+ span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque, edition)
}
-pub fn span_with_call_site_ctxt(db: &dyn ExpandDatabase, span: Span, expn_id: MacroCallId) -> Span {
- span_with_ctxt_from_mark(db, span, expn_id, Transparency::Transparent)
+pub fn span_with_call_site_ctxt(
+ db: &dyn ExpandDatabase,
+ span: Span,
+ expn_id: MacroCallId,
+ edition: Edition,
+) -> Span {
+ span_with_ctxt_from_mark(db, span, expn_id, Transparency::Transparent, edition)
}
pub fn span_with_mixed_site_ctxt(
db: &dyn ExpandDatabase,
span: Span,
expn_id: MacroCallId,
+ edition: Edition,
) -> Span {
- span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiTransparent)
+ span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiTransparent, edition)
}
fn span_with_ctxt_from_mark(
@@ -51,8 +62,12 @@ fn span_with_ctxt_from_mark(
span: Span,
expn_id: MacroCallId,
transparency: Transparency,
+ edition: Edition,
) -> Span {
- Span { ctx: apply_mark(db, SyntaxContextId::ROOT, expn_id, transparency), ..span }
+ Span {
+ ctx: apply_mark(db, SyntaxContextId::root(edition), expn_id, transparency, edition),
+ ..span
+ }
}
pub(super) fn apply_mark(
@@ -60,9 +75,10 @@ pub(super) fn apply_mark(
ctxt: SyntaxContextId,
call_id: MacroCallId,
transparency: Transparency,
+ edition: Edition,
) -> SyntaxContextId {
if transparency == Transparency::Opaque {
- return apply_mark_internal(db, ctxt, call_id, transparency);
+ return apply_mark_internal(db, ctxt, call_id, transparency, edition);
}
let call_site_ctxt = db.lookup_intern_macro_call(call_id).ctxt;
@@ -73,7 +89,7 @@ pub(super) fn apply_mark(
};
if call_site_ctxt.is_root() {
- return apply_mark_internal(db, ctxt, call_id, transparency);
+ return apply_mark_internal(db, ctxt, call_id, transparency, edition);
}
// Otherwise, `expn_id` is a macros 1.0 definition and the call site is in a
@@ -86,9 +102,9 @@ pub(super) fn apply_mark(
//
// See the example at `test/ui/hygiene/legacy_interaction.rs`.
for (call_id, transparency) in ctxt.marks(db) {
- call_site_ctxt = apply_mark_internal(db, call_site_ctxt, call_id, transparency);
+ call_site_ctxt = apply_mark_internal(db, call_site_ctxt, call_id, transparency, edition);
}
- apply_mark_internal(db, call_site_ctxt, call_id, transparency)
+ apply_mark_internal(db, call_site_ctxt, call_id, transparency, edition)
}
fn apply_mark_internal(
@@ -96,6 +112,7 @@ fn apply_mark_internal(
ctxt: SyntaxContextId,
call_id: MacroCallId,
transparency: Transparency,
+ edition: Edition,
) -> SyntaxContextId {
use base_db::ra_salsa;
@@ -108,13 +125,14 @@ fn apply_mark_internal(
if transparency >= Transparency::Opaque {
let parent = opaque;
opaque = ra_salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert(
- (parent, call_id, transparency),
+ (parent, call_id, transparency, edition),
|new_opaque| SyntaxContextData {
outer_expn: call_id,
outer_transparency: transparency,
parent,
opaque: new_opaque,
opaque_and_semitransparent: new_opaque,
+ edition,
},
);
}
@@ -123,13 +141,14 @@ fn apply_mark_internal(
let parent = opaque_and_semitransparent;
opaque_and_semitransparent =
ra_salsa::plumbing::get_query_table::<InternSyntaxContextQuery>(db).get_or_insert(
- (parent, call_id, transparency),
+ (parent, call_id, transparency, edition),
|new_opaque_and_semitransparent| SyntaxContextData {
outer_expn: call_id,
outer_transparency: transparency,
parent,
opaque,
opaque_and_semitransparent: new_opaque_and_semitransparent,
+ edition,
},
);
}
@@ -141,6 +160,7 @@ fn apply_mark_internal(
parent,
opaque,
opaque_and_semitransparent,
+ edition,
})
}
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index a0c4c125db..2c664029f6 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -188,6 +188,8 @@ impl fmt::Display for RenderedExpandError {
impl RenderedExpandError {
const GENERAL_KIND: &str = "macro-error";
+ const DISABLED: &str = "proc-macro-disabled";
+ const ATTR_EXP_DISABLED: &str = "attribute-expansion-disabled";
}
impl ExpandErrorKind {
@@ -196,12 +198,12 @@ impl ExpandErrorKind {
ExpandErrorKind::ProcMacroAttrExpansionDisabled => RenderedExpandError {
message: "procedural attribute macro expansion is disabled".to_owned(),
error: false,
- kind: "proc-macros-disabled",
+ kind: RenderedExpandError::ATTR_EXP_DISABLED,
},
ExpandErrorKind::MacroDisabled => RenderedExpandError {
message: "proc-macro is explicitly disabled".to_owned(),
error: false,
- kind: "proc-macro-disabled",
+ kind: RenderedExpandError::DISABLED,
},
&ExpandErrorKind::MissingProcMacroExpander(def_crate) => {
match db.proc_macros().get_error_for_crate(def_crate) {
diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs
index 7ecf521987..89eae862bd 100644
--- a/crates/hir-expand/src/mod_path.rs
+++ b/crates/hir-expand/src/mod_path.rs
@@ -273,10 +273,9 @@ fn convert_path(
res
}
}
- ast::PathSegmentKind::SelfTypeKw => ModPath::from_segments(
- PathKind::Plain,
- Some(Name::new_symbol(sym::Self_.clone(), SyntaxContextId::ROOT)),
- ),
+ ast::PathSegmentKind::SelfTypeKw => {
+ ModPath::from_segments(PathKind::Plain, Some(Name::new_symbol_root(sym::Self_.clone())))
+ }
ast::PathSegmentKind::CrateKw => ModPath::from_segments(PathKind::Crate, iter::empty()),
ast::PathSegmentKind::SelfKw => handle_super_kw(0)?,
ast::PathSegmentKind::SuperKw => handle_super_kw(1)?,
@@ -399,6 +398,9 @@ macro_rules! __known_path {
(core::fmt::Debug) => {};
(std::fmt::format) => {};
(core::ops::Try) => {};
+ (core::convert::From) => {};
+ (core::convert::TryFrom) => {};
+ (core::str::FromStr) => {};
($path:path) => {
compile_error!("Please register your known path in the path module")
};
@@ -415,3 +417,14 @@ macro_rules! __path {
}
pub use crate::__path as path;
+
+#[macro_export]
+macro_rules! __tool_path {
+ ($start:ident $(:: $seg:ident)*) => ({
+ $crate::mod_path::ModPath::from_segments($crate::mod_path::PathKind::Plain, vec![
+ $crate::name::Name::new_symbol_root(intern::sym::rust_analyzer.clone()), $crate::name::Name::new_symbol_root(intern::sym::$start.clone()), $($crate::name::Name::new_symbol_root(intern::sym::$seg.clone()),)*
+ ])
+ });
+}
+
+pub use crate::__tool_path as tool_path;
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 267d545833..cc53d2e34a 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -11,7 +11,7 @@ use syntax::utils::is_raw_identifier;
/// and declarations. In theory, names should also carry hygiene info, but we are
/// not there yet!
///
-/// Note that the rawness (`r#`) of names does not depend on whether they are written raw.
+/// Note that the rawness (`r#`) of names is not preserved. Names are always stored without a `r#` prefix.
/// This is because we want to show (in completions etc.) names as raw depending on the needs
/// of the current crate, for example if it is edition 2021 complete `gen` even if the defining
/// crate is in edition 2024 and wrote `r#gen`, and the opposite holds as well.
@@ -77,20 +77,49 @@ impl Name {
/// Hopefully, this should allow us to integrate hygiene cleaner in the
/// future, and to switch to interned representation of names.
fn new_text(text: &str) -> Name {
+ debug_assert!(!text.starts_with("r#"));
Name { symbol: Symbol::intern(text), ctx: () }
}
- pub fn new(text: &str, ctx: SyntaxContextId) -> Name {
+ pub fn new(text: &str, mut ctx: SyntaxContextId) -> Name {
+ // For comparisons etc. we remove the edition, because sometimes we search for some `Name`
+ // and we don't know which edition it came from.
+ // Can't do that for all `SyntaxContextId`s because it breaks Salsa.
+ ctx.remove_root_edition();
_ = ctx;
Self::new_text(text)
}
+ pub fn new_root(text: &str) -> Name {
+ // The edition doesn't matter for hygiene.
+ Self::new(text.trim_start_matches("r#"), SyntaxContextId::root(Edition::Edition2015))
+ }
+
pub fn new_tuple_field(idx: usize) -> Name {
- Name { symbol: Symbol::intern(&idx.to_string()), ctx: () }
+ let symbol = match idx {
+ 0 => sym::INTEGER_0.clone(),
+ 1 => sym::INTEGER_1.clone(),
+ 2 => sym::INTEGER_2.clone(),
+ 3 => sym::INTEGER_3.clone(),
+ 4 => sym::INTEGER_4.clone(),
+ 5 => sym::INTEGER_5.clone(),
+ 6 => sym::INTEGER_6.clone(),
+ 7 => sym::INTEGER_7.clone(),
+ 8 => sym::INTEGER_8.clone(),
+ 9 => sym::INTEGER_9.clone(),
+ 10 => sym::INTEGER_10.clone(),
+ 11 => sym::INTEGER_11.clone(),
+ 12 => sym::INTEGER_12.clone(),
+ 13 => sym::INTEGER_13.clone(),
+ 14 => sym::INTEGER_14.clone(),
+ 15 => sym::INTEGER_15.clone(),
+ _ => Symbol::intern(&idx.to_string()),
+ };
+ Name { symbol, ctx: () }
}
pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
- Name { symbol: Symbol::intern(lt.text().as_str()), ctx: () }
+ Self::new_text(lt.text().as_str().trim_start_matches("r#"))
}
/// Resolve a name from the text of token.
@@ -133,15 +162,18 @@ impl Name {
}
/// Returns the text this name represents if it isn't a tuple field.
+ ///
+ /// Do not use this for user-facing text, use `display` instead to handle editions properly.
pub fn as_str(&self) -> &str {
self.symbol.as_str()
}
+ // FIXME: Remove this
pub fn unescaped(&self) -> UnescapedName<'_> {
UnescapedName(self)
}
- pub fn is_escaped(&self, edition: Edition) -> bool {
+ pub fn needs_escape(&self, edition: Edition) -> bool {
is_raw_identifier(self.symbol.as_str(), edition)
}
@@ -164,16 +196,19 @@ impl Name {
&self.symbol
}
- pub const fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self {
+ pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self {
+ debug_assert!(!symbol.as_str().starts_with("r#"));
_ = ctx;
Self { symbol, ctx: () }
}
// FIXME: This needs to go once we have hygiene
- pub const fn new_symbol_root(sym: Symbol) -> Self {
+ pub fn new_symbol_root(sym: Symbol) -> Self {
+ debug_assert!(!sym.as_str().starts_with("r#"));
Self { symbol: sym, ctx: () }
}
+ // FIXME: Remove this
#[inline]
pub fn eq_ident(&self, ident: &str) -> bool {
self.as_str() == ident.trim_start_matches("r#")
diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs
index 9f01f1eb25..c8ff6cba3d 100644
--- a/crates/hir-ty/src/chalk_db.rs
+++ b/crates/hir-ty/src/chalk_db.rs
@@ -856,7 +856,7 @@ fn impl_def_datum(
let associated_ty_value_ids = impl_data
.items
.iter()
- .filter_map(|item| match item {
+ .filter_map(|(_, item)| match item {
AssocItemId::TypeAliasId(type_alias) => Some(*type_alias),
_ => None,
})
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 0a8bfaa70f..2d7d4cacd2 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -31,7 +31,10 @@ fn simplify(e: ConstEvalError) -> ConstEvalError {
}
#[track_caller]
-fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
+fn check_fail(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ error: impl FnOnce(ConstEvalError) -> bool,
+) {
let (db, file_id) = TestDB::with_single_file(ra_fixture);
match eval_goal(&db, file_id) {
Ok(_) => panic!("Expected fail, but it succeeded"),
@@ -42,7 +45,7 @@ fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
}
#[track_caller]
-fn check_number(ra_fixture: &str, answer: i128) {
+fn check_number(#[rust_analyzer::rust_fixture] ra_fixture: &str, answer: i128) {
check_answer(ra_fixture, |b, _| {
assert_eq!(
b,
@@ -54,7 +57,7 @@ fn check_number(ra_fixture: &str, answer: i128) {
}
#[track_caller]
-fn check_str(ra_fixture: &str, answer: &str) {
+fn check_str(#[rust_analyzer::rust_fixture] ra_fixture: &str, answer: &str) {
check_answer(ra_fixture, |b, mm| {
let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
@@ -71,7 +74,10 @@ fn check_str(ra_fixture: &str, answer: &str) {
}
#[track_caller]
-fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8], &MemoryMap)) {
+fn check_answer(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ check: impl FnOnce(&[u8], &MemoryMap),
+) {
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
let file_id = *file_ids.last().unwrap();
let r = match eval_goal(&db, file_id) {
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index a4e052a036..3545bf7677 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -471,10 +471,55 @@ impl HirDisplay for ProjectionTy {
if f.should_truncate() {
return write!(f, "{TYPE_HINT_TRUNCATION}");
}
-
let trait_ref = self.trait_ref(f.db);
+ let self_ty = trait_ref.self_type_parameter(Interner);
+
+ // if we are projection on a type parameter, check if the projection target has bounds
+ // itself, if so, we render them directly as `impl Bound` instead of the less useful
+ // `<Param as Trait>::Assoc`
+ if !f.display_target.is_source_code() {
+ if let TyKind::Placeholder(idx) = self_ty.kind(Interner) {
+ let db = f.db;
+ let id = from_placeholder_idx(db, *idx);
+ let generics = generics(db.upcast(), id.parent);
+
+ let substs = generics.placeholder_subst(db);
+ let bounds = db
+ .generic_predicates(id.parent)
+ .iter()
+ .map(|pred| pred.clone().substitute(Interner, &substs))
+ .filter(|wc| match wc.skip_binders() {
+ WhereClause::Implemented(tr) => {
+ match tr.self_type_parameter(Interner).kind(Interner) {
+ TyKind::Alias(AliasTy::Projection(proj)) => proj == self,
+ _ => false,
+ }
+ }
+ WhereClause::TypeOutlives(t) => match t.ty.kind(Interner) {
+ TyKind::Alias(AliasTy::Projection(proj)) => proj == self,
+ _ => false,
+ },
+ // We shouldn't be here if these exist
+ WhereClause::AliasEq(_) => false,
+ WhereClause::LifetimeOutlives(_) => false,
+ })
+ .collect::<Vec<_>>();
+ if !bounds.is_empty() {
+ return write_bounds_like_dyn_trait_with_prefix(
+ f,
+ "impl",
+ Either::Left(
+ &TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner),
+ ),
+ &bounds,
+ SizedByDefault::NotSized,
+ );
+ };
+ }
+ }
+
write!(f, "<")?;
- trait_ref.self_type_parameter(Interner).hir_fmt(f)?;
+ self_ty.hir_fmt(f)?;
write!(f, " as ")?;
trait_ref.hir_fmt(f)?;
write!(
diff --git a/crates/hir-ty/src/dyn_compatibility/tests.rs b/crates/hir-ty/src/dyn_compatibility/tests.rs
index 8a56bd28b5..3060b610bb 100644
--- a/crates/hir-ty/src/dyn_compatibility/tests.rs
+++ b/crates/hir-ty/src/dyn_compatibility/tests.rs
@@ -26,7 +26,7 @@ enum DynCompatibilityViolationKind {
}
fn check_dyn_compatibility<'a>(
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
expected: impl IntoIterator<Item = (&'a str, Vec<DynCompatibilityViolationKind>)>,
) {
let mut expected: FxHashMap<_, _> =
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
index 66159ddce2..4d3896660b 100644
--- a/crates/hir-ty/src/layout/tests.rs
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -25,7 +25,10 @@ fn current_machine_data_layout() -> String {
.unwrap()
}
-fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
+fn eval_goal(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ minicore: &str,
+) -> Result<Arc<Layout>, LayoutError> {
let target_data_layout = current_machine_data_layout();
let ra_fixture = format!(
"//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\n{ra_fixture}",
@@ -81,7 +84,10 @@ fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutErro
}
/// A version of `eval_goal` for types that can not be expressed in ADTs, like closures and `impl Trait`
-fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
+fn eval_expr(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ minicore: &str,
+) -> Result<Arc<Layout>, LayoutError> {
let target_data_layout = current_machine_data_layout();
let ra_fixture = format!(
"//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\nfn main(){{let goal = {{{ra_fixture}}};}}",
@@ -114,21 +120,31 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutErro
}
#[track_caller]
-fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
+fn check_size_and_align(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ minicore: &str,
+ size: u64,
+ align: u64,
+) {
let l = eval_goal(ra_fixture, minicore).unwrap();
assert_eq!(l.size.bytes(), size, "size mismatch");
assert_eq!(l.align.abi.bytes(), align, "align mismatch");
}
#[track_caller]
-fn check_size_and_align_expr(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
+fn check_size_and_align_expr(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ minicore: &str,
+ size: u64,
+ align: u64,
+) {
let l = eval_expr(ra_fixture, minicore).unwrap();
assert_eq!(l.size.bytes(), size, "size mismatch");
assert_eq!(l.align.abi.bytes(), align, "align mismatch");
}
#[track_caller]
-fn check_fail(ra_fixture: &str, e: LayoutError) {
+fn check_fail(#[rust_analyzer::rust_fixture] ra_fixture: &str, e: LayoutError) {
let r = eval_goal(ra_fixture, "");
assert_eq!(r, Err(e));
}
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 24f67fd660..432b8f4d94 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -761,8 +761,8 @@ impl<'a> TyLoweringContext<'a> {
path: &Path,
on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
) -> Option<(TypeNs, Option<usize>)> {
- let (resolution, remaining_index, _) =
- self.resolver.resolve_path_in_type_ns(self.db.upcast(), path)?;
+ let (resolution, remaining_index, _, prefix_info) =
+ self.resolver.resolve_path_in_type_ns_with_prefix_info(self.db.upcast(), path)?;
let segments = path.segments();
match path {
@@ -771,13 +771,12 @@ impl<'a> TyLoweringContext<'a> {
_ => return Some((resolution, remaining_index)),
};
- let (module_segments, resolved_segment_idx, resolved_segment) = match remaining_index {
- None => (
- segments.strip_last(),
- segments.len() - 1,
- segments.last().expect("resolved path has at least one element"),
- ),
- Some(i) => (segments.take(i - 1), i - 1, segments.get(i - 1).unwrap()),
+ let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index {
+ None if prefix_info.enum_variant => {
+ (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2))
+ }
+ None => (segments.strip_last(), segments.len() - 1, None),
+ Some(i) => (segments.take(i - 1), i - 1, None),
};
for (i, mod_segment) in module_segments.iter().enumerate() {
@@ -792,9 +791,23 @@ impl<'a> TyLoweringContext<'a> {
}
}
+ if let Some(enum_segment) = enum_segment {
+ if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
+ && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
+ {
+ on_diagnostic(
+ self,
+ PathLoweringDiagnostic::GenericArgsProhibited {
+ segment: (enum_segment + 1) as u32,
+ reason: GenericArgsProhibitedReason::EnumVariant,
+ },
+ );
+ }
+ }
+
self.handle_type_ns_resolution(
&resolution,
- resolved_segment,
+ segments.get(resolved_segment_idx).expect("should have resolved segment"),
resolved_segment_idx,
on_diagnostic,
);
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 62b071b2f3..182032f048 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -746,16 +746,9 @@ fn lookup_impl_assoc_item_for_trait_ref(
let table = InferenceTable::new(db, env);
let (impl_data, impl_subst) = find_matching_impl(impls, table, trait_ref)?;
- let item = impl_data.items.iter().find_map(|&it| match it {
- AssocItemId::FunctionId(f) => {
- (db.function_data(f).name == *name).then_some(AssocItemId::FunctionId(f))
- }
- AssocItemId::ConstId(c) => db
- .const_data(c)
- .name
- .as_ref()
- .map(|n| n == name)
- .and_then(|result| if result { Some(AssocItemId::ConstId(c)) } else { None }),
+ let item = impl_data.items.iter().find_map(|(n, it)| match *it {
+ AssocItemId::FunctionId(f) => (n == name).then_some(AssocItemId::FunctionId(f)),
+ AssocItemId::ConstId(c) => (n == name).then_some(AssocItemId::ConstId(c)),
AssocItemId::TypeAliasId(_) => None,
})?;
Some((item, impl_subst))
@@ -850,7 +843,7 @@ fn is_inherent_impl_coherent(
};
rustc_has_incoherent_inherent_impls
&& !impl_data.items.is_empty()
- && impl_data.items.iter().copied().all(|assoc| match assoc {
+ && impl_data.items.iter().all(|&(_, assoc)| match assoc {
AssocItemId::FunctionId(it) => db.function_data(it).rustc_allow_incoherent_impl,
AssocItemId::ConstId(it) => db.const_data(it).rustc_allow_incoherent_impl,
AssocItemId::TypeAliasId(it) => db.type_alias_data(it).rustc_allow_incoherent_impl,
@@ -1399,7 +1392,7 @@ fn iterate_inherent_methods(
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
) -> ControlFlow<()> {
for &impl_id in impls.for_self_ty(self_ty) {
- for &item in table.db.impl_data(impl_id).items.iter() {
+ for &(ref item_name, item) in table.db.impl_data(impl_id).items.iter() {
let visible = match is_valid_impl_method_candidate(
table,
self_ty,
@@ -1408,6 +1401,7 @@ fn iterate_inherent_methods(
name,
impl_id,
item,
+ item_name,
) {
IsValidCandidate::Yes => true,
IsValidCandidate::NotVisible => false,
@@ -1467,6 +1461,7 @@ fn is_valid_impl_method_candidate(
name: Option<&Name>,
impl_id: ImplId,
item: AssocItemId,
+ item_name: &Name,
) -> IsValidCandidate {
match item {
AssocItemId::FunctionId(f) => is_valid_impl_fn_candidate(
@@ -1477,11 +1472,12 @@ fn is_valid_impl_method_candidate(
receiver_ty,
self_ty,
visible_from_module,
+ item_name,
),
AssocItemId::ConstId(c) => {
let db = table.db;
check_that!(receiver_ty.is_none());
- check_that!(name.is_none_or(|n| db.const_data(c).name.as_ref() == Some(n)));
+ check_that!(name.is_none_or(|n| n == item_name));
if let Some(from_module) = visible_from_module {
if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
@@ -1565,11 +1561,13 @@ fn is_valid_impl_fn_candidate(
receiver_ty: Option<&Ty>,
self_ty: &Ty,
visible_from_module: Option<ModuleId>,
+ item_name: &Name,
) -> IsValidCandidate {
+ check_that!(name.is_none_or(|n| n == item_name));
+
let db = table.db;
let data = db.function_data(fn_id);
- check_that!(name.is_none_or(|n| n == &data.name));
if let Some(from_module) = visible_from_module {
if !db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module) {
cov_mark::hit!(autoderef_candidate_not_visible);
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index ce43e90df7..f1e86daea2 100644
--- a/crates/hir-ty/src/mir/eval/tests.rs
+++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -37,11 +37,15 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String),
Ok((output.stdout().into_owned(), output.stderr().into_owned()))
}
-fn check_pass(ra_fixture: &str) {
+fn check_pass(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_pass_and_stdio(ra_fixture, "", "");
}
-fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr: &str) {
+fn check_pass_and_stdio(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expected_stdout: &str,
+ expected_stderr: &str,
+) {
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
let file_id = *file_ids.last().unwrap();
let x = eval_main(&db, file_id);
@@ -73,7 +77,7 @@ fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr
}
}
-fn check_panic(ra_fixture: &str, expected_panic: &str) {
+fn check_panic(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected_panic: &str) {
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
let file_id = *file_ids.last().unwrap();
let e = eval_main(&db, file_id).unwrap_err();
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index b7607b5f63..00da9b2517 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -69,27 +69,32 @@ fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
}
#[track_caller]
-fn check_types(ra_fixture: &str) {
+fn check_types(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_impl(ra_fixture, false, true, false)
}
#[track_caller]
-fn check_types_source_code(ra_fixture: &str) {
+fn check_types_source_code(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_impl(ra_fixture, false, true, true)
}
#[track_caller]
-fn check_no_mismatches(ra_fixture: &str) {
+fn check_no_mismatches(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_impl(ra_fixture, true, false, false)
}
#[track_caller]
-fn check(ra_fixture: &str) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_impl(ra_fixture, false, false, false)
}
#[track_caller]
-fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_source: bool) {
+fn check_impl(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ allow_none: bool,
+ only_types: bool,
+ display_source: bool,
+) {
let _tracing = setup_tracing();
let (db, files) = TestDB::with_many_files(ra_fixture);
@@ -282,7 +287,7 @@ fn pat_node(
})
}
-fn infer(ra_fixture: &str) -> String {
+fn infer(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
infer_with_mismatches(ra_fixture, false)
}
@@ -430,7 +435,7 @@ pub(crate) fn visit_module(
visit_scope(db, crate_def_map, &crate_def_map[module_id].scope, cb);
for impl_id in crate_def_map[module_id].scope.impls() {
let impl_data = db.impl_data(impl_id);
- for &item in impl_data.items.iter() {
+ for &(_, item) in impl_data.items.iter() {
match item {
AssocItemId::FunctionId(it) => {
let body = db.body(it.into());
@@ -520,13 +525,13 @@ fn ellipsize(mut text: String, max_len: usize) -> String {
text
}
-fn check_infer(ra_fixture: &str, expect: Expect) {
+fn check_infer(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let mut actual = infer(ra_fixture);
actual.push('\n');
expect.assert_eq(&actual);
}
-fn check_infer_with_mismatches(ra_fixture: &str, expect: Expect) {
+fn check_infer_with_mismatches(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let mut actual = infer_with_mismatches(ra_fixture, true);
actual.push('\n');
expect.assert_eq(&actual);
diff --git a/crates/hir-ty/src/tests/closure_captures.rs b/crates/hir-ty/src/tests/closure_captures.rs
index 7de92d6b16..34d299edd1 100644
--- a/crates/hir-ty/src/tests/closure_captures.rs
+++ b/crates/hir-ty/src/tests/closure_captures.rs
@@ -14,7 +14,7 @@ use crate::test_db::TestDB;
use super::visit_module;
-fn check_closure_captures(ra_fixture: &str, expect: Expect) {
+fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (db, file_id) = TestDB::with_single_file(ra_fixture);
let module = db.module_for_file(file_id);
let def_map = module.def_map(&db);
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 5f0f341f39..1563660457 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -3802,3 +3802,15 @@ fn foo() {
"#,
);
}
+
+#[test]
+fn tool_attr_skip() {
+ check_no_mismatches(
+ r#"
+#[rust_analyzer::skip]
+async fn foo(a: (), b: i32) -> u32 {
+ 0 + 1 + b()
+}
+ "#,
+ );
+}
diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs
index 30711b16df..afd163fbd9 100644
--- a/crates/hir-ty/src/variance.rs
+++ b/crates/hir-ty/src/variance.rs
@@ -968,7 +968,7 @@ struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V);
}
#[track_caller]
- fn check(ra_fixture: &str, expected: Expect) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: Expect) {
// use tracing_subscriber::{layer::SubscriberExt, Layer};
// let my_layer = tracing_subscriber::fmt::layer();
// let _g = tracing::subscriber::set_default(tracing_subscriber::registry().with(
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index 6aadc5c4f7..c68ff706e4 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -20,6 +20,7 @@ itertools.workspace = true
smallvec.workspace = true
tracing.workspace = true
triomphe.workspace = true
+indexmap.workspace = true
# local deps
base-db.workspace = true
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index a23fdf1b39..4351a34e82 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -12,7 +12,6 @@ use hir_def::{
};
use hir_expand::{mod_path::PathKind, name::Name};
use hir_ty::{db::HirDatabase, method_resolution};
-use span::SyntaxContextId;
use crate::{
Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
@@ -90,6 +89,16 @@ impl HasAttrs for AssocItem {
}
}
+impl HasAttrs for crate::Crate {
+ fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
+ let def = AttrDefId::ModuleId(self.root_module().id);
+ AttrsWithOwner::new(db.upcast(), def)
+ }
+ fn attr_id(self) -> AttrDefId {
+ AttrDefId::ModuleId(self.root_module().id)
+ }
+}
+
/// Resolves the item `link` points to in the scope of `def`.
pub fn resolve_doc_path_on(
db: &dyn HirDatabase,
@@ -328,9 +337,7 @@ fn doc_modpath_from_str(link: &str) -> Option<ModPath> {
};
let parts = first_segment.into_iter().chain(parts).map(|segment| match segment.parse() {
Ok(idx) => Name::new_tuple_field(idx),
- Err(_) => {
- Name::new(segment.split_once('<').map_or(segment, |it| it.0), SyntaxContextId::ROOT)
- }
+ Err(_) => Name::new_root(segment.split_once('<').map_or(segment, |it| it.0)),
});
Some(ModPath::from_segments(kind, parts))
};
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index e09ded32fb..b29c91694d 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -23,10 +23,10 @@ use hir_ty::{
use itertools::Itertools;
use crate::{
- Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl,
- Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module,
- SelfParam, Static, Struct, Trait, TraitAlias, TraitRef, TupleField, TyBuilder, Type, TypeAlias,
- TypeOrConstParam, TypeParam, Union, Variant,
+ Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
+ ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam,
+ Macro, Module, SelfParam, Static, Struct, Trait, TraitAlias, TraitRef, TupleField, TyBuilder,
+ Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant,
};
impl HirDisplay for Function {
@@ -846,14 +846,27 @@ impl HirDisplay for TypeAlias {
impl HirDisplay for Module {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
- // FIXME: Module doesn't have visibility saved in data.
+ match self.parent(f.db) {
+ Some(m) => write_visibility(m.id, self.visibility(f.db), f)?,
+ None => {
+ return match self.krate(f.db).display_name(f.db) {
+ Some(name) => write!(f, "extern crate {name}"),
+ None => f.write_str("extern crate {unknown}"),
+ }
+ }
+ }
match self.name(f.db) {
Some(name) => write!(f, "mod {}", name.display(f.db.upcast(), f.edition())),
- None if self.is_crate_root() => match self.krate(f.db).display_name(f.db) {
- Some(name) => write!(f, "extern crate {name}"),
- None => f.write_str("extern crate {unknown}"),
- },
- None => f.write_str("mod {unnamed}"),
+ None => f.write_str("mod {unknown}"),
+ }
+ }
+}
+
+impl HirDisplay for Crate {
+ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+ match self.display_name(f.db) {
+ Some(name) => write!(f, "extern crate {name}"),
+ None => f.write_str("extern crate {unknown}"),
}
}
}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 00b4db5437..db3121d3cd 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -54,11 +54,11 @@ use hir_def::{
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, SyntheticSyntax, TraitAliasId, TraitId, TupleId,
- TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
+ AdtId, AssocItemId, AssocItemLoc, AttrDefId, CallableDefId, ConstId, ConstParamId,
+ CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId, FunctionId,
+ GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstId, ItemContainerId,
+ LifetimeParamId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
+ SyntheticSyntax, TraitAliasId, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
};
use hir_expand::{
attrs::collect_attrs, proc_macro::ProcMacroKind, AstId, MacroCallKind, RenderedExpandError,
@@ -83,7 +83,7 @@ use itertools::Itertools;
use nameres::diagnostics::DefDiagnosticKind;
use rustc_hash::FxHashSet;
use smallvec::SmallVec;
-use span::{Edition, EditionedFileId, FileId, MacroCallId, SyntaxContextId};
+use span::{Edition, EditionedFileId, FileId, MacroCallId};
use stdx::{format_to, impl_from, never};
use syntax::{
ast::{self, HasAttrs as _, HasGenericParams, HasName},
@@ -127,7 +127,7 @@ pub use {
ImportPathConfig,
// FIXME: This is here since some queries take it as input that are used
// outside of hir.
- {AdtId, MacroId, ModuleDefId},
+ {ModuleDefId, TraitId},
},
hir_expand::{
attrs::{Attr, AttrId},
@@ -775,29 +775,16 @@ impl Module {
AssocItemId::ConstId(id) => !db.const_data(id).has_body,
AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(),
});
- impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().filter_map(
- |&item| {
- Some((
- item,
- match item {
- AssocItemId::FunctionId(it) => db.function_data(it).name.clone(),
- AssocItemId::ConstId(it) => {
- db.const_data(it).name.as_ref()?.clone()
- }
- AssocItemId::TypeAliasId(it) => db.type_alias_data(it).name.clone(),
- },
- ))
- },
- ));
+ impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().cloned());
let redundant = impl_assoc_items_scratch
.iter()
- .filter(|(id, name)| {
+ .filter(|(name, id)| {
!items.iter().any(|(impl_name, impl_item)| {
discriminant(impl_item) == discriminant(id) && impl_name == name
})
})
- .map(|(item, name)| (name.clone(), AssocItem::from(*item)));
+ .map(|(name, item)| (name.clone(), AssocItem::from(*item)));
for (name, assoc_item) in redundant {
acc.push(
TraitImplRedundantAssocItems {
@@ -812,7 +799,7 @@ impl Module {
let missing: Vec<_> = required_items
.filter(|(name, id)| {
- !impl_assoc_items_scratch.iter().any(|(impl_item, impl_name)| {
+ !impl_assoc_items_scratch.iter().any(|(impl_name, impl_item)| {
discriminant(impl_item) == discriminant(id) && impl_name == name
})
})
@@ -844,7 +831,7 @@ impl Module {
source_map,
);
- for &item in db.impl_data(impl_def.id).items.iter() {
+ for &(_, item) in db.impl_data(impl_def.id).items.iter() {
AssocItem::from(item).diagnostics(db, acc, style_lints);
}
}
@@ -3000,6 +2987,10 @@ impl Macro {
matches!(self.id, MacroId::MacroRulesId(id) if db.macro_rules_data(id).macro_export)
}
+ pub fn is_proc_macro(self) -> bool {
+ matches!(self.id, MacroId::ProcMacroId(_))
+ }
+
pub fn kind(&self, db: &dyn HirDatabase) -> MacroKind {
match self.id {
MacroId::Macro2Id(it) => match it.lookup(db.upcast()).expander {
@@ -3046,14 +3037,23 @@ impl Macro {
MacroId::Macro2Id(it) => {
matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltInEager(eager) if eager.is_env_or_option_env())
}
- MacroId::MacroRulesId(_) | MacroId::ProcMacroId(_) => false,
+ MacroId::MacroRulesId(it) => {
+ matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltInEager(eager) if eager.is_env_or_option_env())
+ }
+ MacroId::ProcMacroId(_) => false,
}
}
pub fn is_asm_or_global_asm(&self, db: &dyn HirDatabase) -> bool {
- matches!(self.id, MacroId::Macro2Id(it) if {
- matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltIn(m) if m.is_asm())
- })
+ match self.id {
+ MacroId::Macro2Id(it) => {
+ matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltIn(m) if m.is_asm())
+ }
+ MacroId::MacroRulesId(it) => {
+ matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltIn(m) if m.is_asm())
+ }
+ MacroId::ProcMacroId(_) => false,
+ }
}
pub fn is_attr(&self, db: &dyn HirDatabase) -> bool {
@@ -3902,6 +3902,10 @@ impl ToolModule {
db.crate_def_map(self.krate).registered_tools()[self.idx as usize].clone(),
)
}
+
+ pub fn krate(&self) -> Crate {
+ Crate { id: self.krate }
+ }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -4290,7 +4294,7 @@ impl Impl {
}
pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
- db.impl_data(self.id).items.iter().map(|&it| it.into()).collect()
+ db.impl_data(self.id).items.iter().map(|&(_, it)| it.into()).collect()
}
pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
@@ -4731,6 +4735,14 @@ impl Type {
Some((self.derived(ty.clone()), m))
}
+ pub fn add_reference(&self, mutability: Mutability) -> Type {
+ let ty_mutability = match mutability {
+ Mutability::Shared => hir_ty::Mutability::Not,
+ Mutability::Mut => hir_ty::Mutability::Mut,
+ };
+ self.derived(TyKind::Ref(ty_mutability, error_lifetime(), self.ty.clone()).intern(Interner))
+ }
+
pub fn is_slice(&self) -> bool {
matches!(self.ty.kind(Interner), TyKind::Slice(..))
}
@@ -4786,9 +4798,9 @@ impl Type {
}
/// Checks that particular type `ty` implements `std::future::IntoFuture` or
- /// `std::future::Future`.
+ /// `std::future::Future` and returns the `Output` associated type.
/// This function is used in `.await` syntax completion.
- pub fn impls_into_future(&self, db: &dyn HirDatabase) -> bool {
+ pub fn into_future_output(&self, db: &dyn HirDatabase) -> Option<Type> {
let trait_ = db
.lang_item(self.env.krate, LangItem::IntoFutureIntoFuture)
.and_then(|it| {
@@ -4800,16 +4812,18 @@ impl Type {
.or_else(|| {
let future_trait = db.lang_item(self.env.krate, LangItem::Future)?;
future_trait.as_trait()
- });
-
- let trait_ = match trait_ {
- Some(it) => it,
- None => return false,
- };
+ })?;
let canonical_ty =
Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
- method_resolution::implements_trait(&canonical_ty, db, &self.env, trait_)
+ if !method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, trait_) {
+ return None;
+ }
+
+ let output_assoc_type = db
+ .trait_data(trait_)
+ .associated_type_by_name(&Name::new_symbol_root(sym::Output.clone()))?;
+ self.normalize_trait_assoc_type(db, &[], output_assoc_type.into())
}
/// This does **not** resolve `IntoFuture`, only `Future`.
@@ -4824,10 +4838,31 @@ impl Type {
let iterator_trait = db.lang_item(self.env.krate, LangItem::Iterator)?.as_trait()?;
let iterator_item = db
.trait_data(iterator_trait)
- .associated_type_by_name(&Name::new_symbol(sym::Item.clone(), SyntaxContextId::ROOT))?;
+ .associated_type_by_name(&Name::new_symbol_root(sym::Item.clone()))?;
self.normalize_trait_assoc_type(db, &[], iterator_item.into())
}
+ /// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type
+ pub fn into_iterator_iter(self, db: &dyn HirDatabase) -> Option<Type> {
+ let trait_ = db.lang_item(self.env.krate, LangItem::IntoIterIntoIter).and_then(|it| {
+ let into_iter_fn = it.as_function()?;
+ let assoc_item = as_assoc_item(db, AssocItem::Function, into_iter_fn)?;
+ let into_iter_trait = assoc_item.container_or_implemented_trait(db)?;
+ Some(into_iter_trait.id)
+ })?;
+
+ let canonical_ty =
+ Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) };
+ if !method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, trait_) {
+ return None;
+ }
+
+ let into_iter_assoc_type = db
+ .trait_data(trait_)
+ .associated_type_by_name(&Name::new_symbol_root(sym::IntoIter.clone()))?;
+ self.normalize_trait_assoc_type(db, &[], into_iter_assoc_type.into())
+ }
+
/// Checks that particular type `ty` implements `std::ops::FnOnce`.
///
/// This function can be used to check if a particular type is callable, since FnOnce is a
@@ -5117,7 +5152,7 @@ impl Type {
let impls = db.inherent_impls_in_crate(krate);
for impl_def in impls.for_self_ty(&self.ty) {
- for &item in db.impl_data(*impl_def).items.iter() {
+ for &(_, item) in db.impl_data(*impl_def).items.iter() {
if callback(item) {
return;
}
@@ -5535,6 +5570,7 @@ impl Type {
walk_substs(db, type_, &opaque_ty.substitution, cb);
}
TyKind::Placeholder(_) => {
+ cb(type_.derived(ty.clone()));
if let Some(bounds) = ty.impl_trait_bounds(db) {
walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 34d169cd76..523bc6f10a 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -39,8 +39,8 @@ use stdx::TupleExt;
use syntax::{
algo::skip_trivia_token,
ast::{self, HasAttrs as _, HasGenericParams},
- AstNode, AstToken, Direction, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken,
- TextRange, TextSize,
+ AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
+ TextSize,
};
use triomphe::Arc;
@@ -136,8 +136,6 @@ pub struct Semantics<'db, DB> {
pub struct SemanticsImpl<'db> {
pub db: &'db dyn HirDatabase,
s2d_cache: RefCell<SourceToDefCache>,
- /// Rootnode to HirFileId cache
- root_to_file_cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>,
/// MacroCall to its expansion's MacroFileId cache
macro_call_cache: RefCell<FxHashMap<InFile<ast::MacroCall>, MacroFileId>>,
}
@@ -304,12 +302,7 @@ impl<DB: HirDatabase> Semantics<'_, DB> {
impl<'db> SemanticsImpl<'db> {
fn new(db: &'db dyn HirDatabase) -> Self {
- SemanticsImpl {
- db,
- s2d_cache: Default::default(),
- root_to_file_cache: Default::default(),
- macro_call_cache: Default::default(),
- }
+ SemanticsImpl { db, s2d_cache: Default::default(), macro_call_cache: Default::default() }
}
pub fn parse(&self, file_id: EditionedFileId) -> ast::SourceFile {
@@ -483,7 +476,7 @@ impl<'db> SemanticsImpl<'db> {
Some(
calls
.into_iter()
- .map(|call| macro_call_to_macro_id(self, ctx, call?).map(|id| Macro { id }))
+ .map(|call| macro_call_to_macro_id(ctx, call?).map(|id| Macro { id }))
.collect(),
)
})
@@ -962,7 +955,7 @@ impl<'db> SemanticsImpl<'db> {
let InMacroFile { file_id, value: mapped_tokens } = self.with_ctx(|ctx| {
Some(
ctx.cache
- .get_or_insert_expansion(self, macro_file)
+ .get_or_insert_expansion(ctx.db, macro_file)
.map_range_down(span)?
.map(SmallVec::<[_; 2]>::from_iter),
)
@@ -986,7 +979,10 @@ impl<'db> SemanticsImpl<'db> {
process_expansion_for_token(&mut stack, include)?;
}
None => {
- stack.push((file_id.into(), smallvec![(token, SyntaxContextId::ROOT)]));
+ stack.push((
+ file_id.into(),
+ smallvec![(token, SyntaxContextId::root(file_id.edition()))],
+ ));
}
}
@@ -1284,7 +1280,7 @@ impl<'db> SemanticsImpl<'db> {
let macro_file = file_id.macro_file()?;
self.with_ctx(|ctx| {
- let expansion_info = ctx.cache.get_or_insert_expansion(self, macro_file);
+ let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file);
expansion_info.arg().map(|node| node?.parent()).transpose()
})
}
@@ -1315,8 +1311,8 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn resolve_label(&self, label: &ast::Lifetime) -> Option<Label> {
- let (parent, label_id) = self
- .with_ctx(|ctx| ctx.label_ref_to_def(self.wrap_node_infile(label.clone()).as_ref()))?;
+ let src = self.wrap_node_infile(label.clone());
+ let (parent, label_id) = self.with_ctx(|ctx| ctx.label_ref_to_def(src.as_ref()))?;
Some(Label { parent, label_id })
}
@@ -1443,6 +1439,10 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
}
+ pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option<Function> {
+ self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call)
+ }
+
fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {
self.analyze(range_pat.syntax())?.resolve_range_pat(self.db, range_pat)
}
@@ -1516,7 +1516,7 @@ impl<'db> SemanticsImpl<'db> {
let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call);
self.with_ctx(|ctx| {
ctx.macro_call_to_macro_call(macro_call)
- .and_then(|call| macro_call_to_macro_id(self, ctx, call))
+ .and_then(|call| macro_call_to_macro_id(ctx, call))
.map(Into::into)
})
.or_else(|| {
@@ -1558,7 +1558,7 @@ impl<'db> SemanticsImpl<'db> {
let item_in_file = self.wrap_node_infile(item.clone());
let id = self.with_ctx(|ctx| {
let macro_call_id = ctx.item_to_macro_call(item_in_file.as_ref())?;
- macro_call_to_macro_id(self, ctx, macro_call_id)
+ macro_call_to_macro_id(ctx, macro_call_id)
})?;
Some(Macro { id })
}
@@ -1591,14 +1591,11 @@ impl<'db> SemanticsImpl<'db> {
pub fn resolve_mod_path_relative(
&self,
to: Module,
- segments: impl IntoIterator<Item = SmolStr>,
+ segments: impl IntoIterator<Item = Name>,
) -> Option<impl Iterator<Item = ItemInNs>> {
let items = to.id.resolver(self.db.upcast()).resolve_module_path_in_items(
self.db.upcast(),
- &ModPath::from_segments(
- hir_def::path::PathKind::Plain,
- segments.into_iter().map(|it| Name::new(&it, SyntaxContextId::ROOT)),
- ),
+ &ModPath::from_segments(hir_def::path::PathKind::Plain, segments),
);
Some(items.iter_items().map(|(item, _)| item.into()))
}
@@ -1722,10 +1719,11 @@ impl<'db> SemanticsImpl<'db> {
}
fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) {
- assert!(root_node.parent().is_none());
- let mut cache = self.root_to_file_cache.borrow_mut();
- let prev = cache.insert(root_node, file_id);
- assert!(prev.is_none() || prev == Some(file_id));
+ SourceToDefCache::cache(
+ &mut self.s2d_cache.borrow_mut().root_to_file_cache,
+ root_node,
+ file_id,
+ );
}
pub fn assert_contains_node(&self, node: &SyntaxNode) {
@@ -1733,8 +1731,8 @@ impl<'db> SemanticsImpl<'db> {
}
fn lookup(&self, root_node: &SyntaxNode) -> Option<HirFileId> {
- let cache = self.root_to_file_cache.borrow();
- cache.get(root_node).copied()
+ let cache = self.s2d_cache.borrow();
+ cache.root_to_file_cache.get(root_node).copied()
}
fn wrap_node_infile<N: AstNode>(&self, node: N) -> InFile<N> {
@@ -1753,13 +1751,14 @@ impl<'db> SemanticsImpl<'db> {
let file_id = self.lookup(&root_node).unwrap_or_else(|| {
panic!(
"\n\nFailed to lookup {:?} in this Semantics.\n\
- Make sure to use only query nodes, derived from this instance of Semantics.\n\
+ Make sure to only query nodes derived from this instance of Semantics.\n\
root node: {:?}\n\
known nodes: {}\n\n",
node,
root_node,
- self.root_to_file_cache
+ self.s2d_cache
.borrow()
+ .root_to_file_cache
.keys()
.map(|it| format!("{it:?}"))
.collect::<Vec<_>>()
@@ -1906,7 +1905,6 @@ impl<'db> SemanticsImpl<'db> {
}
fn macro_call_to_macro_id(
- sema: &SemanticsImpl<'_>,
ctx: &mut SourceToDefCtx<'_, '_>,
macro_call_id: MacroCallId,
) -> Option<MacroId> {
@@ -1922,7 +1920,7 @@ fn macro_call_to_macro_id(
it.to_ptr(db).to_node(&db.parse(file_id).syntax_node())
}
HirFileIdRepr::MacroFile(macro_file) => {
- let expansion_info = ctx.cache.get_or_insert_expansion(sema, macro_file);
+ let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file);
it.to_ptr(db).to_node(&expansion_info.expanded().value)
}
};
@@ -1934,7 +1932,7 @@ fn macro_call_to_macro_id(
it.to_ptr(db).to_node(&db.parse(file_id).syntax_node())
}
HirFileIdRepr::MacroFile(macro_file) => {
- let expansion_info = ctx.cache.get_or_insert_expansion(sema, macro_file);
+ let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file);
it.to_ptr(db).to_node(&expansion_info.expanded().value)
}
};
diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs
index ec65ea9a9a..d5dfb98571 100644
--- a/crates/hir/src/semantics/child_by_source.rs
+++ b/crates/hir/src/semantics/child_by_source.rs
@@ -56,7 +56,7 @@ impl ChildBySource for ImplId {
res[keys::ATTR_MACRO_CALL].insert(ast_id.to_ptr(db.upcast()), call_id);
},
);
- data.items.iter().for_each(|&item| {
+ data.items.iter().for_each(|&(_, item)| {
add_assoc_item(db, res, file_id, item);
});
}
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index b5cc440fc2..3c9e7065c4 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -110,10 +110,7 @@ use syntax::{
AstNode, AstPtr, SyntaxNode,
};
-use crate::{
- db::HirDatabase, semantics::child_by_source::ChildBySource, InFile, InlineAsmOperand,
- SemanticsImpl,
-};
+use crate::{db::HirDatabase, semantics::child_by_source::ChildBySource, InFile, InlineAsmOperand};
#[derive(Default)]
pub(super) struct SourceToDefCache {
@@ -121,9 +118,21 @@ pub(super) struct SourceToDefCache {
expansion_info_cache: FxHashMap<MacroFileId, ExpansionInfo>,
pub(super) file_to_def_cache: FxHashMap<FileId, SmallVec<[ModuleId; 1]>>,
pub(super) included_file_cache: FxHashMap<EditionedFileId, Option<MacroFileId>>,
+ /// Rootnode to HirFileId cache
+ pub(super) root_to_file_cache: FxHashMap<SyntaxNode, HirFileId>,
}
impl SourceToDefCache {
+ pub(super) fn cache(
+ root_to_file_cache: &mut FxHashMap<SyntaxNode, HirFileId>,
+ root_node: SyntaxNode,
+ file_id: HirFileId,
+ ) {
+ assert!(root_node.parent().is_none());
+ let prev = root_to_file_cache.insert(root_node, file_id);
+ assert!(prev.is_none() || prev == Some(file_id));
+ }
+
pub(super) fn get_or_insert_include_for(
&mut self,
db: &dyn HirDatabase,
@@ -143,14 +152,14 @@ impl SourceToDefCache {
pub(super) fn get_or_insert_expansion(
&mut self,
- sema: &SemanticsImpl<'_>,
+ db: &dyn HirDatabase,
macro_file: MacroFileId,
) -> &ExpansionInfo {
self.expansion_info_cache.entry(macro_file).or_insert_with(|| {
- let exp_info = macro_file.expansion_info(sema.db.upcast());
+ let exp_info = macro_file.expansion_info(db.upcast());
let InMacroFile { file_id, value } = exp_info.expanded();
- sema.cache(value, file_id.into());
+ Self::cache(&mut self.root_to_file_cache, value, file_id.into());
exp_info
})
@@ -520,18 +529,11 @@ impl SourceToDefCtx<'_, '_> {
node: InFile<&SyntaxNode>,
mut cb: impl FnMut(&mut Self, InFile<SyntaxNode>) -> Option<T>,
) -> Option<T> {
- use hir_expand::MacroFileIdExt;
let parent = |this: &mut Self, node: InFile<&SyntaxNode>| match node.value.parent() {
Some(parent) => Some(node.with_value(parent)),
None => {
let macro_file = node.file_id.macro_file()?;
-
- let expansion_info = this
- .cache
- .expansion_info_cache
- .entry(macro_file)
- .or_insert_with(|| macro_file.expansion_info(this.db.upcast()));
-
+ let expansion_info = this.cache.get_or_insert_expansion(this.db, macro_file);
expansion_info.arg().map(|node| node?.parent()).transpose()
}
};
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index b699ccde41..6b78d7a363 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -322,6 +322,68 @@ impl SourceAnalyzer {
}
}
+ // If the method is into(), try_into(), parse(), resolve it to from, try_from, from_str.
+ pub(crate) fn resolve_known_blanket_dual_impls(
+ &self,
+ db: &dyn HirDatabase,
+ call: &ast::MethodCallExpr,
+ ) -> Option<Function> {
+ // e.g. if the method call is let b = a.into(),
+ // - receiver_type is A (type of a)
+ // - return_type is B (type of b)
+ // We will find the definition of B::from(a: A).
+ let callable = self.resolve_method_call_as_callable(db, call)?;
+ let (_, receiver_type) = callable.receiver_param(db)?;
+ let return_type = callable.return_type();
+ let (search_method, substs) = match call.name_ref()?.text().as_str() {
+ "into" => {
+ let trait_ =
+ self.resolver.resolve_known_trait(db.upcast(), &path![core::convert::From])?;
+ (
+ self.trait_fn(db, trait_, "from")?,
+ hir_ty::TyBuilder::subst_for_def(db, trait_, None)
+ .push(return_type.ty)
+ .push(receiver_type.ty)
+ .build(),
+ )
+ }
+ "try_into" => {
+ let trait_ = self
+ .resolver
+ .resolve_known_trait(db.upcast(), &path![core::convert::TryFrom])?;
+ (
+ self.trait_fn(db, trait_, "try_from")?,
+ hir_ty::TyBuilder::subst_for_def(db, trait_, None)
+ // If the method is try_into() or parse(), return_type is Result<T, Error>.
+ // Get T from type arguments of Result<T, Error>.
+ .push(return_type.type_arguments().next()?.ty)
+ .push(receiver_type.ty)
+ .build(),
+ )
+ }
+ "parse" => {
+ let trait_ =
+ self.resolver.resolve_known_trait(db.upcast(), &path![core::str::FromStr])?;
+ (
+ self.trait_fn(db, trait_, "from_str")?,
+ hir_ty::TyBuilder::subst_for_def(db, trait_, None)
+ .push(return_type.type_arguments().next()?.ty)
+ .build(),
+ )
+ }
+ _ => return None,
+ };
+
+ let found_method = self.resolve_impl_method_or_trait_def(db, search_method, substs);
+ // If found_method == search_method, the method in trait itself is resolved.
+ // It means the blanket dual impl is not found.
+ if found_method == search_method {
+ None
+ } else {
+ Some(found_method.into())
+ }
+ }
+
pub(crate) fn resolve_expr_as_callable(
&self,
db: &dyn HirDatabase,
@@ -1247,6 +1309,18 @@ impl SourceAnalyzer {
Some((trait_id, fn_id))
}
+ fn trait_fn(
+ &self,
+ db: &dyn HirDatabase,
+ trait_id: TraitId,
+ method_name: &str,
+ ) -> Option<FunctionId> {
+ db.trait_data(trait_id).items.iter().find_map(|(item_name, item)| match item {
+ AssocItemId::FunctionId(t) if item_name.as_str() == method_name => Some(*t),
+ _ => None,
+ })
+ }
+
fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?)
}
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs
index f8416f86bf..a6b8ed70c3 100644
--- a/crates/hir/src/symbols.rs
+++ b/crates/hir/src/symbols.rs
@@ -1,27 +1,34 @@
//! File symbol extraction.
+use either::Either;
use hir_def::{
db::DefDatabase,
- item_scope::ItemInNs,
+ item_scope::{ImportId, ImportOrExternCrate},
+ per_ns::Item,
src::{HasChildSource, HasSource},
- AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId,
- TraitId,
+ visibility::{Visibility, VisibilityExplicitness},
+ AdtId, AssocItemId, DefWithBodyId, ExternCrateId, HasModule, ImplId, Lookup, MacroId,
+ ModuleDefId, ModuleId, TraitId,
};
-use hir_expand::HirFileId;
+use hir_expand::{name::Name, HirFileId};
use hir_ty::{
db::HirDatabase,
display::{hir_display_with_types_map, HirDisplay},
};
+use intern::Symbol;
+use rustc_hash::FxHashMap;
use span::Edition;
use syntax::{ast::HasName, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, ToSmolStr};
use crate::{Module, ModuleDef, Semantics};
+pub type FxIndexSet<T> = indexmap::IndexSet<T, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
+
/// The actual data that is stored in the index. It should be as compact as
/// possible.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FileSymbol {
- pub name: SmolStr,
+ pub name: Symbol,
pub def: ModuleDef,
pub loc: DeclarationLocation,
pub container_name: Option<SmolStr>,
@@ -37,7 +44,7 @@ pub struct DeclarationLocation {
/// This points to the whole syntax node of the declaration.
pub ptr: SyntaxNodePtr,
/// This points to the [`syntax::ast::Name`] identifier of the declaration.
- pub name_ptr: AstPtr<syntax::ast::Name>,
+ pub name_ptr: AstPtr<Either<syntax::ast::Name, syntax::ast::NameRef>>,
}
impl DeclarationLocation {
@@ -55,7 +62,7 @@ struct SymbolCollectorWork {
pub struct SymbolCollector<'a> {
db: &'a dyn HirDatabase,
- symbols: Vec<FileSymbol>,
+ symbols: FxIndexSet<FileSymbol>,
work: Vec<SymbolCollectorWork>,
current_container_name: Option<SmolStr>,
edition: Edition,
@@ -86,11 +93,11 @@ impl<'a> SymbolCollector<'a> {
}
}
- pub fn finish(self) -> Vec<FileSymbol> {
- self.symbols
+ pub fn finish(self) -> Box<[FileSymbol]> {
+ self.symbols.into_iter().collect()
}
- pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Vec<FileSymbol> {
+ pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> {
let mut symbol_collector = SymbolCollector::new(db);
symbol_collector.collect(module);
symbol_collector.finish()
@@ -104,96 +111,174 @@ impl<'a> SymbolCollector<'a> {
}
fn collect_from_module(&mut self, module_id: ModuleId) {
- let def_map = module_id.def_map(self.db.upcast());
- let scope = &def_map[module_id.local_id].scope;
-
- for module_def_id in scope.declarations() {
- match module_def_id {
- ModuleDefId::ModuleId(id) => self.push_module(id),
+ let push_decl = |this: &mut Self, def, name| {
+ match def {
+ ModuleDefId::ModuleId(id) => this.push_module(id, name),
ModuleDefId::FunctionId(id) => {
- self.push_decl(id, false);
- self.collect_from_body(id);
+ this.push_decl(id, name, false);
+ this.collect_from_body(id);
}
- ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id, false),
- ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id, false),
- ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id, false),
+ ModuleDefId::AdtId(AdtId::StructId(id)) => this.push_decl(id, name, false),
+ ModuleDefId::AdtId(AdtId::EnumId(id)) => this.push_decl(id, name, false),
+ ModuleDefId::AdtId(AdtId::UnionId(id)) => this.push_decl(id, name, false),
ModuleDefId::ConstId(id) => {
- self.push_decl(id, false);
- self.collect_from_body(id);
+ this.push_decl(id, name, false);
+ this.collect_from_body(id);
}
ModuleDefId::StaticId(id) => {
- self.push_decl(id, false);
- self.collect_from_body(id);
+ this.push_decl(id, name, false);
+ this.collect_from_body(id);
}
ModuleDefId::TraitId(id) => {
- self.push_decl(id, false);
- self.collect_from_trait(id);
+ this.push_decl(id, name, false);
+ this.collect_from_trait(id);
}
ModuleDefId::TraitAliasId(id) => {
- self.push_decl(id, false);
+ this.push_decl(id, name, false);
}
ModuleDefId::TypeAliasId(id) => {
- self.push_decl(id, false);
+ this.push_decl(id, name, false);
}
ModuleDefId::MacroId(id) => match id {
- MacroId::Macro2Id(id) => self.push_decl(id, false),
- MacroId::MacroRulesId(id) => self.push_decl(id, false),
- MacroId::ProcMacroId(id) => self.push_decl(id, false),
+ MacroId::Macro2Id(id) => this.push_decl(id, name, false),
+ MacroId::MacroRulesId(id) => this.push_decl(id, name, false),
+ MacroId::ProcMacroId(id) => this.push_decl(id, name, false),
},
// Don't index these.
ModuleDefId::BuiltinType(_) => {}
ModuleDefId::EnumVariantId(_) => {}
}
- }
+ };
- for impl_id in scope.impls() {
- self.collect_from_impl(impl_id);
- }
+ // Nested trees are very common, so a cache here will hit a lot.
+ let import_child_source_cache = &mut FxHashMap::default();
+
+ let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId| {
+ let source = import_child_source_cache
+ .entry(i.import)
+ .or_insert_with(|| i.import.child_source(this.db.upcast()));
+ let Some(use_tree_src) = source.value.get(i.idx) else { return };
+ let Some(name_ptr) = use_tree_src
+ .rename()
+ .and_then(|rename| rename.name())
+ .map(Either::Left)
+ .or_else(|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right))
+ .map(|it| AstPtr::new(&it))
+ else {
+ return;
+ };
+ let dec_loc = DeclarationLocation {
+ hir_file_id: source.file_id,
+ ptr: SyntaxNodePtr::new(use_tree_src.syntax()),
+ name_ptr,
+ };
+ this.symbols.insert(FileSymbol {
+ name: name.symbol().clone(),
+ def: def.into(),
+ container_name: this.current_container_name.clone(),
+ loc: dec_loc,
+ is_alias: false,
+ is_assoc: false,
+ });
+ };
- // Record renamed imports.
- // FIXME: In case it imports multiple items under different namespaces we just pick one arbitrarily
- // for now.
- for id in scope.imports() {
- let source = id.import.child_source(self.db.upcast());
- let Some(use_tree_src) = source.value.get(id.idx) else { continue };
- let Some(rename) = use_tree_src.rename() else { continue };
- let Some(name) = rename.name() else { continue };
-
- let res = scope.fully_resolve_import(self.db.upcast(), id);
- res.iter_items().for_each(|(item, _)| {
- let def = match item {
- ItemInNs::Types(def) | ItemInNs::Values(def) => def,
- ItemInNs::Macros(def) => ModuleDefId::from(def),
- }
- .into();
+ let push_extern_crate =
+ |this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId| {
+ let loc = i.lookup(this.db.upcast());
+ let source = loc.source(this.db.upcast());
+ let Some(name_ptr) = source
+ .value
+ .rename()
+ .and_then(|rename| rename.name())
+ .map(Either::Left)
+ .or_else(|| source.value.name_ref().map(Either::Right))
+ .map(|it| AstPtr::new(&it))
+ else {
+ return;
+ };
let dec_loc = DeclarationLocation {
hir_file_id: source.file_id,
- ptr: SyntaxNodePtr::new(use_tree_src.syntax()),
- name_ptr: AstPtr::new(&name),
+ ptr: SyntaxNodePtr::new(source.value.syntax()),
+ name_ptr,
};
-
- self.symbols.push(FileSymbol {
- name: name.text().into(),
- def,
- container_name: self.current_container_name.clone(),
+ this.symbols.insert(FileSymbol {
+ name: name.symbol().clone(),
+ def: def.into(),
+ container_name: this.current_container_name.clone(),
loc: dec_loc,
is_alias: false,
is_assoc: false,
});
- });
+ };
+
+ let is_explicit_import = |vis| {
+ match vis {
+ Visibility::Module(_, VisibilityExplicitness::Explicit) => true,
+ Visibility::Module(_, VisibilityExplicitness::Implicit) => {
+ // consider imports in the crate root explicit, as these are visibly
+ // crate-wide anyways
+ module_id.is_crate_root()
+ }
+ Visibility::Public => true,
+ }
+ };
+
+ let def_map = module_id.def_map(self.db.upcast());
+ let scope = &def_map[module_id.local_id].scope;
+
+ for impl_id in scope.impls() {
+ self.collect_from_impl(impl_id);
+ }
+
+ for (name, Item { def, vis, import }) in scope.types() {
+ if let Some(i) = import {
+ if is_explicit_import(vis) {
+ match i {
+ ImportOrExternCrate::Import(i) => push_import(self, i, name, def),
+ ImportOrExternCrate::ExternCrate(i) => {
+ push_extern_crate(self, i, name, def)
+ }
+ }
+ }
+ continue;
+ }
+ // self is a declaration
+ push_decl(self, def, name)
+ }
+
+ for (name, Item { def, vis, import }) in scope.macros() {
+ if let Some(i) = import {
+ if is_explicit_import(vis) {
+ push_import(self, i, name, def.into());
+ }
+ continue;
+ }
+ // self is a declaration
+ push_decl(self, def.into(), name)
+ }
+
+ for (name, Item { def, vis, import }) in scope.values() {
+ if let Some(i) = import {
+ if is_explicit_import(vis) {
+ push_import(self, i, name, def);
+ }
+ continue;
+ }
+ // self is a declaration
+ push_decl(self, def, name)
}
for const_id in scope.unnamed_consts() {
self.collect_from_body(const_id);
}
- for (_, id) in scope.legacy_macros() {
+ for (name, id) in scope.legacy_macros() {
for &id in id {
if id.module(self.db.upcast()) == module_id {
match id {
- MacroId::Macro2Id(id) => self.push_decl(id, false),
- MacroId::MacroRulesId(id) => self.push_decl(id, false),
- MacroId::ProcMacroId(id) => self.push_decl(id, false),
+ MacroId::Macro2Id(id) => self.push_decl(id, name, false),
+ MacroId::MacroRulesId(id) => self.push_decl(id, name, false),
+ MacroId::ProcMacroId(id) => self.push_decl(id, name, false),
}
}
}
@@ -223,8 +308,8 @@ impl<'a> SymbolCollector<'a> {
.to_smolstr(),
);
self.with_container_name(impl_name, |s| {
- for &assoc_item_id in impl_data.items.iter() {
- s.push_assoc_item(assoc_item_id)
+ for &(ref name, assoc_item_id) in &impl_data.items {
+ s.push_assoc_item(assoc_item_id, name)
}
})
}
@@ -232,8 +317,8 @@ impl<'a> SymbolCollector<'a> {
fn collect_from_trait(&mut self, trait_id: TraitId) {
let trait_data = self.db.trait_data(trait_id);
self.with_container_name(Some(trait_data.name.as_str().into()), |s| {
- for &(_, assoc_item_id) in &trait_data.items {
- s.push_assoc_item(assoc_item_id);
+ for &(ref name, assoc_item_id) in &trait_data.items {
+ s.push_assoc_item(assoc_item_id, name);
}
});
}
@@ -266,15 +351,15 @@ impl<'a> SymbolCollector<'a> {
}
}
- fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) {
+ fn push_assoc_item(&mut self, assoc_item_id: AssocItemId, name: &Name) {
match assoc_item_id {
- AssocItemId::FunctionId(id) => self.push_decl(id, true),
- AssocItemId::ConstId(id) => self.push_decl(id, true),
- AssocItemId::TypeAliasId(id) => self.push_decl(id, true),
+ AssocItemId::FunctionId(id) => self.push_decl(id, name, true),
+ AssocItemId::ConstId(id) => self.push_decl(id, name, true),
+ AssocItemId::TypeAliasId(id) => self.push_decl(id, name, true),
}
}
- fn push_decl<'db, L>(&mut self, id: L, is_assoc: bool)
+ fn push_decl<'db, L>(&mut self, id: L, name: &Name, is_assoc: bool)
where
L: Lookup<Database<'db> = dyn DefDatabase + 'db> + Into<ModuleDefId>,
<L as Lookup>::Data: HasSource,
@@ -287,13 +372,13 @@ impl<'a> SymbolCollector<'a> {
let dec_loc = DeclarationLocation {
hir_file_id: source.file_id,
ptr: SyntaxNodePtr::new(source.value.syntax()),
- name_ptr: AstPtr::new(&name_node),
+ name_ptr: AstPtr::new(&name_node).wrap_left(),
};
if let Some(attrs) = def.attrs(self.db) {
for alias in attrs.doc_aliases() {
- self.symbols.push(FileSymbol {
- name: alias.as_str().into(),
+ self.symbols.insert(FileSymbol {
+ name: alias.clone(),
def,
loc: dec_loc.clone(),
container_name: self.current_container_name.clone(),
@@ -303,8 +388,8 @@ impl<'a> SymbolCollector<'a> {
}
}
- self.symbols.push(FileSymbol {
- name: name_node.text().into(),
+ self.symbols.insert(FileSymbol {
+ name: name.symbol().clone(),
def,
container_name: self.current_container_name.clone(),
loc: dec_loc,
@@ -313,7 +398,7 @@ impl<'a> SymbolCollector<'a> {
});
}
- fn push_module(&mut self, module_id: ModuleId) {
+ fn push_module(&mut self, module_id: ModuleId, name: &Name) {
let def_map = module_id.def_map(self.db.upcast());
let module_data = &def_map[module_id.local_id];
let Some(declaration) = module_data.origin.declaration() else { return };
@@ -322,15 +407,15 @@ impl<'a> SymbolCollector<'a> {
let dec_loc = DeclarationLocation {
hir_file_id: declaration.file_id,
ptr: SyntaxNodePtr::new(module.syntax()),
- name_ptr: AstPtr::new(&name_node),
+ name_ptr: AstPtr::new(&name_node).wrap_left(),
};
let def = ModuleDef::Module(module_id.into());
if let Some(attrs) = def.attrs(self.db) {
for alias in attrs.doc_aliases() {
- self.symbols.push(FileSymbol {
- name: alias.as_str().into(),
+ self.symbols.insert(FileSymbol {
+ name: alias.clone(),
def,
loc: dec_loc.clone(),
container_name: self.current_container_name.clone(),
@@ -340,8 +425,8 @@ impl<'a> SymbolCollector<'a> {
}
}
- self.symbols.push(FileSymbol {
- name: name_node.text().into(),
+ self.symbols.insert(FileSymbol {
+ name: name.symbol().clone(),
def: ModuleDef::Module(module_id.into()),
container_name: self.current_container_name.clone(),
loc: dec_loc,
diff --git a/crates/ide-assists/src/assist_context.rs b/crates/ide-assists/src/assist_context.rs
index 074d943719..64e77b2d69 100644
--- a/crates/ide-assists/src/assist_context.rs
+++ b/crates/ide-assists/src/assist_context.rs
@@ -109,6 +109,10 @@ impl<'a> AssistContext<'a> {
self.trimmed_range
}
+ pub(crate) fn source_file(&self) -> &SourceFile {
+ &self.source_file
+ }
+
pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
self.source_file.syntax().token_at_offset(self.offset())
}
diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index 24b34f140b..5899ec5a00 100644
--- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -212,8 +212,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
!hidden
})
.map(|(pat, _)| {
- make::match_arm(iter::once(pat), None, make::ext::expr_todo())
- .clone_for_update()
+ make::match_arm(pat, None, make::ext::expr_todo()).clone_for_update()
});
let catch_all_arm = new_match_arm_list
@@ -243,12 +242,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
if needs_catch_all_arm && !has_catch_all_arm {
cov_mark::hit!(added_wildcard_pattern);
- let arm = make::match_arm(
- iter::once(make::wildcard_pat().into()),
- None,
- make::ext::expr_todo(),
- )
- .clone_for_update();
+ let arm =
+ make::match_arm(make::wildcard_pat().into(), None, make::ext::expr_todo())
+ .clone_for_update();
todo_placeholders.push(arm.expr().unwrap());
added_arms.push(arm);
}
diff --git a/crates/ide-assists/src/handlers/add_turbo_fish.rs b/crates/ide-assists/src/handlers/add_turbo_fish.rs
index 62700ab180..04d63f5bc8 100644
--- a/crates/ide-assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ide-assists/src/handlers/add_turbo_fish.rs
@@ -189,7 +189,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
/// This will create a turbofish generic arg list corresponding to the number of arguments
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.generic_arg_list(args, true)
}
#[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs
index f178a7e0ce..70fb568005 100644
--- a/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -252,7 +252,7 @@ fn tail_cb_impl(edit: &mut SourceChangeBuilder, e: &ast::Expr) {
/// Add bang and parentheses to the expression.
fn add_bang_paren(expr: ast::Expr) -> ast::Expr {
- make::expr_prefix(T![!], make::expr_paren(expr))
+ make::expr_prefix(T![!], make::expr_paren(expr)).into()
}
#[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 f699899066..cbd3979624 100644
--- a/crates/ide-assists/src/handlers/bool_to_enum.rs
+++ b/crates/ide-assists/src/handlers/bool_to_enum.rs
@@ -195,6 +195,7 @@ fn bool_expr_to_enum_expr(expr: ast::Expr) -> ast::Expr {
make::tail_only_block_expr(true_expr),
Some(ast::ElseBranch::Block(make::tail_only_block_expr(false_expr))),
)
+ .into()
}
}
diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
index 79f303b37a..bb04a43cf9 100644
--- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
+++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -507,7 +507,7 @@ fn wrap_capture_in_deref_if_needed(
if does_autoderef {
return capture_name;
}
- make::expr_prefix(T![*], capture_name)
+ make::expr_prefix(T![*], capture_name).into()
}
fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture) -> ast::Expr {
diff --git a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
index 67c72a93da..dd2e9cbcb5 100644
--- a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
+++ b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
@@ -97,7 +97,7 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_>
);
for r in return_exprs {
- let t = r.expr().unwrap_or_else(make::expr_unit);
+ let t = r.expr().unwrap_or_else(make::ext::expr_unit);
ted::replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update());
}
diff --git a/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/crates/ide-assists/src/handlers/convert_while_to_loop.rs
index 434daa279c..0b92beefbc 100644
--- a/crates/ide-assists/src/handlers/convert_while_to_loop.rs
+++ b/crates/ide-assists/src/handlers/convert_while_to_loop.rs
@@ -60,7 +60,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>)
.indent(while_indent_level);
let block_expr = if is_pattern_cond(while_cond.clone()) {
let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
- let stmts = iter::once(make::expr_stmt(if_expr).into());
+ let stmts = iter::once(make::expr_stmt(if_expr.into()).into());
make::block_expr(stmts, None)
} else {
let if_cond = invert_boolean_expression(while_cond);
diff --git a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
index 7df6ca1565..39142d6062 100644
--- a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
+++ b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
@@ -1128,7 +1128,10 @@ fn main {
destructure_tuple_binding_impl(acc, ctx, false)
}
- pub(crate) fn check_in_place_assist(ra_fixture_before: &str, ra_fixture_after: &str) {
+ pub(crate) fn check_in_place_assist(
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+ ) {
check_assist_by_label(
in_place_assist,
ra_fixture_before,
@@ -1138,7 +1141,10 @@ fn main {
);
}
- pub(crate) fn check_sub_pattern_assist(ra_fixture_before: &str, ra_fixture_after: &str) {
+ pub(crate) fn check_sub_pattern_assist(
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+ ) {
check_assist_by_label(
assist,
ra_fixture_before,
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index 0d1b6af720..967da41c15 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -1533,7 +1533,7 @@ impl FlowHandler {
.into(),
call_expr,
);
- make::expr_if(condition.into(), block, None)
+ make::expr_if(condition.into(), block, None).into()
}
FlowHandler::IfOption { action } => {
let path = make::ext::ident_path("Some");
@@ -1544,7 +1544,7 @@ impl FlowHandler {
let action_expr = action.make_result_handler(Some(value));
let action_stmt = make::expr_stmt(action_expr);
let then = make::block_expr(iter::once(action_stmt.into()), None);
- make::expr_if(cond.into(), then, None)
+ make::expr_if(cond.into(), then, None).into()
}
FlowHandler::MatchOption { none } => {
let some_name = "value";
@@ -1554,15 +1554,15 @@ impl FlowHandler {
let value_pat = make::ext::simple_ident_pat(make::name(some_name));
let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
let value = make::expr_path(make::ext::ident_path(some_name));
- make::match_arm(iter::once(pat.into()), None, value)
+ make::match_arm(pat.into(), None, value)
};
let none_arm = {
let path = make::ext::ident_path("None");
let pat = make::path_pat(path);
- make::match_arm(iter::once(pat), None, none.make_result_handler(None))
+ make::match_arm(pat, None, none.make_result_handler(None))
};
let arms = make::match_arm_list(vec![some_arm, none_arm]);
- make::expr_match(call_expr, arms)
+ make::expr_match(call_expr, arms).into()
}
FlowHandler::MatchResult { err } => {
let ok_name = "value";
@@ -1573,21 +1573,17 @@ impl FlowHandler {
let value_pat = make::ext::simple_ident_pat(make::name(ok_name));
let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
let value = make::expr_path(make::ext::ident_path(ok_name));
- make::match_arm(iter::once(pat.into()), None, value)
+ make::match_arm(pat.into(), None, value)
};
let err_arm = {
let path = make::ext::ident_path("Err");
let value_pat = make::ext::simple_ident_pat(make::name(err_name));
let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
let value = make::expr_path(make::ext::ident_path(err_name));
- make::match_arm(
- iter::once(pat.into()),
- None,
- err.make_result_handler(Some(value)),
- )
+ make::match_arm(pat.into(), None, err.make_result_handler(Some(value)))
};
let arms = make::match_arm_list(vec![ok_arm, err_arm]);
- make::expr_match(call_expr, arms)
+ make::expr_match(call_expr, arms).into()
}
}
}
@@ -1879,7 +1875,7 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) -
.iter()
.map(|var| path_expr_from_local(ctx, var.local, fun.mods.edition));
let expr = make::expr_tuple(exprs);
- tail_expr = Some(expr);
+ tail_expr = Some(expr.into());
}
},
};
@@ -1910,7 +1906,7 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) -
match &handler {
FlowHandler::None => block,
FlowHandler::Try { kind } => {
- let block = with_default_tail_expr(block, make::expr_unit());
+ let block = with_default_tail_expr(block, make::ext::expr_unit());
map_tail_expr(block, |tail_expr| {
let constructor = match kind {
TryKind::Option => "Some",
@@ -1924,7 +1920,7 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) -
FlowHandler::If { .. } => {
let controlflow_continue = make::expr_call(
make::expr_path(make::path_from_text("ControlFlow::Continue")),
- make::arg_list(iter::once(make::expr_unit())),
+ make::arg_list([make::ext::expr_unit()]),
);
with_tail_expr(block, controlflow_continue)
}
@@ -2127,17 +2123,17 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Op
FlowHandler::None | FlowHandler::Try { .. } => return None,
FlowHandler::If { .. } => make::expr_call(
make::expr_path(make::path_from_text("ControlFlow::Break")),
- make::arg_list(iter::once(make::expr_unit())),
+ make::arg_list([make::ext::expr_unit()]),
),
FlowHandler::IfOption { .. } => {
- let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new()));
- let args = make::arg_list(iter::once(expr));
+ let expr = arg_expr.unwrap_or_else(make::ext::expr_unit);
+ let args = make::arg_list([expr]);
make::expr_call(make::expr_path(make::ext::ident_path("Some")), args)
}
FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")),
FlowHandler::MatchResult { .. } => {
- let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new()));
- let args = make::arg_list(iter::once(expr));
+ let expr = arg_expr.unwrap_or_else(make::ext::expr_unit);
+ let args = make::arg_list([expr]);
make::expr_call(make::expr_path(make::ext::ident_path("Err")), args)
}
};
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index 0cc807aff6..97321f4ec1 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -4,6 +4,7 @@ use ide_db::{
syntax_helpers::{suggest_name, LexedStr},
};
use syntax::{
+ algo::ancestors_at_offset,
ast::{
self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory,
AstNode,
@@ -68,7 +69,10 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
let node = if ctx.has_empty_selection() {
if let Some(t) = ctx.token_at_offset().find(|it| it.kind() == T![;]) {
t.parent().and_then(ast::ExprStmt::cast)?.syntax().clone()
- } else if let Some(expr) = ctx.find_node_at_offset::<ast::Expr>() {
+ } else if let Some(expr) = ancestors_at_offset(ctx.source_file().syntax(), ctx.offset())
+ .next()
+ .and_then(ast::Expr::cast)
+ {
expr.syntax().ancestors().find_map(valid_target_expr)?.syntax().clone()
} else {
return None;
@@ -469,11 +473,11 @@ mod tests {
extract_variable,
r#"
fn main() -> i32 {
- if true {
+ if$0 true {
1
} else {
2
- }$0
+ }
}
"#,
r#"
@@ -581,11 +585,11 @@ fn main() {
extract_variable,
r#"
fn main() -> i32 {
- if true {
+ if$0 true {
1
} else {
2
- }$0
+ }
}
"#,
r#"
@@ -676,11 +680,11 @@ fn main() {
extract_variable,
r#"
fn main() -> i32 {
- if true {
+ if$0 true {
1
} else {
2
- }$0
+ }
}
"#,
r#"
diff --git a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
index c879a4a3d9..ac58af6252 100644
--- a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
+++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -933,7 +933,7 @@ mod tests_setter {
use super::*;
- fn check_not_applicable(ra_fixture: &str) {
+ fn check_not_applicable(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_assist_not_applicable(generate_setter, ra_fixture)
}
diff --git a/crates/ide-assists/src/handlers/inline_macro.rs b/crates/ide-assists/src/handlers/inline_macro.rs
index d558ec3bec..cd6f900ba1 100644
--- a/crates/ide-assists/src/handlers/inline_macro.rs
+++ b/crates/ide-assists/src/handlers/inline_macro.rs
@@ -38,21 +38,21 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?;
let macro_call = ctx.sema.to_def(&unexpanded)?;
- let expanded = ctx.sema.parse_or_expand(macro_call.as_file());
- let span_map = ctx.sema.db.expansion_span_map(macro_call.as_macro_file());
- let expanded = prettify_macro_expansion(
- ctx.db(),
- expanded,
- &span_map,
- ctx.sema.file_to_module_def(ctx.file_id())?.krate().into(),
- );
+ let target_crate_id = ctx.sema.file_to_module_def(ctx.file_id())?.krate().into();
let text_range = unexpanded.syntax().text_range();
acc.add(
AssistId("inline_macro", AssistKind::RefactorInline),
"Inline macro".to_owned(),
text_range,
- |builder| builder.replace(text_range, expanded.to_string()),
+ |builder| {
+ let expanded = ctx.sema.parse_or_expand(macro_call.as_file());
+ let span_map = ctx.sema.db.expansion_span_map(macro_call.as_macro_file());
+ // Don't call `prettify_macro_expansion()` outside the actual assist action; it does some heavy rowan tree manipulation,
+ // which can be very costly for big macros when it is done *even without the assist being invoked*.
+ let expanded = prettify_macro_expansion(ctx.db(), expanded, &span_map, target_crate_id);
+ builder.replace(text_range, expanded.to_string())
+ },
)
}
diff --git a/crates/ide-assists/src/handlers/move_guard.rs b/crates/ide-assists/src/handlers/move_guard.rs
index f0c96fe3cb..a487960d8d 100644
--- a/crates/ide-assists/src/handlers/move_guard.rs
+++ b/crates/ide-assists/src/handlers/move_guard.rs
@@ -61,7 +61,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>)
};
edit.delete(guard.syntax().text_range());
- edit.replace_ast(arm_expr, if_expr);
+ edit.replace_ast(arm_expr, if_expr.into());
},
)
}
diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs
index 94274f6d17..1f57f7d3d3 100644
--- a/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -102,7 +102,7 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
};
(range, None)
},
- _ => (macro_call.syntax().text_range(), Some(make::expr_unit())),
+ _ => (macro_call.syntax().text_range(), Some(make::ext::expr_unit())),
}
}
}
@@ -152,7 +152,7 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
exprs => {
let exprs = exprs.iter().cloned().map(replace_nested_dbgs);
let expr = make::expr_tuple(exprs);
- (macro_call.syntax().text_range(), Some(expr))
+ (macro_call.syntax().text_range(), Some(expr.into()))
}
})
}
@@ -209,7 +209,10 @@ mod tests {
use super::*;
- fn check(ra_fixture_before: &str, ra_fixture_after: &str) {
+ fn check(
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+ ) {
check_assist(
remove_dbg,
&format!("fn main() {{\n{ra_fixture_before}\n}}"),
diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
index b31d45e6d4..e324d6eaaa 100644
--- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
@@ -1,4 +1,4 @@
-use std::iter::{self, successors};
+use std::iter::successors;
use either::Either;
use ide_db::{
@@ -8,11 +8,7 @@ use ide_db::{
RootDatabase,
};
use syntax::{
- ast::{
- self,
- edit::{AstNodeEdit, IndentLevel},
- make, HasName,
- },
+ ast::{self, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory, HasName},
AstNode, TextRange, T,
};
@@ -108,53 +104,58 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite),
format!("Replace if{let_} with match"),
available_range,
- move |edit| {
+ move |builder| {
+ let make = SyntaxFactory::new();
let match_expr = {
- let else_arm = make_else_arm(ctx, else_block, &cond_bodies);
+ let else_arm = make_else_arm(ctx, &make, else_block, &cond_bodies);
let make_match_arm = |(pat, body): (_, ast::BlockExpr)| {
- let body = body.reset_indent().indent(IndentLevel(1));
+ let body = make.block_expr(body.statements(), body.tail_expr());
+ body.indent(IndentLevel::from(1));
+ let body = unwrap_trivial_block(body);
match pat {
- Either::Left(pat) => {
- make::match_arm(iter::once(pat), None, unwrap_trivial_block(body))
+ Either::Left(pat) => make.match_arm(pat, None, body),
+ Either::Right(_) if !pat_seen => {
+ make.match_arm(make.literal_pat("true").into(), None, body)
}
- Either::Right(_) if !pat_seen => make::match_arm(
- iter::once(make::literal_pat("true").into()),
- None,
- unwrap_trivial_block(body),
- ),
- Either::Right(expr) => make::match_arm(
- iter::once(make::wildcard_pat().into()),
- Some(expr),
- unwrap_trivial_block(body),
+ Either::Right(expr) => make.match_arm(
+ make.wildcard_pat().into(),
+ Some(make.match_guard(expr)),
+ body,
),
}
};
- let arms = cond_bodies.into_iter().map(make_match_arm).chain(iter::once(else_arm));
- let match_expr = make::expr_match(scrutinee_to_be_expr, make::match_arm_list(arms));
- match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
+ let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]);
+ let match_expr = make.expr_match(scrutinee_to_be_expr, make.match_arm_list(arms));
+ match_expr.indent(IndentLevel::from_node(if_expr.syntax()));
+ match_expr.into()
};
let has_preceding_if_expr =
if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind()));
let expr = if has_preceding_if_expr {
// make sure we replace the `else if let ...` with a block so we don't end up with `else expr`
- make::block_expr(None, Some(match_expr)).into()
+ make.block_expr([], Some(match_expr)).into()
} else {
match_expr
};
- edit.replace_ast::<ast::Expr>(if_expr.into(), expr);
+
+ let mut editor = builder.make_editor(if_expr.syntax());
+ editor.replace(if_expr.syntax(), expr.syntax());
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.file_id(), editor);
},
)
}
fn make_else_arm(
ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
else_block: Option<ast::BlockExpr>,
conditionals: &[(Either<ast::Pat, ast::Expr>, ast::BlockExpr)],
) -> ast::MatchArm {
let (pattern, expr) = if let Some(else_block) = else_block {
let pattern = match conditionals {
- [(Either::Right(_), _)] => make::literal_pat("false").into(),
+ [(Either::Right(_), _)] => make.literal_pat("false").into(),
[(Either::Left(pat), _)] => match ctx
.sema
.type_of_pat(pat)
@@ -164,24 +165,24 @@ fn make_else_arm(
if does_pat_match_variant(pat, &it.sad_pattern()) {
it.happy_pattern_wildcard()
} else if does_pat_variant_nested_or_literal(ctx, pat) {
- make::wildcard_pat().into()
+ make.wildcard_pat().into()
} else {
it.sad_pattern()
}
}
- None => make::wildcard_pat().into(),
+ None => make.wildcard_pat().into(),
},
- _ => make::wildcard_pat().into(),
+ _ => make.wildcard_pat().into(),
};
(pattern, unwrap_trivial_block(else_block))
} else {
let pattern = match conditionals {
- [(Either::Right(_), _)] => make::literal_pat("false").into(),
- _ => make::wildcard_pat().into(),
+ [(Either::Right(_), _)] => make.literal_pat("false").into(),
+ _ => make.wildcard_pat().into(),
};
- (pattern, make::expr_unit())
+ (pattern, make.expr_unit())
};
- make::match_arm(iter::once(pattern), None, expr)
+ make.match_arm(pattern, None, expr)
}
// Assist: replace_match_with_if_let
@@ -247,21 +248,21 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
}
_ => " let",
};
- let target = match_expr.syntax().text_range();
acc.add(
AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite),
format!("Replace match with if{let_}"),
- target,
- move |edit| {
- fn make_block_expr(expr: ast::Expr) -> ast::BlockExpr {
+ match_expr.syntax().text_range(),
+ move |builder| {
+ let make = SyntaxFactory::new();
+ let make_block_expr = |expr: ast::Expr| {
// Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are
// formatted without enclosing braces. If we encounter such block exprs,
// wrap them in another BlockExpr.
match expr {
ast::Expr::BlockExpr(block) if block.modifier().is_none() => block,
- expr => make::block_expr(iter::empty(), Some(expr)),
+ expr => make.block_expr([], Some(expr)),
}
- }
+ };
let condition = match if_let_pat {
ast::Pat::LiteralPat(p)
@@ -272,20 +273,25 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
ast::Pat::LiteralPat(p)
if p.literal().is_some_and(|it| it.token().kind() == T![false]) =>
{
- make::expr_prefix(T![!], scrutinee)
+ make.expr_prefix(T![!], scrutinee).into()
}
- _ => make::expr_let(if_let_pat, scrutinee).into(),
+ _ => make.expr_let(if_let_pat, scrutinee).into(),
};
- let then_block = make_block_expr(then_expr.reset_indent());
+ let then_expr = then_expr.clone_for_update();
+ then_expr.reindent_to(IndentLevel::single());
+ let then_block = make_block_expr(then_expr);
let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
- let if_let_expr = make::expr_if(
+ let if_let_expr = make.expr_if(
condition,
then_block,
else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
- )
- .indent(IndentLevel::from_node(match_expr.syntax()));
+ );
+ if_let_expr.indent(IndentLevel::from_node(match_expr.syntax()));
- edit.replace_ast::<ast::Expr>(match_expr.into(), if_let_expr);
+ let mut editor = builder.make_editor(match_expr.syntax());
+ editor.replace(match_expr.syntax(), if_let_expr.syntax());
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.file_id(), editor);
},
)
}
diff --git a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
index c2be4593b9..c071d3022d 100644
--- a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
@@ -1,12 +1,6 @@
-use std::iter::once;
-
use ide_db::ty_filter::TryEnum;
use syntax::{
- ast::{
- self,
- edit::{AstNodeEdit, IndentLevel},
- make,
- },
+ ast::{self, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory},
AstNode, T,
};
@@ -47,7 +41,9 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>
AssistId("replace_let_with_if_let", AssistKind::RefactorRewrite),
"Replace let with if let",
target,
- |edit| {
+ |builder| {
+ let mut editor = builder.make_editor(let_stmt.syntax());
+ let make = SyntaxFactory::new();
let ty = ctx.sema.type_of_expr(&init);
let happy_variant = ty
.and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))
@@ -55,17 +51,18 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>
let pat = match happy_variant {
None => original_pat,
Some(var_name) => {
- make::tuple_struct_pat(make::ext::ident_path(var_name), once(original_pat))
- .into()
+ make.tuple_struct_pat(make.ident_path(var_name), [original_pat]).into()
}
};
- let block =
- make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax()));
- let if_ = make::expr_if(make::expr_let(pat, init).into(), block, None);
- let stmt = make::expr_stmt(if_);
+ let block = make.block_expr([], None);
+ block.indent(IndentLevel::from_node(let_stmt.syntax()));
+ let if_expr = make.expr_if(make.expr_let(pat, init).into(), block, None);
+ let if_stmt = make.expr_stmt(if_expr.into());
- edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
+ editor.replace(let_stmt.syntax(), if_stmt.syntax());
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.file_id(), editor);
},
)
}
diff --git a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
index 2e26f59d03..88b50543dd 100644
--- a/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
+++ b/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
@@ -71,19 +71,17 @@ pub(crate) fn replace_try_expr_with_match(
};
let happy_arm = make::match_arm(
- iter::once(
- try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()),
- ),
+ try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()),
None,
make::expr_path(make::ext::ident_path("it")),
);
- let sad_arm = make::match_arm(iter::once(sad_pat), None, sad_expr);
+ let sad_arm = make::match_arm(sad_pat, None, sad_expr);
let match_arm_list = make::match_arm_list([happy_arm, sad_arm]);
let expr_match = make::expr_match(expr, match_arm_list)
.indent(IndentLevel::from_node(qm_kw_parent.syntax()));
- edit.replace_ast::<ast::Expr>(qm_kw_parent.into(), expr_match);
+ edit.replace_ast::<ast::Expr>(qm_kw_parent.into(), expr_match.into());
},
)
}
diff --git a/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
index c6cffb5434..6b9f661d4d 100644
--- a/crates/ide-assists/src/handlers/unmerge_match_arm.rs
+++ b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
@@ -54,13 +54,9 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
let pats_after = pipe_token
.siblings_with_tokens(Direction::Next)
.filter_map(|it| ast::Pat::cast(it.into_node()?));
- // FIXME: We should add a leading pipe if the original arm has one.
- let new_match_arm = make::match_arm(
- pats_after,
- match_arm.guard().and_then(|guard| guard.condition()),
- match_arm_body,
- )
- .clone_for_update();
+ let new_pat = make::or_pat(pats_after, or_pat.leading_pipe().is_some());
+ let new_match_arm =
+ make::match_arm(new_pat, match_arm.guard(), match_arm_body).clone_for_update();
let mut pipe_index = pipe_token.index();
if pipe_token
diff --git a/crates/ide-assists/src/handlers/unwrap_block.rs b/crates/ide-assists/src/handlers/unwrap_block.rs
index f3e7f5f416..fd37140e9c 100644
--- a/crates/ide-assists/src/handlers/unwrap_block.rs
+++ b/crates/ide-assists/src/handlers/unwrap_block.rs
@@ -61,7 +61,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
}
}
None => {
- let empty_tuple = make::expr_tuple([]);
+ let empty_tuple = make::ext::expr_unit();
make::let_stmt(pattern, ty, Some(empty_tuple)).to_string()
}
};
diff --git a/crates/ide-assists/src/handlers/unwrap_return_type.rs b/crates/ide-assists/src/handlers/unwrap_return_type.rs
index 64d5e2c9b8..f647b531b7 100644
--- a/crates/ide-assists/src/handlers/unwrap_return_type.rs
+++ b/crates/ide-assists/src/handlers/unwrap_return_type.rs
@@ -1,11 +1,11 @@
+use either::Either;
use ide_db::{
famous_defs::FamousDefs,
syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
};
-use itertools::Itertools;
use syntax::{
- ast::{self, Expr, HasGenericArgs},
- match_ast, AstNode, NodeOrToken, SyntaxKind, TextRange,
+ ast::{self, syntax_factory::SyntaxFactory, HasArgList, HasGenericArgs},
+ match_ast, AstNode, NodeOrToken, SyntaxKind,
};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -39,11 +39,11 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
let parent = ret_type.syntax().parent()?;
- let body = match_ast! {
+ let body_expr = match_ast! {
match parent {
- ast::Fn(func) => func.body()?,
+ ast::Fn(func) => func.body()?.into(),
ast::ClosureExpr(closure) => match closure.body()? {
- Expr::BlockExpr(block) => block,
+ ast::Expr::BlockExpr(block) => block.into(),
// closures require a block when a return type is specified
_ => return None,
},
@@ -65,72 +65,110 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let happy_type = extract_wrapped_type(type_ref)?;
acc.add(kind.assist_id(), kind.label(), type_ref.syntax().text_range(), |builder| {
- let body = ast::Expr::BlockExpr(body);
+ let mut editor = builder.make_editor(&parent);
+ let make = SyntaxFactory::new();
let mut exprs_to_unwrap = Vec::new();
let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_unwrap, e);
- walk_expr(&body, &mut |expr| {
- if let Expr::ReturnExpr(ret_expr) = expr {
+ walk_expr(&body_expr, &mut |expr| {
+ if let ast::Expr::ReturnExpr(ret_expr) = expr {
if let Some(ret_expr_arg) = &ret_expr.expr() {
for_each_tail_expr(ret_expr_arg, tail_cb);
}
}
});
- for_each_tail_expr(&body, tail_cb);
+ for_each_tail_expr(&body_expr, tail_cb);
let is_unit_type = is_unit_type(&happy_type);
if is_unit_type {
- let mut text_range = ret_type.syntax().text_range();
-
if let Some(NodeOrToken::Token(token)) = ret_type.syntax().next_sibling_or_token() {
if token.kind() == SyntaxKind::WHITESPACE {
- text_range = TextRange::new(text_range.start(), token.text_range().end());
+ editor.delete(token);
}
}
- builder.delete(text_range);
+ editor.delete(ret_type.syntax());
} else {
- builder.replace(type_ref.syntax().text_range(), happy_type.syntax().text());
+ editor.replace(type_ref.syntax(), happy_type.syntax());
}
- for ret_expr_arg in exprs_to_unwrap {
- let ret_expr_str = ret_expr_arg.to_string();
-
- let needs_replacing = match kind {
- UnwrapperKind::Option => ret_expr_str.starts_with("Some("),
- UnwrapperKind::Result => {
- ret_expr_str.starts_with("Ok(") || ret_expr_str.starts_with("Err(")
- }
- };
+ let mut final_placeholder = None;
+ for tail_expr in exprs_to_unwrap {
+ match &tail_expr {
+ ast::Expr::CallExpr(call_expr) => {
+ let ast::Expr::PathExpr(path_expr) = call_expr.expr().unwrap() else {
+ continue;
+ };
+
+ let path_str = path_expr.path().unwrap().to_string();
+ let needs_replacing = match kind {
+ UnwrapperKind::Option => path_str == "Some",
+ UnwrapperKind::Result => path_str == "Ok" || path_str == "Err",
+ };
+
+ if !needs_replacing {
+ continue;
+ }
- if needs_replacing {
- let arg_list = ret_expr_arg.syntax().children().find_map(ast::ArgList::cast);
- if let Some(arg_list) = arg_list {
+ let arg_list = call_expr.arg_list().unwrap();
if is_unit_type {
- match ret_expr_arg.syntax().prev_sibling_or_token() {
- // Useful to delete the entire line without leaving trailing whitespaces
- Some(whitespace) => {
- let new_range = TextRange::new(
- whitespace.text_range().start(),
- ret_expr_arg.syntax().text_range().end(),
- );
- builder.delete(new_range);
+ let tail_parent = tail_expr
+ .syntax()
+ .parent()
+ .and_then(Either::<ast::ReturnExpr, ast::StmtList>::cast)
+ .unwrap();
+ match tail_parent {
+ Either::Left(ret_expr) => {
+ editor.replace(ret_expr.syntax(), make.expr_return(None).syntax())
}
- None => {
- builder.delete(ret_expr_arg.syntax().text_range());
+ 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(),
+ new_block.stmt_list().unwrap().syntax(),
+ );
}
}
- } else {
- builder.replace(
- ret_expr_arg.syntax().text_range(),
- arg_list.args().join(", "),
+ } else if let Some(first_arg) = arg_list.args().next() {
+ editor.replace(tail_expr.syntax(), first_arg.syntax());
+ }
+ }
+ ast::Expr::PathExpr(path_expr) => {
+ let UnwrapperKind::Option = kind else {
+ continue;
+ };
+
+ if path_expr.path().unwrap().to_string() != "None" {
+ continue;
+ }
+
+ let new_tail_expr = make.expr_unit();
+ editor.replace(path_expr.syntax(), new_tail_expr.syntax());
+ if let Some(cap) = ctx.config.snippet_cap {
+ editor.add_annotation(
+ new_tail_expr.syntax(),
+ builder.make_placeholder_snippet(cap),
);
+
+ final_placeholder = Some(new_tail_expr);
}
}
- } else if matches!(kind, UnwrapperKind::Option if ret_expr_str == "None") {
- builder.replace(ret_expr_arg.syntax().text_range(), "()");
+ _ => (),
}
}
+
+ if let Some(cap) = ctx.config.snippet_cap {
+ if let Some(final_placeholder) = final_placeholder {
+ editor.add_annotation(final_placeholder.syntax(), builder.make_tabstop_after(cap));
+ }
+ }
+
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.file_id(), editor);
})
}
@@ -168,12 +206,12 @@ impl UnwrapperKind {
fn tail_cb_impl(acc: &mut Vec<ast::Expr>, e: &ast::Expr) {
match e {
- Expr::BreakExpr(break_expr) => {
+ ast::Expr::BreakExpr(break_expr) => {
if let Some(break_expr_arg) = break_expr.expr() {
for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e))
}
}
- Expr::ReturnExpr(_) => {
+ ast::Expr::ReturnExpr(_) => {
// all return expressions have already been handled by the walk loop
}
e => acc.push(e.clone()),
@@ -238,8 +276,7 @@ fn foo() -> Option<()$0> {
}
"#,
r#"
-fn foo() {
-}
+fn foo() {}
"#,
"Unwrap Option return type",
);
@@ -254,8 +291,7 @@ fn foo() -> Option<()$0>{
}
"#,
r#"
-fn foo() {
-}
+fn foo() {}
"#,
"Unwrap Option return type",
);
@@ -280,7 +316,42 @@ fn foo() -> i32 {
if true {
42
} else {
- ()
+ ${1:()}$0
+ }
+}
+"#,
+ "Unwrap Option return type",
+ );
+ }
+
+ #[test]
+ fn unwrap_option_return_type_multi_none() {
+ check_assist_by_label(
+ unwrap_return_type,
+ r#"
+//- minicore: option
+fn foo() -> Option<i3$02> {
+ if false {
+ return None;
+ }
+
+ if true {
+ Some(42)
+ } else {
+ None
+ }
+}
+"#,
+ r#"
+fn foo() -> i32 {
+ if false {
+ return ${1:()};
+ }
+
+ if true {
+ 42
+ } else {
+ ${2:()}$0
}
}
"#,
@@ -1262,8 +1333,7 @@ fn foo() -> Result<(), Box<dyn Error$0>> {
}
"#,
r#"
-fn foo() {
-}
+fn foo() {}
"#,
"Unwrap Result return type",
);
@@ -1278,8 +1348,7 @@ fn foo() -> Result<(), Box<dyn Error$0>>{
}
"#,
r#"
-fn foo() {
-}
+fn foo() {}
"#,
"Unwrap Result return type",
);
diff --git a/crates/ide-assists/src/handlers/wrap_return_type.rs b/crates/ide-assists/src/handlers/wrap_return_type.rs
index 658600cd2d..0b145dcb06 100644
--- a/crates/ide-assists/src/handlers/wrap_return_type.rs
+++ b/crates/ide-assists/src/handlers/wrap_return_type.rs
@@ -6,10 +6,9 @@ use ide_db::{
famous_defs::FamousDefs,
syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
};
-use itertools::Itertools;
use syntax::{
- ast::{self, make, Expr, HasGenericParams},
- match_ast, ted, AstNode, ToSmolStr,
+ ast::{self, syntax_factory::SyntaxFactory, Expr, HasGenericArgs, HasGenericParams},
+ match_ast, AstNode,
};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -43,11 +42,11 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
let parent = ret_type.syntax().parent()?;
- let body = match_ast! {
+ let body_expr = match_ast! {
match parent {
- ast::Fn(func) => func.body()?,
+ ast::Fn(func) => func.body()?.into(),
ast::ClosureExpr(closure) => match closure.body()? {
- Expr::BlockExpr(block) => block,
+ Expr::BlockExpr(block) => block.into(),
// closures require a block when a return type is specified
_ => return None,
},
@@ -75,56 +74,65 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
kind.assist_id(),
kind.label(),
type_ref.syntax().text_range(),
- |edit| {
- let alias = wrapper_alias(ctx, &core_wrapper, type_ref, kind.symbol());
- let new_return_ty =
- alias.unwrap_or_else(|| kind.wrap_type(type_ref)).clone_for_update();
-
- let body = edit.make_mut(ast::Expr::BlockExpr(body.clone()));
+ |builder| {
+ let mut editor = builder.make_editor(&parent);
+ let make = SyntaxFactory::new();
+ let alias = wrapper_alias(ctx, &make, &core_wrapper, type_ref, kind.symbol());
+ let new_return_ty = alias.unwrap_or_else(|| match kind {
+ WrapperKind::Option => make.ty_option(type_ref.clone()),
+ WrapperKind::Result => make.ty_result(type_ref.clone(), make.ty_infer().into()),
+ });
let mut exprs_to_wrap = Vec::new();
let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e);
- walk_expr(&body, &mut |expr| {
+ walk_expr(&body_expr, &mut |expr| {
if let Expr::ReturnExpr(ret_expr) = expr {
if let Some(ret_expr_arg) = &ret_expr.expr() {
for_each_tail_expr(ret_expr_arg, tail_cb);
}
}
});
- for_each_tail_expr(&body, tail_cb);
+ for_each_tail_expr(&body_expr, tail_cb);
for ret_expr_arg in exprs_to_wrap {
- let happy_wrapped = make::expr_call(
- make::expr_path(make::ext::ident_path(kind.happy_ident())),
- make::arg_list(iter::once(ret_expr_arg.clone())),
- )
- .clone_for_update();
- ted::replace(ret_expr_arg.syntax(), happy_wrapped.syntax());
+ let happy_wrapped = make.expr_call(
+ make.expr_path(make.ident_path(kind.happy_ident())),
+ make.arg_list(iter::once(ret_expr_arg.clone())),
+ );
+ editor.replace(ret_expr_arg.syntax(), happy_wrapped.syntax());
}
- let old_return_ty = edit.make_mut(type_ref.clone());
- ted::replace(old_return_ty.syntax(), new_return_ty.syntax());
+ editor.replace(type_ref.syntax(), new_return_ty.syntax());
if let WrapperKind::Result = kind {
// Add a placeholder snippet at the first generic argument that doesn't equal the return type.
// This is normally the error type, but that may not be the case when we inserted a type alias.
- let args =
- new_return_ty.syntax().descendants().find_map(ast::GenericArgList::cast);
- let error_type_arg = args.and_then(|list| {
- list.generic_args().find(|arg| match arg {
- ast::GenericArg::TypeArg(_) => {
- arg.syntax().text() != type_ref.syntax().text()
- }
- ast::GenericArg::LifetimeArg(_) => false,
- _ => true,
- })
+ let args = new_return_ty
+ .path()
+ .unwrap()
+ .segment()
+ .unwrap()
+ .generic_arg_list()
+ .unwrap();
+ let error_type_arg = args.generic_args().find(|arg| match arg {
+ ast::GenericArg::TypeArg(_) => {
+ arg.syntax().text() != type_ref.syntax().text()
+ }
+ ast::GenericArg::LifetimeArg(_) => false,
+ _ => true,
});
if let Some(error_type_arg) = error_type_arg {
if let Some(cap) = ctx.config.snippet_cap {
- edit.add_placeholder_snippet(cap, error_type_arg);
+ editor.add_annotation(
+ error_type_arg.syntax(),
+ builder.make_placeholder_snippet(cap),
+ );
}
}
}
+
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.file_id(), editor);
},
);
}
@@ -176,22 +184,16 @@ impl WrapperKind {
WrapperKind::Result => hir::sym::Result.clone(),
}
}
-
- fn wrap_type(&self, type_ref: &ast::Type) -> ast::Type {
- match self {
- WrapperKind::Option => make::ext::ty_option(type_ref.clone()),
- WrapperKind::Result => make::ext::ty_result(type_ref.clone(), make::ty_placeholder()),
- }
- }
}
// Try to find an wrapper type alias in the current scope (shadowing the default).
fn wrapper_alias(
ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
core_wrapper: &hir::Enum,
ret_type: &ast::Type,
wrapper: hir::Symbol,
-) -> Option<ast::Type> {
+) -> Option<ast::PathType> {
let wrapper_path = hir::ModPath::from_segments(
hir::PathKind::Plain,
iter::once(hir::Name::new_symbol_root(wrapper)),
@@ -207,25 +209,28 @@ fn wrapper_alias(
})
.find_map(|alias| {
let mut inserted_ret_type = false;
- let generic_params = alias
- .source(ctx.db())?
- .value
- .generic_param_list()?
- .generic_params()
- .map(|param| match param {
- // Replace the very first type parameter with the functions return type.
- ast::GenericParam::TypeParam(_) if !inserted_ret_type => {
- inserted_ret_type = true;
- ret_type.to_smolstr()
+ let generic_args =
+ alias.source(ctx.db())?.value.generic_param_list()?.generic_params().map(|param| {
+ match param {
+ // Replace the very first type parameter with the function's return type.
+ ast::GenericParam::TypeParam(_) if !inserted_ret_type => {
+ inserted_ret_type = true;
+ make.type_arg(ret_type.clone()).into()
+ }
+ ast::GenericParam::LifetimeParam(_) => {
+ make.lifetime_arg(make.lifetime("'_")).into()
+ }
+ _ => make.type_arg(make.ty_infer().into()).into(),
}
- ast::GenericParam::LifetimeParam(_) => make::lifetime("'_").to_smolstr(),
- _ => make::ty_placeholder().to_smolstr(),
- })
- .join(", ");
+ });
let name = alias.name(ctx.db());
- let name = name.as_str();
- Some(make::ty(&format!("{name}<{generic_params}>")))
+ let generic_arg_list = make.generic_arg_list(generic_args, false);
+ let path = make.path_unqualified(
+ make.path_segment_generics(make.name_ref(name.as_str()), generic_arg_list),
+ );
+
+ Some(make.ty_path(path))
})
})
}
diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs
index 0b1ff87c5c..48d2af6d3f 100644
--- a/crates/ide-assists/src/tests.rs
+++ b/crates/ide-assists/src/tests.rs
@@ -77,7 +77,11 @@ pub(crate) fn with_single_file(text: &str) -> (RootDatabase, EditionedFileId) {
}
#[track_caller]
-pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) {
+pub(crate) fn check_assist(
+ assist: Handler,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
let ra_fixture_after = trim_indent(ra_fixture_after);
check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after), None);
}
@@ -85,8 +89,8 @@ pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_
#[track_caller]
pub(crate) fn check_assist_no_snippet_cap(
assist: Handler,
- ra_fixture_before: &str,
- ra_fixture_after: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
) {
let ra_fixture_after = trim_indent(ra_fixture_after);
check_with_config(
@@ -101,8 +105,8 @@ pub(crate) fn check_assist_no_snippet_cap(
#[track_caller]
pub(crate) fn check_assist_import_one(
assist: Handler,
- ra_fixture_before: &str,
- ra_fixture_after: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
) {
let ra_fixture_after = trim_indent(ra_fixture_after);
check_with_config(
@@ -118,8 +122,8 @@ pub(crate) fn check_assist_import_one(
// so this is here to allow you choose.
pub(crate) fn check_assist_by_label(
assist: Handler,
- ra_fixture_before: &str,
- ra_fixture_after: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
label: &str,
) {
let ra_fixture_after = trim_indent(ra_fixture_after);
@@ -130,22 +134,36 @@ pub(crate) fn check_assist_by_label(
// `extract_ranges` and mark the target as `<target> </target>` in the
// fixture?
#[track_caller]
-pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) {
+pub(crate) fn check_assist_target(
+ assist: Handler,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ target: &str,
+) {
check(assist, ra_fixture, ExpectedResult::Target(target), None);
}
#[track_caller]
-pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) {
+pub(crate) fn check_assist_not_applicable(
+ assist: Handler,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) {
check(assist, ra_fixture, ExpectedResult::NotApplicable, None);
}
#[track_caller]
-pub(crate) fn check_assist_not_applicable_by_label(assist: Handler, ra_fixture: &str, label: &str) {
+pub(crate) fn check_assist_not_applicable_by_label(
+ assist: Handler,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ label: &str,
+) {
check(assist, ra_fixture, ExpectedResult::NotApplicable, Some(label));
}
#[track_caller]
-pub(crate) fn check_assist_not_applicable_for_import_one(assist: Handler, ra_fixture: &str) {
+pub(crate) fn check_assist_not_applicable_for_import_one(
+ assist: Handler,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) {
check_with_config(
TEST_CONFIG_IMPORT_ONE,
assist,
@@ -157,7 +175,10 @@ pub(crate) fn check_assist_not_applicable_for_import_one(assist: Handler, ra_fix
/// Check assist in unresolved state. Useful to check assists for lazy computation.
#[track_caller]
-pub(crate) fn check_assist_unresolved(assist: Handler, ra_fixture: &str) {
+pub(crate) fn check_assist_unresolved(
+ assist: Handler,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) {
check(assist, ra_fixture, ExpectedResult::Unresolved, None);
}
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index e20c4ef09e..78ff441791 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -246,7 +246,7 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize {
}
pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
- invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr))
+ invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into())
}
fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
@@ -262,7 +262,7 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
T![>] => T![<=],
T![>=] => T![<],
// Parenthesize other expressions before prefixing `!`
- _ => return Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))),
+ _ => return Some(make::expr_prefix(T![!], make::expr_paren(expr.clone())).into()),
};
ted::replace(op_token, make::token(rev_token));
Some(bin.into())
diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs
index 75caf6d49f..7a9bdfe1ec 100644
--- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs
+++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs
@@ -66,7 +66,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
let pat = make::record_pat(variant_name.clone(), pats.into_iter());
let fields = make::record_expr_field_list(fields);
let record_expr = make::record_expr(variant_name, fields).into();
- arms.push(make::match_arm(Some(pat.into()), None, record_expr));
+ arms.push(make::match_arm(pat.into(), None, record_expr));
}
// => match self { Self::Name(arg1) => Self::Name(arg1.clone()) }
@@ -84,21 +84,21 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter());
let struct_name = make::expr_path(variant_name);
let tuple_expr = make::expr_call(struct_name, make::arg_list(fields));
- arms.push(make::match_arm(Some(pat.into()), None, tuple_expr));
+ arms.push(make::match_arm(pat.into(), None, tuple_expr));
}
// => match self { Self::Name => Self::Name }
None => {
let pattern = make::path_pat(variant_name.clone());
let variant_expr = make::expr_path(variant_name);
- arms.push(make::match_arm(Some(pattern), None, variant_expr));
+ arms.push(make::match_arm(pattern, None, variant_expr));
}
}
}
let match_target = make::expr_path(make::ext::ident_path("self"));
let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
- make::expr_match(match_target, list)
+ make::expr_match(match_target, list).into()
}
ast::Adt::Struct(strukt) => {
match strukt.field_list() {
@@ -190,7 +190,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
// => MyStruct { fields.. } => f.debug_struct("MyStruct")...finish(),
let pat = make::record_pat(variant_name.clone(), pats.into_iter());
- arms.push(make::match_arm(Some(pat.into()), None, expr));
+ arms.push(make::match_arm(pat.into(), None, expr));
}
Some(ast::FieldList::TupleFieldList(list)) => {
// => f.debug_tuple(name)
@@ -223,7 +223,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
// => MyStruct (fields..) => f.debug_tuple("MyStruct")...finish(),
let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter());
- arms.push(make::match_arm(Some(pat.into()), None, expr));
+ arms.push(make::match_arm(pat.into(), None, expr));
}
None => {
let fmt_string = make::expr_literal(&(format!("\"{name}\""))).into();
@@ -232,7 +232,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
let macro_call = make::expr_macro_call(macro_name, args);
let variant_name = make::path_pat(variant_name);
- arms.push(make::match_arm(Some(variant_name), None, macro_call));
+ arms.push(make::match_arm(variant_name, None, macro_call));
}
}
}
@@ -241,7 +241,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> {
let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
let match_expr = make::expr_match(match_target, list);
- let body = make::block_expr(None, Some(match_expr));
+ let body = make::block_expr(None, Some(match_expr.into()));
let body = body.indent(ast::edit::IndentLevel(1));
ted::replace(func.body()?.syntax(), body.clone_for_update().syntax());
Some(())
@@ -485,7 +485,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>) -
let tuple = make::tuple_pat(vec![left.into(), right.into()]);
if let Some(expr) = expr {
- arms.push(make::match_arm(Some(tuple.into()), None, expr));
+ arms.push(make::match_arm(tuple.into(), None, expr));
}
}
@@ -518,7 +518,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>) -
let tuple = make::tuple_pat(vec![left.into(), right.into()]);
if let Some(expr) = expr {
- arms.push(make::match_arm(Some(tuple.into()), None, expr));
+ arms.push(make::match_arm(tuple.into(), None, expr));
}
}
None => continue,
@@ -538,12 +538,12 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>) -
} else {
eq_check
};
- arms.push(make::match_arm(Some(lhs), None, rhs));
+ arms.push(make::match_arm(lhs, None, rhs));
}
- let match_target = make::expr_tuple(vec![lhs_name, rhs_name]);
+ let match_target = make::expr_tuple([lhs_name, rhs_name]).into();
let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
- make::expr_match(match_target, list)
+ make::expr_match(match_target, list).into()
}
};
@@ -599,15 +599,15 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option<TraitRef>)
let variant_name =
make::path_pat(make::ext::path_from_idents(["core", "cmp", "Ordering", "Equal"])?);
let lhs = make::tuple_struct_pat(make::ext::path_from_idents(["Some"])?, [variant_name]);
- arms.push(make::match_arm(Some(lhs.into()), None, make::expr_empty_block()));
+ arms.push(make::match_arm(lhs.into(), None, make::expr_empty_block().into()));
arms.push(make::match_arm(
- [make::ident_pat(false, false, make::name("ord")).into()],
+ make::ident_pat(false, false, make::name("ord")).into(),
None,
make::expr_return(Some(make::expr_path(make::ext::ident_path("ord")))),
));
let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
- Some(make::expr_stmt(make::expr_match(match_target, list)).into())
+ Some(make::expr_stmt(make::expr_match(match_target, list).into()).into())
}
fn gen_partial_cmp_call(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
diff --git a/crates/ide-assists/src/utils/ref_field_expr.rs b/crates/ide-assists/src/utils/ref_field_expr.rs
index e95b291dd7..d434872ea5 100644
--- a/crates/ide-assists/src/utils/ref_field_expr.rs
+++ b/crates/ide-assists/src/utils/ref_field_expr.rs
@@ -121,7 +121,7 @@ impl RefData {
/// Derefs `expr` and wraps it in parens if necessary
pub(crate) fn wrap_expr(&self, mut expr: ast::Expr) -> ast::Expr {
if self.needs_deref {
- expr = make::expr_prefix(T![*], expr);
+ expr = make::expr_prefix(T![*], expr).into();
}
if self.needs_parentheses {
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 414627fbab..40669c65c5 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -329,7 +329,7 @@ impl Completions {
ctx: &CompletionContext<'_>,
dot_access: &DotAccess,
func: hir::Function,
- receiver: Option<hir::Name>,
+ receiver: Option<SmolStr>,
local_name: Option<hir::Name>,
) {
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
@@ -475,7 +475,7 @@ impl Completions {
&mut self,
ctx: &CompletionContext<'_>,
dot_access: &DotAccess,
- receiver: Option<hir::Name>,
+ receiver: Option<SmolStr>,
field: hir::Field,
ty: &hir::Type,
) {
@@ -533,7 +533,7 @@ impl Completions {
pub(crate) fn add_tuple_field(
&mut self,
ctx: &CompletionContext<'_>,
- receiver: Option<hir::Name>,
+ receiver: Option<SmolStr>,
field: usize,
ty: &hir::Type,
) {
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 26074672ba..7679d9076d 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -2,7 +2,7 @@
use std::ops::ControlFlow;
-use hir::{sym, HasContainer, ItemContainer, MethodCandidateCallback, Name};
+use hir::{HasContainer, ItemContainer, MethodCandidateCallback, Name};
use ide_db::FxHashSet;
use syntax::SmolStr;
@@ -25,21 +25,49 @@ pub(crate) fn complete_dot(
_ => return,
};
+ let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
+ let is_method_access_with_parens =
+ matches!(dot_access.kind, DotAccessKind::Method { has_parens: true });
+ let traits_in_scope = ctx.traits_in_scope();
+
// Suggest .await syntax for types that implement Future trait
- if receiver_ty.impls_into_future(ctx.db) {
+ if let Some(future_output) = receiver_ty.into_future_output(ctx.db) {
+ let await_str = SmolStr::new_static("await");
let mut item = CompletionItem::new(
CompletionItemKind::Keyword,
ctx.source_range(),
- SmolStr::new_static("await"),
+ await_str.clone(),
ctx.edition,
);
item.detail("expr.await");
item.add_to(acc, ctx.db);
- }
- let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. });
- let is_method_access_with_parens =
- matches!(dot_access.kind, DotAccessKind::Method { has_parens: true });
+ // Completions that skip `.await`, e.g. `.await.foo()`.
+ let dot_access_kind = match &dot_access.kind {
+ DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
+ DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
+ }
+ it @ DotAccessKind::Method { .. } => *it,
+ };
+ let dot_access = DotAccess {
+ receiver: dot_access.receiver.clone(),
+ receiver_ty: Some(hir::TypeInfo { original: future_output.clone(), adjusted: None }),
+ kind: dot_access_kind,
+ ctx: dot_access.ctx,
+ };
+ complete_fields(
+ acc,
+ ctx,
+ &future_output,
+ |acc, field, ty| acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty),
+ |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty),
+ is_field_access,
+ is_method_access_with_parens,
+ );
+ complete_methods(ctx, &future_output, &traits_in_scope, |func| {
+ acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None)
+ });
+ }
complete_fields(
acc,
@@ -50,8 +78,44 @@ pub(crate) fn complete_dot(
is_field_access,
is_method_access_with_parens,
);
+ complete_methods(ctx, receiver_ty, &traits_in_scope, |func| {
+ acc.add_method(ctx, dot_access, func, None, None)
+ });
- complete_methods(ctx, receiver_ty, |func| acc.add_method(ctx, dot_access, func, None, None));
+ // FIXME:
+ // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute
+ // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`.
+ // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid
+ let iter = receiver_ty
+ .strip_references()
+ .add_reference(hir::Mutability::Shared)
+ .into_iterator_iter(ctx.db)
+ .map(|ty| (ty, SmolStr::new_static("iter()")));
+ // Does <receiver_ty as IntoIterator>::IntoIter` exist?
+ let into_iter = || {
+ receiver_ty
+ .clone()
+ .into_iterator_iter(ctx.db)
+ .map(|ty| (ty, SmolStr::new_static("into_iter()")))
+ };
+ if let Some((iter, iter_sym)) = iter.or_else(into_iter) {
+ // Skip iterators, e.g. complete `.iter().filter_map()`.
+ let dot_access_kind = match &dot_access.kind {
+ DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
+ DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
+ }
+ it @ DotAccessKind::Method { .. } => *it,
+ };
+ let dot_access = DotAccess {
+ receiver: dot_access.receiver.clone(),
+ receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }),
+ kind: dot_access_kind,
+ ctx: dot_access.ctx,
+ };
+ complete_methods(ctx, &iter, &traits_in_scope, |func| {
+ acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None)
+ });
+ }
}
pub(crate) fn complete_undotted_self(
@@ -94,18 +158,16 @@ pub(crate) fn complete_undotted_self(
in_breakable: expr_ctx.in_breakable,
},
},
- Some(Name::new_symbol_root(sym::self_.clone())),
+ Some(SmolStr::new_static("self")),
field,
&ty,
)
},
- |acc, field, ty| {
- acc.add_tuple_field(ctx, Some(Name::new_symbol_root(sym::self_.clone())), field, &ty)
- },
+ |acc, field, ty| acc.add_tuple_field(ctx, Some(SmolStr::new_static("self")), field, &ty),
true,
false,
);
- complete_methods(ctx, &ty, |func| {
+ complete_methods(ctx, &ty, &ctx.traits_in_scope(), |func| {
acc.add_method(
ctx,
&DotAccess {
@@ -118,7 +180,7 @@ pub(crate) fn complete_undotted_self(
},
},
func,
- Some(Name::new_symbol_root(sym::self_.clone())),
+ Some(SmolStr::new_static("self")),
None,
)
});
@@ -160,6 +222,7 @@ fn complete_fields(
fn complete_methods(
ctx: &CompletionContext<'_>,
receiver: &hir::Type,
+ traits_in_scope: &FxHashSet<hir::TraitId>,
f: impl FnMut(hir::Function),
) {
struct Callback<'a, F> {
@@ -205,7 +268,7 @@ fn complete_methods(
receiver.iterate_method_candidates_split_inherent(
ctx.db,
&ctx.scope,
- &ctx.traits_in_scope(),
+ traits_in_scope,
Some(ctx.module),
None,
Callback { ctx, f, seen_methods: FxHashSet::default() },
@@ -214,25 +277,13 @@ fn complete_methods(
#[cfg(test)]
mod tests {
- use expect_test::{expect, Expect};
+ use expect_test::expect;
- use crate::tests::{
- check_edit, completion_list_no_kw, completion_list_no_kw_with_private_editable,
- };
-
- fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list_no_kw(ra_fixture);
- expect.assert_eq(&actual);
- }
-
- fn check_with_private_editable(ra_fixture: &str, expect: Expect) {
- let actual = completion_list_no_kw_with_private_editable(ra_fixture);
- expect.assert_eq(&actual);
- }
+ use crate::tests::{check_edit, check_no_kw, check_with_private_editable};
#[test]
fn test_struct_field_and_method_completion() {
- check(
+ check_no_kw(
r#"
struct S { foo: u32 }
impl S {
@@ -249,7 +300,7 @@ fn foo(s: S) { s.$0 }
#[test]
fn no_unstable_method_on_stable() {
- check(
+ check_no_kw(
r#"
//- /main.rs crate:main deps:std
fn foo(s: std::S) { s.$0 }
@@ -266,7 +317,7 @@ impl S {
#[test]
fn unstable_method_on_nightly() {
- check(
+ check_no_kw(
r#"
//- toolchain:nightly
//- /main.rs crate:main deps:std
@@ -286,7 +337,7 @@ impl S {
#[test]
fn test_struct_field_completion_self() {
- check(
+ check_no_kw(
r#"
struct S { the_field: (u32,) }
impl S {
@@ -302,7 +353,7 @@ impl S {
#[test]
fn test_struct_field_completion_autoderef() {
- check(
+ check_no_kw(
r#"
struct A { the_field: (u32, i32) }
impl A {
@@ -318,7 +369,7 @@ impl A {
#[test]
fn test_no_struct_field_completion_for_method_call() {
- check(
+ check_no_kw(
r#"
struct A { the_field: u32 }
fn foo(a: A) { a.$0() }
@@ -329,7 +380,7 @@ fn foo(a: A) { a.$0() }
#[test]
fn test_visibility_filtering() {
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib new_source_root:local
pub mod m {
@@ -348,7 +399,7 @@ fn foo(a: lib::m::A) { a.$0 }
"#]],
);
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib new_source_root:library
pub mod m {
@@ -367,7 +418,7 @@ fn foo(a: lib::m::A) { a.$0 }
"#]],
);
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib new_source_root:library
pub mod m {
@@ -384,7 +435,7 @@ fn foo(a: lib::m::A) { a.$0 }
"#]],
);
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib new_source_root:local
pub struct A {}
@@ -402,7 +453,7 @@ fn foo(a: lib::A) { a.$0 }
me pub_method() fn(&self)
"#]],
);
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib new_source_root:library
pub struct A {}
@@ -524,7 +575,7 @@ fn foo(a: lib::A) { a.$0 }
#[test]
fn test_local_impls() {
- check(
+ check_no_kw(
r#"
pub struct A {}
mod m {
@@ -553,7 +604,7 @@ fn foo(a: A) {
#[test]
fn test_doc_hidden_filtering() {
- check(
+ check_no_kw(
r#"
//- /lib.rs crate:lib deps:dep
fn foo(a: dep::A) { a.$0 }
@@ -580,7 +631,7 @@ impl A {
#[test]
fn test_union_field_completion() {
- check(
+ check_no_kw(
r#"
union U { field: u8, other: u16 }
fn foo(u: U) { u.$0 }
@@ -594,7 +645,7 @@ fn foo(u: U) { u.$0 }
#[test]
fn test_method_completion_only_fitting_impls() {
- check(
+ check_no_kw(
r#"
struct A<T> {}
impl A<u32> {
@@ -613,7 +664,7 @@ fn foo(a: A<u32>) { a.$0 }
#[test]
fn test_trait_method_completion() {
- check(
+ check_no_kw(
r#"
struct A {}
trait Trait { fn the_method(&self); }
@@ -643,7 +694,7 @@ fn foo(a: A) { a.the_method();$0 }
#[test]
fn test_trait_method_completion_deduplicated() {
- check(
+ check_no_kw(
r"
struct A {}
trait Trait { fn the_method(&self); }
@@ -658,7 +709,7 @@ fn foo(a: &A) { a.$0 }
#[test]
fn completes_trait_method_from_other_module() {
- check(
+ check_no_kw(
r"
struct A {}
mod m {
@@ -676,7 +727,7 @@ fn foo(a: A) { a.$0 }
#[test]
fn test_no_non_self_method() {
- check(
+ check_no_kw(
r#"
struct A {}
impl A {
@@ -692,7 +743,7 @@ fn foo(a: A) {
#[test]
fn test_tuple_field_completion() {
- check(
+ check_no_kw(
r#"
fn foo() {
let b = (0, 3.14);
@@ -708,7 +759,7 @@ fn foo() {
#[test]
fn test_tuple_struct_field_completion() {
- check(
+ check_no_kw(
r#"
struct S(i32, f64);
fn foo() {
@@ -725,7 +776,7 @@ fn foo() {
#[test]
fn test_tuple_field_inference() {
- check(
+ check_no_kw(
r#"
pub struct S;
impl S { pub fn blah(&self) {} }
@@ -747,7 +798,7 @@ impl T {
#[test]
fn test_field_no_same_name() {
- check(
+ check_no_kw(
r#"
//- minicore: deref
struct A { field: u8 }
@@ -770,7 +821,7 @@ fn test(a: A) {
#[test]
fn test_tuple_field_no_same_index() {
- check(
+ check_no_kw(
r#"
//- minicore: deref
struct A(u8);
@@ -793,7 +844,7 @@ fn test(a: A) {
#[test]
fn test_tuple_struct_deref_to_tuple_no_same_index() {
- check(
+ check_no_kw(
r#"
//- minicore: deref
struct A(u8);
@@ -815,7 +866,7 @@ fn test(a: A) {
#[test]
fn test_completion_works_in_consts() {
- check(
+ check_no_kw(
r#"
struct A { the_field: u32 }
const X: u32 = {
@@ -830,7 +881,7 @@ const X: u32 = {
#[test]
fn works_in_simple_macro_1() {
- check(
+ check_no_kw(
r#"
macro_rules! m { ($e:expr) => { $e } }
struct A { the_field: u32 }
@@ -847,7 +898,7 @@ fn foo(a: A) {
#[test]
fn works_in_simple_macro_2() {
// this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery
- check(
+ check_no_kw(
r#"
macro_rules! m { ($e:expr) => { $e } }
struct A { the_field: u32 }
@@ -863,7 +914,7 @@ fn foo(a: A) {
#[test]
fn works_in_simple_macro_recursive_1() {
- check(
+ check_no_kw(
r#"
macro_rules! m { ($e:expr) => { $e } }
struct A { the_field: u32 }
@@ -879,7 +930,7 @@ fn foo(a: A) {
#[test]
fn macro_expansion_resilient() {
- check(
+ check_no_kw(
r#"
macro_rules! d {
() => {};
@@ -905,7 +956,7 @@ fn foo(a: A) {
#[test]
fn test_method_completion_issue_3547() {
- check(
+ check_no_kw(
r#"
struct HashSet<T> {}
impl<T> HashSet<T> {
@@ -924,7 +975,7 @@ fn foo() {
#[test]
fn completes_method_call_when_receiver_is_a_macro_call() {
- check(
+ check_no_kw(
r#"
struct S;
impl S { fn foo(&self) {} }
@@ -939,7 +990,7 @@ fn main() { make_s!().f$0; }
#[test]
fn completes_after_macro_call_in_submodule() {
- check(
+ check_no_kw(
r#"
macro_rules! empty {
() => {};
@@ -967,7 +1018,7 @@ mod foo {
#[test]
fn issue_8931() {
- check(
+ check_no_kw(
r#"
//- minicore: fn
struct S;
@@ -994,7 +1045,7 @@ impl S {
#[test]
fn completes_bare_fields_and_methods_in_methods() {
- check(
+ check_no_kw(
r#"
struct Foo { field: i32 }
@@ -1008,7 +1059,7 @@ impl Foo { fn foo(&self) { $0 } }"#,
bt u32 u32
"#]],
);
- check(
+ check_no_kw(
r#"
struct Foo(i32);
@@ -1026,7 +1077,7 @@ impl Foo { fn foo(&mut self) { $0 } }"#,
#[test]
fn macro_completion_after_dot() {
- check(
+ check_no_kw(
r#"
macro_rules! m {
($e:expr) => { $e };
@@ -1051,7 +1102,7 @@ fn f() {
#[test]
fn completes_method_call_when_receiver_type_has_errors_issue_10297() {
- check(
+ check_no_kw(
r#"
//- minicore: iterator, sized
struct Vec<T>;
@@ -1102,7 +1153,7 @@ fn main() {
#[test]
fn issue_12484() {
- check(
+ check_no_kw(
r#"
//- minicore: sized
trait SizeUser {
@@ -1124,7 +1175,7 @@ fn test(thing: impl Encrypt) {
#[test]
fn only_consider_same_type_once() {
- check(
+ check_no_kw(
r#"
//- minicore: deref
struct A(u8);
@@ -1150,7 +1201,7 @@ fn test(a: A) {
#[test]
fn no_inference_var_in_completion() {
- check(
+ check_no_kw(
r#"
struct S<T>(T);
fn test(s: S<Unknown>) {
@@ -1165,7 +1216,7 @@ fn test(s: S<Unknown>) {
#[test]
fn assoc_impl_1() {
- check(
+ check_no_kw(
r#"
//- minicore: deref
fn main() {
@@ -1206,7 +1257,7 @@ impl<F: core::ops::Deref<Target = impl Bar>> Foo<F> {
#[test]
fn assoc_impl_2() {
- check(
+ check_no_kw(
r#"
//- minicore: deref
fn main() {
@@ -1242,7 +1293,7 @@ impl<B: Bar, F: core::ops::Deref<Target = B>> Foo<F> {
#[test]
fn test_struct_function_field_completion() {
- check(
+ check_no_kw(
r#"
struct S { va_field: u32, fn_field: fn() }
fn foo() { S { va_field: 0, fn_field: || {} }.fi$0() }
@@ -1267,7 +1318,7 @@ fn foo() { (S { va_field: 0, fn_field: || {} }.fn_field)() }
#[test]
fn test_tuple_function_field_completion() {
- check(
+ check_no_kw(
r#"
struct B(u32, fn())
fn foo() {
@@ -1301,7 +1352,7 @@ fn foo() {
#[test]
fn test_fn_field_dot_access_method_has_parens_false() {
- check(
+ check_no_kw(
r#"
struct Foo { baz: fn() }
impl Foo {
@@ -1318,4 +1369,101 @@ fn baz() {
"#]],
);
}
+
+ #[test]
+ fn skip_iter() {
+ check_no_kw(
+ r#"
+ //- minicore: iterator
+ fn foo() {
+ [].$0
+ }
+ "#,
+ expect![[r#"
+ me clone() (as Clone) fn(&self) -> Self
+ me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
+ "#]],
+ );
+ check_no_kw(
+ r#"
+//- minicore: iterator
+struct MyIntoIter;
+impl IntoIterator for MyIntoIter {
+ type Item = ();
+ type IntoIter = MyIterator;
+ fn into_iter(self) -> Self::IntoIter {
+ MyIterator
+ }
+}
+
+struct MyIterator;
+impl Iterator for MyIterator {
+ type Item = ();
+ fn next(&mut self) -> Self::Item {}
+}
+
+fn foo() {
+ MyIntoIter.$0
+}
+"#,
+ expect![[r#"
+ me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
+ me into_iter().by_ref() (as Iterator) fn(&mut self) -> &mut Self
+ me into_iter().into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter
+ me into_iter().next() (as Iterator) fn(&mut self) -> Option<<Self as Iterator>::Item>
+ me into_iter().nth(…) (as Iterator) fn(&mut self, usize) -> Option<<Self as Iterator>::Item>
+ "#]],
+ );
+ }
+
+ #[test]
+ fn skip_await() {
+ check_no_kw(
+ r#"
+//- minicore: future
+struct Foo;
+impl Foo {
+ fn foo(self) {}
+}
+
+async fn foo() -> Foo { Foo }
+
+async fn bar() {
+ foo().$0
+}
+"#,
+ expect![[r#"
+ me await.foo() fn(self)
+ me into_future() (use core::future::IntoFuture) fn(self) -> <Self as IntoFuture>::IntoFuture
+"#]],
+ );
+ check_edit(
+ "foo",
+ r#"
+//- minicore: future
+struct Foo;
+impl Foo {
+ fn foo(self) {}
+}
+
+async fn foo() -> Foo { Foo }
+
+async fn bar() {
+ foo().$0
+}
+"#,
+ r#"
+struct Foo;
+impl Foo {
+ fn foo(self) {}
+}
+
+async fn foo() -> Foo { Foo }
+
+async fn bar() {
+ foo().await.foo();$0
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs
index 0b6790d42a..40af5203e9 100644
--- a/crates/ide-completion/src/completions/env_vars.rs
+++ b/crates/ide-completion/src/completions/env_vars.rs
@@ -68,43 +68,40 @@ pub(crate) fn complete_cargo_env_vars(
mod tests {
use crate::tests::{check_edit, completion_list};
- fn check(macro_name: &str) {
+ #[test]
+ fn completes_env_variable_in_env() {
check_edit(
"CARGO_BIN_NAME",
- &format!(
- r#"
- #[rustc_builtin_macro]
- macro {macro_name} {{
- ($var:literal) => {{ 0 }}
- }}
-
- fn main() {{
- let foo = {macro_name}!("CAR$0");
- }}
- "#
- ),
- &format!(
- r#"
- #[rustc_builtin_macro]
- macro {macro_name} {{
- ($var:literal) => {{ 0 }}
- }}
-
- fn main() {{
- let foo = {macro_name}!("CARGO_BIN_NAME");
- }}
- "#
- ),
+ r#"
+//- minicore: env
+fn main() {
+ let foo = env!("CAR$0");
+}
+ "#,
+ r#"
+fn main() {
+ let foo = env!("CARGO_BIN_NAME");
+}
+ "#,
);
}
- #[test]
- fn completes_env_variable_in_env() {
- check("env")
- }
#[test]
fn completes_env_variable_in_option_env() {
- check("option_env");
+ check_edit(
+ "CARGO_BIN_NAME",
+ r#"
+//- minicore: env
+fn main() {
+ let foo = option_env!("CAR$0");
+}
+ "#,
+ r#"
+fn main() {
+ let foo = option_env!("CARGO_BIN_NAME");
+}
+ "#,
+ );
}
#[test]
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index c2e5eefe10..db18b531d7 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -62,6 +62,7 @@ pub(crate) fn complete_expr_path(
in_condition,
incomplete_let,
ref ref_expr_parent,
+ after_amp,
ref is_func_update,
ref innermost_ret_ty,
ref impl_,
@@ -69,8 +70,23 @@ pub(crate) fn complete_expr_path(
..
} = expr_ctx;
- let wants_mut_token =
- ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);
+ let (has_raw_token, has_const_token, has_mut_token) = ref_expr_parent
+ .as_ref()
+ .map(|it| (it.raw_token().is_some(), it.const_token().is_some(), it.mut_token().is_some()))
+ .unwrap_or((false, false, false));
+
+ let wants_raw_token = ref_expr_parent.is_some() && !has_raw_token && after_amp;
+ let wants_const_token =
+ ref_expr_parent.is_some() && has_raw_token && !has_const_token && !has_mut_token;
+ let wants_mut_token = if ref_expr_parent.is_some() {
+ if has_raw_token {
+ !has_const_token && !has_mut_token
+ } else {
+ !has_mut_token
+ }
+ } else {
+ false
+ };
let scope_def_applicable = |def| match def {
ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) | ScopeDef::Label(_) => false,
@@ -354,6 +370,12 @@ pub(crate) fn complete_expr_path(
add_keyword("else if", "else if $1 {\n $0\n}");
}
+ if wants_raw_token {
+ add_keyword("raw", "raw ");
+ }
+ if wants_const_token {
+ add_keyword("const", "const ");
+ }
if wants_mut_token {
add_keyword("mut", "mut ");
}
diff --git a/crates/ide-completion/src/completions/extern_abi.rs b/crates/ide-completion/src/completions/extern_abi.rs
index bcfda928c4..7c2cc2a6c1 100644
--- a/crates/ide-completion/src/completions/extern_abi.rs
+++ b/crates/ide-completion/src/completions/extern_abi.rs
@@ -65,18 +65,13 @@ pub(crate) fn complete_extern_abi(
#[cfg(test)]
mod tests {
- use expect_test::{expect, Expect};
+ use expect_test::expect;
- use crate::tests::{check_edit, completion_list_no_kw};
-
- fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list_no_kw(ra_fixture);
- expect.assert_eq(&actual);
- }
+ use crate::tests::{check_edit, check_no_kw};
#[test]
fn only_completes_in_string_literals() {
- check(
+ check_no_kw(
r#"
$0 fn foo {}
"#,
@@ -86,7 +81,7 @@ $0 fn foo {}
#[test]
fn requires_extern_prefix() {
- check(
+ check_no_kw(
r#"
"$0" fn foo {}
"#,
@@ -96,7 +91,7 @@ $0 fn foo {}
#[test]
fn works() {
- check(
+ check_no_kw(
r#"
extern "$0" fn foo {}
"#,
diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 3b2b2fd706..73313eeaa6 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -5,7 +5,7 @@ use ide_db::imports::{
insert_use::ImportScope,
};
use itertools::Itertools;
-use syntax::{ast, AstNode, SyntaxNode, ToSmolStr, T};
+use syntax::{ast, AstNode, SyntaxNode, ToSmolStr};
use crate::{
config::AutoImportExclusionType,
@@ -403,10 +403,11 @@ fn import_on_the_fly_method(
fn import_name(ctx: &CompletionContext<'_>) -> String {
let token_kind = ctx.token.kind();
- if matches!(token_kind, T![.] | T![::]) {
- String::new()
- } else {
+
+ if token_kind.is_any_identifier() {
ctx.token.to_string()
+ } else {
+ String::new()
}
}
diff --git a/crates/ide-completion/src/completions/format_string.rs b/crates/ide-completion/src/completions/format_string.rs
index a87c60c694..dcd40c3412 100644
--- a/crates/ide-completion/src/completions/format_string.rs
+++ b/crates/ide-completion/src/completions/format_string.rs
@@ -61,18 +61,13 @@ pub(crate) fn format_string(
#[cfg(test)]
mod tests {
- use expect_test::{expect, Expect};
+ use expect_test::expect;
- use crate::tests::{check_edit, completion_list_no_kw};
-
- fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list_no_kw(ra_fixture);
- expect.assert_eq(&actual);
- }
+ use crate::tests::{check_edit, check_no_kw};
#[test]
fn works_when_wrapped() {
- check(
+ check_no_kw(
r#"
//- minicore: fmt
macro_rules! print {
@@ -89,7 +84,7 @@ fn main() {
#[test]
fn no_completion_without_brace() {
- check(
+ check_no_kw(
r#"
//- minicore: fmt
fn main() {
diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 80d72b460f..6d1945c453 100644
--- a/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -514,18 +514,13 @@ fn function_declaration(
#[cfg(test)]
mod tests {
- use expect_test::{expect, Expect};
+ use expect_test::expect;
- use crate::tests::{check_edit, completion_list_no_kw};
-
- fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list_no_kw(ra_fixture);
- expect.assert_eq(&actual)
- }
+ use crate::tests::{check_edit, check_no_kw};
#[test]
fn no_completion_inside_fn() {
- check(
+ check_no_kw(
r"
trait Test { fn test(); fn test2(); }
struct T;
@@ -544,7 +539,7 @@ impl Test for T {
"#]],
);
- check(
+ check_no_kw(
r"
trait Test { fn test(); fn test2(); }
struct T;
@@ -558,7 +553,7 @@ impl Test for T {
expect![[""]],
);
- check(
+ check_no_kw(
r"
trait Test { fn test(); fn test2(); }
struct T;
@@ -573,7 +568,7 @@ impl Test for T {
);
// https://github.com/rust-lang/rust-analyzer/pull/5976#issuecomment-692332191
- check(
+ check_no_kw(
r"
trait Test { fn test(); fn test2(); }
struct T;
@@ -587,7 +582,7 @@ impl Test for T {
expect![[r#""#]],
);
- check(
+ check_no_kw(
r"
trait Test { fn test(_: i32); fn test2(); }
struct T;
@@ -606,7 +601,7 @@ impl Test for T {
"#]],
);
- check(
+ check_no_kw(
r"
trait Test { fn test(_: fn()); fn test2(); }
struct T;
@@ -624,7 +619,7 @@ impl Test for T {
#[test]
fn no_completion_inside_const() {
- check(
+ check_no_kw(
r"
trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
struct T;
@@ -636,7 +631,7 @@ impl Test for T {
expect![[r#""#]],
);
- check(
+ check_no_kw(
r"
trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
struct T;
@@ -653,7 +648,7 @@ impl Test for T {
"#]],
);
- check(
+ check_no_kw(
r"
trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
struct T;
@@ -670,7 +665,7 @@ impl Test for T {
"#]],
);
- check(
+ check_no_kw(
r"
trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
struct T;
@@ -689,7 +684,7 @@ impl Test for T {
"#]],
);
- check(
+ check_no_kw(
r"
trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
struct T;
@@ -703,7 +698,7 @@ impl Test for T {
expect![[""]],
);
- check(
+ check_no_kw(
r"
trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
struct T;
@@ -720,7 +715,7 @@ impl Test for T {
#[test]
fn no_completion_inside_type() {
- check(
+ check_no_kw(
r"
trait Test { type Test; type Test2; fn test(); }
struct T;
@@ -737,7 +732,7 @@ impl Test for T {
"#]],
);
- check(
+ check_no_kw(
r"
trait Test { type Test; type Test2; fn test(); }
struct T;
@@ -1263,7 +1258,7 @@ impl Foo<u32> for Bar {
#[test]
fn works_directly_in_impl() {
- check(
+ check_no_kw(
r#"
trait Tr {
fn required();
@@ -1277,7 +1272,7 @@ impl Tr for () {
fn fn required()
"#]],
);
- check(
+ check_no_kw(
r#"
trait Tr {
fn provided() {}
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs
index 4700ed6c1a..6541ee502d 100644
--- a/crates/ide-completion/src/completions/keyword.rs
+++ b/crates/ide-completion/src/completions/keyword.rs
@@ -32,14 +32,9 @@ pub(crate) fn complete_for_and_where(
#[cfg(test)]
mod tests {
- use expect_test::{expect, Expect};
+ use expect_test::expect;
- use crate::tests::{check_edit, completion_list};
-
- fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual)
- }
+ use crate::tests::{check, check_edit};
#[test]
fn test_else_edit_after_if() {
diff --git a/crates/ide-completion/src/completions/lifetime.rs b/crates/ide-completion/src/completions/lifetime.rs
index 0692446381..53a62fe49c 100644
--- a/crates/ide-completion/src/completions/lifetime.rs
+++ b/crates/ide-completion/src/completions/lifetime.rs
@@ -59,14 +59,9 @@ pub(crate) fn complete_label(
#[cfg(test)]
mod tests {
- use expect_test::{expect, Expect};
+ use expect_test::expect;
- use crate::tests::{check_edit, completion_list};
-
- fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual);
- }
+ use crate::tests::{check, check_edit};
#[test]
fn check_lifetime_edit() {
diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs
index f12f011a6b..bafe329420 100644
--- a/crates/ide-completion/src/completions/mod_.rs
+++ b/crates/ide-completion/src/completions/mod_.rs
@@ -159,14 +159,9 @@ fn module_chain_to_containing_module_file(
#[cfg(test)]
mod tests {
- use expect_test::{expect, Expect};
+ use expect_test::expect;
- use crate::tests::completion_list;
-
- fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual);
- }
+ use crate::tests::check;
#[test]
fn lib_module_completion() {
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs
index 7b57eea052..67ea05e002 100644
--- a/crates/ide-completion/src/completions/postfix.rs
+++ b/crates/ide-completion/src/completions/postfix.rs
@@ -303,7 +303,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) {
resulting_element = ast::Expr::from(parent_deref_element);
- new_element_opt = make::expr_prefix(syntax::T![*], new_element_opt);
+ new_element_opt = make::expr_prefix(syntax::T![*], new_element_opt).into();
}
if let Some(first_ref_expr) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) {
@@ -401,18 +401,13 @@ fn add_custom_postfix_completions(
#[cfg(test)]
mod tests {
- use expect_test::{expect, Expect};
+ use expect_test::expect;
use crate::{
- tests::{check_edit, check_edit_with_config, completion_list, TEST_CONFIG},
+ tests::{check, check_edit, check_edit_with_config, TEST_CONFIG},
CompletionConfig, Snippet,
};
- fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual)
- }
-
#[test]
fn postfix_completion_works_for_trivial_path_expression() {
check(
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 3705e2c73d..3a2a4a23a1 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -146,6 +146,7 @@ pub(crate) struct PathExprCtx {
pub(crate) in_condition: bool,
pub(crate) incomplete_let: bool,
pub(crate) ref_expr_parent: Option<ast::RefExpr>,
+ pub(crate) after_amp: bool,
/// The surrounding RecordExpression we are completing a functional update
pub(crate) is_func_update: Option<ast::RecordExpr>,
pub(crate) self_param: Option<hir::SelfParam>,
@@ -390,7 +391,7 @@ pub(crate) struct DotAccess {
pub(crate) ctx: DotAccessExprCtx,
}
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
pub(crate) enum DotAccessKind {
Field {
/// True if the receiver is an integer and there is no ident in the original file after it yet
@@ -402,7 +403,7 @@ pub(crate) enum DotAccessKind {
},
}
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct DotAccessExprCtx {
pub(crate) in_block_expr: bool,
pub(crate) in_breakable: BreakableKind,
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index acce62a041..3c4d489c0f 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -123,10 +123,11 @@ fn expand(
) -> Option<ExpansionResult> {
let _p = tracing::info_span!("CompletionContext::expand").entered();
+ // Left biased since there may already be an identifier token there, and we appended to it.
if !sema.might_be_inside_macro_call(&fake_ident_token)
&& original_file
.token_at_offset(original_offset + relative_offset)
- .right_biased()
+ .left_biased()
.is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token))
{
// Recursion base case.
@@ -1150,6 +1151,9 @@ fn classify_name_ref(
let after_if_expr = after_if_expr(it.clone());
let ref_expr_parent =
path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
+ let after_amp = non_trivia_sibling(it.clone().into(), Direction::Prev)
+ .map(|it| it.kind() == SyntaxKind::AMP)
+ .unwrap_or(false);
let (innermost_ret_ty, self_param) = {
let find_ret_ty = |it: SyntaxNode| {
if let Some(item) = ast::Item::cast(it.clone()) {
@@ -1219,6 +1223,7 @@ fn classify_name_ref(
after_if_expr,
in_condition,
ref_expr_parent,
+ after_amp,
is_func_update,
innermost_ret_ty,
self_param,
diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs
index 82a1c10c53..fc2bfc01e6 100644
--- a/crates/ide-completion/src/context/tests.rs
+++ b/crates/ide-completion/src/context/tests.rs
@@ -6,7 +6,7 @@ use crate::{
tests::{position, TEST_CONFIG},
};
-fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) {
+fn check_expected_type_and_name(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (db, pos) = position(ra_fixture);
let config = TEST_CONFIG;
let (completion_context, _analysis) = CompletionContext::new(&db, pos, &config).unwrap();
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index b91f915619..dc2f9a7680 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -79,7 +79,7 @@ pub struct CompletionItem {
// FIXME: We shouldn't expose Mutability here (that is HIR types at all), its fine for now though
// until we have more splitting completions in which case we should think about
// generalizing this. See https://github.com/rust-lang/rust-analyzer/issues/12571
- pub ref_match: Option<(Mutability, TextSize)>,
+ pub ref_match: Option<(CompletionItemRefMode, TextSize)>,
/// The import data to add to completion's edits.
/// (ImportPath, LastSegment)
@@ -128,8 +128,15 @@ impl fmt::Debug for CompletionItem {
s.field("relevance", &self.relevance);
}
- if let Some((mutability, offset)) = &self.ref_match {
- s.field("ref_match", &format!("&{}@{offset:?}", mutability.as_keyword_for_ref()));
+ if let Some((ref_mode, offset)) = self.ref_match {
+ let prefix = match ref_mode {
+ CompletionItemRefMode::Reference(mutability) => match mutability {
+ Mutability::Shared => "&",
+ Mutability::Mut => "&mut ",
+ },
+ CompletionItemRefMode::Dereference => "*",
+ };
+ s.field("ref_match", &format!("{}@{offset:?}", prefix));
}
if self.trigger_call_info {
s.field("trigger_call_info", &true);
@@ -400,6 +407,12 @@ impl CompletionItemKind {
}
}
+#[derive(Copy, Clone, Debug)]
+pub enum CompletionItemRefMode {
+ Reference(Mutability),
+ Dereference,
+}
+
impl CompletionItem {
pub(crate) fn new(
kind: impl Into<CompletionItemKind>,
@@ -441,15 +454,14 @@ impl CompletionItem {
let mut relevance = self.relevance;
relevance.type_match = Some(CompletionRelevanceTypeMatch::Exact);
- self.ref_match.map(|(mutability, offset)| {
- (
- format!("&{}{}", mutability.as_keyword_for_ref(), self.label.primary),
- ide_db::text_edit::Indel::insert(
- offset,
- format!("&{}", mutability.as_keyword_for_ref()),
- ),
- relevance,
- )
+ self.ref_match.map(|(mode, offset)| {
+ let prefix = match mode {
+ CompletionItemRefMode::Reference(Mutability::Shared) => "&",
+ CompletionItemRefMode::Reference(Mutability::Mut) => "&mut ",
+ CompletionItemRefMode::Dereference => "*",
+ };
+ let label = format!("{prefix}{}", self.label.primary);
+ (label, ide_db::text_edit::Indel::insert(offset, String::from(prefix)), relevance)
})
}
}
@@ -473,7 +485,7 @@ pub(crate) struct Builder {
deprecated: bool,
trigger_call_info: bool,
relevance: CompletionRelevance,
- ref_match: Option<(Mutability, TextSize)>,
+ ref_match: Option<(CompletionItemRefMode, TextSize)>,
edition: Edition,
}
@@ -657,8 +669,12 @@ impl Builder {
self.imports_to_add.push(import_to_add);
self
}
- pub(crate) fn ref_match(&mut self, mutability: Mutability, offset: TextSize) -> &mut Builder {
- self.ref_match = Some((mutability, offset));
+ pub(crate) fn ref_match(
+ &mut self,
+ ref_mode: CompletionItemRefMode,
+ offset: TextSize,
+ ) -> &mut Builder {
+ self.ref_match = Some((ref_mode, offset));
self
}
}
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index ca6c9ad9f0..56d7eeaf8e 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -33,8 +33,9 @@ use crate::{
pub use crate::{
config::{AutoImportExclusionType, CallableSnippets, CompletionConfig},
item::{
- CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch,
- CompletionRelevanceReturnType, CompletionRelevanceTypeMatch,
+ CompletionItem, CompletionItemKind, CompletionItemRefMode, CompletionRelevance,
+ CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
+ CompletionRelevanceTypeMatch,
},
snippet::{Snippet, SnippetScope},
};
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index c239ca512d..61e8114d38 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -18,7 +18,7 @@ use ide_db::{
imports::import_assets::LocatedImport,
RootDatabase, SnippetCap, SymbolKind,
};
-use syntax::{ast, format_smolstr, AstNode, Edition, SmolStr, SyntaxKind, TextRange, ToSmolStr};
+use syntax::{ast, format_smolstr, AstNode, SmolStr, SyntaxKind, TextRange, ToSmolStr};
use crate::{
context::{DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext},
@@ -28,7 +28,8 @@ use crate::{
literal::render_variant_lit,
macro_::{render_macro, render_macro_pat},
},
- CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance,
+ CompletionContext, CompletionItem, CompletionItemKind, CompletionItemRefMode,
+ CompletionRelevance,
};
/// Interface for data and methods required for items rendering.
#[derive(Debug, Clone)]
@@ -122,7 +123,7 @@ impl<'a> RenderContext<'a> {
pub(crate) fn render_field(
ctx: RenderContext<'_>,
dot_access: &DotAccess,
- receiver: Option<hir::Name>,
+ receiver: Option<SmolStr>,
field: hir::Field,
ty: &hir::Type,
) -> CompletionItem {
@@ -136,7 +137,7 @@ pub(crate) fn render_field(
let mut item = CompletionItem::new(
SymbolKind::Field,
ctx.source_range(),
- field_with_receiver(db, receiver.as_ref(), &name, ctx.completion.edition),
+ field_with_receiver(receiver.as_deref(), &name),
ctx.completion.edition,
);
item.set_relevance(CompletionRelevance {
@@ -158,8 +159,7 @@ pub(crate) fn render_field(
builder.replace(
ctx.source_range(),
- field_with_receiver(db, receiver.as_ref(), &escaped_name, ctx.completion.edition)
- .into(),
+ field_with_receiver(receiver.as_deref(), &escaped_name).into(),
);
let expected_fn_type =
@@ -183,17 +183,12 @@ pub(crate) fn render_field(
item.text_edit(builder.finish());
} else {
- item.insert_text(field_with_receiver(
- db,
- receiver.as_ref(),
- &escaped_name,
- ctx.completion.edition,
- ));
+ item.insert_text(field_with_receiver(receiver.as_deref(), &escaped_name));
}
if let Some(receiver) = &dot_access.receiver {
if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) {
- if let Some(ref_match) = compute_ref_match(ctx.completion, ty) {
- item.ref_match(ref_match, original.syntax().text_range().start());
+ if let Some(ref_mode) = compute_ref_match(ctx.completion, ty) {
+ item.ref_match(ref_mode, original.syntax().text_range().start());
}
}
}
@@ -201,33 +196,21 @@ pub(crate) fn render_field(
item.build(db)
}
-fn field_with_receiver(
- db: &RootDatabase,
- receiver: Option<&hir::Name>,
- field_name: &str,
- edition: Edition,
-) -> SmolStr {
- receiver.map_or_else(
- || field_name.into(),
- |receiver| format_smolstr!("{}.{field_name}", receiver.display(db, edition)),
- )
+fn field_with_receiver(receiver: Option<&str>, field_name: &str) -> SmolStr {
+ receiver
+ .map_or_else(|| field_name.into(), |receiver| format_smolstr!("{}.{field_name}", receiver))
}
pub(crate) fn render_tuple_field(
ctx: RenderContext<'_>,
- receiver: Option<hir::Name>,
+ receiver: Option<SmolStr>,
field: usize,
ty: &hir::Type,
) -> CompletionItem {
let mut item = CompletionItem::new(
SymbolKind::Field,
ctx.source_range(),
- field_with_receiver(
- ctx.db(),
- receiver.as_ref(),
- &field.to_string(),
- ctx.completion.edition,
- ),
+ field_with_receiver(receiver.as_deref(), &field.to_string()),
ctx.completion.edition,
);
item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string())
@@ -440,7 +423,7 @@ fn render_resolution_path(
let name = local_name.display_no_db(ctx.completion.edition).to_smolstr();
let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution);
- if local_name.is_escaped(completion.edition) {
+ if local_name.needs_escape(completion.edition) {
item.insert_text(local_name.display_no_db(completion.edition).to_smolstr());
}
// Add `<>` for generic types
@@ -638,20 +621,34 @@ fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str)
fn compute_ref_match(
ctx: &CompletionContext<'_>,
completion_ty: &hir::Type,
-) -> Option<hir::Mutability> {
+) -> Option<CompletionItemRefMode> {
let expected_type = ctx.expected_type.as_ref()?;
- if completion_ty != expected_type {
- let expected_type_without_ref = expected_type.remove_ref()?;
- if completion_ty.autoderef(ctx.db).any(|deref_ty| deref_ty == expected_type_without_ref) {
+ let expected_without_ref = expected_type.remove_ref();
+ let completion_without_ref = completion_ty.remove_ref();
+
+ if completion_ty == expected_type {
+ return None;
+ }
+
+ if let Some(expected_without_ref) = &expected_without_ref {
+ if completion_ty.autoderef(ctx.db).any(|ty| ty == *expected_without_ref) {
cov_mark::hit!(suggest_ref);
let mutability = if expected_type.is_mutable_reference() {
hir::Mutability::Mut
} else {
hir::Mutability::Shared
};
- return Some(mutability);
- };
+ return Some(CompletionItemRefMode::Reference(mutability));
+ }
+ }
+
+ if let Some(completion_without_ref) = completion_without_ref {
+ if completion_without_ref == *expected_type && completion_without_ref.is_copy(ctx.db) {
+ cov_mark::hit!(suggest_deref);
+ return Some(CompletionItemRefMode::Dereference);
+ }
}
+
None
}
@@ -664,16 +661,16 @@ fn path_ref_match(
if let Some(original_path) = &path_ctx.original_path {
// At least one char was typed by the user already, in that case look for the original path
if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) {
- if let Some(ref_match) = compute_ref_match(completion, ty) {
- item.ref_match(ref_match, original_path.syntax().text_range().start());
+ if let Some(ref_mode) = compute_ref_match(completion, ty) {
+ item.ref_match(ref_mode, original_path.syntax().text_range().start());
}
}
} else {
// completion requested on an empty identifier, there is no path here yet.
// FIXME: This might create inconsistent completions where we show a ref match in macro inputs
// as long as nothing was typed yet
- if let Some(ref_match) = compute_ref_match(completion, ty) {
- item.ref_match(ref_match, completion.position.offset);
+ if let Some(ref_mode) = compute_ref_match(completion, ty) {
+ item.ref_match(ref_mode, completion.position.offset);
}
}
}
@@ -693,20 +690,28 @@ mod tests {
};
#[track_caller]
- fn check(ra_fixture: &str, kind: impl Into<CompletionItemKind>, expect: Expect) {
+ fn check(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ kind: impl Into<CompletionItemKind>,
+ expect: Expect,
+ ) {
let actual = do_completion(ra_fixture, kind.into());
expect.assert_debug_eq(&actual);
}
#[track_caller]
- fn check_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) {
+ fn check_kinds(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ kinds: &[CompletionItemKind],
+ expect: Expect,
+ ) {
let actual: Vec<_> =
kinds.iter().flat_map(|&kind| do_completion(ra_fixture, kind)).collect();
expect.assert_debug_eq(&actual);
}
#[track_caller]
- fn check_function_relevance(ra_fixture: &str, expect: Expect) {
+ fn check_function_relevance(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let actual: Vec<_> =
do_completion(ra_fixture, CompletionItemKind::SymbolKind(SymbolKind::Method))
.into_iter()
@@ -717,7 +722,11 @@ mod tests {
}
#[track_caller]
- fn check_relevance_for_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) {
+ fn check_relevance_for_kinds(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ kinds: &[CompletionItemKind],
+ expect: Expect,
+ ) {
let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);
actual.retain(|it| kinds.contains(&it.kind));
actual.sort_by_key(|it| cmp::Reverse(it.relevance.score()));
@@ -725,7 +734,7 @@ mod tests {
}
#[track_caller]
- fn check_relevance(ra_fixture: &str, expect: Expect) {
+ fn check_relevance(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);
actual.retain(|it| it.kind != CompletionItemKind::Snippet);
actual.retain(|it| it.kind != CompletionItemKind::Keyword);
@@ -2053,7 +2062,42 @@ fn main() {
}
#[test]
- fn suggest_deref() {
+ fn suggest_deref_copy() {
+ cov_mark::check!(suggest_deref);
+ check_relevance(
+ r#"
+//- minicore: copy
+struct Foo;
+
+impl Copy for Foo {}
+impl Clone for Foo {
+ fn clone(&self) -> Self { *self }
+}
+
+fn bar(x: Foo) {}
+
+fn main() {
+ let foo = &Foo;
+ bar($0);
+}
+"#,
+ expect![[r#"
+ st Foo Foo [type]
+ st Foo Foo [type]
+ ex Foo [type]
+ lc foo &Foo [local]
+ lc *foo [type+local]
+ fn bar(…) fn(Foo) []
+ fn main() fn() []
+ md core []
+ tt Clone []
+ tt Copy []
+ "#]],
+ );
+ }
+
+ #[test]
+ fn suggest_deref_trait() {
check_relevance(
r#"
//- minicore: deref
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index a859d79e24..3b97d67169 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -23,7 +23,7 @@ use crate::{
#[derive(Debug)]
enum FuncKind<'ctx> {
Function(&'ctx PathCompletionCtx),
- Method(&'ctx DotAccess, Option<hir::Name>),
+ Method(&'ctx DotAccess, Option<SmolStr>),
}
pub(crate) fn render_fn(
@@ -39,7 +39,7 @@ pub(crate) fn render_fn(
pub(crate) fn render_method(
ctx: RenderContext<'_>,
dot_access: &DotAccess,
- receiver: Option<hir::Name>,
+ receiver: Option<SmolStr>,
local_name: Option<hir::Name>,
func: hir::Function,
) -> Builder {
@@ -59,16 +59,8 @@ fn render(
let (call, escaped_call) = match &func_kind {
FuncKind::Method(_, Some(receiver)) => (
- format_smolstr!(
- "{}.{}",
- receiver.unescaped().display(ctx.db()),
- name.unescaped().display(ctx.db())
- ),
- format_smolstr!(
- "{}.{}",
- receiver.display(ctx.db(), completion.edition),
- name.display(ctx.db(), completion.edition)
- ),
+ format_smolstr!("{}.{}", receiver, name.unescaped().display(ctx.db())),
+ format_smolstr!("{}.{}", receiver, name.display(ctx.db(), completion.edition)),
),
_ => (
name.unescaped().display(db).to_smolstr(),
@@ -143,8 +135,8 @@ fn render(
}
FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => {
if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) {
- if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
- item.ref_match(ref_match, original_expr.syntax().text_range().start());
+ if let Some(ref_mode) = compute_ref_match(completion, &ret_type) {
+ item.ref_match(ref_mode, original_expr.syntax().text_range().start());
}
}
}
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs
index 1815f34053..b7dbf0a630 100644
--- a/crates/ide-completion/src/tests.rs
+++ b/crates/ide-completion/src/tests.rs
@@ -89,22 +89,24 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig {
exclude_traits: &[],
};
-pub(crate) fn completion_list(ra_fixture: &str) -> String {
+pub(crate) fn completion_list(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
completion_list_with_config(TEST_CONFIG, ra_fixture, true, None)
}
-pub(crate) fn completion_list_no_kw(ra_fixture: &str) -> String {
+pub(crate) fn completion_list_no_kw(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
completion_list_with_config(TEST_CONFIG, ra_fixture, false, None)
}
-pub(crate) fn completion_list_no_kw_with_private_editable(ra_fixture: &str) -> String {
+pub(crate) fn completion_list_no_kw_with_private_editable(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) -> String {
let mut config = TEST_CONFIG;
config.enable_private_editable = true;
completion_list_with_config(config, ra_fixture, false, None)
}
pub(crate) fn completion_list_with_trigger_character(
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
trigger_character: Option<char>,
) -> String {
completion_list_with_config(TEST_CONFIG, ra_fixture, true, trigger_character)
@@ -112,7 +114,7 @@ pub(crate) fn completion_list_with_trigger_character(
fn completion_list_with_config_raw(
config: CompletionConfig<'_>,
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
include_keywords: bool,
trigger_character: Option<char>,
) -> Vec<CompletionItem> {
@@ -135,7 +137,7 @@ fn completion_list_with_config_raw(
fn completion_list_with_config(
config: CompletionConfig<'_>,
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
include_keywords: bool,
trigger_character: Option<char>,
) -> String {
@@ -148,7 +150,9 @@ fn completion_list_with_config(
}
/// Creates analysis from a multi-file fixture, returns positions marked with $0.
-pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
+pub(crate) fn position(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) -> (RootDatabase, FilePosition) {
let change_fixture = ChangeFixture::parse(ra_fixture);
let mut database = RootDatabase::default();
database.enable_proc_attr_macros();
@@ -216,7 +220,11 @@ fn render_completion_list(completions: Vec<CompletionItem>) -> String {
}
#[track_caller]
-pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+pub(crate) fn check_edit(
+ what: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
check_edit_with_config(TEST_CONFIG, what, ra_fixture_before, ra_fixture_after)
}
@@ -253,11 +261,40 @@ pub(crate) fn check_edit_with_config(
assert_eq_text!(&ra_fixture_after, &actual)
}
-fn check_empty(ra_fixture: &str, expect: Expect) {
+pub(crate) fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture);
expect.assert_eq(&actual);
}
+pub(crate) fn check_with_base_items(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: Expect,
+) {
+ check(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"), expect)
+}
+
+pub(crate) fn check_no_kw(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
+ let actual = completion_list_no_kw(ra_fixture);
+ expect.assert_eq(&actual)
+}
+
+pub(crate) fn check_with_private_editable(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: Expect,
+) {
+ let actual = completion_list_no_kw_with_private_editable(ra_fixture);
+ expect.assert_eq(&actual);
+}
+
+pub(crate) fn check_with_trigger_character(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ trigger_character: Option<char>,
+ expect: Expect,
+) {
+ let actual = completion_list_with_trigger_character(ra_fixture, trigger_character);
+ expect.assert_eq(&actual)
+}
+
pub(crate) fn get_all_items(
config: CompletionConfig<'_>,
code: &str,
diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs
index ebf3582057..32d3b50f23 100644
--- a/crates/ide-completion/src/tests/attribute.rs
+++ b/crates/ide-completion/src/tests/attribute.rs
@@ -1,12 +1,7 @@
//! Completion tests for attributes.
-use expect_test::{expect, Expect};
+use expect_test::expect;
-use crate::tests::{check_edit, completion_list};
-
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual);
-}
+use crate::tests::{check, check_edit};
#[test]
fn derive_helpers() {
@@ -788,14 +783,9 @@ mod cfg {
mod derive {
use super::*;
- fn check_derive(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual);
- }
-
#[test]
fn no_completion_for_incorrect_derive() {
- check_derive(
+ check(
r#"
//- minicore: derive, copy, clone, ord, eq, default, fmt
#[derive{$0)] struct Test;
@@ -806,7 +796,7 @@ mod derive {
#[test]
fn empty_derive() {
- check_derive(
+ check(
r#"
//- minicore: derive, copy, clone, ord, eq, default, fmt
#[derive($0)] struct Test;
@@ -828,7 +818,7 @@ mod derive {
#[test]
fn derive_with_input_before() {
- check_derive(
+ check(
r#"
//- minicore: derive, copy, clone, ord, eq, default, fmt
#[derive(serde::Serialize, PartialEq, $0)] struct Test;
@@ -849,7 +839,7 @@ mod derive {
#[test]
fn derive_with_input_after() {
- check_derive(
+ check(
r#"
//- minicore: derive, copy, clone, ord, eq, default, fmt
#[derive($0 serde::Serialize, PartialEq)] struct Test;
@@ -870,7 +860,7 @@ mod derive {
#[test]
fn derive_with_existing_derives() {
- check_derive(
+ check(
r#"
//- minicore: derive, copy, clone, ord, eq, default, fmt
#[derive(PartialEq, Eq, Or$0)] struct Test;
@@ -890,7 +880,7 @@ mod derive {
#[test]
fn derive_flyimport() {
- check_derive(
+ check(
r#"
//- proc_macros: derive_identity
//- minicore: derive
@@ -904,7 +894,7 @@ mod derive {
kw self::
"#]],
);
- check_derive(
+ check(
r#"
//- proc_macros: derive_identity
//- minicore: derive
@@ -940,7 +930,7 @@ use proc_macros::DeriveIdentity;
#[test]
fn qualified() {
- check_derive(
+ check(
r#"
//- proc_macros: derive_identity
//- minicore: derive, copy, clone
@@ -950,7 +940,7 @@ use proc_macros::DeriveIdentity;
de DeriveIdentity proc_macro DeriveIdentity
"#]],
);
- check_derive(
+ check(
r#"
//- proc_macros: derive_identity
//- minicore: derive, copy, clone
@@ -1056,19 +1046,14 @@ mod lint {
mod repr {
use super::*;
- fn check_repr(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual);
- }
-
#[test]
fn no_completion_for_incorrect_repr() {
- check_repr(r#"#[repr{$0)] struct Test;"#, expect![[]])
+ check(r#"#[repr{$0)] struct Test;"#, expect![[]])
}
#[test]
fn empty() {
- check_repr(
+ check(
r#"#[repr($0)] struct Test;"#,
expect![[r#"
ba C
@@ -1093,12 +1078,12 @@ mod repr {
#[test]
fn transparent() {
- check_repr(r#"#[repr(transparent, $0)] struct Test;"#, expect![[r#""#]]);
+ check(r#"#[repr(transparent, $0)] struct Test;"#, expect![[r#""#]]);
}
#[test]
fn align() {
- check_repr(
+ check(
r#"#[repr(align(1), $0)] struct Test;"#,
expect![[r#"
ba C
@@ -1121,7 +1106,7 @@ mod repr {
#[test]
fn packed() {
- check_repr(
+ check(
r#"#[repr(packed, $0)] struct Test;"#,
expect![[r#"
ba C
@@ -1144,7 +1129,7 @@ mod repr {
#[test]
fn c() {
- check_repr(
+ check(
r#"#[repr(C, $0)] struct Test;"#,
expect![[r#"
ba align($0)
@@ -1167,7 +1152,7 @@ mod repr {
#[test]
fn prim() {
- check_repr(
+ check(
r#"#[repr(usize, $0)] struct Test;"#,
expect![[r#"
ba C
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index 3046614868..e117dbf4bd 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -4,18 +4,17 @@ use expect_test::{expect, Expect};
use crate::{
config::AutoImportExclusionType,
tests::{
- check_edit, check_empty, completion_list, completion_list_with_config, BASE_ITEMS_FIXTURE,
+ check, check_edit, check_with_base_items, completion_list_with_config, BASE_ITEMS_FIXTURE,
TEST_CONFIG,
},
CompletionConfig,
};
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
- expect.assert_eq(&actual)
-}
-
-fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Expect) {
+fn check_with_config(
+ config: CompletionConfig<'_>,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: Expect,
+) {
let actual = completion_list_with_config(
config,
&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"),
@@ -28,7 +27,7 @@ fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Exp
#[test]
fn complete_literal_struct_with_a_private_field() {
// `FooDesc.bar` is private, the completion should not be triggered.
- check(
+ check_with_base_items(
r#"
mod _69latrick {
pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, bar: bool }
@@ -67,6 +66,7 @@ fn baz() {
kw loop
kw match
kw mut
+ kw raw
kw return
kw self::
kw true
@@ -79,7 +79,7 @@ fn baz() {
#[test]
fn completes_various_bindings() {
- check_empty(
+ check(
r#"
fn func(param0 @ (param1, param2): (i32, i32)) {
let letlocal = 92;
@@ -125,7 +125,7 @@ fn func(param0 @ (param1, param2): (i32, i32)) {
#[test]
fn completes_all_the_things_in_fn_body() {
- check(
+ check_with_base_items(
r#"
use non_existent::Unresolved;
mod qualified { pub enum Enum { Variant } }
@@ -191,7 +191,7 @@ impl Unit {
?? Unresolved
"#]],
);
- check(
+ check_with_base_items(
r#"
use non_existent::Unresolved;
mod qualified { pub enum Enum { Variant } }
@@ -224,7 +224,7 @@ impl Unit {
#[test]
fn complete_in_block() {
- check_empty(
+ check(
r#"
fn foo() {
if true {
@@ -273,7 +273,7 @@ fn complete_in_block() {
#[test]
fn complete_after_if_expr() {
- check_empty(
+ check(
r#"
fn foo() {
if true {}
@@ -321,7 +321,7 @@ fn complete_after_if_expr() {
#[test]
fn complete_in_match_arm() {
- check_empty(
+ check(
r#"
fn foo() {
match () {
@@ -351,7 +351,7 @@ fn complete_in_match_arm() {
#[test]
fn completes_in_loop_ctx() {
- check_empty(
+ check(
r"fn my() { loop { $0 } }",
expect![[r#"
fn my() fn()
@@ -390,7 +390,7 @@ fn completes_in_loop_ctx() {
sn ppd
"#]],
);
- check_empty(
+ check(
r"fn my() { loop { foo.$0 } }",
expect![[r#"
sn box Box::new(expr)
@@ -415,7 +415,7 @@ fn completes_in_loop_ctx() {
#[test]
fn completes_in_let_initializer() {
- check_empty(
+ check(
r#"fn main() { let _ = $0 }"#,
expect![[r#"
fn main() fn()
@@ -438,8 +438,116 @@ fn completes_in_let_initializer() {
}
#[test]
+fn completes_after_ref_expr() {
+ check(
+ r#"fn main() { let _ = &$0 }"#,
+ expect![[r#"
+ fn main() fn()
+ bt u32 u32
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw mut
+ kw raw
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+ check(
+ r#"fn main() { let _ = &raw $0 }"#,
+ expect![[r#"
+ fn main() fn()
+ bt u32 u32
+ kw const
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw mut
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+ check(
+ r#"fn main() { let _ = &raw const $0 }"#,
+ expect![[r#"
+ fn main() fn()
+ bt u32 u32
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+ check(
+ r#"fn main() { let _ = &raw mut $0 }"#,
+ expect![[r#"
+ fn main() fn()
+ bt u32 u32
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+ check(
+ r#"fn main() { let _ = &mut $0 }"#,
+ expect![[r#"
+ fn main() fn()
+ bt u32 u32
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ )
+}
+
+#[test]
fn struct_initializer_field_expr() {
- check_empty(
+ check(
r#"
struct Foo {
pub f: i32,
@@ -475,7 +583,7 @@ fn foo() {
fn shadowing_shows_single_completion() {
cov_mark::check!(shadowing_shows_single_completion);
- check_empty(
+ check(
r#"
fn foo() {
let bar = 92;
@@ -508,7 +616,7 @@ fn foo() {
#[test]
fn in_macro_expr_frag() {
- check_empty(
+ check(
r#"
macro_rules! m { ($e:expr) => { $e } }
fn quux(x: i32) {
@@ -535,7 +643,7 @@ fn quux(x: i32) {
kw while let
"#]],
);
- check_empty(
+ check(
r"
macro_rules! m { ($e:expr) => { $e } }
fn quux(x: i32) {
@@ -562,7 +670,7 @@ fn quux(x: i32) {
kw while let
"#]],
);
- check_empty(
+ check(
r#"
macro_rules! m { ($e:expr) => { $e } }
fn quux(x: i32) {
@@ -595,7 +703,7 @@ fn quux(x: i32) {
#[test]
fn enum_qualified() {
- check(
+ check_with_base_items(
r#"
impl Enum {
type AssocType = ();
@@ -619,7 +727,7 @@ fn func() {
#[test]
fn ty_qualified_no_drop() {
- check_empty(
+ check(
r#"
//- minicore: drop
struct Foo;
@@ -636,7 +744,7 @@ fn func() {
#[test]
fn with_parens() {
- check_empty(
+ check(
r#"
enum Enum {
Variant()
@@ -657,7 +765,7 @@ fn func() {
#[test]
fn detail_impl_trait_in_return_position() {
- check_empty(
+ check(
r"
//- minicore: sized
trait Trait<T> {}
@@ -676,7 +784,7 @@ fn main() {
#[test]
fn detail_async_fn() {
- check_empty(
+ check(
r#"
//- minicore: future, sized
trait Trait<T> {}
@@ -697,7 +805,7 @@ fn main() {
#[test]
fn detail_impl_trait_in_argument_position() {
- check_empty(
+ check(
r"
//- minicore: sized
trait Trait<T> {}
@@ -717,7 +825,7 @@ fn main() {
#[test]
fn complete_record_expr_path() {
- check(
+ check_with_base_items(
r#"
struct Zulu;
impl Zulu {
@@ -738,7 +846,7 @@ fn main() {
#[test]
fn variant_with_struct() {
- check_empty(
+ check(
r#"
pub struct YoloVariant {
pub f: usize
@@ -813,7 +921,7 @@ fn return_value_no_block() {
#[test]
fn else_completion_after_if() {
- check_empty(
+ check(
r#"
fn foo() { if foo {} $0 }
"#,
@@ -854,7 +962,7 @@ fn foo() { if foo {} $0 }
sn ppd
"#]],
);
- check_empty(
+ check(
r#"
fn foo() { if foo {} el$0 }
"#,
@@ -895,7 +1003,7 @@ fn foo() { if foo {} el$0 }
sn ppd
"#]],
);
- check_empty(
+ check(
r#"
fn foo() { bar(if foo {} $0) }
"#,
@@ -919,7 +1027,7 @@ fn foo() { bar(if foo {} $0) }
kw while let
"#]],
);
- check_empty(
+ check(
r#"
fn foo() { bar(if foo {} el$0) }
"#,
@@ -943,7 +1051,7 @@ fn foo() { bar(if foo {} el$0) }
kw while let
"#]],
);
- check_empty(
+ check(
r#"
fn foo() { if foo {} $0 let x = 92; }
"#,
@@ -984,7 +1092,7 @@ fn foo() { if foo {} $0 let x = 92; }
sn ppd
"#]],
);
- check_empty(
+ check(
r#"
fn foo() { if foo {} el$0 let x = 92; }
"#,
@@ -1025,7 +1133,7 @@ fn foo() { if foo {} el$0 let x = 92; }
sn ppd
"#]],
);
- check_empty(
+ check(
r#"
fn foo() { if foo {} el$0 { let x = 92; } }
"#,
@@ -1070,7 +1178,7 @@ fn foo() { if foo {} el$0 { let x = 92; } }
#[test]
fn expr_no_unstable_item_on_stable() {
- check_empty(
+ check(
r#"
//- /main.rs crate:main deps:std
use std::*;
@@ -1121,7 +1229,7 @@ pub struct UnstableThisShouldNotBeListed;
#[test]
fn expr_unstable_item_on_nightly() {
- check_empty(
+ check(
r#"
//- toolchain:nightly
//- /main.rs crate:main deps:std
@@ -1174,7 +1282,7 @@ pub struct UnstableButWeAreOnNightlyAnyway;
#[test]
fn inside_format_args_completions_work() {
- check_empty(
+ check(
r#"
//- minicore: fmt
struct Foo;
@@ -1200,7 +1308,7 @@ fn main() {
sn unsafe unsafe {}
"#]],
);
- check_empty(
+ check(
r#"
//- minicore: fmt
struct Foo;
@@ -1230,7 +1338,7 @@ fn main() {
#[test]
fn inside_faulty_format_args_completions_work() {
- check_empty(
+ check(
r#"
//- minicore: fmt
struct Foo;
@@ -1256,7 +1364,7 @@ fn main() {
sn unsafe unsafe {}
"#]],
);
- check_empty(
+ check(
r#"
//- minicore: fmt
struct Foo;
@@ -1282,7 +1390,7 @@ fn main() {
sn unsafe unsafe {}
"#]],
);
- check_empty(
+ check(
r#"
//- minicore: fmt
struct Foo;
@@ -1308,7 +1416,7 @@ fn main() {
sn unsafe unsafe {}
"#]],
);
- check_empty(
+ check(
r#"
//- minicore: fmt
struct Foo;
@@ -1340,7 +1448,7 @@ fn main() {
#[test]
fn macro_that_ignores_completion_marker() {
- check(
+ check_with_base_items(
r#"
macro_rules! helper {
($v:ident) => {};
@@ -1788,7 +1896,7 @@ fn foo<T: ExcludedTrait>() {
#[test]
fn hide_ragennew_synthetic_identifiers() {
- check_empty(
+ check(
r#"
//- minicore: iterator
fn bar() {
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index d413977f7c..d491e438fe 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -6,11 +6,15 @@ use crate::{
CompletionConfig,
};
-fn check(ra_fixture: &str, expect: Expect) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
check_with_config(TEST_CONFIG, ra_fixture, expect);
}
-fn check_with_config(config: CompletionConfig<'_>, ra_fixture: &str, expect: Expect) {
+fn check_with_config(
+ config: CompletionConfig<'_>,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: Expect,
+) {
let (db, position) = crate::tests::position(ra_fixture);
let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap();
@@ -1742,7 +1746,7 @@ fn intrinsics() {
fn function() {
transmute$0
}
- "#,
+"#,
expect![[r#"
fn transmute(…) (use core::mem::transmute) unsafe fn(Src) -> Dst
"#]],
@@ -1763,7 +1767,9 @@ fn function() {
mem::transmute$0
}
"#,
- expect![""],
+ expect![[r#"
+ fn transmute(…) (use core::mem) unsafe fn(Src) -> Dst
+ "#]],
);
}
diff --git a/crates/ide-completion/src/tests/fn_param.rs b/crates/ide-completion/src/tests/fn_param.rs
index 4a89f874e1..451ce07c74 100644
--- a/crates/ide-completion/src/tests/fn_param.rs
+++ b/crates/ide-completion/src/tests/fn_param.rs
@@ -1,16 +1,6 @@
-use expect_test::{expect, Expect};
+use expect_test::expect;
-use crate::tests::{completion_list, completion_list_with_trigger_character};
-
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual);
-}
-
-fn check_with_trigger_character(ra_fixture: &str, trigger_character: char, expect: Expect) {
- let actual = completion_list_with_trigger_character(ra_fixture, Some(trigger_character));
- expect.assert_eq(&actual)
-}
+use crate::tests::{check, check_with_trigger_character};
#[test]
fn only_param() {
@@ -124,7 +114,7 @@ fn trigger_by_l_paren() {
r#"
fn foo($0)
"#,
- '(',
+ Some('('),
expect![[]],
)
}
diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs
index 79561a0419..bea6d60769 100644
--- a/crates/ide-completion/src/tests/item.rs
+++ b/crates/ide-completion/src/tests/item.rs
@@ -2,20 +2,13 @@
//!
//! Except for use items which are tested in [super::use_tree] and mod declarations with are tested
//! in [crate::completions::mod_].
-use expect_test::{expect, Expect};
+use expect_test::expect;
-use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};
-
-use super::check_edit;
-
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
- expect.assert_eq(&actual)
-}
+use crate::tests::{check_edit, check_with_base_items};
#[test]
fn target_type_or_trait_in_impl_block() {
- check(
+ check_with_base_items(
r#"
impl Tra$0
"#,
@@ -37,7 +30,7 @@ impl Tra$0
#[test]
fn target_type_in_trait_impl_block() {
- check(
+ check_with_base_items(
r#"
impl Trait for Str$0
"#,
@@ -59,7 +52,7 @@ impl Trait for Str$0
#[test]
fn after_trait_name_in_trait_def() {
- check(
+ check_with_base_items(
r"trait A $0",
expect![[r#"
kw where
@@ -69,21 +62,21 @@ fn after_trait_name_in_trait_def() {
#[test]
fn after_target_name_in_impl() {
- check(
+ check_with_base_items(
r"impl Trait $0",
expect![[r#"
kw for
kw where
"#]],
);
- check(
+ check_with_base_items(
r"impl Trait f$0",
expect![[r#"
kw for
kw where
"#]],
);
- check(
+ check_with_base_items(
r"impl Trait for Type $0",
expect![[r#"
kw where
@@ -93,44 +86,44 @@ fn after_target_name_in_impl() {
#[test]
fn completes_where() {
- check(
+ check_with_base_items(
r"struct Struct $0",
expect![[r#"
kw where
"#]],
);
- check(
+ check_with_base_items(
r"struct Struct $0 {}",
expect![[r#"
kw where
"#]],
);
// FIXME: This shouldn't be completed here
- check(
+ check_with_base_items(
r"struct Struct $0 ()",
expect![[r#"
kw where
"#]],
);
- check(
+ check_with_base_items(
r"fn func() $0",
expect![[r#"
kw where
"#]],
);
- check(
+ check_with_base_items(
r"enum Enum $0",
expect![[r#"
kw where
"#]],
);
- check(
+ check_with_base_items(
r"enum Enum $0 {}",
expect![[r#"
kw where
"#]],
);
- check(
+ check_with_base_items(
r"trait Trait $0 {}",
expect![[r#"
kw where
@@ -140,7 +133,7 @@ fn completes_where() {
#[test]
fn before_record_field() {
- check(
+ check_with_base_items(
r#"
struct Foo {
$0
@@ -244,7 +237,7 @@ impl Copy for S where $0
#[test]
fn test_is_not_considered_macro() {
- check(
+ check_with_base_items(
r#"
#[rustc_builtin]
pub macro test($item:item) {
diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs
index d3d52dc6df..841c42123a 100644
--- a/crates/ide-completion/src/tests/item_list.rs
+++ b/crates/ide-completion/src/tests/item_list.rs
@@ -1,16 +1,11 @@
//! Completion tests for item list position.
-use expect_test::{expect, Expect};
+use expect_test::expect;
-use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
-
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
- expect.assert_eq(&actual)
-}
+use crate::tests::{check, check_edit, check_with_base_items};
#[test]
fn in_mod_item_list() {
- check(
+ check_with_base_items(
r#"mod tests { $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -43,7 +38,7 @@ fn in_mod_item_list() {
#[test]
fn in_source_file_item_list() {
- check(
+ check_with_base_items(
r#"$0"#,
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -76,7 +71,7 @@ fn in_source_file_item_list() {
#[test]
fn in_item_list_after_attr() {
- check(
+ check_with_base_items(
r#"#[attr] $0"#,
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -109,7 +104,7 @@ fn in_item_list_after_attr() {
#[test]
fn in_qualified_path() {
- check(
+ check_with_base_items(
r#"crate::$0"#,
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -120,7 +115,7 @@ fn in_qualified_path() {
#[test]
fn after_unsafe_token() {
- check(
+ check_with_base_items(
r#"unsafe $0"#,
expect![[r#"
kw async
@@ -134,7 +129,7 @@ fn after_unsafe_token() {
#[test]
fn after_async_token() {
- check(
+ check_with_base_items(
r#"async $0"#,
expect![[r#"
kw fn
@@ -145,7 +140,7 @@ fn after_async_token() {
#[test]
fn after_visibility() {
- check(
+ check_with_base_items(
r#"pub $0"#,
expect![[r#"
kw async
@@ -167,7 +162,7 @@ fn after_visibility() {
#[test]
fn after_visibility_unsafe() {
- check(
+ check_with_base_items(
r#"pub unsafe $0"#,
expect![[r#"
kw async
@@ -179,7 +174,7 @@ fn after_visibility_unsafe() {
#[test]
fn in_impl_assoc_item_list() {
- check(
+ check_with_base_items(
r#"impl Struct { $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -199,7 +194,7 @@ fn in_impl_assoc_item_list() {
#[test]
fn in_impl_assoc_item_list_after_attr() {
- check(
+ check_with_base_items(
r#"impl Struct { #[attr] $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -219,7 +214,7 @@ fn in_impl_assoc_item_list_after_attr() {
#[test]
fn in_trait_assoc_item_list() {
- check(
+ check_with_base_items(
r"trait Foo { $0 }",
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -237,7 +232,7 @@ fn in_trait_assoc_item_list() {
#[test]
fn in_trait_assoc_fn_missing_body() {
- check(
+ check_with_base_items(
r#"trait Foo { fn function(); $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -255,7 +250,7 @@ fn in_trait_assoc_fn_missing_body() {
#[test]
fn in_trait_assoc_const_missing_body() {
- check(
+ check_with_base_items(
r#"trait Foo { const CONST: (); $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -273,7 +268,7 @@ fn in_trait_assoc_const_missing_body() {
#[test]
fn in_trait_assoc_type_aliases_missing_ty() {
- check(
+ check_with_base_items(
r#"trait Foo { type Type; $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -291,7 +286,7 @@ fn in_trait_assoc_type_aliases_missing_ty() {
#[test]
fn in_trait_impl_assoc_item_list() {
- check(
+ check_with_base_items(
r#"
trait Test {
type Type0;
@@ -326,7 +321,7 @@ impl Test for () {
#[test]
fn in_trait_impl_no_unstable_item_on_stable() {
- check_empty(
+ check(
r#"
trait Test {
#[unstable]
@@ -350,7 +345,7 @@ impl Test for () {
#[test]
fn in_trait_impl_unstable_item_on_nightly() {
- check_empty(
+ check(
r#"
//- toolchain:nightly
trait Test {
@@ -378,7 +373,7 @@ impl Test for () {
#[test]
fn after_unit_struct() {
- check(
+ check_with_base_items(
r#"struct S; f$0"#,
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -500,7 +495,7 @@ type O = $0;
#[test]
fn inside_extern_blocks() {
// Should suggest `fn`, `static`, `unsafe`
- check(
+ check_with_base_items(
r#"extern { $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -517,7 +512,7 @@ fn inside_extern_blocks() {
);
// Should suggest `fn`, `static`, `safe`, `unsafe`
- check(
+ check_with_base_items(
r#"unsafe extern { $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
@@ -534,7 +529,7 @@ fn inside_extern_blocks() {
"#]],
);
- check(
+ check_with_base_items(
r#"unsafe extern { pub safe $0 }"#,
expect![[r#"
kw fn
@@ -542,7 +537,7 @@ fn inside_extern_blocks() {
"#]],
);
- check(
+ check_with_base_items(
r#"unsafe extern { pub unsafe $0 }"#,
expect![[r#"
kw fn
diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs
index 2f1f555e52..9ec27252fd 100644
--- a/crates/ide-completion/src/tests/pattern.rs
+++ b/crates/ide-completion/src/tests/pattern.rs
@@ -1,16 +1,11 @@
//! Completion tests for pattern position.
-use expect_test::{expect, Expect};
+use expect_test::expect;
-use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
-
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
- expect.assert_eq(&actual)
-}
+use crate::tests::{check, check_edit, check_with_base_items};
#[test]
fn wildcard() {
- check(
+ check_with_base_items(
r#"
fn quux() {
let _$0
@@ -22,7 +17,7 @@ fn quux() {
#[test]
fn ident_rebind_pat() {
- check_empty(
+ check(
r#"
fn quux() {
let en$0 @ x
@@ -37,7 +32,7 @@ fn quux() {
#[test]
fn ident_ref_pat() {
- check_empty(
+ check(
r#"
fn quux() {
let ref en$0
@@ -47,7 +42,7 @@ fn quux() {
kw mut
"#]],
);
- check_empty(
+ check(
r#"
fn quux() {
let ref en$0 @ x
@@ -61,7 +56,7 @@ fn quux() {
#[test]
fn ident_ref_mut_pat() {
- check_empty(
+ check(
r#"
fn quux() {
let ref mut en$0
@@ -69,7 +64,7 @@ fn quux() {
"#,
expect![[r#""#]],
);
- check_empty(
+ check(
r#"
fn quux() {
let ref mut en$0 @ x
@@ -81,7 +76,7 @@ fn quux() {
#[test]
fn ref_pat() {
- check_empty(
+ check(
r#"
fn quux() {
let &en$0
@@ -91,7 +86,7 @@ fn quux() {
kw mut
"#]],
);
- check_empty(
+ check(
r#"
fn quux() {
let &mut en$0
@@ -99,7 +94,7 @@ fn quux() {
"#,
expect![[r#""#]],
);
- check_empty(
+ check(
r#"
fn foo() {
for &$0 in () {}
@@ -113,7 +108,7 @@ fn foo() {
#[test]
fn refutable() {
- check(
+ check_with_base_items(
r#"
fn foo() {
if let a$0
@@ -139,7 +134,7 @@ fn foo() {
#[test]
fn irrefutable() {
- check(
+ check_with_base_items(
r#"
enum SingleVariantEnum {
Variant
@@ -168,7 +163,7 @@ fn foo() {
#[test]
fn in_param() {
- check(
+ check_with_base_items(
r#"
fn foo(a$0) {
}
@@ -185,7 +180,7 @@ fn foo(a$0) {
kw ref
"#]],
);
- check(
+ check_with_base_items(
r#"
fn foo(a$0: Tuple) {
}
@@ -207,7 +202,7 @@ fn foo(a$0: Tuple) {
#[test]
fn only_fn_like_macros() {
- check_empty(
+ check(
r#"
macro_rules! m { ($e:expr) => { $e } }
@@ -228,7 +223,7 @@ fn foo() {
#[test]
fn in_simple_macro_call() {
- check_empty(
+ check(
r#"
macro_rules! m { ($e:expr) => { $e } }
enum E { X }
@@ -249,7 +244,7 @@ fn foo() {
#[test]
fn omits_private_fields_pat() {
- check_empty(
+ check(
r#"
mod foo {
pub struct Record { pub field: i32, _field: i32 }
@@ -277,7 +272,7 @@ fn outer() {
#[test]
fn completes_self_pats() {
- check_empty(
+ check(
r#"
struct Foo(i32);
impl Foo {
@@ -301,7 +296,7 @@ impl Foo {
#[test]
fn enum_qualified() {
- check(
+ check_with_base_items(
r#"
impl Enum {
type AssocType = ();
@@ -323,7 +318,7 @@ fn func() {
#[test]
fn completes_in_record_field_pat() {
- check_empty(
+ check(
r#"
struct Foo { bar: Bar }
struct Bar(u32);
@@ -342,7 +337,7 @@ fn outer(Foo { bar: $0 }: Foo) {}
#[test]
fn skips_in_record_field_pat_name() {
- check_empty(
+ check(
r#"
struct Foo { bar: Bar }
struct Bar(u32);
@@ -357,7 +352,7 @@ fn outer(Foo { bar$0 }: Foo) {}
#[test]
fn completes_in_record_field_pat_with_generic_type_alias() {
- check_empty(
+ check(
r#"
type Wrap<T> = T;
@@ -386,7 +381,7 @@ fn main() {
#[test]
fn completes_in_fn_param() {
- check_empty(
+ check(
r#"
struct Foo { bar: Bar }
struct Bar(u32);
@@ -405,7 +400,7 @@ fn foo($0) {}
#[test]
fn completes_in_closure_param() {
- check_empty(
+ check(
r#"
struct Foo { bar: Bar }
struct Bar(u32);
@@ -426,7 +421,7 @@ fn foo() {
#[test]
fn completes_no_delims_if_existing() {
- check_empty(
+ check(
r#"
struct Bar(u32);
fn foo() {
@@ -441,7 +436,7 @@ fn foo() {
kw self::
"#]],
);
- check_empty(
+ check(
r#"
struct Foo { bar: u32 }
fn foo() {
@@ -456,7 +451,7 @@ fn foo() {
kw self::
"#]],
);
- check_empty(
+ check(
r#"
enum Enum {
TupleVariant(u32)
@@ -471,7 +466,7 @@ fn foo() {
bn TupleVariant TupleVariant
"#]],
);
- check_empty(
+ check(
r#"
enum Enum {
RecordVariant { field: u32 }
@@ -519,7 +514,7 @@ fn foo() {
#[test]
fn completes_enum_variant_pat_escape() {
cov_mark::check!(enum_variant_pattern_path);
- check_empty(
+ check(
r#"
enum Enum {
A,
@@ -544,7 +539,7 @@ fn foo() {
"#]],
);
- check_empty(
+ check(
r#"
enum Enum {
A,
@@ -569,7 +564,7 @@ fn foo() {
#[test]
fn completes_associated_const() {
- check_empty(
+ check(
r#"
#[derive(PartialEq, Eq)]
struct Ty(u8);
@@ -590,7 +585,7 @@ fn f(t: Ty) {
"#]],
);
- check_empty(
+ check(
r#"
enum MyEnum {}
@@ -612,7 +607,7 @@ fn f(e: MyEnum) {
"#]],
);
- check_empty(
+ check(
r#"
union U {
i: i32,
@@ -637,7 +632,7 @@ fn f(u: U) {
"#]],
);
- check_empty(
+ check(
r#"
#![rustc_coherence_is_core]
#[lang = "u32"]
@@ -659,7 +654,7 @@ fn f(v: u32) {
#[test]
fn in_method_param() {
- check_empty(
+ check(
r#"
struct Ty(u8);
@@ -680,7 +675,7 @@ impl Ty {
kw ref
"#]],
);
- check_empty(
+ check(
r#"
struct Ty(u8);
@@ -701,7 +696,7 @@ impl Ty {
kw ref
"#]],
);
- check_empty(
+ check(
r#"
struct Ty(u8);
@@ -722,7 +717,7 @@ impl Ty {
kw ref
"#]],
);
- check_empty(
+ check(
r#"
struct Ty(u8);
@@ -743,7 +738,7 @@ impl Ty {
#[test]
fn through_alias() {
- check_empty(
+ check(
r#"
enum Enum<T> {
Unit,
@@ -770,7 +765,7 @@ fn f(x: EnumAlias<u8>) {
#[test]
fn pat_no_unstable_item_on_stable() {
- check_empty(
+ check(
r#"
//- /main.rs crate:main deps:std
use std::*;
@@ -795,7 +790,7 @@ pub enum Enum {
#[test]
fn pat_unstable_item_on_nightly() {
- check_empty(
+ check(
r#"
//- toolchain:nightly
//- /main.rs crate:main deps:std
@@ -908,7 +903,7 @@ fn foo() {
);
// Do not suggest reserved keywords
- check_empty(
+ check(
r#"
struct Struct;
@@ -926,7 +921,7 @@ fn foo() {
#[test]
fn private_item_in_module_in_function_body() {
- check_empty(
+ check(
r#"
fn main() {
mod foo {
diff --git a/crates/ide-completion/src/tests/predicate.rs b/crates/ide-completion/src/tests/predicate.rs
index c1926359ef..65036f6a22 100644
--- a/crates/ide-completion/src/tests/predicate.rs
+++ b/crates/ide-completion/src/tests/predicate.rs
@@ -1,17 +1,12 @@
//! Completion tests for predicates and bounds.
-use expect_test::{expect, Expect};
+use expect_test::expect;
-use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE};
-
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
- expect.assert_eq(&actual)
-}
+use crate::tests::{check, check_with_base_items};
#[test]
fn predicate_start() {
// FIXME: `for` kw
- check(
+ check_with_base_items(
r#"
struct Foo<'lt, T, const C: usize> where $0 {}
"#,
@@ -34,7 +29,7 @@ struct Foo<'lt, T, const C: usize> where $0 {}
#[test]
fn bound_for_type_pred() {
- check(
+ check_with_base_items(
r#"
struct Foo<'lt, T, const C: usize> where T: $0 {}
"#,
@@ -52,7 +47,7 @@ struct Foo<'lt, T, const C: usize> where T: $0 {}
fn bound_for_lifetime_pred() {
// FIXME: should only show lifetimes here, that is we shouldn't get any completions here when not typing
// a `'`
- check(
+ check_with_base_items(
r#"
struct Foo<'lt, T, const C: usize> where 'lt: $0 {}
"#,
@@ -68,7 +63,7 @@ struct Foo<'lt, T, const C: usize> where 'lt: $0 {}
#[test]
fn bound_for_for_pred() {
- check(
+ check_with_base_items(
r#"
struct Foo<'lt, T, const C: usize> where for<'a> T: $0 {}
"#,
@@ -84,7 +79,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> T: $0 {}
#[test]
fn param_list_for_for_pred() {
- check(
+ check_with_base_items(
r#"
struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
"#,
@@ -107,7 +102,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
#[test]
fn pred_on_fn_in_impl() {
- check(
+ check_with_base_items(
r#"
impl Record {
fn method(self) where $0 {}
@@ -132,7 +127,7 @@ impl Record {
#[test]
fn pred_no_unstable_item_on_stable() {
- check_empty(
+ check(
r#"
//- /main.rs crate:main deps:std
use std::*;
@@ -151,7 +146,7 @@ pub trait Trait {}
#[test]
fn pred_unstable_item_on_nightly() {
- check_empty(
+ check(
r#"
//- toolchain:nightly
//- /main.rs crate:main deps:std
diff --git a/crates/ide-completion/src/tests/proc_macros.rs b/crates/ide-completion/src/tests/proc_macros.rs
index afc286b6fb..6b1dfe366c 100644
--- a/crates/ide-completion/src/tests/proc_macros.rs
+++ b/crates/ide-completion/src/tests/proc_macros.rs
@@ -1,12 +1,7 @@
//! Completion tests for expressions.
-use expect_test::{expect, Expect};
+use expect_test::expect;
-use crate::tests::completion_list;
-
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual)
-}
+use crate::tests::check;
#[test]
fn complete_dot_in_attr() {
diff --git a/crates/ide-completion/src/tests/raw_identifiers.rs b/crates/ide-completion/src/tests/raw_identifiers.rs
index d81b3d697a..9ab66243b5 100644
--- a/crates/ide-completion/src/tests/raw_identifiers.rs
+++ b/crates/ide-completion/src/tests/raw_identifiers.rs
@@ -4,7 +4,7 @@ use itertools::Itertools;
use crate::tests::{completion_list_with_config_raw, position, TEST_CONFIG};
-fn check(ra_fixture: &str, expect: Expect) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let completions = completion_list_with_config_raw(TEST_CONFIG, ra_fixture, true, None);
let (db, position) = position(ra_fixture);
let mut actual = db.file_text(position.file_id).to_string();
diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs
index a9c9f604e0..a1013b8654 100644
--- a/crates/ide-completion/src/tests/record.rs
+++ b/crates/ide-completion/src/tests/record.rs
@@ -1,14 +1,9 @@
-use expect_test::{expect, Expect};
+use expect_test::expect;
-use crate::tests::completion_list;
+use crate::tests::check;
use super::check_edit;
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual);
-}
-
#[test]
fn without_default_impl() {
check(
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index 6cfb2231a9..2b05184bdb 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -5,32 +5,12 @@ use ide_db::SymbolKind;
use crate::{
tests::{
- check_edit, completion_list, completion_list_no_kw, completion_list_with_trigger_character,
+ check, check_edit, check_no_kw, check_with_trigger_character, do_completion_with_config,
+ TEST_CONFIG,
},
CompletionItemKind,
};
-use super::{do_completion_with_config, TEST_CONFIG};
-
-fn check_no_kw(ra_fixture: &str, expect: Expect) {
- let actual = completion_list_no_kw(ra_fixture);
- expect.assert_eq(&actual)
-}
-
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual)
-}
-
-pub(crate) fn check_with_trigger_character(
- ra_fixture: &str,
- trigger_character: Option<char>,
- expect: Expect,
-) {
- let actual = completion_list_with_trigger_character(ra_fixture, trigger_character);
- expect.assert_eq(&actual)
-}
-
#[test]
fn completes_if_prefix_is_keyword() {
check_edit(
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index 9ea262bcc5..c7e2d05825 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -1,16 +1,11 @@
//! Completion tests for type position.
-use expect_test::{expect, Expect};
+use expect_test::expect;
-use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE};
-
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
- expect.assert_eq(&actual)
-}
+use crate::tests::{check, check_with_base_items};
#[test]
fn record_field_ty() {
- check(
+ check_with_base_items(
r#"
struct Foo<'lt, T, const C: usize> {
f: $0
@@ -37,7 +32,7 @@ struct Foo<'lt, T, const C: usize> {
#[test]
fn tuple_struct_field() {
- check(
+ check_with_base_items(
r#"
struct Foo<'lt, T, const C: usize>(f$0);
"#,
@@ -65,7 +60,7 @@ struct Foo<'lt, T, const C: usize>(f$0);
#[test]
fn fn_return_type() {
- check(
+ check_with_base_items(
r#"
fn x<'lt, T, const C: usize>() -> $0
"#,
@@ -88,7 +83,7 @@ fn x<'lt, T, const C: usize>() -> $0
#[test]
fn fn_return_type_no_local_items() {
- check(
+ check_with_base_items(
r#"
fn foo() -> B$0 {
struct Bar;
@@ -118,7 +113,7 @@ fn foo() -> B$0 {
#[test]
fn inferred_type_const() {
- check(
+ check_with_base_items(
r#"
struct Foo<T>(T);
const FOO: $0 = Foo(2);
@@ -143,7 +138,7 @@ const FOO: $0 = Foo(2);
#[test]
fn inferred_type_closure_param() {
- check(
+ check_with_base_items(
r#"
fn f1(f: fn(i32) -> i32) {}
fn f2() {
@@ -169,7 +164,7 @@ fn f2() {
#[test]
fn inferred_type_closure_return() {
- check(
+ check_with_base_items(
r#"
fn f1(f: fn(u64) -> u64) {}
fn f2() {
@@ -197,7 +192,7 @@ fn f2() {
#[test]
fn inferred_type_fn_return() {
- check(
+ check_with_base_items(
r#"
fn f2(x: u64) -> $0 {
x + 5
@@ -222,7 +217,7 @@ fn f2(x: u64) -> $0 {
#[test]
fn inferred_type_fn_param() {
- check(
+ check_with_base_items(
r#"
fn f1(x: i32) {}
fn f2(x: $0) {
@@ -248,7 +243,7 @@ fn f2(x: $0) {
#[test]
fn inferred_type_not_in_the_scope() {
- check(
+ check_with_base_items(
r#"
mod a {
pub struct Foo<T>(T);
@@ -282,7 +277,7 @@ fn foo<'lt, T, const C: usize>() {
#[test]
fn inferred_type_let() {
- check(
+ check_with_base_items(
r#"
struct Foo<T>(T);
fn foo<'lt, T, const C: usize>() {
@@ -311,7 +306,7 @@ fn foo<'lt, T, const C: usize>() {
#[test]
fn body_type_pos() {
- check(
+ check_with_base_items(
r#"
fn foo<'lt, T, const C: usize>() {
let local = ();
@@ -333,7 +328,7 @@ fn foo<'lt, T, const C: usize>() {
kw self::
"#]],
);
- check(
+ check_with_base_items(
r#"
fn foo<'lt, T, const C: usize>() {
let local = ();
@@ -356,7 +351,7 @@ fn foo<'lt, T, const C: usize>() {
#[test]
fn completes_types_and_const_in_arg_list() {
cov_mark::check!(complete_assoc_type_in_generics_list);
- check(
+ check_with_base_items(
r#"
trait Trait1 {
type Super;
@@ -372,7 +367,7 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
ta Super = (as Trait1) type Super
"#]],
);
- check(
+ check_with_base_items(
r#"
trait Trait1 {
type Super;
@@ -400,7 +395,7 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
kw self::
"#]],
);
- check(
+ check_with_base_items(
r#"
trait Trait2<T> {
type Foo;
@@ -424,7 +419,7 @@ fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {}
#[test]
fn no_assoc_completion_outside_type_bounds() {
- check(
+ check_with_base_items(
r#"
struct S;
trait Tr<T> {
@@ -454,7 +449,7 @@ impl Tr<$0
#[test]
fn enum_qualified() {
- check(
+ check_with_base_items(
r#"
impl Enum {
type AssocType = ();
@@ -471,7 +466,7 @@ fn func(_: Enum::$0) {}
#[test]
fn completes_type_parameter_or_associated_type() {
- check(
+ check_with_base_items(
r#"
trait MyTrait<T, U> {
type Item1;
@@ -496,7 +491,7 @@ fn f(t: impl MyTrait<u$0
"#]],
);
- check(
+ check_with_base_items(
r#"
trait MyTrait<T, U> {
type Item1;
@@ -521,7 +516,7 @@ fn f(t: impl MyTrait<u8, u$0
"#]],
);
- check(
+ check_with_base_items(
r#"
trait MyTrait<T, U> {
type Item1;
@@ -539,7 +534,7 @@ fn f(t: impl MyTrait<u8, u8, I$0
#[test]
fn completes_type_parameter_or_associated_type_with_default_value() {
- check(
+ check_with_base_items(
r#"
trait MyTrait<T, U = u8> {
type Item1;
@@ -564,7 +559,7 @@ fn f(t: impl MyTrait<u$0
"#]],
);
- check(
+ check_with_base_items(
r#"
trait MyTrait<T, U = u8> {
type Item1;
@@ -591,7 +586,7 @@ fn f(t: impl MyTrait<u8, u$0
"#]],
);
- check(
+ check_with_base_items(
r#"
trait MyTrait<T, U = u8> {
type Item1;
@@ -609,7 +604,7 @@ fn f(t: impl MyTrait<u8, u8, I$0
#[test]
fn completes_types_after_associated_type() {
- check(
+ check_with_base_items(
r#"
trait MyTrait {
type Item1;
@@ -634,7 +629,7 @@ fn f(t: impl MyTrait<Item1 = $0
"#]],
);
- check(
+ check_with_base_items(
r#"
trait MyTrait {
type Item1;
@@ -659,7 +654,7 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
"#]],
);
- check(
+ check_with_base_items(
r#"
trait MyTrait {
const C: usize;
@@ -678,7 +673,7 @@ fn f(t: impl MyTrait<C = $0
#[test]
fn type_pos_no_unstable_type_on_stable() {
- check_empty(
+ check(
r#"
//- /main.rs crate:main deps:std
use std::*;
@@ -702,7 +697,7 @@ pub struct S;
#[test]
fn type_pos_unstable_type_on_nightly() {
- check_empty(
+ check(
r#"
//- toolchain:nightly
//- /main.rs crate:main deps:std
@@ -729,7 +724,7 @@ pub struct S;
#[test]
fn completes_const_and_type_generics_separately() {
// Function generic params
- check(
+ check_with_base_items(
r#"
struct Foo;
const X: usize = 0;
@@ -756,7 +751,7 @@ fn completes_const_and_type_generics_separately() {
// FIXME: This should probably also suggest completions for types, at least those that have
// associated constants usable in this position. For example, a user could be typing
// `foo::<_, { usize::MAX }>()`, but we currently don't suggest `usize` in constant position.
- check(
+ check_with_base_items(
r#"
struct Foo;
const X: usize = 0;
@@ -775,7 +770,7 @@ fn completes_const_and_type_generics_separately() {
);
// Method generic params
- check(
+ check_with_base_items(
r#"
const X: usize = 0;
struct Foo;
@@ -799,7 +794,7 @@ fn completes_const_and_type_generics_separately() {
kw self::
"#]],
);
- check(
+ check_with_base_items(
r#"
const X: usize = 0;
struct Foo;
@@ -818,7 +813,7 @@ fn completes_const_and_type_generics_separately() {
);
// Associated type generic params
- check(
+ check_with_base_items(
r#"
const X: usize = 0;
struct Foo;
@@ -843,7 +838,7 @@ fn completes_const_and_type_generics_separately() {
kw self::
"#]],
);
- check(
+ check_with_base_items(
r#"
const X: usize = 0;
struct Foo;
@@ -862,7 +857,7 @@ fn completes_const_and_type_generics_separately() {
);
// Type generic params
- check(
+ check_with_base_items(
r#"
const X: usize = 0;
struct Foo<T, const N: usize>(T);
@@ -880,7 +875,7 @@ fn completes_const_and_type_generics_separately() {
);
// Type alias generic params
- check(
+ check_with_base_items(
r#"
const X: usize = 0;
struct Foo<T, const N: usize>(T);
@@ -899,7 +894,7 @@ fn completes_const_and_type_generics_separately() {
);
// Enum variant params
- check(
+ check_with_base_items(
r#"
const X: usize = 0;
enum Foo<T, const N: usize> { A(T), B }
@@ -917,7 +912,7 @@ fn completes_const_and_type_generics_separately() {
);
// Trait params
- check(
+ check_with_base_items(
r#"
const X: usize = 0;
trait Foo<T, const N: usize> {}
@@ -933,7 +928,7 @@ fn completes_const_and_type_generics_separately() {
);
// Trait alias params
- check(
+ check_with_base_items(
r#"
#![feature(trait_alias)]
const X: usize = 0;
@@ -951,7 +946,7 @@ fn completes_const_and_type_generics_separately() {
);
// Omitted lifetime params
- check(
+ check_with_base_items(
r#"
struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
fn foo<'a>() { S::<F$0, _>; }
@@ -964,7 +959,7 @@ fn foo<'a>() { S::<F$0, _>; }
"#]],
);
// Explicit lifetime params
- check(
+ check_with_base_items(
r#"
struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
fn foo<'a>() { S::<'static, 'static, F$0, _>; }
@@ -976,7 +971,7 @@ fn foo<'a>() { S::<'static, 'static, F$0, _>; }
kw self::
"#]],
);
- check(
+ check_with_base_items(
r#"
struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
fn foo<'a>() { S::<'static, F$0, _, _>; }
@@ -992,7 +987,7 @@ fn foo<'a>() { S::<'static, F$0, _, _>; }
#[test]
fn complete_traits_on_impl_trait_block() {
- check(
+ check_with_base_items(
r#"
trait Foo {}
@@ -1012,7 +1007,7 @@ impl $0 for Bar { }
#[test]
fn complete_traits_with_path_on_impl_trait_block() {
- check(
+ check_with_base_items(
r#"
mod outer {
pub trait Foo {}
diff --git a/crates/ide-completion/src/tests/use_tree.rs b/crates/ide-completion/src/tests/use_tree.rs
index 2ea2e4e4c9..04b3a47a64 100644
--- a/crates/ide-completion/src/tests/use_tree.rs
+++ b/crates/ide-completion/src/tests/use_tree.rs
@@ -1,12 +1,7 @@
//! Completion tests for use trees.
-use expect_test::{expect, Expect};
+use expect_test::expect;
-use crate::tests::completion_list;
-
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual)
-}
+use crate::tests::check;
#[test]
fn use_tree_completion() {
diff --git a/crates/ide-completion/src/tests/visibility.rs b/crates/ide-completion/src/tests/visibility.rs
index c18d6e66dd..4b5a0ac1c2 100644
--- a/crates/ide-completion/src/tests/visibility.rs
+++ b/crates/ide-completion/src/tests/visibility.rs
@@ -1,17 +1,7 @@
//! Completion tests for visibility modifiers.
-use expect_test::{expect, Expect};
+use expect_test::expect;
-use crate::tests::{completion_list, completion_list_with_trigger_character};
-
-fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual)
-}
-
-fn check_with_trigger_character(ra_fixture: &str, trigger_character: char, expect: Expect) {
- let actual = completion_list_with_trigger_character(ra_fixture, Some(trigger_character));
- expect.assert_eq(&actual)
-}
+use crate::tests::{check, check_with_trigger_character};
#[test]
fn empty_pub() {
@@ -20,7 +10,7 @@ fn empty_pub() {
r#"
pub($0)
"#,
- '(',
+ Some('('),
expect![[r#"
kw crate
kw in
diff --git a/crates/ide-db/src/active_parameter.rs b/crates/ide-db/src/active_parameter.rs
index 7482491fc6..11808fed3b 100644
--- a/crates/ide-db/src/active_parameter.rs
+++ b/crates/ide-db/src/active_parameter.rs
@@ -4,7 +4,7 @@ use either::Either;
use hir::{InFile, Semantics, Type};
use parser::T;
use syntax::{
- ast::{self, HasArgList, HasName},
+ ast::{self, AstChildren, HasArgList, HasAttrs, HasName},
match_ast, AstNode, NodeOrToken, SyntaxToken,
};
@@ -37,6 +37,10 @@ impl ActiveParameter {
_ => None,
})
}
+
+ pub fn attrs(&self) -> Option<AstChildren<ast::Attr>> {
+ self.src.as_ref().and_then(|param| Some(param.value.as_ref().right()?.attrs()))
+ }
}
/// Returns a [`hir::Callable`] this token is a part of and its argument index of said callable.
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index 2d30bb4127..49d26dfe25 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -32,6 +32,7 @@ pub enum Definition {
Field(Field),
TupleField(TupleField),
Module(Module),
+ Crate(Crate),
Function(Function),
Adt(Adt),
Variant(Variant),
@@ -62,14 +63,19 @@ impl Definition {
pub fn krate(&self, db: &RootDatabase) -> Option<Crate> {
Some(match self {
Definition::Module(m) => m.krate(),
+ &Definition::Crate(it) => it,
_ => self.module(db)?.krate(),
})
}
+ /// Returns the module this definition resides in.
+ ///
+ /// As such, for modules themselves this will return the parent module.
pub fn module(&self, db: &RootDatabase) -> Option<Module> {
let module = match self {
Definition::Macro(it) => it.module(db),
Definition::Module(it) => it.parent(db)?,
+ Definition::Crate(_) => return None,
Definition::Field(it) => it.parent_def(db).module(db),
Definition::Function(it) => it.module(db),
Definition::Adt(it) => it.module(db),
@@ -86,11 +92,11 @@ impl Definition {
Definition::ExternCrateDecl(it) => it.module(db),
Definition::DeriveHelper(it) => it.derive().module(db),
Definition::InlineAsmOperand(it) => it.parent(db).module(db),
+ Definition::ToolModule(t) => t.krate().root_module(),
Definition::BuiltinAttr(_)
| Definition::BuiltinType(_)
| Definition::BuiltinLifetime(_)
| Definition::TupleField(_)
- | Definition::ToolModule(_)
| Definition::InlineAsmRegOrRegClass(_) => return None,
};
Some(module)
@@ -108,6 +114,7 @@ impl Definition {
match self {
Definition::Macro(it) => Some(it.module(db).into()),
Definition::Module(it) => it.parent(db).map(Definition::Module),
+ Definition::Crate(_) => None,
Definition::Field(it) => Some(it.parent_def(db).into()),
Definition::Function(it) => container_to_definition(it.container(db)),
Definition::Adt(it) => Some(it.module(db).into()),
@@ -137,6 +144,7 @@ impl Definition {
let vis = match self {
Definition::Field(sf) => sf.visibility(db),
Definition::Module(it) => it.visibility(db),
+ Definition::Crate(_) => return None,
Definition::Function(it) => it.visibility(db),
Definition::Adt(it) => it.visibility(db),
Definition::Const(it) => it.visibility(db),
@@ -146,8 +154,8 @@ impl Definition {
Definition::TypeAlias(it) => it.visibility(db),
Definition::Variant(it) => it.visibility(db),
Definition::ExternCrateDecl(it) => it.visibility(db),
+ Definition::Macro(it) => it.visibility(db),
Definition::BuiltinType(_) | Definition::TupleField(_) => Visibility::Public,
- Definition::Macro(_) => return None,
Definition::BuiltinAttr(_)
| Definition::BuiltinLifetime(_)
| Definition::ToolModule(_)
@@ -167,6 +175,9 @@ impl Definition {
Definition::Macro(it) => it.name(db),
Definition::Field(it) => it.name(db),
Definition::Module(it) => it.name(db)?,
+ Definition::Crate(it) => {
+ Name::new_symbol_root(it.display_name(db)?.crate_name().symbol().clone())
+ }
Definition::Function(it) => it.name(db),
Definition::Adt(it) => it.name(db),
Definition::Variant(it) => it.name(db),
@@ -202,6 +213,7 @@ impl Definition {
Definition::Macro(it) => it.docs(db),
Definition::Field(it) => it.docs(db),
Definition::Module(it) => it.docs(db),
+ Definition::Crate(it) => it.docs(db),
Definition::Function(it) => it.docs(db),
Definition::Adt(it) => it.docs(db),
Definition::Variant(it) => it.docs(db),
@@ -282,6 +294,7 @@ impl Definition {
Definition::Field(it) => it.display(db, edition).to_string(),
Definition::TupleField(it) => it.display(db, edition).to_string(),
Definition::Module(it) => it.display(db, edition).to_string(),
+ Definition::Crate(it) => it.display(db, edition).to_string(),
Definition::Function(it) => it.display(db, edition).to_string(),
Definition::Adt(it) => it.display(db, edition).to_string(),
Definition::Variant(it) => it.display(db, edition).to_string(),
@@ -415,7 +428,7 @@ impl IdentClass {
}
IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
res.push((Definition::ExternCrateDecl(decl), None));
- res.push((Definition::Module(krate.root_module()), None));
+ res.push((Definition::Crate(krate), None));
}
IdentClass::Operator(
OperatorClass::Await(func)
@@ -456,7 +469,7 @@ impl IdentClass {
}
IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
res.push(Definition::ExternCrateDecl(decl));
- res.push(Definition::Module(krate.root_module()));
+ res.push(Definition::Crate(krate));
}
IdentClass::Operator(_) => (),
}
@@ -800,7 +813,7 @@ impl NameRefClass {
let extern_crate = sema.to_def(&extern_crate_ast)?;
let krate = extern_crate.resolved_crate(sema.db)?;
Some(if extern_crate_ast.rename().is_some() {
- NameRefClass::Definition(Definition::Module(krate.root_module()), None)
+ NameRefClass::Definition(Definition::Crate(krate), None)
} else {
NameRefClass::ExternCrateShorthand { krate, decl: extern_crate }
})
@@ -976,3 +989,19 @@ impl From<GenericDef> for Definition {
}
}
}
+
+impl TryFrom<Definition> for GenericDef {
+ type Error = ();
+ fn try_from(def: Definition) -> Result<Self, Self::Error> {
+ match def {
+ Definition::Function(it) => Ok(it.into()),
+ Definition::Adt(it) => Ok(it.into()),
+ Definition::Trait(it) => Ok(it.into()),
+ Definition::TraitAlias(it) => Ok(it.into()),
+ Definition::TypeAlias(it) => Ok(it.into()),
+ Definition::SelfType(it) => Ok(it.into()),
+ Definition::Const(it) => Ok(it.into()),
+ _ => Err(()),
+ }
+ }
+}
diff --git a/crates/ide-db/src/documentation.rs b/crates/ide-db/src/documentation.rs
index a0ef0f90a6..b83efcd02f 100644
--- a/crates/ide-db/src/documentation.rs
+++ b/crates/ide-db/src/documentation.rs
@@ -178,7 +178,7 @@ macro_rules! impl_has_docs {
impl_has_docs![
Variant, Field, Static, Const, Trait, TraitAlias, TypeAlias, Macro, Function, Adt, Module,
- Impl,
+ Impl, Crate,
];
macro_rules! impl_has_docs_enum {
diff --git a/crates/ide-db/src/famous_defs.rs b/crates/ide-db/src/famous_defs.rs
index ba6e50abf6..9e3506d6f5 100644
--- a/crates/ide-db/src/famous_defs.rs
+++ b/crates/ide-db/src/famous_defs.rs
@@ -106,6 +106,10 @@ impl FamousDefs<'_, '_> {
self.find_trait("core:marker:Copy")
}
+ pub fn core_marker_Sized(&self) -> Option<Trait> {
+ self.find_trait("core:marker:Sized")
+ }
+
pub fn core_future_Future(&self) -> Option<Trait> {
self.find_trait("core:future:Future")
}
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index 8f0be1d903..ad86d855b5 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -1,15 +1,17 @@
//! Look up accessible paths for items.
+use std::ops::ControlFlow;
+
use hir::{
db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ImportPathConfig,
- ItemInNs, ModPath, Module, ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics,
+ ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics,
SemanticsScope, Trait, TyFingerprint, Type,
};
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::{
ast::{self, make, HasName},
- AstNode, SmolStr, SyntaxNode,
+ AstNode, SyntaxNode,
};
use crate::{
@@ -51,7 +53,7 @@ pub struct TraitImportCandidate {
#[derive(Debug)]
pub struct PathImportCandidate {
/// Optional qualifier before name.
- pub qualifier: Vec<SmolStr>,
+ pub qualifier: Vec<Name>,
/// The name the item (struct, trait, enum, etc.) should have.
pub name: NameToImport,
}
@@ -70,10 +72,18 @@ pub enum NameToImport {
impl NameToImport {
pub fn exact_case_sensitive(s: String) -> NameToImport {
+ let s = match s.strip_prefix("r#") {
+ Some(s) => s.to_owned(),
+ None => s,
+ };
NameToImport::Exact(s, true)
}
pub fn fuzzy(s: String) -> NameToImport {
+ let s = match s.strip_prefix("r#") {
+ Some(s) => s.to_owned(),
+ None => s,
+ };
// unless all chars are lowercase, we do a case sensitive search
let case_sensitive = s.chars().any(|c| c.is_uppercase());
NameToImport::Fuzzy(s, case_sensitive)
@@ -350,21 +360,27 @@ fn path_applicable_imports(
.take(DEFAULT_QUERY_SEARCH_LIMIT.inner())
.collect()
}
+ // we have some unresolved qualifier that we search an import for
+ // The key here is that whatever we import must form a resolved path for the remainder of
+ // what follows
+ // FIXME: This doesn't handle visibility
[first_qsegment, qualifier_rest @ ..] => items_locator::items_with_name(
sema,
current_crate,
- NameToImport::Exact(first_qsegment.to_string(), true),
+ NameToImport::Exact(first_qsegment.as_str().to_owned(), true),
AssocSearchMode::Exclude,
)
.filter_map(|item| {
- import_for_item(
+ // we found imports for `first_qsegment`, now we need to filter these imports by whether
+ // they result in resolving the rest of the path successfully
+ validate_resolvable(
sema,
scope,
mod_path,
+ scope_filter,
&path_candidate.name,
item,
qualifier_rest,
- scope_filter,
)
})
.take(DEFAULT_QUERY_SEARCH_LIMIT.inner())
@@ -372,14 +388,16 @@ fn path_applicable_imports(
}
}
-fn import_for_item(
+/// Validates and builds an import for `resolved_qualifier` if the `unresolved_qualifier` appended
+/// to it resolves and there is a validate `candidate` after that.
+fn validate_resolvable(
sema: &Semantics<'_, RootDatabase>,
scope: &SemanticsScope<'_>,
mod_path: impl Fn(ItemInNs) -> Option<ModPath>,
+ scope_filter: impl Fn(ItemInNs) -> bool,
candidate: &NameToImport,
resolved_qualifier: ItemInNs,
- unresolved_qualifier: &[SmolStr],
- scope_filter: impl Fn(ItemInNs) -> bool,
+ unresolved_qualifier: &[Name],
) -> Option<LocatedImport> {
let _p = tracing::info_span!("ImportAssets::import_for_item").entered();
@@ -410,8 +428,11 @@ fn import_for_item(
module,
candidate.clone(),
AssocSearchMode::Exclude,
+ |it| match scope_filter(it) {
+ true => ControlFlow::Break(it),
+ false => ControlFlow::Continue(()),
+ },
)
- .find(|&it| scope_filter(it))
.map(|item| LocatedImport::new(import_path_candidate, resolved_qualifier, item))
}
// FIXME
@@ -709,7 +730,7 @@ fn path_import_candidate(
if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) {
let qualifier = qualifier
.segments()
- .map(|seg| seg.name_ref().map(|name| SmolStr::new(name.text())))
+ .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text())))
.collect::<Option<Vec<_>>>()?;
ImportCandidate::Path(PathImportCandidate { qualifier, name })
} else {
diff --git a/crates/ide-db/src/imports/insert_use/tests.rs b/crates/ide-db/src/imports/insert_use/tests.rs
index 81604b5527..decb0ea9d8 100644
--- a/crates/ide-db/src/imports/insert_use/tests.rs
+++ b/crates/ide-db/src/imports/insert_use/tests.rs
@@ -1244,8 +1244,8 @@ use ::ext::foo::Foo;
fn check_with_config(
path: &str,
- ra_fixture_before: &str,
- ra_fixture_after: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
config: &InsertUseConfig,
) {
let (db, file_id, pos) = if ra_fixture_before.contains(CURSOR_MARKER) {
@@ -1277,8 +1277,8 @@ fn check_with_config(
fn check(
path: &str,
- ra_fixture_before: &str,
- ra_fixture_after: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
granularity: ImportGranularity,
) {
check_with_config(
@@ -1295,19 +1295,35 @@ fn check(
)
}
-fn check_crate(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+fn check_crate(
+ path: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Crate)
}
-fn check_module(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+fn check_module(
+ path: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Module)
}
-fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+fn check_none(
+ path: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::Item)
}
-fn check_one(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+fn check_one(
+ path: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
check(path, ra_fixture_before, ra_fixture_after, ImportGranularity::One)
}
@@ -1330,7 +1346,7 @@ fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior
assert_eq!(result.map(|u| u.to_string()), None);
}
-fn check_guess(ra_fixture: &str, expected: ImportGranularityGuess) {
+fn check_guess(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: ImportGranularityGuess) {
let syntax = ast::SourceFile::parse(ra_fixture, span::Edition::CURRENT).tree().syntax().clone();
let file = ImportScope::from(syntax).unwrap();
assert_eq!(super::guess_granularity_from_scope(&file), expected);
diff --git a/crates/ide-db/src/items_locator.rs b/crates/ide-db/src/items_locator.rs
index 7f66ea0c10..a2062f36d3 100644
--- a/crates/ide-db/src/items_locator.rs
+++ b/crates/ide-db/src/items_locator.rs
@@ -2,6 +2,8 @@
//! by its name and a few criteria.
//! The main reason for this module to exist is the fact that project's items and dependencies' items
//! are located in different caches, with different APIs.
+use std::ops::ControlFlow;
+
use either::Either;
use hir::{import_map, Crate, ItemInNs, Module, Semantics};
use limit::Limit;
@@ -17,6 +19,7 @@ pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(100);
pub use import_map::AssocSearchMode;
+// FIXME: Do callbacks instead to avoid allocations.
/// Searches for importable items with the given name in the crate and its dependencies.
pub fn items_with_name<'a>(
sema: &'a Semantics<'_, RootDatabase>,
@@ -70,12 +73,13 @@ pub fn items_with_name<'a>(
}
/// Searches for importable items with the given name in the crate and its dependencies.
-pub fn items_with_name_in_module<'a>(
- sema: &'a Semantics<'_, RootDatabase>,
+pub fn items_with_name_in_module<T>(
+ sema: &Semantics<'_, RootDatabase>,
module: Module,
name: NameToImport,
assoc_item_search: AssocSearchMode,
-) -> impl Iterator<Item = ItemInNs> + 'a {
+ mut cb: impl FnMut(ItemInNs) -> ControlFlow<T>,
+) -> Option<T> {
let _p = tracing::info_span!("items_with_name_in", name = name.text(), assoc_item_search = ?assoc_item_search, ?module)
.entered();
@@ -107,14 +111,12 @@ pub fn items_with_name_in_module<'a>(
local_query
}
};
- let mut local_results = Vec::new();
local_query.search(&[sema.db.module_symbols(module)], |local_candidate| {
- local_results.push(match local_candidate.def {
+ cb(match local_candidate.def {
hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
def => ItemInNs::from(def),
})
- });
- local_results.into_iter()
+ })
}
fn find_items<'a>(
@@ -142,7 +144,8 @@ fn find_items<'a>(
local_results.push(match local_candidate.def {
hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
def => ItemInNs::from(def),
- })
+ });
+ ControlFlow::<()>::Continue(())
});
local_results.into_iter().chain(external_importables)
}
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index b3105e6524..3a29232d33 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -260,23 +260,23 @@ impl From<hir::MacroKind> for SymbolKind {
}
}
-impl From<hir::ModuleDefId> for SymbolKind {
- fn from(it: hir::ModuleDefId) -> Self {
+impl From<hir::ModuleDef> for SymbolKind {
+ fn from(it: hir::ModuleDef) -> Self {
match it {
- hir::ModuleDefId::ConstId(..) => SymbolKind::Const,
- hir::ModuleDefId::EnumVariantId(..) => SymbolKind::Variant,
- hir::ModuleDefId::FunctionId(..) => SymbolKind::Function,
- hir::ModuleDefId::MacroId(hir::MacroId::ProcMacroId(..)) => SymbolKind::ProcMacro,
- hir::ModuleDefId::MacroId(..) => SymbolKind::Macro,
- hir::ModuleDefId::ModuleId(..) => SymbolKind::Module,
- hir::ModuleDefId::StaticId(..) => SymbolKind::Static,
- hir::ModuleDefId::AdtId(hir::AdtId::StructId(..)) => SymbolKind::Struct,
- hir::ModuleDefId::AdtId(hir::AdtId::EnumId(..)) => SymbolKind::Enum,
- hir::ModuleDefId::AdtId(hir::AdtId::UnionId(..)) => SymbolKind::Union,
- hir::ModuleDefId::TraitId(..) => SymbolKind::Trait,
- hir::ModuleDefId::TraitAliasId(..) => SymbolKind::TraitAlias,
- hir::ModuleDefId::TypeAliasId(..) => SymbolKind::TypeAlias,
- hir::ModuleDefId::BuiltinType(..) => SymbolKind::TypeAlias,
+ hir::ModuleDef::Const(..) => SymbolKind::Const,
+ hir::ModuleDef::Variant(..) => SymbolKind::Variant,
+ hir::ModuleDef::Function(..) => SymbolKind::Function,
+ hir::ModuleDef::Macro(mac) if mac.is_proc_macro() => SymbolKind::ProcMacro,
+ hir::ModuleDef::Macro(..) => SymbolKind::Macro,
+ hir::ModuleDef::Module(..) => SymbolKind::Module,
+ hir::ModuleDef::Static(..) => SymbolKind::Static,
+ hir::ModuleDef::Adt(hir::Adt::Struct(..)) => SymbolKind::Struct,
+ hir::ModuleDef::Adt(hir::Adt::Enum(..)) => SymbolKind::Enum,
+ hir::ModuleDef::Adt(hir::Adt::Union(..)) => SymbolKind::Union,
+ hir::ModuleDef::Trait(..) => SymbolKind::Trait,
+ hir::ModuleDef::TraitAlias(..) => SymbolKind::TraitAlias,
+ hir::ModuleDef::TypeAlias(..) => SymbolKind::TypeAlias,
+ hir::ModuleDef::BuiltinType(..) => SymbolKind::TypeAlias,
}
}
}
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index 1d1679c3ff..42efbd68e3 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -134,6 +134,7 @@ impl Definition {
FieldSource::Pos(_) => None,
}
}
+ Definition::Crate(_) => None,
Definition::Module(module) => {
let src = module.declaration_source(sema.db)?;
let name = src.value.name()?;
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index 68199dd871..a75aba137b 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -953,14 +953,19 @@ impl<'a> FindUsages<'a> {
// Search for occurrences of the items name
for offset in Self::match_indices(&text, finder, search_range) {
- tree.token_at_offset(offset).for_each(|token| {
- let Some(str_token) = ast::String::cast(token.clone()) else { return };
+ let ret = tree.token_at_offset(offset).any(|token| {
+ let Some(str_token) = ast::String::cast(token.clone()) else { return false };
if let Some((range, Some(nameres))) =
sema.check_for_format_args_template(token, offset)
{
- if self.found_format_args_ref(file_id, range, str_token, nameres, sink) {}
+ return self
+ .found_format_args_ref(file_id, range, str_token, nameres, sink);
}
+ false
});
+ if ret {
+ return;
+ }
for name in
Self::find_nodes(sema, name, &tree, offset).filter_map(ast::NameLike::cast)
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs
index 94d354d28e..c94644eeb8 100644
--- a/crates/ide-db/src/symbol_index.rs
+++ b/crates/ide-db/src/symbol_index.rs
@@ -25,6 +25,7 @@ use std::{
fmt,
hash::{Hash, Hasher},
mem,
+ ops::ControlFlow,
};
use base_db::{
@@ -136,16 +137,13 @@ fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Ar
// the module or crate indices for those in salsa unless we need to.
.for_each(|module| symbol_collector.collect(module));
- let mut symbols = symbol_collector.finish();
- symbols.shrink_to_fit();
- Arc::new(SymbolIndex::new(symbols))
+ Arc::new(SymbolIndex::new(symbol_collector.finish()))
}
fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc<SymbolIndex> {
let _p = tracing::info_span!("module_symbols").entered();
- let symbols = SymbolCollector::collect_module(db.upcast(), module);
- Arc::new(SymbolIndex::new(symbols))
+ Arc::new(SymbolIndex::new(SymbolCollector::collect_module(db.upcast(), module)))
}
pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc<SymbolIndex>]> {
@@ -222,13 +220,16 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
};
let mut res = vec![];
- query.search(&indices, |f| res.push(f.clone()));
+ query.search::<()>(&indices, |f| {
+ res.push(f.clone());
+ ControlFlow::Continue(())
+ });
res
}
#[derive(Default)]
pub struct SymbolIndex {
- symbols: Vec<FileSymbol>,
+ symbols: Box<[FileSymbol]>,
map: fst::Map<Vec<u8>>,
}
@@ -253,10 +254,10 @@ impl Hash for SymbolIndex {
}
impl SymbolIndex {
- fn new(mut symbols: Vec<FileSymbol>) -> SymbolIndex {
+ fn new(mut symbols: Box<[FileSymbol]>) -> SymbolIndex {
fn cmp(lhs: &FileSymbol, rhs: &FileSymbol) -> Ordering {
- let lhs_chars = lhs.name.chars().map(|c| c.to_ascii_lowercase());
- let rhs_chars = rhs.name.chars().map(|c| c.to_ascii_lowercase());
+ let lhs_chars = lhs.name.as_str().chars().map(|c| c.to_ascii_lowercase());
+ let rhs_chars = rhs.name.as_str().chars().map(|c| c.to_ascii_lowercase());
lhs_chars.cmp(rhs_chars)
}
@@ -316,11 +317,11 @@ impl SymbolIndex {
}
impl Query {
- pub(crate) fn search<'sym>(
+ pub(crate) fn search<'sym, T>(
self,
indices: &'sym [Arc<SymbolIndex>],
- cb: impl FnMut(&'sym FileSymbol),
- ) {
+ cb: impl FnMut(&'sym FileSymbol) -> ControlFlow<T>,
+ ) -> Option<T> {
let _p = tracing::info_span!("symbol_index::Query::search").entered();
let mut op = fst::map::OpBuilder::new();
match self.mode {
@@ -351,12 +352,12 @@ impl Query {
}
}
- fn search_maps<'sym>(
+ fn search_maps<'sym, T>(
&self,
indices: &'sym [Arc<SymbolIndex>],
mut stream: fst::map::Union<'_>,
- mut cb: impl FnMut(&'sym FileSymbol),
- ) {
+ mut cb: impl FnMut(&'sym FileSymbol) -> ControlFlow<T>,
+ ) -> Option<T> {
let ignore_underscore_prefixed = !self.query.starts_with("__");
while let Some((_, indexed_values)) = stream.next() {
for &IndexedValue { index, value } in indexed_values {
@@ -377,15 +378,19 @@ impl Query {
continue;
}
// Hide symbols that start with `__` unless the query starts with `__`
- if ignore_underscore_prefixed && symbol.name.starts_with("__") {
+ let symbol_name = symbol.name.as_str();
+ if ignore_underscore_prefixed && symbol_name.starts_with("__") {
continue;
}
- if self.mode.check(&self.query, self.case_sensitive, &symbol.name) {
- cb(symbol);
+ if self.mode.check(&self.query, self.case_sensitive, symbol_name) {
+ if let Some(b) = cb(symbol).break_value() {
+ return Some(b);
+ }
}
}
}
}
+ None
}
fn matches_assoc_mode(&self, is_trait_assoc_item: bool) -> bool {
@@ -476,9 +481,9 @@ use Macro as ItemLikeMacro;
use Macro as Trait; // overlay namespaces
//- /b_mod.rs
struct StructInModB;
-use super::Macro as SuperItemLikeMacro;
-use crate::b_mod::StructInModB as ThisStruct;
-use crate::Trait as IsThisJustATrait;
+pub(self) use super::Macro as SuperItemLikeMacro;
+pub(self) use crate::b_mod::StructInModB as ThisStruct;
+pub(self) use crate::Trait as IsThisJustATrait;
"#,
);
@@ -487,7 +492,7 @@ use crate::Trait as IsThisJustATrait;
.into_iter()
.map(|module_id| {
let mut symbols = SymbolCollector::collect_module(&db, module_id);
- symbols.sort_by_key(|it| it.name.clone());
+ symbols.sort_by_key(|it| it.name.as_str().to_owned());
(module_id, symbols)
})
.collect();
@@ -514,7 +519,7 @@ struct Duplicate;
.into_iter()
.map(|module_id| {
let mut symbols = SymbolCollector::collect_module(&db, module_id);
- symbols.sort_by_key(|it| it.name.clone());
+ symbols.sort_by_key(|it| it.name.as_str().to_owned());
(module_id, symbols)
})
.collect();
diff --git a/crates/ide-db/src/syntax_helpers/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs
index 365d726d2a..557c95f704 100644
--- a/crates/ide-db/src/syntax_helpers/suggest_name.rs
+++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs
@@ -421,7 +421,7 @@ mod tests {
use super::*;
#[track_caller]
- fn check(ra_fixture: &str, expected: &str) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: &str) {
let (db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
let frange = FileRange { file_id, range: range_or_offset.into() };
diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 9d70942199..535777dfcb 100644
--- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -631,7 +631,7 @@
def: Function(
Function {
id: FunctionId(
- 3,
+ 2,
),
},
),
@@ -664,7 +664,7 @@
def: Function(
Function {
id: FunctionId(
- 2,
+ 1,
),
},
),
@@ -794,7 +794,7 @@
def: Function(
Function {
id: FunctionId(
- 1,
+ 3,
),
},
),
@@ -879,12 +879,10 @@
[
FileSymbol {
name: "IsThisJustATrait",
- def: Macro(
- Macro {
- id: Macro2Id(
- Macro2Id(
- 0,
- ),
+ def: Trait(
+ Trait {
+ id: TraitId(
+ 0,
),
},
),
@@ -897,12 +895,12 @@
),
ptr: SyntaxNodePtr {
kind: USE_TREE,
- range: 111..143,
+ range: 141..173,
},
name_ptr: AstPtr(
SyntaxNodePtr {
kind: NAME,
- range: 127..143,
+ range: 157..173,
},
),
},
@@ -911,40 +909,7 @@
is_assoc: false,
},
FileSymbol {
- name: "StructInModB",
- def: Adt(
- Struct(
- Struct {
- id: StructId(
- 4,
- ),
- },
- ),
- ),
- loc: DeclarationLocation {
- hir_file_id: EditionedFileId(
- FileId(
- 1,
- ),
- Edition2021,
- ),
- ptr: SyntaxNodePtr {
- kind: STRUCT,
- range: 0..20,
- },
- name_ptr: AstPtr(
- SyntaxNodePtr {
- kind: NAME,
- range: 7..19,
- },
- ),
- },
- container_name: None,
- is_alias: false,
- is_assoc: false,
- },
- FileSymbol {
- name: "SuperItemLikeMacro",
+ name: "IsThisJustATrait",
def: Macro(
Macro {
id: Macro2Id(
@@ -963,12 +928,12 @@
),
ptr: SyntaxNodePtr {
kind: USE_TREE,
- range: 25..59,
+ range: 141..173,
},
name_ptr: AstPtr(
SyntaxNodePtr {
kind: NAME,
- range: 41..59,
+ range: 157..173,
},
),
},
@@ -977,7 +942,7 @@
is_assoc: false,
},
FileSymbol {
- name: "ThisStruct",
+ name: "StructInModB",
def: Adt(
Struct(
Struct {
@@ -995,13 +960,13 @@
Edition2021,
),
ptr: SyntaxNodePtr {
- kind: USE_TREE,
- range: 65..105,
+ kind: STRUCT,
+ range: 0..20,
},
name_ptr: AstPtr(
SyntaxNodePtr {
kind: NAME,
- range: 95..105,
+ range: 7..19,
},
),
},
@@ -1010,15 +975,15 @@
is_assoc: false,
},
FileSymbol {
- name: "ThisStruct",
- def: Adt(
- Struct(
- Struct {
- id: StructId(
- 4,
+ name: "SuperItemLikeMacro",
+ def: Macro(
+ Macro {
+ id: Macro2Id(
+ Macro2Id(
+ 0,
),
- },
- ),
+ ),
+ },
),
loc: DeclarationLocation {
hir_file_id: EditionedFileId(
@@ -1029,12 +994,12 @@
),
ptr: SyntaxNodePtr {
kind: USE_TREE,
- range: 65..105,
+ range: 35..69,
},
name_ptr: AstPtr(
SyntaxNodePtr {
kind: NAME,
- range: 95..105,
+ range: 51..69,
},
),
},
diff --git a/crates/ide-db/src/traits.rs b/crates/ide-db/src/traits.rs
index 82aca50d03..0f67496d09 100644
--- a/crates/ide-db/src/traits.rs
+++ b/crates/ide-db/src/traits.rs
@@ -123,7 +123,9 @@ mod tests {
use crate::RootDatabase;
/// Creates analysis from a multi-file fixture, returns positions marked with $0.
- pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
+ pub(crate) fn position(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ ) -> (RootDatabase, FilePosition) {
let change_fixture = ChangeFixture::parse(ra_fixture);
let mut database = RootDatabase::default();
database.apply_change(change_fixture.change);
@@ -133,7 +135,7 @@ mod tests {
(database, FilePosition { file_id, offset })
}
- fn check_trait(ra_fixture: &str, expect: Expect) {
+ fn check_trait(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (db, position) = position(ra_fixture);
let sema = Semantics::new(&db);
let file = sema.parse(position.file_id);
@@ -147,7 +149,7 @@ mod tests {
expect.assert_eq(&actual);
}
- fn check_missing_assoc(ra_fixture: &str, expect: Expect) {
+ fn check_missing_assoc(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (db, position) = position(ra_fixture);
let sema = Semantics::new(&db);
let file = sema.parse(position.file_id);
diff --git a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
index 2b59c1a22f..7d62daf716 100644
--- a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
+++ b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
@@ -604,4 +604,23 @@ fn bar() {
"#,
);
}
+
+ #[test]
+ fn enum_variant_type_ns() {
+ check_diagnostics(
+ r#"
+enum KvnDeserializerErr<I> {
+ UnexpectedKeyword { found: I, expected: I },
+}
+
+fn foo() {
+ let _x: KvnDeserializerErr<()> =
+ KvnDeserializerErr::<()>::UnexpectedKeyword { found: (), expected: () };
+ let _x: KvnDeserializerErr<()> =
+ KvnDeserializerErr::<()>::UnexpectedKeyword::<()> { found: (), expected: () };
+ // ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs
index 4c0c685e55..96a368eb0e 100644
--- a/crates/ide-diagnostics/src/handlers/inactive_code.rs
+++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs
@@ -41,7 +41,7 @@ pub(crate) fn inactive_code(
mod tests {
use crate::{tests::check_diagnostics_with_config, DiagnosticsConfig};
- pub(crate) fn check(ra_fixture: &str) {
+ pub(crate) fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let config = DiagnosticsConfig {
disabled: std::iter::once("unlinked-file".to_owned()).collect(),
..DiagnosticsConfig::test_sample()
diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs
index e177b72e4d..99894fefef 100644
--- a/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -4,13 +4,13 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
//
// This diagnostic is shown for macro expansion errors.
-// Diagnostic: proc-macros-disabled
+// Diagnostic: attribute-expansion-disabled
//
-// This diagnostic is shown for proc macros where proc macros have been disabled.
+// This diagnostic is shown for attribute proc macros when attribute expansions have been disabled.
// Diagnostic: proc-macro-disabled
//
-// This diagnostic is shown for proc macros that has been specifically disabled via `rust-analyzer.procMacro.ignored`.
+// This diagnostic is shown for proc macros that have been specifically disabled via `rust-analyzer.procMacro.ignored`.
pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
// Use more accurate position if available.
let display_range = ctx.resolve_precise_location(&d.node, d.precise_location);
@@ -291,4 +291,30 @@ mod prim_never {}
"#,
);
}
+
+ #[test]
+ fn no_stack_overflow_for_missing_binding() {
+ check_diagnostics(
+ r#"
+#[macro_export]
+macro_rules! boom {
+ (
+ $($code:literal),+,
+ $(param: $param:expr,)?
+ ) => {{
+ let _ = $crate::boom!(@param $($param)*);
+ }};
+ (@param) => { () };
+ (@param $param:expr) => { $param };
+}
+
+fn it_works() {
+ // NOTE: there is an error, but RA crashes before showing it
+ boom!("RAND", param: c7.clone());
+ // ^^^^^ error: expected literal
+}
+
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index 08e6e7dced..0bf600e5df 100644
--- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -26,7 +26,7 @@ mod tests {
use test_utils::skip_slow_tests;
#[track_caller]
- fn check_diagnostics_no_bails(ra_fixture: &str) {
+ fn check_diagnostics_no_bails(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
cov_mark::check_count!(validate_match_bailed_out, 0);
crate::tests::check_diagnostics(ra_fixture)
}
diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs
index e5d871975b..0f126a1a65 100644
--- a/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -399,4 +399,38 @@ fn f(s@m::Struct {
"#,
)
}
+
+ #[test]
+ fn editions_between_macros() {
+ check_diagnostics(
+ r#"
+//- /edition2015.rs crate:edition2015 edition:2015
+#[macro_export]
+macro_rules! pass_expr_thorough {
+ ($e:expr) => { $e };
+}
+
+//- /edition2018.rs crate:edition2018 deps:edition2015 edition:2018
+async fn bar() {}
+async fn foo() {
+ edition2015::pass_expr_thorough!(bar().await);
+}
+ "#,
+ );
+ check_diagnostics(
+ r#"
+//- /edition2018.rs crate:edition2018 edition:2018
+pub async fn bar() {}
+#[macro_export]
+macro_rules! make_await {
+ () => { async { $crate::bar().await }; };
+}
+
+//- /edition2015.rs crate:edition2015 deps:edition2018 edition:2015
+fn foo() {
+ edition2018::make_await!();
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index f481365f2a..4aff446de6 100644
--- a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
+++ b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -61,7 +61,7 @@ mod tests {
};
#[track_caller]
- pub(crate) fn check_diagnostics(ra_fixture: &str) {
+ pub(crate) fn check_diagnostics(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let mut config = DiagnosticsConfig::test_sample();
config.disabled.insert("inactive-code".to_owned());
config.disabled.insert("E0599".to_owned());
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index bfdda53740..56afb38cc8 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1164,6 +1164,37 @@ struct Bar {
}
#[test]
+ fn trait_upcast_ok() {
+ check_diagnostics(
+ r#"
+//- minicore: coerce_unsized
+trait A: B {}
+trait B {}
+
+fn test(a: &dyn A) -> &dyn B {
+ a
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn trait_upcast_err() {
+ check_diagnostics(
+ r#"
+//- minicore: coerce_unsized
+trait A {} // `A` does not have `B` as a supertrait, so no upcast :c
+trait B {}
+
+fn test(a: &dyn A) -> &dyn B {
+ a
+ //^ error: expected &dyn B, found &dyn A
+}
+"#,
+ );
+ }
+
+ #[test]
fn return_no_value() {
check_diagnostics_with_disabled(
r#"
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs
index ec74640a54..fc2a7db717 100644
--- a/crates/ide-diagnostics/src/tests.rs
+++ b/crates/ide-diagnostics/src/tests.rs
@@ -1,5 +1,7 @@
#![allow(clippy::print_stderr)]
+mod overly_long_real_world_cases;
+
use ide_db::{
assists::AssistResolveStrategy, base_db::SourceDatabase, LineIndexDatabase, RootDatabase,
};
@@ -16,7 +18,10 @@ use crate::{DiagnosticsConfig, ExprFillDefaultMode, Severity};
/// * the first diagnostic fix trigger range touches the input cursor position
/// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
#[track_caller]
-pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
+pub(crate) fn check_fix(
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
check_nth_fix(0, ra_fixture_before, ra_fixture_after);
}
/// Takes a multi-file input fixture with annotated cursor positions,
@@ -24,14 +29,21 @@ pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
/// * a diagnostic is produced
/// * every diagnostic fixes trigger range touches the input cursor position
/// * that the contents of the file containing the cursor match `after` after each diagnostic fix is applied
-pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) {
+pub(crate) fn check_fixes(
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ ra_fixtures_after: Vec<&str>,
+) {
for (i, ra_fixture_after) in ra_fixtures_after.iter().enumerate() {
check_nth_fix(i, ra_fixture_before, ra_fixture_after)
}
}
#[track_caller]
-fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
+fn check_nth_fix(
+ nth: usize,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
let mut config = DiagnosticsConfig::test_sample();
config.expr_fill_default = ExprFillDefaultMode::Default;
check_nth_fix_with_config(config, nth, ra_fixture_before, ra_fixture_after)
@@ -39,8 +51,8 @@ fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
#[track_caller]
pub(crate) fn check_fix_with_disabled(
- ra_fixture_before: &str,
- ra_fixture_after: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
disabled: impl Iterator<Item = String>,
) {
let mut config = DiagnosticsConfig::test_sample();
@@ -53,8 +65,8 @@ pub(crate) fn check_fix_with_disabled(
fn check_nth_fix_with_config(
config: DiagnosticsConfig,
nth: usize,
- ra_fixture_before: &str,
- ra_fixture_after: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
) {
let after = trim_indent(ra_fixture_after);
@@ -93,14 +105,20 @@ fn check_nth_fix_with_config(
assert_eq_text!(&after, &actual);
}
-pub(crate) fn check_fixes_unordered(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) {
+pub(crate) fn check_fixes_unordered(
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ ra_fixtures_after: Vec<&str>,
+) {
for ra_fixture_after in ra_fixtures_after.iter() {
check_has_fix(ra_fixture_before, ra_fixture_after)
}
}
#[track_caller]
-pub(crate) fn check_has_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
+pub(crate) fn check_has_fix(
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
let after = trim_indent(ra_fixture_after);
let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
@@ -143,7 +161,10 @@ pub(crate) fn check_has_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
}
#[track_caller]
-pub(crate) fn check_has_single_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
+pub(crate) fn check_has_single_fix(
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+) {
let after = trim_indent(ra_fixture_after);
let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
@@ -189,7 +210,7 @@ pub(crate) fn check_has_single_fix(ra_fixture_before: &str, ra_fixture_after: &s
}
/// Checks that there's a diagnostic *without* fix at `$0`.
-pub(crate) fn check_no_fix(ra_fixture: &str) {
+pub(crate) fn check_no_fix(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (db, file_position) = RootDatabase::with_position(ra_fixture);
let diagnostic = super::full_diagnostics(
&db,
@@ -203,21 +224,27 @@ pub(crate) fn check_no_fix(ra_fixture: &str) {
}
#[track_caller]
-pub(crate) fn check_diagnostics(ra_fixture: &str) {
+pub(crate) fn check_diagnostics(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let mut config = DiagnosticsConfig::test_sample();
config.disabled.insert("inactive-code".to_owned());
check_diagnostics_with_config(config, ra_fixture)
}
#[track_caller]
-pub(crate) fn check_diagnostics_with_disabled(ra_fixture: &str, disabled: &[&str]) {
+pub(crate) fn check_diagnostics_with_disabled(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ disabled: &[&str],
+) {
let mut config = DiagnosticsConfig::test_sample();
config.disabled.extend(disabled.iter().map(|&s| s.to_owned()));
check_diagnostics_with_config(config, ra_fixture)
}
#[track_caller]
-pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) {
+pub(crate) fn check_diagnostics_with_config(
+ config: DiagnosticsConfig,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) {
let (db, files) = RootDatabase::with_many_files(ra_fixture);
let mut annotations = files
.iter()
diff --git a/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs b/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
new file mode 100644
index 0000000000..c6831d818a
--- /dev/null
+++ b/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
@@ -0,0 +1,2726 @@
+//! Overly long excerpts of failures from real world cases, that I was too lazy to minimize.
+
+use crate::tests::check_diagnostics_with_disabled;
+
+#[test]
+fn tracing_infinite_repeat() {
+ check_diagnostics_with_disabled(
+ r#"
+//- /core.rs crate:core
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! concat {
+($($e:expr),* $(,)?) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! file {
+() => {
+ /* compiler built-in */
+};
+}
+#[allow_internal_unsafe]
+#[allow_internal_unstable(fmt_internals)]
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {
+($fmt:expr) => {{ /* compiler built-in */ }};
+($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! line {
+() => {
+ /* compiler built-in */
+};
+}
+
+//- /tracing_core.rs crate:tracing_core deps:core
+#[macro_export]
+macro_rules! identify_callsite {
+($callsite:expr) => {
+ $crate::callsite::Identifier($callsite)
+};
+}
+
+#[macro_export]
+macro_rules! metadata {
+(
+ name: $name:expr,
+ target: $target:expr,
+ level: $level:expr,
+ fields: $fields:expr,
+ callsite: $callsite:expr,
+ kind: $kind:expr
+) => {
+ $crate::metadata! {
+ name: $name,
+ target: $target,
+ level: $level,
+ fields: $fields,
+ callsite: $callsite,
+ kind: $kind,
+ }
+};
+(
+ name: $name:expr,
+ target: $target:expr,
+ level: $level:expr,
+ fields: $fields:expr,
+ callsite: $callsite:expr,
+ kind: $kind:expr,
+) => {
+ $crate::metadata::Metadata::new(
+ $name,
+ $target,
+ $level,
+ $crate::__macro_support::Option::Some($crate::__macro_support::file!()),
+ $crate::__macro_support::Option::Some($crate::__macro_support::line!()),
+ $crate::__macro_support::Option::Some($crate::__macro_support::module_path!()),
+ $crate::field::FieldSet::new($fields, $crate::identify_callsite!($callsite)),
+ $kind,
+ )
+};
+}
+
+//- /tracing.rs crate:tracing deps:core,tracing_core
+#[doc(hidden)]
+pub mod __macro_support {
+// Re-export the `core` functions that are used in macros. This allows
+// a crate to be named `core` and avoid name clashes.
+// See here: https://github.com/tokio-rs/tracing/issues/2761
+pub use core::{concat, file, format_args, iter::Iterator, line, option::Option};
+}
+
+#[macro_export]
+macro_rules! span {
+(target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr) => {
+ $crate::span!(target: $target, parent: $parent, $lvl, $name,)
+};
+(target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr, $($fields:tt)*) => {
+ {
+ use $crate::__macro_support::Callsite as _;
+ static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+ name: $name,
+ kind: $crate::metadata::Kind::SPAN,
+ target: $target,
+ level: $lvl,
+ fields: $($fields)*
+ };
+ let mut interest = $crate::subscriber::Interest::never();
+ if $crate::level_enabled!($lvl)
+ && { interest = __CALLSITE.interest(); !interest.is_never() }
+ && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+ {
+ let meta = __CALLSITE.metadata();
+ // span with explicit parent
+ $crate::Span::child_of(
+ $parent,
+ meta,
+ &$crate::valueset!(meta.fields(), $($fields)*),
+ )
+ } else {
+ let span = $crate::__macro_support::__disabled_span(__CALLSITE.metadata());
+ $crate::if_log_enabled! { $lvl, {
+ span.record_all(&$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+ }};
+ span
+ }
+ }
+};
+(target: $target:expr, $lvl:expr, $name:expr, $($fields:tt)*) => {
+ {
+ use $crate::__macro_support::Callsite as _;
+ static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
+ name: $name,
+ kind: $crate::metadata::Kind::SPAN,
+ target: $target,
+ level: $lvl,
+ fields: $($fields)*
+ };
+ let mut interest = $crate::subscriber::Interest::never();
+ if $crate::level_enabled!($lvl)
+ && { interest = __CALLSITE.interest(); !interest.is_never() }
+ && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+ {
+ let meta = __CALLSITE.metadata();
+ // span with contextual parent
+ $crate::Span::new(
+ meta,
+ &$crate::valueset!(meta.fields(), $($fields)*),
+ )
+ } else {
+ let span = $crate::__macro_support::__disabled_span(__CALLSITE.metadata());
+ $crate::if_log_enabled! { $lvl, {
+ span.record_all(&$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+ }};
+ span
+ }
+ }
+};
+(target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr) => {
+ $crate::span!(target: $target, parent: $parent, $lvl, $name,)
+};
+(parent: $parent:expr, $lvl:expr, $name:expr, $($fields:tt)*) => {
+ $crate::span!(
+ target: module_path!(),
+ parent: $parent,
+ $lvl,
+ $name,
+ $($fields)*
+ )
+};
+(parent: $parent:expr, $lvl:expr, $name:expr) => {
+ $crate::span!(
+ target: module_path!(),
+ parent: $parent,
+ $lvl,
+ $name,
+ )
+};
+(target: $target:expr, $lvl:expr, $name:expr, $($fields:tt)*) => {
+ $crate::span!(
+ target: $target,
+ $lvl,
+ $name,
+ $($fields)*
+ )
+};
+(target: $target:expr, $lvl:expr, $name:expr) => {
+ $crate::span!(target: $target, $lvl, $name,)
+};
+($lvl:expr, $name:expr, $($fields:tt)*) => {
+ $crate::span!(
+ target: module_path!(),
+ $lvl,
+ $name,
+ $($fields)*
+ )
+};
+($lvl:expr, $name:expr) => {
+ $crate::span!(
+ target: module_path!(),
+ $lvl,
+ $name,
+ )
+};
+}
+
+#[macro_export]
+macro_rules! trace_span {
+(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: $target,
+ parent: $parent,
+ $crate::Level::TRACE,
+ $name,
+ $($field)*
+ )
+};
+(target: $target:expr, parent: $parent:expr, $name:expr) => {
+ $crate::trace_span!(target: $target, parent: $parent, $name,)
+};
+(parent: $parent:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::TRACE,
+ $name,
+ $($field)*
+ )
+};
+(parent: $parent:expr, $name:expr) => {
+ $crate::trace_span!(parent: $parent, $name,)
+};
+(target: $target:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: $target,
+ $crate::Level::TRACE,
+ $name,
+ $($field)*
+ )
+};
+(target: $target:expr, $name:expr) => {
+ $crate::trace_span!(target: $target, $name,)
+};
+($name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: module_path!(),
+ $crate::Level::TRACE,
+ $name,
+ $($field)*
+ )
+};
+($name:expr) => { $crate::trace_span!($name,) };
+}
+
+#[macro_export]
+macro_rules! debug_span {
+(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: $target,
+ parent: $parent,
+ $crate::Level::DEBUG,
+ $name,
+ $($field)*
+ )
+};
+(target: $target:expr, parent: $parent:expr, $name:expr) => {
+ $crate::debug_span!(target: $target, parent: $parent, $name,)
+};
+(parent: $parent:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::DEBUG,
+ $name,
+ $($field)*
+ )
+};
+(parent: $parent:expr, $name:expr) => {
+ $crate::debug_span!(parent: $parent, $name,)
+};
+(target: $target:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: $target,
+ $crate::Level::DEBUG,
+ $name,
+ $($field)*
+ )
+};
+(target: $target:expr, $name:expr) => {
+ $crate::debug_span!(target: $target, $name,)
+};
+($name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: module_path!(),
+ $crate::Level::DEBUG,
+ $name,
+ $($field)*
+ )
+};
+($name:expr) => {$crate::debug_span!($name,)};
+}
+
+#[macro_export]
+macro_rules! info_span {
+(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: $target,
+ parent: $parent,
+ $crate::Level::INFO,
+ $name,
+ $($field)*
+ )
+};
+(target: $target:expr, parent: $parent:expr, $name:expr) => {
+ $crate::info_span!(target: $target, parent: $parent, $name,)
+};
+(parent: $parent:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::INFO,
+ $name,
+ $($field)*
+ )
+};
+(parent: $parent:expr, $name:expr) => {
+ $crate::info_span!(parent: $parent, $name,)
+};
+(target: $target:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: $target,
+ $crate::Level::INFO,
+ $name,
+ $($field)*
+ )
+};
+(target: $target:expr, $name:expr) => {
+ $crate::info_span!(target: $target, $name,)
+};
+($name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: module_path!(),
+ $crate::Level::INFO,
+ $name,
+ $($field)*
+ )
+};
+($name:expr) => {$crate::info_span!($name,)};
+}
+
+#[macro_export]
+macro_rules! warn_span {
+(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: $target,
+ parent: $parent,
+ $crate::Level::WARN,
+ $name,
+ $($field)*
+ )
+};
+(target: $target:expr, parent: $parent:expr, $name:expr) => {
+ $crate::warn_span!(target: $target, parent: $parent, $name,)
+};
+(parent: $parent:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::WARN,
+ $name,
+ $($field)*
+ )
+};
+(parent: $parent:expr, $name:expr) => {
+ $crate::warn_span!(parent: $parent, $name,)
+};
+(target: $target:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: $target,
+ $crate::Level::WARN,
+ $name,
+ $($field)*
+ )
+};
+(target: $target:expr, $name:expr) => {
+ $crate::warn_span!(target: $target, $name,)
+};
+($name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: module_path!(),
+ $crate::Level::WARN,
+ $name,
+ $($field)*
+ )
+};
+($name:expr) => {$crate::warn_span!($name,)};
+}
+
+#[macro_export]
+macro_rules! error_span {
+(target: $target:expr, parent: $parent:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: $target,
+ parent: $parent,
+ $crate::Level::ERROR,
+ $name,
+ $($field)*
+ )
+};
+(target: $target:expr, parent: $parent:expr, $name:expr) => {
+ $crate::error_span!(target: $target, parent: $parent, $name,)
+};
+(parent: $parent:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::ERROR,
+ $name,
+ $($field)*
+ )
+};
+(parent: $parent:expr, $name:expr) => {
+ $crate::error_span!(parent: $parent, $name,)
+};
+(target: $target:expr, $name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: $target,
+ $crate::Level::ERROR,
+ $name,
+ $($field)*
+ )
+};
+(target: $target:expr, $name:expr) => {
+ $crate::error_span!(target: $target, $name,)
+};
+($name:expr, $($field:tt)*) => {
+ $crate::span!(
+ target: module_path!(),
+ $crate::Level::ERROR,
+ $name,
+ $($field)*
+ )
+};
+($name:expr) => {$crate::error_span!($name,)};
+}
+
+#[macro_export]
+macro_rules! event {
+// Name / target / parent.
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+ use $crate::__macro_support::Callsite as _;
+ static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+ name: $name,
+ kind: $crate::metadata::Kind::EVENT,
+ target: $target,
+ level: $lvl,
+ fields: $($fields)*
+ };
+
+ let enabled = $crate::level_enabled!($lvl) && {
+ let interest = __CALLSITE.interest();
+ !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+ };
+ if enabled {
+ (|value_set: $crate::field::ValueSet| {
+ $crate::__tracing_log!(
+ $lvl,
+ __CALLSITE,
+ &value_set
+ );
+ let meta = __CALLSITE.metadata();
+ // event with explicit parent
+ $crate::Event::child_of(
+ $parent,
+ meta,
+ &value_set
+ );
+ })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+ } else {
+ $crate::__tracing_log!(
+ $lvl,
+ __CALLSITE,
+ &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
+ );
+ }
+});
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+ $crate::event!(
+ name: $name,
+ target: $target,
+ parent: $parent,
+ $lvl,
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+ )
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $lvl, { $($k).+ = $($fields)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $lvl, { $($arg)+ })
+);
+
+// Name / target.
+(name: $name:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+ use $crate::__macro_support::Callsite as _;
+ static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+ name: $name,
+ kind: $crate::metadata::Kind::EVENT,
+ target: $target,
+ level: $lvl,
+ fields: $($fields)*
+ };
+ let enabled = $crate::level_enabled!($lvl) && {
+ let interest = __CALLSITE.interest();
+ !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+ };
+ if enabled {
+ (|value_set: $crate::field::ValueSet| {
+ let meta = __CALLSITE.metadata();
+ // event with contextual parent
+ $crate::Event::dispatch(
+ meta,
+ &value_set
+ );
+ $crate::__tracing_log!(
+ $lvl,
+ __CALLSITE,
+ &value_set
+ );
+ })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+ } else {
+ $crate::__tracing_log!(
+ $lvl,
+ __CALLSITE,
+ &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
+ );
+ }
+});
+(name: $name:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+ $crate::event!(
+ name: $name,
+ target: $target,
+ $lvl,
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+ )
+);
+(name: $name:expr, target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $lvl, { $($k).+ = $($fields)* })
+);
+(name: $name:expr, target: $target:expr, $lvl:expr, $($arg:tt)+) => (
+ $crate::event!(name: $name, target: $target, $lvl, { $($arg)+ })
+);
+
+// Target / parent.
+(target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+ use $crate::__macro_support::Callsite as _;
+ static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
+ name: $crate::__macro_support::concat!(
+ "event ",
+ $crate::__macro_support::file!(),
+ ":",
+ $crate::__macro_support::line!()
+ ),
+ kind: $crate::metadata::Kind::EVENT,
+ target: $target,
+ level: $lvl,
+ fields: $($fields)*
+ };
+
+ let enabled = $crate::level_enabled!($lvl) && {
+ let interest = __CALLSITE.interest();
+ !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+ };
+ if enabled {
+ (|value_set: $crate::field::ValueSet| {
+ $crate::__tracing_log!(
+ $lvl,
+ __CALLSITE,
+ &value_set
+ );
+ let meta = __CALLSITE.metadata();
+ // event with explicit parent
+ $crate::Event::child_of(
+ $parent,
+ meta,
+ &value_set
+ );
+ })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+ } else {
+ $crate::__tracing_log!(
+ $lvl,
+ __CALLSITE,
+ &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
+ );
+ }
+});
+(target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: $target,
+ parent: $parent,
+ $lvl,
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+ )
+);
+(target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $lvl, { $($k).+ = $($fields)* })
+);
+(target: $target:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => (
+ $crate::event!(target: $target, parent: $parent, $lvl, { $($arg)+ })
+);
+
+// Name / parent.
+(name: $name:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+ use $crate::__macro_support::Callsite as _;
+ static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+ name: $name,
+ kind: $crate::metadata::Kind::EVENT,
+ target: module_path!(),
+ level: $lvl,
+ fields: $($fields)*
+ };
+
+ let enabled = $crate::level_enabled!($lvl) && {
+ let interest = __CALLSITE.interest();
+ !interest.is_never() && __CALLSITE.is_enabled(interest)
+ };
+ if enabled {
+ (|value_set: $crate::field::ValueSet| {
+ $crate::__tracing_log!(
+ $lvl,
+ __CALLSITE,
+ &value_set
+ );
+ let meta = __CALLSITE.metadata();
+ // event with explicit parent
+ $crate::Event::child_of(
+ $parent,
+ meta,
+ &value_set
+ );
+ })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+ } else {
+ $crate::__tracing_log!(
+ $lvl,
+ __CALLSITE,
+ &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
+ );
+ }
+});
+(name: $name:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+ $crate::event!(
+ name: $name,
+ parent: $parent,
+ $lvl,
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+ )
+);
+(name: $name:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $lvl, { $($k).+ = $($fields)* })
+);
+(name: $name:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => (
+ $crate::event!(name: $name, parent: $parent, $lvl, { $($arg)+ })
+);
+
+// Name.
+(name: $name:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+ use $crate::__macro_support::Callsite as _;
+ static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+ name: $name,
+ kind: $crate::metadata::Kind::EVENT,
+ target: module_path!(),
+ level: $lvl,
+ fields: $($fields)*
+ };
+ let enabled = $crate::level_enabled!($lvl) && {
+ let interest = __CALLSITE.interest();
+ !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+ };
+ if enabled {
+ (|value_set: $crate::field::ValueSet| {
+ let meta = __CALLSITE.metadata();
+ // event with contextual parent
+ $crate::Event::dispatch(
+ meta,
+ &value_set
+ );
+ $crate::__tracing_log!(
+ $lvl,
+ __CALLSITE,
+ &value_set
+ );
+ })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+ } else {
+ $crate::__tracing_log!(
+ $lvl,
+ __CALLSITE,
+ &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
+ );
+ }
+});
+(name: $name:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+ $crate::event!(
+ name: $name,
+ $lvl,
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+ )
+);
+(name: $name:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+ $crate::event!(name: $name, $lvl, { $($k).+ = $($fields)* })
+);
+(name: $name:expr, $lvl:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, $lvl, { $($arg)+ })
+);
+
+// Target.
+(target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+ use $crate::__macro_support::Callsite as _;
+ static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
+ name: $crate::__macro_support::concat!(
+ "event ",
+ $crate::__macro_support::file!(),
+ ":",
+ $crate::__macro_support::line!()
+ ),
+ kind: $crate::metadata::Kind::EVENT,
+ target: $target,
+ level: $lvl,
+ fields: $($fields)*
+ };
+ let enabled = $crate::level_enabled!($lvl) && {
+ let interest = __CALLSITE.interest();
+ !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest)
+ };
+ if enabled {
+ (|value_set: $crate::field::ValueSet| {
+ let meta = __CALLSITE.metadata();
+ // event with contextual parent
+ $crate::Event::dispatch(
+ meta,
+ &value_set
+ );
+ $crate::__tracing_log!(
+ $lvl,
+ __CALLSITE,
+ &value_set
+ );
+ })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
+ } else {
+ $crate::__tracing_log!(
+ $lvl,
+ __CALLSITE,
+ &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
+ );
+ }
+});
+(target: $target:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: $target,
+ $lvl,
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+ )
+);
+(target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+ $crate::event!(target: $target, $lvl, { $($k).+ = $($fields)* })
+);
+(target: $target:expr, $lvl:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: $target, $lvl, { $($arg)+ })
+);
+
+// Parent.
+(parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $lvl,
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+ )
+);
+(parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $lvl,
+ { $($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, $lvl:expr, ?$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $lvl,
+ { ?$($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, $lvl:expr, %$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $lvl,
+ { %$($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, $lvl:expr, $($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $lvl,
+ { $($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, $lvl:expr, %$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $lvl,
+ { %$($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, $lvl:expr, ?$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $lvl,
+ { ?$($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, $lvl:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: module_path!(), parent: $parent, $lvl, { $($arg)+ })
+);
+
+// ...
+( $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ $lvl,
+ { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
+ )
+);
+( $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ $lvl,
+ { message = format_args!($($arg)+), $($fields)* }
+ )
+);
+($lvl:expr, $($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $lvl,
+ { $($k).+ = $($field)*}
+ )
+);
+($lvl:expr, $($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $lvl,
+ { $($k).+, $($field)*}
+ )
+);
+($lvl:expr, ?$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $lvl,
+ { ?$($k).+, $($field)*}
+ )
+);
+($lvl:expr, %$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $lvl,
+ { %$($k).+, $($field)*}
+ )
+);
+($lvl:expr, ?$($k:ident).+) => (
+ $crate::event!($lvl, ?$($k).+,)
+);
+($lvl:expr, %$($k:ident).+) => (
+ $crate::event!($lvl, %$($k).+,)
+);
+($lvl:expr, $($k:ident).+) => (
+ $crate::event!($lvl, $($k).+,)
+);
+( $lvl:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: module_path!(), $lvl, { $($arg)+ })
+);
+}
+
+#[macro_export]
+macro_rules! event_enabled {
+($($rest:tt)*)=> (
+ $crate::enabled!(kind: $crate::metadata::Kind::EVENT, $($rest)*)
+)
+}
+
+#[macro_export]
+macro_rules! span_enabled {
+($($rest:tt)*)=> (
+ $crate::enabled!(kind: $crate::metadata::Kind::SPAN, $($rest)*)
+)
+}
+
+#[macro_export]
+macro_rules! enabled {
+(kind: $kind:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+ if $crate::level_enabled!($lvl) {
+ use $crate::__macro_support::Callsite as _;
+ static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! {
+ name: $crate::__macro_support::concat!(
+ "enabled ",
+ $crate::__macro_support::file!(),
+ ":",
+ $crate::__macro_support::line!()
+ ),
+ kind: $kind.hint(),
+ target: $target,
+ level: $lvl,
+ fields: $($fields)*
+ };
+ let interest = __CALLSITE.interest();
+ if !interest.is_never() && $crate::__macro_support::__is_enabled(__CALLSITE.metadata(), interest) {
+ let meta = __CALLSITE.metadata();
+ $crate::dispatcher::get_default(|current| current.enabled(meta))
+ } else {
+ false
+ }
+ } else {
+ false
+ }
+});
+// Just target and level
+(kind: $kind:expr, target: $target:expr, $lvl:expr ) => (
+ $crate::enabled!(kind: $kind, target: $target, $lvl, { })
+);
+(target: $target:expr, $lvl:expr ) => (
+ $crate::enabled!(kind: $crate::metadata::Kind::HINT, target: $target, $lvl, { })
+);
+
+// These four cases handle fields with no values
+(kind: $kind:expr, target: $target:expr, $lvl:expr, $($field:tt)*) => (
+ $crate::enabled!(
+ kind: $kind,
+ target: $target,
+ $lvl,
+ { $($field)*}
+ )
+);
+(target: $target:expr, $lvl:expr, $($field:tt)*) => (
+ $crate::enabled!(
+ kind: $crate::metadata::Kind::HINT,
+ target: $target,
+ $lvl,
+ { $($field)*}
+ )
+);
+
+// Level and field case
+(kind: $kind:expr, $lvl:expr, $($field:tt)*) => (
+ $crate::enabled!(
+ kind: $kind,
+ target: module_path!(),
+ $lvl,
+ { $($field)*}
+ )
+);
+
+// Simplest `enabled!` case
+(kind: $kind:expr, $lvl:expr) => (
+ $crate::enabled!(kind: $kind, target: module_path!(), $lvl, { })
+);
+($lvl:expr) => (
+ $crate::enabled!(kind: $crate::metadata::Kind::HINT, target: module_path!(), $lvl, { })
+);
+
+// Fallthrough from above
+($lvl:expr, $($field:tt)*) => (
+ $crate::enabled!(
+ kind: $crate::metadata::Kind::HINT,
+ target: module_path!(),
+ $lvl,
+ { $($field)*}
+ )
+);
+}
+
+#[macro_export]
+macro_rules! trace {
+// Name / target / parent.
+(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
+);
+
+// Name / target.
+(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::TRACE, {}, $($arg)+)
+);
+
+// Target / parent.
+(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
+);
+
+// Name / parent.
+(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
+);
+
+// Name.
+(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::TRACE, { $($k).+ $($field)* })
+);
+(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::TRACE, { %$($k).+ $($field)* })
+);
+(name: $name:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, $crate::Level::TRACE, {}, $($arg)+)
+);
+
+// Target.
+(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::TRACE, { $($k).+ $($field)* })
+);
+(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::TRACE, { %$($k).+ $($field)* })
+);
+(target: $target:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: $target, $crate::Level::TRACE, {}, $($arg)+)
+);
+
+// Parent.
+(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::TRACE,
+ { $($field)+ },
+ $($arg)+
+ )
+);
+(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::TRACE,
+ { $($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::TRACE,
+ { ?$($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::TRACE,
+ { %$($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::TRACE,
+ { $($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::TRACE,
+ { ?$($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::TRACE,
+ { %$($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, $($arg:tt)+) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::TRACE,
+ {},
+ $($arg)+
+ )
+);
+
+// ...
+({ $($field:tt)+ }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::TRACE,
+ { $($field)+ },
+ $($arg)+
+ )
+);
+($($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::TRACE,
+ { $($k).+ = $($field)*}
+ )
+);
+(?$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::TRACE,
+ { ?$($k).+ = $($field)*}
+ )
+);
+(%$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::TRACE,
+ { %$($k).+ = $($field)*}
+ )
+);
+($($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::TRACE,
+ { $($k).+, $($field)*}
+ )
+);
+(?$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::TRACE,
+ { ?$($k).+, $($field)*}
+ )
+);
+(%$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::TRACE,
+ { %$($k).+, $($field)*}
+ )
+);
+(?$($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::TRACE,
+ { ?$($k).+ }
+ )
+);
+(%$($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::TRACE,
+ { %$($k).+ }
+ )
+);
+($($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::TRACE,
+ { $($k).+ }
+ )
+);
+($($arg:tt)+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::TRACE,
+ $($arg)+
+ )
+);
+}
+
+#[macro_export]
+macro_rules! debug {
+// Name / target / parent.
+(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
+);
+
+// Name / target.
+(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, {}, $($arg)+)
+);
+
+// Target / parent.
+(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
+);
+
+// Name / parent.
+(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
+);
+
+// Name.
+(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::DEBUG, { $($k).+ $($field)* })
+);
+(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+);
+(name: $name:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, $crate::Level::DEBUG, {}, $($arg)+)
+);
+
+// Target.
+(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::DEBUG, { $($k).+ $($field)* })
+);
+(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+);
+(target: $target:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: $target, $crate::Level::DEBUG, {}, $($arg)+)
+);
+
+// Parent.
+(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::DEBUG,
+ { $($field)+ },
+ $($arg)+
+ )
+);
+(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::DEBUG,
+ { $($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::DEBUG,
+ { ?$($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::DEBUG,
+ { %$($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::DEBUG,
+ { $($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::DEBUG,
+ { ?$($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::DEBUG,
+ { %$($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, $($arg:tt)+) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::DEBUG,
+ {},
+ $($arg)+
+ )
+);
+
+// ...
+({ $($field:tt)+ }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::DEBUG,
+ { $($field)+ },
+ $($arg)+
+ )
+);
+($($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::DEBUG,
+ { $($k).+ = $($field)*}
+ )
+);
+(?$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::DEBUG,
+ { ?$($k).+ = $($field)*}
+ )
+);
+(%$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::DEBUG,
+ { %$($k).+ = $($field)*}
+ )
+);
+($($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::DEBUG,
+ { $($k).+, $($field)*}
+ )
+);
+(?$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::DEBUG,
+ { ?$($k).+, $($field)*}
+ )
+);
+(%$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::DEBUG,
+ { %$($k).+, $($field)*}
+ )
+);
+(?$($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::DEBUG,
+ { ?$($k).+ }
+ )
+);
+(%$($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::DEBUG,
+ { %$($k).+ }
+ )
+);
+($($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::DEBUG,
+ { $($k).+ }
+ )
+);
+($($arg:tt)+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::DEBUG,
+ $($arg)+
+ )
+);
+}
+
+#[macro_export]
+macro_rules! info {
+// Name / target / parent.
+(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
+);
+
+// Name / target.
+(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::INFO, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::INFO, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::INFO, {}, $($arg)+)
+);
+
+// Target / parent.
+(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
+);
+
+// Name / parent.
+(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
+);
+
+// Name.
+(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::INFO, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::INFO, { $($k).+ $($field)* })
+);
+(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::INFO, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::INFO, { %$($k).+ $($field)* })
+);
+(name: $name:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, $crate::Level::INFO, {}, $($arg)+)
+);
+
+// Target.
+(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::INFO, { $($k).+ $($field)* })
+);
+(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::INFO, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::INFO, { %$($k).+ $($field)* })
+);
+(target: $target:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: $target, $crate::Level::INFO, {}, $($arg)+)
+);
+
+// Parent.
+(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::INFO,
+ { $($field)+ },
+ $($arg)+
+ )
+);
+(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::INFO,
+ { $($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::INFO,
+ { ?$($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::INFO,
+ { %$($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::INFO,
+ { $($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::INFO,
+ { ?$($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::INFO,
+ { %$($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, $($arg:tt)+) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::INFO,
+ {},
+ $($arg)+
+ )
+);
+
+// ...
+({ $($field:tt)+ }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::INFO,
+ { $($field)+ },
+ $($arg)+
+ )
+);
+($($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::INFO,
+ { $($k).+ = $($field)*}
+ )
+);
+(?$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::INFO,
+ { ?$($k).+ = $($field)*}
+ )
+);
+(%$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::INFO,
+ { %$($k).+ = $($field)*}
+ )
+);
+($($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::INFO,
+ { $($k).+, $($field)*}
+ )
+);
+(?$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::INFO,
+ { ?$($k).+, $($field)*}
+ )
+);
+(%$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::INFO,
+ { %$($k).+, $($field)*}
+ )
+);
+(?$($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::INFO,
+ { ?$($k).+ }
+ )
+);
+(%$($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::INFO,
+ { %$($k).+ }
+ )
+);
+($($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::INFO,
+ { $($k).+ }
+ )
+);
+($($arg:tt)+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::INFO,
+ $($arg)+
+ )
+);
+}
+
+#[macro_export]
+macro_rules! warn {
+// Name / target / parent.
+(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
+);
+
+// Name / target.
+(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::WARN, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::WARN, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::WARN, {}, $($arg)+)
+);
+
+// Target / parent.
+(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
+);
+
+// Name / parent.
+(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
+);
+
+// Name.
+(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::WARN, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::WARN, { $($k).+ $($field)* })
+);
+(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::WARN, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::WARN, { %$($k).+ $($field)* })
+);
+(name: $name:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, $crate::Level::WARN, {}, $($arg)+)
+);
+
+// Target.
+(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::WARN, { $($k).+ $($field)* })
+);
+(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::WARN, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::WARN, { %$($k).+ $($field)* })
+);
+(target: $target:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: $target, $crate::Level::WARN, {}, $($arg)+)
+);
+
+// Parent.
+(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::WARN,
+ { $($field)+ },
+ $($arg)+
+ )
+);
+(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::WARN,
+ { $($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::WARN,
+ { ?$($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::WARN,
+ { %$($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::WARN,
+ { $($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::WARN,
+ { ?$($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::WARN,
+ { %$($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, $($arg:tt)+) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::WARN,
+ {},
+ $($arg)+
+ )
+);
+
+// ...
+({ $($field:tt)+ }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::WARN,
+ { $($field)+ },
+ $($arg)+
+ )
+);
+($($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::WARN,
+ { $($k).+ = $($field)*}
+ )
+);
+(?$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::WARN,
+ { ?$($k).+ = $($field)*}
+ )
+);
+(%$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::WARN,
+ { %$($k).+ = $($field)*}
+ )
+);
+($($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::WARN,
+ { $($k).+, $($field)*}
+ )
+);
+(?$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::WARN,
+ { ?$($k).+, $($field)*}
+ )
+);
+(%$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::WARN,
+ { %$($k).+, $($field)*}
+ )
+);
+(?$($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::WARN,
+ { ?$($k).+ }
+ )
+);
+(%$($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::WARN,
+ { %$($k).+ }
+ )
+);
+($($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::WARN,
+ { $($k).+ }
+ )
+);
+($($arg:tt)+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::WARN,
+ $($arg)+
+ )
+);
+}
+
+#[macro_export]
+macro_rules! error {
+// Name / target / parent.
+(name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
+);
+
+// Name / target.
+(name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { %$($k).+ $($field)* })
+);
+(name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, target: $target, $crate::Level::ERROR, {}, $($arg)+)
+);
+
+// Target / parent.
+(target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
+);
+(target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
+);
+
+// Name / parent.
+(name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
+);
+(name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
+);
+
+// Name.
+(name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+);
+(name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::ERROR, { $($k).+ $($field)* })
+);
+(name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+);
+(name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(name: $name, $crate::Level::ERROR, { %$($k).+ $($field)* })
+);
+(name: $name:expr, $($arg:tt)+ ) => (
+ $crate::event!(name: $name, $crate::Level::ERROR, {}, $($arg)+)
+);
+
+// Target.
+(target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+);
+(target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::ERROR, { $($k).+ $($field)* })
+);
+(target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+);
+(target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+ $crate::event!(target: $target, $crate::Level::ERROR, { %$($k).+ $($field)* })
+);
+(target: $target:expr, $($arg:tt)+ ) => (
+ $crate::event!(target: $target, $crate::Level::ERROR, {}, $($arg)+)
+);
+
+// Parent.
+(parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::ERROR,
+ { $($field)+ },
+ $($arg)+
+ )
+);
+(parent: $parent:expr, $($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::ERROR,
+ { $($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, ?$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::ERROR,
+ { ?$($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, %$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::ERROR,
+ { %$($k).+ = $($field)*}
+ )
+);
+(parent: $parent:expr, $($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::ERROR,
+ { $($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, ?$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::ERROR,
+ { ?$($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, %$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::ERROR,
+ { %$($k).+, $($field)*}
+ )
+);
+(parent: $parent:expr, $($arg:tt)+) => (
+ $crate::event!(
+ target: module_path!(),
+ parent: $parent,
+ $crate::Level::ERROR,
+ {},
+ $($arg)+
+ )
+);
+
+// ...
+({ $($field:tt)+ }, $($arg:tt)+ ) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::ERROR,
+ { $($field)+ },
+ $($arg)+
+ )
+);
+($($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::ERROR,
+ { $($k).+ = $($field)*}
+ )
+);
+(?$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::ERROR,
+ { ?$($k).+ = $($field)*}
+ )
+);
+(%$($k:ident).+ = $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::ERROR,
+ { %$($k).+ = $($field)*}
+ )
+);
+($($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::ERROR,
+ { $($k).+, $($field)*}
+ )
+);
+(?$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::ERROR,
+ { ?$($k).+, $($field)*}
+ )
+);
+(%$($k:ident).+, $($field:tt)*) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::ERROR,
+ { %$($k).+, $($field)*}
+ )
+);
+(?$($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::ERROR,
+ { ?$($k).+ }
+ )
+);
+(%$($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::ERROR,
+ { %$($k).+ }
+ )
+);
+($($k:ident).+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::ERROR,
+ { $($k).+ }
+ )
+);
+($($arg:tt)+) => (
+ $crate::event!(
+ target: module_path!(),
+ $crate::Level::ERROR,
+ $($arg)+
+ )
+);
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! callsite {
+(name: $name:expr, kind: $kind:expr, fields: $($fields:tt)*) => {{
+ $crate::callsite! {
+ name: $name,
+ kind: $kind,
+ target: module_path!(),
+ level: $crate::Level::TRACE,
+ fields: $($fields)*
+ }
+}};
+(
+ name: $name:expr,
+ kind: $kind:expr,
+ level: $lvl:expr,
+ fields: $($fields:tt)*
+) => {{
+ $crate::callsite! {
+ name: $name,
+ kind: $kind,
+ target: module_path!(),
+ level: $lvl,
+ fields: $($fields)*
+ }
+}};
+(
+ name: $name:expr,
+ kind: $kind:expr,
+ target: $target:expr,
+ level: $lvl:expr,
+ fields: $($fields:tt)*
+) => {{
+ static META: $crate::Metadata<'static> = {
+ $crate::metadata! {
+ name: $name,
+ target: $target,
+ level: $lvl,
+ fields: $crate::fieldset!( $($fields)* ),
+ callsite: &__CALLSITE,
+ kind: $kind,
+ }
+ };
+ static __CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite::DefaultCallsite::new(&META);
+ __CALLSITE.register();
+ &__CALLSITE
+}};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! callsite2 {
+(name: $name:expr, kind: $kind:expr, fields: $($fields:tt)*) => {{
+ $crate::callsite2! {
+ name: $name,
+ kind: $kind,
+ target: module_path!(),
+ level: $crate::Level::TRACE,
+ fields: $($fields)*
+ }
+}};
+(
+ name: $name:expr,
+ kind: $kind:expr,
+ level: $lvl:expr,
+ fields: $($fields:tt)*
+) => {{
+ $crate::callsite2! {
+ name: $name,
+ kind: $kind,
+ target: module_path!(),
+ level: $lvl,
+ fields: $($fields)*
+ }
+}};
+(
+ name: $name:expr,
+ kind: $kind:expr,
+ target: $target:expr,
+ level: $lvl:expr,
+ fields: $($fields:tt)*
+) => {{
+ static META: $crate::Metadata<'static> = {
+ $crate::metadata! {
+ name: $name,
+ target: $target,
+ level: $lvl,
+ fields: $crate::fieldset!( $($fields)* ),
+ callsite: &__CALLSITE,
+ kind: $kind,
+ }
+ };
+ $crate::callsite::DefaultCallsite::new(&META)
+}};
+}
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! level_enabled {
+($lvl:expr) => {
+ $lvl <= $crate::level_filters::STATIC_MAX_LEVEL
+ && $lvl <= $crate::level_filters::LevelFilter::current()
+};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! valueset {
+
+// === base case ===
+(@ { $(,)* $($val:expr),* $(,)* }, $next:expr $(,)*) => {
+ &[ $($val),* ]
+};
+
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr, $($rest:tt)*) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
+ $next,
+ $($rest)*
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr, $($rest:tt)*) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
+ $next,
+ $($rest)*
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr, $($rest:tt)*) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
+ $next,
+ $($rest)*
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+, $($rest:tt)*) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$($k).+ as &dyn Value)) },
+ $next,
+ $($rest)*
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+, $($rest:tt)*) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$($k).+) as &dyn Value)) },
+ $next,
+ $($rest)*
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+, $($rest:tt)*) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$($k).+) as &dyn Value)) },
+ $next,
+ $($rest)*
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
+ $next,
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
+ $next,
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
+ $next,
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$($k).+ as &dyn Value)) },
+ $next,
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$($k).+) as &dyn Value)) },
+ $next,
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$($k).+) as &dyn Value)) },
+ $next,
+ )
+};
+
+// Handle literal names
+(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr, $($rest:tt)*) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
+ $next,
+ $($rest)*
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr, $($rest:tt)*) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
+ $next,
+ $($rest)*
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr, $($rest:tt)*) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
+ $next,
+ $($rest)*
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
+ $next,
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
+ $next,
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
+ $next,
+ )
+};
+
+// Handle constant names
+(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = ?$val:expr, $($rest:tt)*) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) },
+ $next,
+ $($rest)*
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = %$val:expr, $($rest:tt)*) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) },
+ $next,
+ $($rest)*
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = $val:expr, $($rest:tt)*) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, Some(&$val as &dyn Value)) },
+ $next,
+ $($rest)*
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = ?$val:expr) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) },
+ $next,
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = %$val:expr) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) },
+ $next,
+ )
+};
+(@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = $val:expr) => {
+ $crate::valueset!(
+ @ { $($out),*, (&$next, Some(&$val as &dyn Value)) },
+ $next,
+ )
+};
+
+(@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => {
+ $crate::valueset!(@ { (&$next, $crate::__macro_support::Option::Some(&$crate::__macro_support::format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, )
+};
+
+($fields:expr, $($kvs:tt)+) => {
+ {
+ #[allow(unused_imports)]
+ use $crate::field::{debug, display, Value};
+ let mut iter = $fields.iter();
+ $fields.value_set($crate::valueset!(
+ @ { },
+ $crate::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
+ $($kvs)+
+ ))
+ }
+};
+($fields:expr,) => {
+ {
+ $fields.value_set(&[])
+ }
+};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! fieldset {
+(@ { $(,)* $($out:expr),* $(,)* } $(,)*) => {
+ &[ $($out),* ]
+};
+
+(@ { $(,)* $($out:expr),* } $($k:ident).+ = ?$val:expr, $($rest:tt)*) => {
+ $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } $($k:ident).+ = %$val:expr, $($rest:tt)*) => {
+ $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } $($k:ident).+ = $val:expr, $($rest:tt)*) => {
+ $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } ?$($k:ident).+, $($rest:tt)*) => {
+ $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } %$($k:ident).+, $($rest:tt)*) => {
+ $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } $($k:ident).+, $($rest:tt)*) => {
+ $crate::fieldset!(@ { $($out),*, $crate::__tracing_stringify!($($k).+) } $($rest)*)
+};
+
+// Handle literal names
+(@ { $(,)* $($out:expr),* } $k:literal = ?$val:expr, $($rest:tt)*) => {
+ $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } $k:literal = %$val:expr, $($rest:tt)*) => {
+ $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } $k:literal = $val:expr, $($rest:tt)*) => {
+ $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+};
+
+// Handle constant names
+(@ { $(,)* $($out:expr),* } { $k:expr } = ?$val:expr, $($rest:tt)*) => {
+ $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } { $k:expr } = %$val:expr, $($rest:tt)*) => {
+ $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+};
+(@ { $(,)* $($out:expr),* } { $k:expr } = $val:expr, $($rest:tt)*) => {
+ $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+};
+
+(@ { $(,)* $($out:expr),* } $($rest:tt)+) => {
+ $crate::fieldset!(@ { "message", $($out),*, })
+};
+
+($($args:tt)*) => {
+ $crate::fieldset!(@ { } $($args)*,)
+};
+
+}
+
+#[cfg(feature = "log")]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! level_to_log {
+($level:expr) => {
+ match $level {
+ $crate::Level::ERROR => $crate::log::Level::Error,
+ $crate::Level::WARN => $crate::log::Level::Warn,
+ $crate::Level::INFO => $crate::log::Level::Info,
+ $crate::Level::DEBUG => $crate::log::Level::Debug,
+ _ => $crate::log::Level::Trace,
+ }
+};
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __tracing_stringify {
+($($t:tt)*) => {
+ stringify!($($t)*)
+};
+}
+
+#[cfg(not(feature = "log"))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __tracing_log {
+($level:expr, $callsite:expr, $value_set:expr) => {};
+}
+
+#[cfg(feature = "log")]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! __tracing_log {
+($level:expr, $callsite:expr, $value_set:expr) => {
+ $crate::if_log_enabled! { $level, {
+ use $crate::log;
+ let level = $crate::level_to_log!($level);
+ if level <= log::max_level() {
+ let meta = $callsite.metadata();
+ let log_meta = log::Metadata::builder()
+ .level(level)
+ .target(meta.target())
+ .build();
+ let logger = log::logger();
+ if logger.enabled(&log_meta) {
+ $crate::__macro_support::__tracing_log(meta, logger, log_meta, $value_set)
+ }
+ }
+ }}
+};
+}
+
+#[cfg(not(feature = "log"))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! if_log_enabled {
+($lvl:expr, $e:expr;) => {
+ $crate::if_log_enabled! { $lvl, $e }
+};
+($lvl:expr, $if_log:block) => {
+ $crate::if_log_enabled! { $lvl, $if_log else {} }
+};
+($lvl:expr, $if_log:block else $else_block:block) => {
+ $else_block
+};
+}
+
+#[cfg(all(feature = "log", not(feature = "log-always")))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! if_log_enabled {
+($lvl:expr, $e:expr;) => {
+ $crate::if_log_enabled! { $lvl, $e }
+};
+($lvl:expr, $if_log:block) => {
+ $crate::if_log_enabled! { $lvl, $if_log else {} }
+};
+($lvl:expr, $if_log:block else $else_block:block) => {
+ if $crate::level_to_log!($lvl) <= $crate::log::STATIC_MAX_LEVEL {
+ if !$crate::dispatcher::has_been_set() {
+ $if_log
+ } else {
+ $else_block
+ }
+ } else {
+ $else_block
+ }
+};
+}
+
+#[cfg(all(feature = "log", feature = "log-always"))]
+#[doc(hidden)]
+#[macro_export]
+macro_rules! if_log_enabled {
+($lvl:expr, $e:expr;) => {
+ $crate::if_log_enabled! { $lvl, $e }
+};
+($lvl:expr, $if_log:block) => {
+ $crate::if_log_enabled! { $lvl, $if_log else {} }
+};
+($lvl:expr, $if_log:block else $else_block:block) => {
+ if $crate::level_to_log!($lvl) <= $crate::log::STATIC_MAX_LEVEL {
+ #[allow(unused_braces)]
+ $if_log
+ } else {
+ $else_block
+ }
+};
+}
+
+//- /lib.rs crate:ra_test_fixture deps:tracing
+fn foo() {
+tracing::error!();
+}
+ "#,
+ &["E0432", "inactive-code", "unresolved-macro-call", "syntax-error", "macro-error"],
+ );
+}
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index 6a4e5ba290..18f866eb9f 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -220,7 +220,11 @@ mod tests {
location: AnnotationLocation::AboveName,
};
- fn check_with_config(ra_fixture: &str, expect: Expect, config: &AnnotationConfig) {
+ fn check_with_config(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: Expect,
+ config: &AnnotationConfig,
+ ) {
let (analysis, file_id) = fixture::file(ra_fixture);
let annotations: Vec<Annotation> = analysis
@@ -233,7 +237,7 @@ mod tests {
expect.assert_debug_eq(&annotations);
}
- fn check(ra_fixture: &str, expect: Expect) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
check_with_config(ra_fixture, expect, &DEFAULT_CONFIG);
}
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index 8066894cd8..afd6f740c4 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -173,7 +173,7 @@ mod tests {
fn check_hierarchy(
exclude_tests: bool,
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
expected_nav: Expect,
expected_incoming: Expect,
expected_outgoing: Expect,
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 72fcac5417..bc9843f3f3 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -199,6 +199,7 @@ pub(crate) fn resolve_doc_path_for_def(
) -> Option<Definition> {
match def {
Definition::Module(it) => it.resolve_doc_path(db, link, ns),
+ Definition::Crate(it) => it.resolve_doc_path(db, link, ns),
Definition::Function(it) => it.resolve_doc_path(db, link, ns),
Definition::Adt(it) => it.resolve_doc_path(db, link, ns),
Definition::Variant(it) => it.resolve_doc_path(db, link, ns),
@@ -594,6 +595,7 @@ fn filename_and_frag_for_def(
Adt::Enum(e) => format!("enum.{}.html", e.name(db).unescaped().display(db.upcast())),
Adt::Union(u) => format!("union.{}.html", u.name(db).unescaped().display(db.upcast())),
},
+ Definition::Crate(_) => String::from("index.html"),
Definition::Module(m) => match m.name(db) {
// `#[doc(keyword = "...")]` is internal used only by rust compiler
Some(name) => {
diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs
index fe91c81a61..d7291c4b9f 100644
--- a/crates/ide/src/doc_links/tests.rs
+++ b/crates/ide/src/doc_links/tests.rs
@@ -16,7 +16,7 @@ use crate::{
};
fn check_external_docs(
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
target_dir: Option<&str>,
expect_web_url: Option<Expect>,
expect_local_url: Option<Expect>,
@@ -41,7 +41,7 @@ fn check_external_docs(
}
}
-fn check_rewrite(ra_fixture: &str, expect: Expect) {
+fn check_rewrite(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let sema = &Semantics::new(&*analysis.db);
let (cursor_def, docs) = def_under_cursor(sema, &position);
@@ -49,7 +49,7 @@ fn check_rewrite(ra_fixture: &str, expect: Expect) {
expect.assert_eq(&res)
}
-fn check_doc_links(ra_fixture: &str) {
+fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let key_fn = |&(FileRange { file_id, range }, _): &_| (file_id, range.start());
let (analysis, position, mut expected) = fixture::annotations(ra_fixture);
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index e028c5ff0c..0ad894427b 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -296,7 +296,7 @@ mod tests {
use crate::fixture;
#[track_caller]
- fn check(ra_fixture: &str, expect: Expect) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (analysis, pos) = fixture::position(ra_fixture);
let expansion = analysis.expand_macro(pos).unwrap().unwrap();
let actual = format!("{}\n{}", expansion.name, expansion.expansion);
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs
index 5ef65c209c..50977ee840 100644
--- a/crates/ide/src/file_structure.rs
+++ b/crates/ide/src/file_structure.rs
@@ -257,7 +257,7 @@ mod tests {
use super::*;
- fn check(ra_fixture: &str, expect: Expect) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let file = SourceFile::parse(ra_fixture, span::Edition::CURRENT).ok().unwrap();
let structure = file_structure(&file);
expect.assert_debug_eq(&structure)
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs
index b16511072b..a0612f48d3 100644
--- a/crates/ide/src/fixture.rs
+++ b/crates/ide/src/fixture.rs
@@ -5,7 +5,7 @@ use test_utils::{extract_annotations, RangeOrOffset};
use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
/// Creates analysis for a single file.
-pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
+pub(crate) fn file(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Analysis, FileId) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
host.db.enable_proc_attr_macros();
@@ -14,7 +14,9 @@ pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
}
/// Creates analysis from a multi-file fixture, returns positions marked with $0.
-pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
+pub(crate) fn position(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) -> (Analysis, FilePosition) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
host.db.enable_proc_attr_macros();
@@ -25,7 +27,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
}
/// Creates analysis for a single file, returns range marked with a pair of $0.
-pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
+pub(crate) fn range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Analysis, FileRange) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
host.db.enable_proc_attr_macros();
@@ -36,7 +38,9 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
}
/// Creates analysis for a single file, returns range marked with a pair of $0 or a position marked with $0.
-pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrOffset) {
+pub(crate) fn range_or_position(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) -> (Analysis, FileId, RangeOrOffset) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
host.db.enable_proc_attr_macros();
@@ -46,7 +50,9 @@ pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrO
}
/// Creates analysis from a multi-file fixture, returns positions marked with $0.
-pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) {
+pub(crate) fn annotations(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) -> (Analysis, FilePosition, Vec<(FileRange, String)>) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
host.db.enable_proc_attr_macros();
@@ -69,7 +75,9 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
}
/// Creates analysis from a multi-file fixture with annotations without $0
-pub(crate) fn annotations_without_marker(ra_fixture: &str) -> (Analysis, Vec<(FileRange, String)>) {
+pub(crate) fn annotations_without_marker(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+) -> (Analysis, Vec<(FileRange, String)>) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
host.db.enable_proc_attr_macros();
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index c1b7693a65..e5a94ff9fe 100755
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -286,7 +286,7 @@ mod tests {
use super::*;
- fn check(ra_fixture: &str) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (ranges, text) = extract_tags(ra_fixture, "fold");
let parse = SourceFile::parse(&text, span::Edition::CURRENT);
diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs
index 7b6a5ef13e..3742edc8db 100644
--- a/crates/ide/src/goto_declaration.rs
+++ b/crates/ide/src/goto_declaration.rs
@@ -83,7 +83,7 @@ mod tests {
use crate::fixture;
- fn check(ra_fixture: &str) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
let navs = analysis
.goto_declaration(position)
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 6c66907ec3..f804cc3677 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -81,6 +81,10 @@ pub(crate) fn goto_definition(
return Some(RangeInfo::new(original_token.text_range(), navs));
}
+ if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &original_token) {
+ return Some(RangeInfo::new(original_token.text_range(), navs));
+ }
+
let navs = sema
.descend_into_macros_no_opaque(original_token.clone())
.into_iter()
@@ -125,6 +129,18 @@ pub(crate) fn goto_definition(
Some(RangeInfo::new(original_token.text_range(), navs))
}
+// If the token is into(), try_into(), parse(), search the definition of From, TryFrom, FromStr.
+fn find_definition_for_known_blanket_dual_impls(
+ sema: &Semantics<'_, RootDatabase>,
+ original_token: &SyntaxToken,
+) -> Option<Vec<NavigationTarget>> {
+ let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?;
+ let target_method = sema.resolve_known_blanket_dual_impls(&method_call)?;
+
+ let def = Definition::from(target_method);
+ Some(def_to_nav(sema.db, def))
+}
+
fn try_lookup_include_path(
sema: &Semantics<'_, RootDatabase>,
token: ast::String,
@@ -424,7 +440,7 @@ mod tests {
use syntax::SmolStr;
#[track_caller]
- fn check(ra_fixture: &str) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
@@ -443,14 +459,14 @@ mod tests {
assert_eq!(expected, navs);
}
- fn check_unresolved(ra_fixture: &str) {
+ fn check_unresolved(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position) = fixture::position(ra_fixture);
let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
}
- fn check_name(expected_name: &str, ra_fixture: &str) {
+ fn check_name(expected_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, _) = fixture::annotations(ra_fixture);
let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
assert!(navs.len() < 2, "expected single navigation target but encountered {}", navs.len());
@@ -3022,4 +3038,150 @@ fn foo() {
"#,
);
}
+ #[test]
+ fn into_call_to_from_definition() {
+ check(
+ r#"
+//- minicore: from
+struct A;
+
+struct B;
+
+impl From<A> for B {
+ fn from(value: A) -> Self {
+ //^^^^
+ B
+ }
+}
+
+fn f() {
+ let a = A;
+ let b: B = a.into$0();
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn into_call_to_from_definition_with_trait_bounds() {
+ check(
+ r#"
+//- minicore: from, iterator
+struct A;
+
+impl<T> From<T> for A
+where
+ T: IntoIterator<Item = i64>,
+{
+ fn from(value: T) -> Self {
+ //^^^^
+ A
+ }
+}
+
+fn f() {
+ let a: A = [1, 2, 3].into$0();
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn goto_into_definition_if_exists() {
+ check(
+ r#"
+//- minicore: from
+struct A;
+
+struct B;
+
+impl Into<B> for A {
+ fn into(self) -> B {
+ //^^^^
+ B
+ }
+}
+
+fn f() {
+ let a = A;
+ let b: B = a.into$0();
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn try_into_call_to_try_from_definition() {
+ check(
+ r#"
+//- minicore: from
+struct A;
+
+struct B;
+
+impl TryFrom<A> for B {
+ type Error = String;
+
+ fn try_from(value: A) -> Result<Self, Self::Error> {
+ //^^^^^^^^
+ Ok(B)
+ }
+}
+
+fn f() {
+ let a = A;
+ let b: Result<B, _> = a.try_into$0();
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn goto_try_into_definition_if_exists() {
+ check(
+ r#"
+//- minicore: from
+struct A;
+
+struct B;
+
+impl TryInto<B> for A {
+ type Error = String;
+
+ fn try_into(self) -> Result<B, Self::Error> {
+ //^^^^^^^^
+ Ok(B)
+ }
+}
+
+fn f() {
+ let a = A;
+ let b: Result<B, _> = a.try_into$0();
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn parse_call_to_from_str_definition() {
+ check(
+ r#"
+//- minicore: from, str
+struct A;
+
+impl FromStr for A {
+ type Error = String;
+
+ fn from_str(value: &str) -> Result<Self, Self::Error> {
+ //^^^^^^^^
+ Ok(A)
+ }
+}
+
+fn f() {
+ let a: Result<A, _> = "aaaaaa".parse$0();
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 04da1f67e9..e926378367 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -129,7 +129,7 @@ mod tests {
use crate::fixture;
- fn check(ra_fixture: &str) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index c7ebd9a353..2610d6c886 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -24,9 +24,10 @@ pub(crate) fn goto_type_definition(
let file: ast::SourceFile = sema.parse_guess_edition(file_id);
let token: SyntaxToken =
pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind {
- IDENT | INT_NUMBER | T![self] => 2,
+ IDENT | INT_NUMBER | T![self] => 3,
kind if kind.is_trivia() => 0,
- _ => 1,
+ T![;] => 1,
+ _ => 2,
})?;
let mut res = Vec::new();
@@ -118,7 +119,7 @@ mod tests {
use crate::fixture;
- fn check(ra_fixture: &str) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
let navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
assert!(!navs.is_empty(), "navigation is empty");
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index 4002cbebad..612bc36f62 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -684,12 +684,15 @@ mod tests {
};
#[track_caller]
- fn check(ra_fixture: &str) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_with_config(ra_fixture, ENABLED_CONFIG);
}
#[track_caller]
- fn check_with_config(ra_fixture: &str, config: HighlightRelatedConfig) {
+ fn check_with_config(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ config: HighlightRelatedConfig,
+ ) {
let (analysis, pos, annotations) = fixture::annotations(ra_fixture);
let hls = analysis.highlight_related(config, pos).unwrap().unwrap_or_default();
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 1431bd8ca2..18a3fed07e 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -6,7 +6,9 @@ mod tests;
use std::{iter, ops::Not};
use either::Either;
-use hir::{db::DefDatabase, GenericSubstitution, HasCrate, HasSource, LangItem, Semantics};
+use hir::{
+ db::DefDatabase, GenericDef, GenericSubstitution, HasCrate, HasSource, LangItem, Semantics,
+};
use ide_db::{
defs::{Definition, IdentClass, NameRefClass, OperatorClass},
famous_defs::FamousDefs,
@@ -548,24 +550,29 @@ fn goto_type_action_for_def(
});
}
- if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def {
- let krate = it.module(db).krate();
- let sized_trait =
- db.lang_item(krate.into(), LangItem::Sized).and_then(|lang_item| lang_item.as_trait());
-
- it.trait_bounds(db)
- .into_iter()
- .filter(|&it| Some(it.into()) != sized_trait)
- .for_each(|it| push_new_def(it.into()));
- } else {
- let ty = match def {
- Definition::Local(it) => it.ty(db),
- Definition::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
- Definition::Field(field) => field.ty(db),
- Definition::Function(function) => function.ret_type(db),
- _ => return HoverAction::goto_type_from_targets(db, targets, edition),
- };
+ if let Ok(generic_def) = GenericDef::try_from(def) {
+ generic_def.type_or_const_params(db).into_iter().for_each(|it| {
+ walk_and_push_ty(db, &it.ty(db), &mut push_new_def);
+ });
+ }
+ let ty = match def {
+ Definition::Local(it) => Some(it.ty(db)),
+ Definition::Field(field) => Some(field.ty(db)),
+ Definition::TupleField(field) => Some(field.ty(db)),
+ Definition::Const(it) => Some(it.ty(db)),
+ Definition::Static(it) => Some(it.ty(db)),
+ Definition::Function(func) => {
+ for param in func.assoc_fn_params(db) {
+ walk_and_push_ty(db, param.ty(), &mut push_new_def);
+ }
+ Some(func.ret_type(db))
+ }
+ Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)),
+ Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)),
+ _ => None,
+ };
+ if let Some(ty) = ty {
walk_and_push_ty(db, &ty, &mut push_new_def);
}
@@ -592,6 +599,14 @@ fn walk_and_push_ty(
traits.for_each(|it| push_new_def(it.into()));
} else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
push_new_def(trait_.into());
+ } else if let Some(tp) = t.as_type_param(db) {
+ let sized_trait = db
+ .lang_item(t.krate(db).into(), LangItem::Sized)
+ .and_then(|lang_item| lang_item.as_trait());
+ tp.trait_bounds(db)
+ .into_iter()
+ .filter(|&it| Some(it.into()) != sized_trait)
+ .for_each(|it| push_new_def(it.into()));
}
});
}
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 8fbd445d96..46242b75dd 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -3,7 +3,7 @@ use std::{env, mem, ops::Not};
use either::Either;
use hir::{
- db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, AssocItemContainer, CaptureKind,
+ db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind,
DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError,
MethodViolationCode, Name, Semantics, Symbol, Trait, Type, TypeInfo, VariantDef,
};
@@ -376,7 +376,7 @@ pub(super) fn process_markup(
Markup::from(markup)
}
-fn definition_owner_name(db: &RootDatabase, def: &Definition, edition: Edition) -> Option<String> {
+fn definition_owner_name(db: &RootDatabase, def: Definition, edition: Edition) -> Option<String> {
match def {
Definition::Field(f) => {
let parent = f.parent_def(db);
@@ -390,9 +390,52 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition, edition: Edition)
_ => Some(parent_name),
};
}
- Definition::Local(l) => l.parent(db).name(db),
Definition::Variant(e) => Some(e.parent_enum(db).name(db)),
-
+ Definition::GenericParam(generic_param) => match generic_param.parent() {
+ hir::GenericDef::Adt(it) => Some(it.name(db)),
+ hir::GenericDef::Trait(it) => Some(it.name(db)),
+ hir::GenericDef::TraitAlias(it) => Some(it.name(db)),
+ hir::GenericDef::TypeAlias(it) => Some(it.name(db)),
+
+ hir::GenericDef::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)),
+ hir::GenericDef::Function(it) => {
+ let container = it.as_assoc_item(db).and_then(|assoc| match assoc.container(db) {
+ hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
+ hir::AssocItemContainer::Impl(i) => {
+ i.self_ty(db).as_adt().map(|adt| adt.name(db))
+ }
+ });
+ match container {
+ Some(name) => {
+ return Some(format!(
+ "{}::{}",
+ name.display(db, edition),
+ it.name(db).display(db, edition)
+ ))
+ }
+ None => Some(it.name(db)),
+ }
+ }
+ hir::GenericDef::Const(it) => {
+ let container = it.as_assoc_item(db).and_then(|assoc| match assoc.container(db) {
+ hir::AssocItemContainer::Trait(t) => Some(t.name(db)),
+ hir::AssocItemContainer::Impl(i) => {
+ i.self_ty(db).as_adt().map(|adt| adt.name(db))
+ }
+ });
+ match container {
+ Some(name) => {
+ return Some(format!(
+ "{}::{}",
+ name.display(db, edition),
+ it.name(db)?.display(db, edition)
+ ))
+ }
+ None => it.name(db),
+ }
+ }
+ },
+ Definition::DeriveHelper(derive_helper) => Some(derive_helper.derive().name(db)),
d => {
if let Some(assoc_item) = d.as_assoc_item(db) {
match assoc_item.container(db) {
@@ -436,7 +479,7 @@ pub(super) fn definition(
config: &HoverConfig,
edition: Edition,
) -> Markup {
- let mod_path = definition_mod_path(db, &def, edition);
+ let mod_path = definition_path(db, &def, edition);
let label = match def {
Definition::Trait(trait_) => {
trait_.display_limited(db, config.max_trait_assoc_items_count, edition).to_string()
@@ -915,19 +958,22 @@ fn closure_ty(
Some(res)
}
-fn definition_mod_path(db: &RootDatabase, def: &Definition, edition: Edition) -> Option<String> {
- if matches!(def, Definition::GenericParam(_) | Definition::Local(_) | Definition::Label(_)) {
+fn definition_path(db: &RootDatabase, &def: &Definition, edition: Edition) -> Option<String> {
+ if matches!(
+ def,
+ Definition::TupleField(_)
+ | Definition::Label(_)
+ | Definition::Local(_)
+ | Definition::BuiltinAttr(_)
+ | Definition::BuiltinLifetime(_)
+ | Definition::BuiltinType(_)
+ | Definition::InlineAsmRegOrRegClass(_)
+ | Definition::InlineAsmOperand(_)
+ ) {
return None;
}
- let container: Option<Definition> =
- def.as_assoc_item(db).and_then(|assoc| match assoc.container(db) {
- AssocItemContainer::Trait(trait_) => Some(trait_.into()),
- AssocItemContainer::Impl(impl_) => impl_.self_ty(db).as_adt().map(|adt| adt.into()),
- });
- container
- .unwrap_or(*def)
- .module(db)
- .map(|module| path(db, module, definition_owner_name(db, def, edition), edition))
+ let rendered_parent = definition_owner_name(db, def, edition);
+ def.module(db).map(|module| path(db, module, rendered_parent, edition))
}
fn markup(
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 2e7637e467..014b751f95 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -23,7 +23,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
max_subst_ty_len: super::SubstTyLen::Unlimited,
};
-fn check_hover_no_result(ra_fixture: &str) {
+fn check_hover_no_result(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
.hover(
@@ -35,7 +35,7 @@ fn check_hover_no_result(ra_fixture: &str) {
}
#[track_caller]
-fn check(ra_fixture: &str, expect: Expect) {
+fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
.hover(
@@ -55,7 +55,7 @@ fn check(ra_fixture: &str, expect: Expect) {
#[track_caller]
fn check_hover_fields_limit(
fields_count: impl Into<Option<usize>>,
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
expect: Expect,
) {
let (analysis, position) = fixture::position(ra_fixture);
@@ -81,7 +81,7 @@ fn check_hover_fields_limit(
#[track_caller]
fn check_hover_enum_variants_limit(
variants_count: impl Into<Option<usize>>,
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
expect: Expect,
) {
let (analysis, position) = fixture::position(ra_fixture);
@@ -105,7 +105,11 @@ fn check_hover_enum_variants_limit(
}
#[track_caller]
-fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) {
+fn check_assoc_count(
+ count: usize,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: Expect,
+) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
.hover(
@@ -126,7 +130,7 @@ fn check_assoc_count(count: usize, ra_fixture: &str, expect: Expect) {
expect.assert_eq(&actual)
}
-fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
+fn check_hover_no_links(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
.hover(
@@ -143,7 +147,7 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
expect.assert_eq(&actual)
}
-fn check_hover_no_memory_layout(ra_fixture: &str, expect: Expect) {
+fn check_hover_no_memory_layout(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
.hover(
@@ -160,7 +164,7 @@ fn check_hover_no_memory_layout(ra_fixture: &str, expect: Expect) {
expect.assert_eq(&actual)
}
-fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
+fn check_hover_no_markdown(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
.hover(
@@ -181,7 +185,7 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
expect.assert_eq(&actual)
}
-fn check_actions(ra_fixture: &str, expect: Expect) {
+fn check_actions(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (analysis, file_id, position) = fixture::range_or_position(ra_fixture);
let mut hover = analysis
.hover(
@@ -206,13 +210,13 @@ fn check_actions(ra_fixture: &str, expect: Expect) {
expect.assert_debug_eq(&hover.info.actions)
}
-fn check_hover_range(ra_fixture: &str, expect: Expect) {
+fn check_hover_range(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (analysis, range) = fixture::range(ra_fixture);
let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap().unwrap();
expect.assert_eq(hover.info.markup.as_str())
}
-fn check_hover_range_actions(ra_fixture: &str, expect: Expect) {
+fn check_hover_range_actions(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (analysis, range) = fixture::range(ra_fixture);
let mut hover = analysis
.hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range)
@@ -234,7 +238,7 @@ fn check_hover_range_actions(ra_fixture: &str, expect: Expect) {
expect.assert_debug_eq(&hover.info.actions);
}
-fn check_hover_range_no_results(ra_fixture: &str) {
+fn check_hover_range_no_results(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, range) = fixture::range(ra_fixture);
let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap();
assert!(hover.is_none());
@@ -2365,6 +2369,97 @@ fn test() {
}
#[test]
+fn test_hover_show_type_def_for_func_param() {
+ check_actions(
+ r#"
+struct Bar;
+fn f(b: Bar) {
+
+}
+
+fn test() {
+ let b = Bar;
+ f$0(b);
+}
+"#,
+ expect![[r#"
+ [
+ Reference(
+ FilePositionWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 15,
+ },
+ ),
+ GoToType(
+ [
+ HoverGotoTypeData {
+ mod_path: "ra_test_fixture::Bar",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 0..11,
+ focus_range: 7..10,
+ name: "Bar",
+ kind: Struct,
+ description: "struct Bar",
+ },
+ },
+ ],
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
+fn test_hover_show_type_def_for_trait_bound() {
+ check_actions(
+ r#"
+trait Bar {}
+fn f<T: Bar>(b: T) {
+
+}
+
+fn test() {
+ f$0();
+}
+"#,
+ expect![[r#"
+ [
+ Reference(
+ FilePositionWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 16,
+ },
+ ),
+ GoToType(
+ [
+ HoverGotoTypeData {
+ mod_path: "ra_test_fixture::Bar",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 0..12,
+ focus_range: 6..9,
+ name: "Bar",
+ kind: Trait,
+ description: "trait Bar",
+ },
+ },
+ ],
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
fn test_hover_non_ascii_space_doc() {
check(
"
@@ -4700,6 +4795,10 @@ fn hover_lifetime() {
*'lifetime*
```rust
+ ra_test_fixture::foo
+ ```
+
+ ```rust
'lifetime
```
"#]],
@@ -4730,6 +4829,10 @@ impl<T: TraitA + TraitB> Foo<T$0> where T: Sized {}
*T*
```rust
+ ra_test_fixture::Foo
+ ```
+
+ ```rust
T: TraitA + TraitB
```
"#]],
@@ -4744,6 +4847,10 @@ impl<T> Foo<T$0> {}
*T*
```rust
+ ra_test_fixture::Foo
+ ```
+
+ ```rust
T
```
"#]],
@@ -4758,6 +4865,10 @@ impl<T: 'static> Foo<T$0> {}
*T*
```rust
+ ra_test_fixture::Foo
+ ```
+
+ ```rust
T: 'static
```
"#]],
@@ -4778,6 +4889,10 @@ impl<T$0: Trait> Foo<T> {}
*T*
```rust
+ ra_test_fixture::Foo
+ ```
+
+ ```rust
T: Trait
```
"#]],
@@ -4793,6 +4908,10 @@ impl<T$0: Trait + ?Sized> Foo<T> {}
*T*
```rust
+ ra_test_fixture::Foo
+ ```
+
+ ```rust
T: Trait + ?Sized
```
"#]],
@@ -4813,6 +4932,10 @@ fn foo<T$0>() {}
*T*
```rust
+ ra_test_fixture::foo
+ ```
+
+ ```rust
T
```
@@ -4834,6 +4957,10 @@ fn foo<T$0: Sized>() {}
*T*
```rust
+ ra_test_fixture::foo
+ ```
+
+ ```rust
T
```
@@ -4855,6 +4982,10 @@ fn foo<T$0: ?Sized>() {}
*T*
```rust
+ ra_test_fixture::foo
+ ```
+
+ ```rust
T: ?Sized
```
@@ -4877,6 +5008,10 @@ fn foo<T$0: Trait>() {}
*T*
```rust
+ ra_test_fixture::foo
+ ```
+
+ ```rust
T: Trait
```
@@ -4899,6 +5034,10 @@ fn foo<T$0: Trait + Sized>() {}
*T*
```rust
+ ra_test_fixture::foo
+ ```
+
+ ```rust
T: Trait
```
@@ -4921,6 +5060,10 @@ fn foo<T$0: Trait + ?Sized>() {}
*T*
```rust
+ ra_test_fixture::foo
+ ```
+
+ ```rust
T: Trait + ?Sized
```
@@ -4942,6 +5085,10 @@ fn foo<T$0: ?Sized + Sized + Sized>() {}
*T*
```rust
+ ra_test_fixture::foo
+ ```
+
+ ```rust
T
```
@@ -4964,6 +5111,10 @@ fn foo<T$0: Sized + ?Sized + Sized + Trait>() {}
*T*
```rust
+ ra_test_fixture::foo
+ ```
+
+ ```rust
T: Trait
```
@@ -5011,6 +5162,10 @@ impl<const LEN: usize> Foo<LEN$0> {}
*LEN*
```rust
+ ra_test_fixture::Foo
+ ```
+
+ ```rust
const LEN: usize
```
"#]],
@@ -6112,7 +6267,7 @@ use foo::bar::{self$0};
```
```rust
- mod bar
+ pub mod bar
```
---
@@ -7857,7 +8012,7 @@ fn test() {
*foo*
```rust
- ra_test_fixture::S
+ ra_test_fixture::m::S
```
```rust
@@ -7886,7 +8041,7 @@ fn test() {
*foo*
```rust
- ra_test_fixture::S
+ ra_test_fixture::m::S
```
```rust
@@ -7916,7 +8071,7 @@ mod m {
*foo*
```rust
- ra_test_fixture::S
+ ra_test_fixture::m::inner::S
```
```rust
@@ -7946,7 +8101,7 @@ fn test() {
*A*
```rust
- ra_test_fixture::S
+ ra_test_fixture::m::S
```
```rust
@@ -7975,7 +8130,7 @@ fn test() {
*A*
```rust
- ra_test_fixture::S
+ ra_test_fixture::m::S
```
```rust
@@ -8005,7 +8160,7 @@ mod m {
*A*
```rust
- ra_test_fixture::S
+ ra_test_fixture::m::inner::S
```
```rust
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index faa65019ee..6d83a747d7 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1,6 +1,6 @@
use std::{
fmt::{self, Write},
- mem::take,
+ mem::{self, take},
};
use either::Either;
@@ -24,6 +24,7 @@ use crate::{navigation_target::TryToNav, FileId};
mod adjustment;
mod bind_pat;
mod binding_mode;
+mod bounds;
mod chaining;
mod closing_brace;
mod closure_captures;
@@ -111,6 +112,9 @@ pub(crate) fn inlay_hints(
}
hints(event);
}
+ if let Some(range_limit) = range_limit {
+ acc.retain(|hint| range_limit.contains_range(hint.range));
+ }
acc
}
@@ -264,6 +268,7 @@ fn hints(
ast::Type::PathType(path) => lifetime::fn_path_hints(hints, ctx, famous_defs, config, file_id, path),
_ => Some(()),
},
+ ast::GenericParamList(it) => bounds::hints(hints, famous_defs, config, file_id, it),
_ => Some(()),
}
};
@@ -273,6 +278,7 @@ fn hints(
pub struct InlayHintsConfig {
pub render_colons: bool,
pub type_hints: bool,
+ pub sized_bound: bool,
pub discriminant_hints: DiscriminantHints,
pub parameter_hints: bool,
pub generic_parameter_hints: GenericParameterHints,
@@ -294,6 +300,36 @@ pub struct InlayHintsConfig {
pub closing_brace_hints_min_lines: Option<usize>,
pub fields_to_resolve: InlayFieldsToResolve,
}
+impl InlayHintsConfig {
+ fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> Lazy<TextEdit> {
+ if self.fields_to_resolve.resolve_text_edits {
+ Lazy::Lazy
+ } else {
+ let edit = finish();
+ never!(edit.is_empty(), "inlay hint produced an empty text edit");
+ Lazy::Computed(edit)
+ }
+ }
+
+ fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> Lazy<InlayTooltip> {
+ if self.fields_to_resolve.resolve_hint_tooltip
+ && self.fields_to_resolve.resolve_label_tooltip
+ {
+ Lazy::Lazy
+ } else {
+ let tooltip = finish();
+ never!(
+ match &tooltip {
+ InlayTooltip::String(s) => s,
+ InlayTooltip::Markdown(s) => s,
+ }
+ .is_empty(),
+ "inlay hint produced an empty tooltip"
+ );
+ Lazy::Computed(tooltip)
+ }
+ }
+}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct InlayFieldsToResolve {
@@ -405,12 +441,32 @@ pub struct InlayHint {
/// The actual label to show in the inlay hint.
pub label: InlayHintLabel,
/// Text edit to apply when "accepting" this inlay hint.
- pub text_edit: Option<TextEdit>,
+ pub text_edit: Option<Lazy<TextEdit>>,
/// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the
/// hint does not support resolving.
pub resolve_parent: Option<TextRange>,
}
+/// A type signaling that a value is either computed, or is available for computation.
+#[derive(Clone, Debug)]
+pub enum Lazy<T> {
+ Computed(T),
+ Lazy,
+}
+
+impl<T> Lazy<T> {
+ pub fn computed(self) -> Option<T> {
+ match self {
+ Lazy::Computed(it) => Some(it),
+ _ => None,
+ }
+ }
+
+ pub fn is_lazy(&self) -> bool {
+ matches!(self, Self::Lazy)
+ }
+}
+
impl std::hash::Hash for InlayHint {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.range.hash(state);
@@ -419,7 +475,7 @@ impl std::hash::Hash for InlayHint {
self.pad_right.hash(state);
self.kind.hash(state);
self.label.hash(state);
- self.text_edit.is_some().hash(state);
+ mem::discriminant(&self.text_edit).hash(state);
}
}
@@ -436,10 +492,6 @@ impl InlayHint {
resolve_parent: None,
}
}
-
- pub fn needs_resolve(&self) -> Option<TextRange> {
- self.resolve_parent.filter(|_| self.text_edit.is_some() || self.label.needs_resolve())
- }
}
#[derive(Debug, Hash)]
@@ -456,7 +508,7 @@ pub struct InlayHintLabel {
impl InlayHintLabel {
pub fn simple(
s: impl Into<String>,
- tooltip: Option<InlayTooltip>,
+ tooltip: Option<Lazy<InlayTooltip>>,
linked_location: Option<FileRange>,
) -> InlayHintLabel {
InlayHintLabel {
@@ -500,10 +552,6 @@ impl InlayHintLabel {
}
self.parts.push(part);
}
-
- pub fn needs_resolve(&self) -> bool {
- self.parts.iter().any(|part| part.linked_location.is_some() || part.tooltip.is_some())
- }
}
impl From<String> for InlayHintLabel {
@@ -538,7 +586,6 @@ impl fmt::Debug for InlayHintLabel {
}
}
-#[derive(Hash)]
pub struct InlayHintLabelPart {
pub text: String,
/// Source location represented by this label part. The client will use this to fetch the part's
@@ -549,13 +596,21 @@ pub struct InlayHintLabelPart {
pub linked_location: Option<FileRange>,
/// The tooltip to show when hovering over the inlay hint, this may invoke other actions like
/// hover requests to show.
- pub tooltip: Option<InlayTooltip>,
+ pub tooltip: Option<Lazy<InlayTooltip>>,
+}
+
+impl std::hash::Hash for InlayHintLabelPart {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.text.hash(state);
+ self.linked_location.hash(state);
+ self.tooltip.is_some().hash(state);
+ }
}
impl fmt::Debug for InlayHintLabelPart {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- Self { text, linked_location: None, tooltip: None } => text.fmt(f),
+ Self { text, linked_location: None, tooltip: None | Some(Lazy::Lazy) } => text.fmt(f),
Self { text, linked_location, tooltip } => f
.debug_struct("InlayHintLabelPart")
.field("text", text)
@@ -563,7 +618,8 @@ impl fmt::Debug for InlayHintLabelPart {
.field(
"tooltip",
&tooltip.as_ref().map_or("", |it| match it {
- InlayTooltip::String(it) | InlayTooltip::Markdown(it) => it,
+ Lazy::Computed(InlayTooltip::String(it) | InlayTooltip::Markdown(it)) => it,
+ Lazy::Lazy => "",
}),
)
.finish(),
@@ -722,19 +778,22 @@ fn hint_iterator(
fn ty_to_text_edit(
sema: &Semantics<'_, RootDatabase>,
+ config: &InlayHintsConfig,
node_for_hint: &SyntaxNode,
ty: &hir::Type,
offset_to_insert: TextSize,
- prefix: String,
-) -> Option<TextEdit> {
- let scope = sema.scope(node_for_hint)?;
+ prefix: impl Into<String>,
+) -> Option<Lazy<TextEdit>> {
// FIXME: Limit the length and bail out on excess somehow?
- let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?;
-
- let mut builder = TextEdit::builder();
- builder.insert(offset_to_insert, prefix);
- builder.insert(offset_to_insert, rendered);
- Some(builder.finish())
+ let rendered = sema
+ .scope(node_for_hint)
+ .and_then(|scope| ty.display_source_code(scope.db, scope.module().into(), false).ok())?;
+ Some(config.lazy_text_edit(|| {
+ let mut builder = TextEdit::builder();
+ builder.insert(offset_to_insert, prefix.into());
+ builder.insert(offset_to_insert, rendered);
+ builder.finish()
+ }))
}
fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool {
@@ -760,6 +819,7 @@ mod tests {
render_colons: false,
type_hints: false,
parameter_hints: false,
+ sized_bound: false,
generic_parameter_hints: GenericParameterHints {
type_hints: false,
lifetime_hints: false,
@@ -794,12 +854,15 @@ mod tests {
};
#[track_caller]
- pub(super) fn check(ra_fixture: &str) {
+ pub(super) fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_with_config(TEST_CONFIG, ra_fixture);
}
#[track_caller]
- pub(super) fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
+ pub(super) fn check_with_config(
+ config: InlayHintsConfig,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ ) {
let (analysis, file_id) = fixture::file(ra_fixture);
let mut expected = extract_annotations(&analysis.file_text(file_id).unwrap());
let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
@@ -814,16 +877,33 @@ mod tests {
assert_eq!(expected, actual, "\nExpected:\n{expected:#?}\n\nActual:\n{actual:#?}");
}
+ #[track_caller]
+ pub(super) fn check_expect(
+ config: InlayHintsConfig,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: Expect,
+ ) {
+ let (analysis, file_id) = fixture::file(ra_fixture);
+ let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
+ let filtered =
+ inlay_hints.into_iter().map(|hint| (hint.range, hint.label)).collect::<Vec<_>>();
+ expect.assert_debug_eq(&filtered)
+ }
+
/// Computes inlay hints for the fixture, applies all the provided text edits and then runs
/// expect test.
#[track_caller]
- pub(super) fn check_edit(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
+ pub(super) fn check_edit(
+ config: InlayHintsConfig,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: Expect,
+ ) {
let (analysis, file_id) = fixture::file(ra_fixture);
let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
let edits = inlay_hints
.into_iter()
- .filter_map(|hint| hint.text_edit)
+ .filter_map(|hint| hint.text_edit?.computed())
.reduce(|mut acc, next| {
acc.union(next).expect("merging text edits failed");
acc
@@ -836,11 +916,15 @@ mod tests {
}
#[track_caller]
- pub(super) fn check_no_edit(config: InlayHintsConfig, ra_fixture: &str) {
+ pub(super) fn check_no_edit(
+ config: InlayHintsConfig,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ ) {
let (analysis, file_id) = fixture::file(ra_fixture);
let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
- let edits: Vec<_> = inlay_hints.into_iter().filter_map(|hint| hint.text_edit).collect();
+ let edits: Vec<_> =
+ inlay_hints.into_iter().filter_map(|hint| hint.text_edit?.computed()).collect();
assert!(edits.is_empty(), "unexpected edits: {edits:?}");
}
@@ -870,4 +954,17 @@ fn foo() {
"#,
);
}
+
+ #[test]
+ fn regression_18898() {
+ check(
+ r#"
+//- proc_macros: issue_18898
+#[proc_macros::issue_18898]
+fn foo() {
+ let
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs
index 4e48baa6f1..2acd4021cc 100644
--- a/crates/ide/src/inlay_hints/adjustment.rs
+++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -162,11 +162,13 @@ pub(super) fn hints(
let label = InlayHintLabelPart {
text: if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
linked_location: None,
- tooltip: Some(InlayTooltip::Markdown(format!(
- "`{}` → `{}` ({coercion} coercion)",
- source.display(sema.db, file_id.edition()),
- target.display(sema.db, file_id.edition()),
- ))),
+ tooltip: Some(config.lazy_tooltip(|| {
+ InlayTooltip::Markdown(format!(
+ "`{}` → `{}` ({coercion} coercion)",
+ source.display(sema.db, file_id.edition()),
+ target.display(sema.db, file_id.edition()),
+ ))
+ })),
};
if postfix { &mut post } else { &mut pre }.label.append_part(label);
}
@@ -183,7 +185,7 @@ pub(super) fn hints(
return None;
}
if allow_edit {
- let edit = {
+ let edit = Some(config.lazy_text_edit(|| {
let mut b = TextEditBuilder::default();
if let Some(pre) = &pre {
b.insert(
@@ -198,14 +200,14 @@ pub(super) fn hints(
);
}
b.finish()
- };
+ }));
match (&mut pre, &mut post) {
(Some(pre), Some(post)) => {
- pre.text_edit = Some(edit.clone());
- post.text_edit = Some(edit);
+ pre.text_edit = edit.clone();
+ post.text_edit = edit;
}
- (Some(pre), None) => pre.text_edit = Some(edit),
- (None, Some(post)) => post.text_edit = Some(edit),
+ (Some(pre), None) => pre.text_edit = edit,
+ (None, Some(post)) => post.text_edit = edit,
(None, None) => (),
}
}
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index 7a808fb4a9..ab5464156f 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -78,13 +78,14 @@ pub(super) fn hints(
let text_edit = if let Some(colon_token) = &type_ascriptable {
ty_to_text_edit(
sema,
+ config,
desc_pat.syntax(),
&ty,
colon_token
.as_ref()
.map_or_else(|| pat.syntax().text_range(), |t| t.text_range())
.end(),
- if colon_token.is_some() { String::new() } else { String::from(": ") },
+ if colon_token.is_some() { "" } else { ": " },
)
} else {
None
@@ -185,7 +186,7 @@ mod tests {
};
#[track_caller]
- fn check_types(ra_fixture: &str) {
+ fn check_types(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_with_config(InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, ra_fixture);
}
@@ -391,36 +392,37 @@ fn main() {
#[test]
fn check_hint_range_limit() {
let fixture = r#"
- //- minicore: fn, sized
- fn foo() -> impl Fn() { loop {} }
- fn foo1() -> impl Fn(f64) { loop {} }
- fn foo2() -> impl Fn(f64, f64) { loop {} }
- fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
- fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
- fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
- fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
- fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
-
- fn main() {
- let foo = foo();
- let foo = foo1();
- let foo = foo2();
- // ^^^ impl Fn(f64, f64)
- let foo = foo3();
- // ^^^ impl Fn(f64, f64) -> u32
- let foo = foo4();
- let foo = foo5();
- let foo = foo6();
- let foo = foo7();
- }
- "#;
+//- minicore: fn, sized
+fn foo() -> impl Fn() { loop {} }
+fn foo1() -> impl Fn(f64) { loop {} }
+fn foo2() -> impl Fn(f64, f64) { loop {} }
+fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
+fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
+fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
+fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
+fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
+
+fn main() {
+ let foo = foo();
+ let foo = foo1();
+ let foo = foo2();
+ // ^^^ impl Fn(f64, f64)
+ let foo = foo3();
+ // ^^^ impl Fn(f64, f64) -> u32
+ let foo = foo4();
+ // ^^^ &dyn Fn(f64, f64) -> u32
+ let foo = foo5();
+ let foo = foo6();
+ let foo = foo7();
+}
+"#;
let (analysis, file_id) = fixture::file(fixture);
let expected = extract_annotations(&analysis.file_text(file_id).unwrap());
let inlay_hints = analysis
.inlay_hints(
&InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
file_id,
- Some(TextRange::new(TextSize::from(500), TextSize::from(600))),
+ Some(TextRange::new(TextSize::from(491), TextSize::from(640))),
)
.unwrap();
let actual =
@@ -1163,4 +1165,45 @@ fn main() {
}"#,
);
}
+
+ #[test]
+ fn collapses_nested_impl_projections() {
+ check_types(
+ r#"
+//- minicore: sized
+trait T {
+ type Assoc;
+ fn f(self) -> Self::Assoc;
+}
+
+trait T2 {}
+trait T3<T> {}
+
+fn f(it: impl T<Assoc: T2>) {
+ let l = it.f();
+ // ^ impl T2
+}
+
+fn f2<G: T<Assoc: T2 + 'static>>(it: G) {
+ let l = it.f();
+ //^ impl T2 + 'static
+}
+
+fn f3<G: T>(it: G) where <G as T>::Assoc: T2 {
+ let l = it.f();
+ //^ impl T2
+}
+
+fn f4<G: T<Assoc: T2 + T3<()>>>(it: G) {
+ let l = it.f();
+ //^ impl T2 + T3<()>
+}
+
+fn f5<G: T<Assoc = ()>>(it: G) {
+ let l = it.f();
+ //^ ()
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs
index 5afb98cb1c..5bbb4fe4e6 100644
--- a/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/crates/ide/src/inlay_hints/binding_mode.rs
@@ -99,17 +99,24 @@ pub(super) fn hints(
}
if let hints @ [_, ..] = &mut acc[acc_base..] {
- let mut edit = TextEditBuilder::default();
- for h in &mut *hints {
- edit.insert(
- match h.position {
- InlayHintPosition::Before => h.range.start(),
- InlayHintPosition::After => h.range.end(),
- },
- h.label.parts.iter().map(|p| &*p.text).collect(),
- );
- }
- let edit = edit.finish();
+ let edit = config.lazy_text_edit(|| {
+ let mut edit = TextEditBuilder::default();
+ for h in &mut *hints {
+ edit.insert(
+ match h.position {
+ InlayHintPosition::Before => h.range.start(),
+ InlayHintPosition::After => h.range.end(),
+ },
+ h.label
+ .parts
+ .iter()
+ .map(|p| &*p.text)
+ .chain(h.pad_right.then_some(" "))
+ .collect(),
+ );
+ }
+ edit.finish()
+ });
hints.iter_mut().for_each(|h| h.text_edit = Some(edit.clone()));
}
@@ -118,8 +125,10 @@ pub(super) fn hints(
#[cfg(test)]
mod tests {
+ use expect_test::expect;
+
use crate::{
- inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
+ inlay_hints::tests::{check_edit, check_with_config, DISABLED_CONFIG},
InlayHintsConfig,
};
@@ -194,4 +203,27 @@ fn foo(s @ Struct { field, .. }: &Struct) {}
"#,
);
}
+
+ #[test]
+ fn edits() {
+ check_edit(
+ InlayHintsConfig { binding_mode_hints: true, ..DISABLED_CONFIG },
+ r#"
+fn main() {
+ match &(0,) {
+ (x,) | (x,) => (),
+ ((x,) | (x,)) => (),
+ }
+}
+"#,
+ expect![[r#"
+ fn main() {
+ match &(0,) {
+ &(&((ref x,) | (ref x,))) => (),
+ &((ref x,) | (ref x,)) => (),
+ }
+ }
+ "#]],
+ );
+ }
}
diff --git a/crates/ide/src/inlay_hints/bounds.rs b/crates/ide/src/inlay_hints/bounds.rs
new file mode 100644
index 0000000000..429ddd31cb
--- /dev/null
+++ b/crates/ide/src/inlay_hints/bounds.rs
@@ -0,0 +1,152 @@
+//! Implementation of trait bound hints.
+//!
+//! Currently this renders the implied `Sized` bound.
+use ide_db::{famous_defs::FamousDefs, FileRange};
+
+use span::EditionedFileId;
+use syntax::ast::{self, AstNode, HasTypeBounds};
+
+use crate::{
+ InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind,
+ TryToNav,
+};
+
+pub(super) fn hints(
+ acc: &mut Vec<InlayHint>,
+ famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
+ config: &InlayHintsConfig,
+ _file_id: EditionedFileId,
+ params: ast::GenericParamList,
+) -> Option<()> {
+ if !config.sized_bound {
+ return None;
+ }
+
+ let linked_location =
+ famous_defs.core_marker_Sized().and_then(|it| it.try_to_nav(sema.db)).map(|it| {
+ let n = it.call_site();
+ FileRange { file_id: n.file_id, range: n.focus_or_full_range() }
+ });
+
+ for param in params.type_or_const_params() {
+ match param {
+ ast::TypeOrConstParam::Type(type_param) => {
+ let c = type_param.colon_token().map(|it| it.text_range());
+ let has_bounds =
+ type_param.type_bound_list().is_some_and(|it| it.bounds().next().is_some());
+ acc.push(InlayHint {
+ range: c.unwrap_or_else(|| type_param.syntax().text_range()),
+ kind: InlayKind::Type,
+ label: {
+ let mut hint = InlayHintLabel::default();
+ if c.is_none() {
+ hint.parts.push(InlayHintLabelPart {
+ text: ": ".to_owned(),
+ linked_location: None,
+ tooltip: None,
+ });
+ }
+ hint.parts.push(InlayHintLabelPart {
+ text: "Sized".to_owned(),
+ linked_location,
+ tooltip: None,
+ });
+ if has_bounds {
+ hint.parts.push(InlayHintLabelPart {
+ text: " +".to_owned(),
+ linked_location: None,
+ tooltip: None,
+ });
+ }
+ hint
+ },
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: c.is_some(),
+ pad_right: has_bounds,
+ resolve_parent: Some(params.syntax().text_range()),
+ });
+ }
+ ast::TypeOrConstParam::Const(_) => (),
+ }
+ }
+
+ Some(())
+}
+
+#[cfg(test)]
+mod tests {
+ use expect_test::expect;
+
+ use crate::inlay_hints::InlayHintsConfig;
+
+ use crate::inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG};
+
+ #[track_caller]
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
+ check_with_config(InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG }, ra_fixture);
+ }
+
+ #[test]
+ fn smoke() {
+ check(
+ r#"
+fn foo<T>() {}
+ // ^ : Sized
+"#,
+ );
+ }
+
+ #[test]
+ fn with_colon() {
+ check(
+ r#"
+fn foo<T:>() {}
+ // ^ Sized
+"#,
+ );
+ }
+
+ #[test]
+ fn with_colon_and_bounds() {
+ check(
+ r#"
+fn foo<T: 'static>() {}
+ // ^ Sized +
+"#,
+ );
+ }
+
+ #[test]
+ fn location_works() {
+ check_expect(
+ InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG },
+ r#"
+//- minicore: sized
+fn foo<T>() {}
+"#,
+ expect![[r#"
+ [
+ (
+ 7..8,
+ [
+ ": ",
+ InlayHintLabelPart {
+ text: "Sized",
+ linked_location: Some(
+ FileRangeWrapper {
+ file_id: FileId(
+ 1,
+ ),
+ range: 135..140,
+ },
+ ),
+ tooltip: "",
+ },
+ ],
+ ),
+ ]
+ "#]],
+ );
+ }
+}
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index 028ed1650f..7fa7ab1a94 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -81,28 +81,19 @@ mod tests {
use crate::{
fixture,
- inlay_hints::tests::{check_with_config, DISABLED_CONFIG, TEST_CONFIG},
+ inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG},
InlayHintsConfig,
};
#[track_caller]
- fn check_chains(ra_fixture: &str) {
+ fn check_chains(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_with_config(InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, ra_fixture);
}
#[track_caller]
- pub(super) fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
- let (analysis, file_id) = fixture::file(ra_fixture);
- let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
- let filtered =
- inlay_hints.into_iter().map(|hint| (hint.range, hint.label)).collect::<Vec<_>>();
- expect.assert_debug_eq(&filtered)
- }
-
- #[track_caller]
pub(super) fn check_expect_clear_loc(
config: InlayHintsConfig,
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
expect: Expect,
) {
let (analysis, file_id) = fixture::file(ra_fixture);
diff --git a/crates/ide/src/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs
index 6827540fa8..7858b1d90a 100644
--- a/crates/ide/src/inlay_hints/closure_ret.rs
+++ b/crates/ide/src/inlay_hints/closure_ret.rs
@@ -52,13 +52,14 @@ pub(super) fn hints(
let text_edit = if has_block_body {
ty_to_text_edit(
sema,
+ config,
closure.syntax(),
&ty,
arrow
.as_ref()
.map_or_else(|| param_list.syntax().text_range(), |t| t.text_range())
.end(),
- if arrow.is_none() { String::from(" -> ") } else { String::new() },
+ if arrow.is_none() { " -> " } else { "" },
)
} else {
None
diff --git a/crates/ide/src/inlay_hints/discriminant.rs b/crates/ide/src/inlay_hints/discriminant.rs
index 8f2949cb38..f1e1955d14 100644
--- a/crates/ide/src/inlay_hints/discriminant.rs
+++ b/crates/ide/src/inlay_hints/discriminant.rs
@@ -36,13 +36,14 @@ pub(super) fn enum_hints(
return None;
}
for variant in enum_.variant_list()?.variants() {
- variant_hints(acc, sema, &enum_, &variant);
+ variant_hints(acc, config, sema, &enum_, &variant);
}
Some(())
}
fn variant_hints(
acc: &mut Vec<InlayHint>,
+ config: &InlayHintsConfig,
sema: &Semantics<'_, RootDatabase>,
enum_: &ast::Enum,
variant: &ast::Variant,
@@ -75,9 +76,11 @@ fn variant_hints(
}
Err(_) => format!("{eq_} ?"),
},
- Some(InlayTooltip::String(match &d {
- Ok(_) => "enum variant discriminant".into(),
- Err(e) => format!("{e:?}"),
+ Some(config.lazy_tooltip(|| {
+ InlayTooltip::String(match &d {
+ Ok(_) => "enum variant discriminant".into(),
+ Err(e) => format!("{e:?}"),
+ })
})),
None,
);
@@ -88,7 +91,9 @@ fn variant_hints(
},
kind: InlayKind::Discriminant,
label,
- text_edit: d.ok().map(|val| TextEdit::insert(range.start(), format!("{eq_} {val}"))),
+ text_edit: d.ok().map(|val| {
+ config.lazy_text_edit(|| TextEdit::insert(range.end(), format!("{eq_} {val}")))
+ }),
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
@@ -99,13 +104,15 @@ fn variant_hints(
}
#[cfg(test)]
mod tests {
+ use expect_test::expect;
+
use crate::inlay_hints::{
- tests::{check_with_config, DISABLED_CONFIG},
+ tests::{check_edit, check_with_config, DISABLED_CONFIG},
DiscriminantHints, InlayHintsConfig,
};
#[track_caller]
- fn check_discriminants(ra_fixture: &str) {
+ fn check_discriminants(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_with_config(
InlayHintsConfig { discriminant_hints: DiscriminantHints::Always, ..DISABLED_CONFIG },
ra_fixture,
@@ -113,7 +120,7 @@ mod tests {
}
#[track_caller]
- fn check_discriminants_fieldless(ra_fixture: &str) {
+ fn check_discriminants_fieldless(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_with_config(
InlayHintsConfig {
discriminant_hints: DiscriminantHints::Fieldless,
@@ -207,4 +214,33 @@ enum Enum {
"#,
);
}
+
+ #[test]
+ fn edit() {
+ check_edit(
+ InlayHintsConfig { discriminant_hints: DiscriminantHints::Always, ..DISABLED_CONFIG },
+ r#"
+#[repr(u8)]
+enum Enum {
+ Variant(),
+ Variant1,
+ Variant2 {},
+ Variant3,
+ Variant5,
+ Variant6,
+}
+"#,
+ expect![[r#"
+ #[repr(u8)]
+ enum Enum {
+ Variant() = 0,
+ Variant1 = 1,
+ Variant2 {} = 2,
+ Variant3 = 3,
+ Variant5 = 4,
+ Variant6 = 5,
+ }
+ "#]],
+ );
+ }
}
diff --git a/crates/ide/src/inlay_hints/extern_block.rs b/crates/ide/src/inlay_hints/extern_block.rs
index 4cc4925cda..2bc91b68ed 100644
--- a/crates/ide/src/inlay_hints/extern_block.rs
+++ b/crates/ide/src/inlay_hints/extern_block.rs
@@ -8,7 +8,7 @@ use crate::{InlayHint, InlayHintsConfig};
pub(super) fn extern_block_hints(
acc: &mut Vec<InlayHint>,
FamousDefs(_sema, _): &FamousDefs<'_, '_>,
- _config: &InlayHintsConfig,
+ config: &InlayHintsConfig,
_file_id: EditionedFileId,
extern_block: ast::ExternBlock,
) -> Option<()> {
@@ -23,7 +23,9 @@ pub(super) fn extern_block_hints(
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())),
+ text_edit: Some(config.lazy_text_edit(|| {
+ TextEdit::insert(abi.syntax().text_range().start(), "unsafe ".to_owned())
+ })),
resolve_parent: Some(extern_block.syntax().text_range()),
});
Some(())
@@ -32,7 +34,7 @@ pub(super) fn extern_block_hints(
pub(super) fn fn_hints(
acc: &mut Vec<InlayHint>,
FamousDefs(_sema, _): &FamousDefs<'_, '_>,
- _config: &InlayHintsConfig,
+ config: &InlayHintsConfig,
_file_id: EditionedFileId,
fn_: &ast::Fn,
extern_block: &ast::ExternBlock,
@@ -42,14 +44,14 @@ pub(super) fn fn_hints(
return None;
}
let fn_ = fn_.fn_token()?;
- acc.push(item_hint(extern_block, fn_));
+ acc.push(item_hint(config, extern_block, fn_));
Some(())
}
pub(super) fn static_hints(
acc: &mut Vec<InlayHint>,
FamousDefs(_sema, _): &FamousDefs<'_, '_>,
- _config: &InlayHintsConfig,
+ config: &InlayHintsConfig,
_file_id: EditionedFileId,
static_: &ast::Static,
extern_block: &ast::ExternBlock,
@@ -59,11 +61,15 @@ pub(super) fn static_hints(
return None;
}
let static_ = static_.static_token()?;
- acc.push(item_hint(extern_block, static_));
+ acc.push(item_hint(config, extern_block, static_));
Some(())
}
-fn item_hint(extern_block: &ast::ExternBlock, token: SyntaxToken) -> InlayHint {
+fn item_hint(
+ config: &InlayHintsConfig,
+ extern_block: &ast::ExternBlock,
+ token: SyntaxToken,
+) -> InlayHint {
InlayHint {
range: token.text_range(),
position: crate::InlayHintPosition::Before,
@@ -71,7 +77,7 @@ fn item_hint(extern_block: &ast::ExternBlock, token: SyntaxToken) -> InlayHint {
pad_right: true,
kind: crate::InlayKind::ExternUnsafety,
label: crate::InlayHintLabel::from("unsafe"),
- text_edit: {
+ text_edit: Some(config.lazy_text_edit(|| {
let mut builder = TextEdit::builder();
builder.insert(token.text_range().start(), "unsafe ".to_owned());
if extern_block.unsafe_token().is_none() {
@@ -79,8 +85,8 @@ fn item_hint(extern_block: &ast::ExternBlock, token: SyntaxToken) -> InlayHint {
builder.insert(abi.syntax().text_range().start(), "unsafe ".to_owned());
}
}
- Some(builder.finish())
- },
+ builder.finish()
+ })),
resolve_parent: Some(extern_block.syntax().text_range()),
}
}
diff --git a/crates/ide/src/inlay_hints/generic_param.rs b/crates/ide/src/inlay_hints/generic_param.rs
index ed7ebc3b1e..037b328d97 100644
--- a/crates/ide/src/inlay_hints/generic_param.rs
+++ b/crates/ide/src/inlay_hints/generic_param.rs
@@ -142,7 +142,7 @@ mod tests {
};
#[track_caller]
- fn generic_param_name_hints_always(ra_fixture: &str) {
+ fn generic_param_name_hints_always(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_with_config(
InlayHintsConfig {
generic_parameter_hints: GenericParameterHints {
@@ -157,7 +157,7 @@ mod tests {
}
#[track_caller]
- fn generic_param_name_hints_const_only(ra_fixture: &str) {
+ fn generic_param_name_hints_const_only(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_with_config(
InlayHintsConfig {
generic_parameter_hints: GenericParameterHints {
diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs
index dd4b3efeec..1358d3722f 100644
--- a/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -108,7 +108,7 @@ pub(super) fn hints(
}
let mut label = InlayHintLabel::simple(
name,
- Some(crate::InlayTooltip::String("moz".into())),
+ Some(config.lazy_tooltip(|| crate::InlayTooltip::String("moz".into()))),
binding_source,
);
label.prepend_str("drop(");
diff --git a/crates/ide/src/inlay_hints/implicit_static.rs b/crates/ide/src/inlay_hints/implicit_static.rs
index 1560df37d0..ae5b519b43 100644
--- a/crates/ide/src/inlay_hints/implicit_static.rs
+++ b/crates/ide/src/inlay_hints/implicit_static.rs
@@ -39,7 +39,9 @@ pub(super) fn hints(
range: t.text_range(),
kind: InlayKind::Lifetime,
label: "'static".into(),
- text_edit: Some(TextEdit::insert(t.text_range().start(), "'static ".into())),
+ text_edit: Some(config.lazy_text_edit(|| {
+ TextEdit::insert(t.text_range().start(), "'static ".into())
+ })),
position: InlayHintPosition::After,
pad_left: false,
pad_right: true,
diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs
index a03ff6a52b..a7b066700c 100644
--- a/crates/ide/src/inlay_hints/param_name.rs
+++ b/crates/ide/src/inlay_hints/param_name.rs
@@ -269,7 +269,7 @@ mod tests {
};
#[track_caller]
- fn check_params(ra_fixture: &str) {
+ fn check_params(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
check_with_config(
InlayHintsConfig { parameter_hints: true, ..DISABLED_CONFIG },
ra_fixture,
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index 5192f91a4a..e4670177ec 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -307,7 +307,10 @@ mod tests {
use super::*;
- fn check_join_lines(ra_fixture_before: &str, ra_fixture_after: &str) {
+ fn check_join_lines(
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+ ) {
let config = JoinLinesConfig {
join_else_if: true,
remove_trailing_comma: true,
@@ -333,7 +336,10 @@ mod tests {
assert_eq_text!(ra_fixture_after, &actual);
}
- fn check_join_lines_sel(ra_fixture_before: &str, ra_fixture_after: &str) {
+ fn check_join_lines_sel(
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+ ) {
let config = JoinLinesConfig {
join_else_if: true,
remove_trailing_comma: true,
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 6e7c718953..346e2862b0 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -48,7 +48,6 @@ mod ssr;
mod static_index;
mod status;
mod syntax_highlighting;
-mod syntax_tree;
mod test_explorer;
mod typing;
mod view_crate_graph;
@@ -56,6 +55,7 @@ mod view_hir;
mod view_item_tree;
mod view_memory_layout;
mod view_mir;
+mod view_syntax_tree;
use std::{iter, panic::UnwindSafe};
@@ -120,7 +120,7 @@ pub use ide_assists::{
};
pub use ide_completion::{
CallableSnippets, CompletionConfig, CompletionFieldsToResolve, CompletionItem,
- CompletionItemKind, CompletionRelevance, Snippet, SnippetScope,
+ CompletionItemKind, CompletionItemRefMode, CompletionRelevance, Snippet, SnippetScope,
};
pub use ide_db::text_edit::{Indel, TextEdit};
pub use ide_db::{
@@ -329,14 +329,8 @@ impl Analysis {
})
}
- /// Returns a syntax tree represented as `String`, for debug purposes.
- // FIXME: use a better name here.
- pub fn syntax_tree(
- &self,
- file_id: FileId,
- text_range: Option<TextRange>,
- ) -> Cancellable<String> {
- self.with_db(|db| syntax_tree::syntax_tree(db, file_id, text_range))
+ pub fn view_syntax_tree(&self, file_id: FileId) -> Cancellable<String> {
+ self.with_db(|db| view_syntax_tree::view_syntax_tree(db, file_id))
}
pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> {
@@ -410,17 +404,11 @@ impl Analysis {
&self,
position: FilePosition,
char_typed: char,
- 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 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))
}
@@ -588,17 +576,17 @@ impl Analysis {
self.with_db(|db| parent_module::parent_module(db, position))
}
- /// Returns crates this file belongs too.
+ /// Returns crates that this file belongs to.
pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
self.with_db(|db| parent_module::crates_for(db, file_id))
}
- /// Returns crates this file belongs too.
+ /// Returns crates that this file belongs to.
pub fn transitive_rev_deps(&self, crate_id: CrateId) -> Cancellable<Vec<CrateId>> {
self.with_db(|db| db.crate_graph().transitive_rev_deps(crate_id).collect())
}
- /// Returns crates this file *might* belong too.
+ /// Returns crates that this file *might* belong to.
pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
self.with_db(|db| db.relevant_crates(file_id).iter().copied().collect())
}
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs
index 052466725f..d97c12ebaf 100644
--- a/crates/ide/src/moniker.rs
+++ b/crates/ide/src/moniker.rs
@@ -191,7 +191,7 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati
MacroKind::ProcMacro => Macro,
},
Definition::Field(..) | Definition::TupleField(..) => Field,
- Definition::Module(..) => Module,
+ Definition::Module(..) | Definition::Crate(..) => Module,
Definition::Function(it) => {
if it.as_assoc_item(db).is_some() {
if it.has_self_param(db) {
@@ -405,7 +405,7 @@ mod tests {
#[allow(dead_code)]
#[track_caller]
- fn no_moniker(ra_fixture: &str) {
+ fn no_moniker(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position) = fixture::position(ra_fixture);
if let Some(x) = analysis.moniker(position).unwrap() {
assert_eq!(x.info.len(), 0, "Moniker found but no moniker expected: {x:?}");
@@ -413,7 +413,12 @@ mod tests {
}
#[track_caller]
- fn check_local_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) {
+ fn check_local_moniker(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ identifier: &str,
+ package: &str,
+ kind: MonikerKind,
+ ) {
let (analysis, position) = fixture::position(ra_fixture);
let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
assert_eq!(x.len(), 1);
@@ -433,7 +438,12 @@ mod tests {
}
#[track_caller]
- fn check_moniker(ra_fixture: &str, identifier: &str, package: &str, kind: MonikerKind) {
+ fn check_moniker(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ identifier: &str,
+ package: &str,
+ kind: MonikerKind,
+ ) {
let (analysis, position) = fixture::position(ra_fixture);
let x = analysis.moniker(position).unwrap().expect("no moniker found").info;
assert_eq!(x.len(), 1);
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index a232df2b82..b0df9257ba 100644
--- a/crates/ide/src/move_item.rs
+++ b/crates/ide/src/move_item.rs
@@ -180,7 +180,11 @@ mod tests {
use crate::Direction;
- fn check(ra_fixture: &str, expect: Expect, direction: Direction) {
+ fn check(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: Expect,
+ direction: Direction,
+ ) {
let (analysis, range) = fixture::range(ra_fixture);
let edit = analysis.move_item(range, direction).unwrap().unwrap_or_default();
let mut file = analysis.file_text(range.file_id).unwrap().to_string();
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 9259243db8..d9f80cb53d 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -44,13 +44,16 @@ pub struct NavigationTarget {
///
/// This range must be contained within [`Self::full_range`].
pub focus_range: Option<TextRange>,
+ // FIXME: Symbol
pub name: SmolStr,
pub kind: Option<SymbolKind>,
+ // FIXME: Symbol
pub container_name: Option<SmolStr>,
pub description: Option<String>,
pub docs: Option<Documentation>,
/// In addition to a `name` field, a `NavigationTarget` may also be aliased
/// In such cases we want a `NavigationTarget` to be accessible by its alias
+ // FIXME: Symbol
pub alias: Option<SmolStr>,
}
@@ -191,11 +194,11 @@ impl TryToNav for FileSymbol {
NavigationTarget {
file_id,
name: self.is_alias.then(|| self.def.name(db)).flatten().map_or_else(
- || self.name.clone(),
+ || self.name.as_str().into(),
|it| it.display_no_db(edition).to_smolstr(),
),
- alias: self.is_alias.then(|| self.name.clone()),
- kind: Some(hir::ModuleDefId::from(self.def).into()),
+ alias: self.is_alias.then(|| self.name.as_str().into()),
+ kind: Some(self.def.into()),
full_range,
focus_range,
container_name: self.container_name.clone(),
@@ -225,6 +228,7 @@ impl TryToNav for Definition {
Definition::Local(it) => Some(it.to_nav(db)),
Definition::Label(it) => it.try_to_nav(db),
Definition::Module(it) => Some(it.to_nav(db)),
+ Definition::Crate(it) => Some(it.to_nav(db)),
Definition::Macro(it) => it.try_to_nav(db),
Definition::Field(it) => it.try_to_nav(db),
Definition::SelfType(it) => it.try_to_nav(db),
@@ -398,6 +402,12 @@ impl ToNav for hir::Module {
}
}
+impl ToNav for hir::Crate {
+ fn to_nav(&self, db: &RootDatabase) -> UpmappingResult<NavigationTarget> {
+ self.root_module().to_nav(db)
+ }
+}
+
impl TryToNav for hir::Impl {
fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
let InFile { file_id, value } = self.source(db)?;
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs
index b51a5cc4f4..7a0c28d925 100644
--- a/crates/ide/src/parent_module.rs
+++ b/crates/ide/src/parent_module.rs
@@ -70,7 +70,7 @@ mod tests {
use crate::fixture;
- fn check(ra_fixture: &str) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
let navs = analysis.parent_module(position).unwrap();
let navs = navs
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 46714df8d6..b1079312d3 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -1255,11 +1255,15 @@ impl Foo {
);
}
- fn check(ra_fixture: &str, expect: Expect) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
check_with_scope(ra_fixture, None, expect)
}
- fn check_with_scope(ra_fixture: &str, search_scope: Option<SearchScope>, expect: Expect) {
+ fn check_with_scope(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ search_scope: Option<SearchScope>,
+ expect: Expect,
+ ) {
let (analysis, pos) = fixture::position(ra_fixture);
let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap();
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index 11bbd99110..ba739df309 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -456,7 +456,11 @@ mod tests {
use super::{RangeInfo, RenameError};
#[track_caller]
- fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
+ fn check(
+ new_name: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+ ) {
let ra_fixture_after = &trim_indent(ra_fixture_after);
let (analysis, position) = fixture::position(ra_fixture_before);
if !ra_fixture_after.starts_with("error: ") {
@@ -494,14 +498,22 @@ mod tests {
};
}
- fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
+ fn check_expect(
+ new_name: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: Expect,
+ ) {
let (analysis, position) = fixture::position(ra_fixture);
let source_change =
analysis.rename(position, new_name).unwrap().expect("Expect returned a RenameError");
expect.assert_eq(&filter_expect(source_change))
}
- fn check_expect_will_rename_file(new_name: &str, ra_fixture: &str, expect: Expect) {
+ fn check_expect_will_rename_file(
+ new_name: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: Expect,
+ ) {
let (analysis, position) = fixture::position(ra_fixture);
let source_change = analysis
.will_rename_file(position.file_id, new_name)
@@ -510,7 +522,7 @@ mod tests {
expect.assert_eq(&filter_expect(source_change))
}
- fn check_prepare(ra_fixture: &str, expect: Expect) {
+ fn check_prepare(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let result = analysis
.prepare_rename(position)
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 3e39c750b1..32edacee51 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -748,7 +748,7 @@ mod tests {
use crate::fixture;
- fn check(ra_fixture: &str, expect: Expect) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let result = analysis
.runnables(position.file_id)
@@ -769,7 +769,7 @@ mod tests {
expect.assert_debug_eq(&result);
}
- fn check_tests(ra_fixture: &str, expect: Expect) {
+ fn check_tests(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let tests = analysis.related_tests(position, None).unwrap();
let navigation_targets = tests.into_iter().map(|runnable| runnable.nav).collect::<Vec<_>>();
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 84ccadc8c4..f8c60418eb 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -692,7 +692,9 @@ mod tests {
use crate::RootDatabase;
/// Creates analysis from a multi-file fixture, returns positions marked with $0.
- pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
+ pub(crate) fn position(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ ) -> (RootDatabase, FilePosition) {
let change_fixture = ChangeFixture::parse(ra_fixture);
let mut database = RootDatabase::default();
database.apply_change(change_fixture.change);
@@ -703,7 +705,7 @@ mod tests {
}
#[track_caller]
- fn check(ra_fixture: &str, expect: Expect) {
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let fixture = format!(
r#"
//- minicore: sized, fn
diff --git a/crates/ide/src/ssr.rs b/crates/ide/src/ssr.rs
index 6def28e0b7..77a011cac1 100644
--- a/crates/ide/src/ssr.rs
+++ b/crates/ide/src/ssr.rs
@@ -67,7 +67,10 @@ mod tests {
use super::ssr_assists;
- fn get_assists(ra_fixture: &str, resolve: AssistResolveStrategy) -> Vec<Assist> {
+ fn get_assists(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ resolve: AssistResolveStrategy,
+ ) -> Vec<Assist> {
let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
let mut local_roots = FxHashSet::default();
local_roots.insert(test_fixture::WORKSPACE);
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 700e166b23..8050a38b3c 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -138,6 +138,7 @@ impl StaticIndex<'_> {
render_colons: true,
discriminant_hints: crate::DiscriminantHints::Fieldless,
type_hints: true,
+ sized_bound: false,
parameter_hints: true,
generic_parameter_hints: crate::GenericParameterHints {
type_hints: false,
@@ -290,7 +291,10 @@ mod tests {
use super::VendoredLibrariesConfig;
- fn check_all_ranges(ra_fixture: &str, vendored_libs_config: VendoredLibrariesConfig<'_>) {
+ fn check_all_ranges(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ vendored_libs_config: VendoredLibrariesConfig<'_>,
+ ) {
let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture);
let s = StaticIndex::compute(&analysis, vendored_libs_config);
let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect();
@@ -309,7 +313,10 @@ mod tests {
}
#[track_caller]
- fn check_definitions(ra_fixture: &str, vendored_libs_config: VendoredLibrariesConfig<'_>) {
+ fn check_definitions(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ vendored_libs_config: VendoredLibrariesConfig<'_>,
+ ) {
let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture);
let s = StaticIndex::compute(&analysis, vendored_libs_config);
let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect();
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 4f3d5d9d00..22a2fe4e9e 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -386,6 +386,9 @@ pub(super) fn highlight_def(
Definition::Field(_) | Definition::TupleField(_) => {
Highlight::new(HlTag::Symbol(SymbolKind::Field))
}
+ Definition::Crate(_) => {
+ Highlight::new(HlTag::Symbol(SymbolKind::Module)) | HlMod::CrateRoot
+ }
Definition::Module(module) => {
let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module));
if module.is_crate_root() {
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 0a157c157c..1be90ad6a1 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -28,7 +28,14 @@ pub(super) fn ra_fixture(
expanded: &ast::String,
) -> Option<()> {
let active_parameter = ActiveParameter::at_token(sema, expanded.syntax().clone())?;
- if !active_parameter.ident().is_some_and(|name| name.text().starts_with("ra_fixture")) {
+ let has_rust_fixture_attr = active_parameter.attrs().is_some_and(|attrs| {
+ attrs.filter_map(|attr| attr.as_simple_path()).any(|path| {
+ path.segments()
+ .zip(["rust_analyzer", "rust_fixture"])
+ .all(|(seg, name)| seg.name_ref().map_or(false, |nr| nr.text() == name))
+ })
+ });
+ if !has_rust_fixture_attr {
return None;
}
let value = literal.value().ok()?;
@@ -287,7 +294,9 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri
fn module_def_to_hl_tag(def: Definition) -> HlTag {
let symbol = match def {
- Definition::Module(_) | Definition::ExternCrateDecl(_) => SymbolKind::Module,
+ Definition::Module(_) | Definition::Crate(_) | Definition::ExternCrateDecl(_) => {
+ SymbolKind::Module
+ }
Definition::Function(_) => SymbolKind::Function,
Definition::Adt(hir::Adt::Struct(_)) => SymbolKind::Struct,
Definition::Adt(hir::Adt::Enum(_)) => SymbolKind::Enum,
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
index cad5a8b593..485d44f97e 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.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="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">allow</span><span class="parenthesis attribute">(</span><span class="none attribute">dead_code</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="tool_module attribute library">rustfmt</span><span class="operator attribute">::</span><span class="tool_module attribute library">skip</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="tool_module attribute">rustfmt</span><span class="operator attribute">::</span><span class="tool_module attribute">skip</span><span class="attribute_bracket attribute">]</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="module attribute crate_root library">proc_macros</span><span class="operator attribute">::</span><span class="attribute attribute library">identity</span><span class="attribute_bracket attribute">]</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="attribute attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="derive attribute default_library library">Default</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
<span class="comment documentation">/// This is a doc comment</span>
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 edd9639768..c6eab90e42 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
@@ -45,7 +45,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
.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>
+<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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>
@@ -53,9 +53,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<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>
- <span class="macro">foo</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="struct declaration macro public">Foo</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">foo</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="struct declaration macro public">Foo</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">mod</span> <span class="module declaration">module</span> <span class="brace">{</span>
- <span class="macro">foo</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="struct declaration macro public">Bar</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">foo</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="struct declaration macro public">Bar</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">fn</span> <span class="function declaration">func</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="module">y</span><span class="operator">::</span><span class="struct public">Bar</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span>
<span class="keyword">struct</span> <span class="struct declaration">Innerest</span><span class="angle">&lt;</span><span class="keyword">const</span> <span class="const_param const declaration">C</span><span class="colon">:</span> <span class="unresolved_reference">usize</span><span class="angle">&gt;</span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="bracket">[</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">{</span><span class="const_param const">C</span><span class="brace">}</span><span class="bracket">]</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 05289cfe3f..96cdb532dd 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
@@ -45,7 +45,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
.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>
+<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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>
@@ -57,7 +57,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword const">const</span> <span class="brace">{</span>
<span class="keyword">const</span> <span class="punctuation">|</span><span class="punctuation">|</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span>
- <span class="macro">id</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
+ <span class="macro public">id</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
<span class="constant const macro">CONST_ITEM</span><span class="semicolon macro">;</span>
<span class="const_param const macro">CONST_PARAM</span><span class="semicolon macro">;</span>
<span class="keyword const macro">const</span> <span class="brace macro">{</span>
@@ -78,7 +78,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword const">const</span> <span class="keyword">fn</span> <span class="function associated const declaration static trait">assoc_const_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">unsafe_deref</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>
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 aa9d23250c..5ff96ae2a7 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -147,10 +147,10 @@ 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="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="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 public">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 public">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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="operator">&gt;</span> <span class="brace">{</span>
<span class="punctuation">$</span>expr
<span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index 7820e4e5a5..fe5f5ab6a9 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -45,7 +45,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
-<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="self_keyword crate_root public">self</span> <span class="keyword">as</span> <span class="module crate_root declaration">this</span><span class="semicolon">;</span>
+<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="self_keyword crate_root">self</span> <span class="keyword">as</span> <span class="module crate_root declaration">this</span><span class="semicolon">;</span>
<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">std</span><span class="semicolon">;</span>
<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">alloc</span> <span class="keyword">as</span> <span class="module crate_root declaration">abc</span><span class="semicolon">;</span>
<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="unresolved_reference">unresolved</span> <span class="keyword">as</span> <span class="module crate_root declaration">definitely_unresolved</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index 6c3fbcfcf4..5fbed35192 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -45,7 +45,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
-<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="punctuation">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="tool_module attribute">rust_analyzer</span><span class="operator attribute">::</span><span class="tool_module attribute">rust_fixture</span><span class="attribute_bracket attribute">]</span> <span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="punctuation">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</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>
<span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span><span class="none injected">
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html b/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
index 361dcd1bc3..06817af1b1 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html
@@ -46,8 +46,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="macro">template</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">template</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">template</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">template</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="module attribute crate_root library">proc_macros</span><span class="operator attribute">::</span><span class="attribute attribute library">issue_18089</span><span class="attribute_bracket attribute">]</span>
-<span class="keyword">fn</span> <span class="macro declaration">template</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file
+<span class="keyword">fn</span> <span class="macro declaration public">template</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
index c2bf94fd9b..2d3407dbcd 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
@@ -53,22 +53,22 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">use</span> <span class="keyword crate_root public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
<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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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>
<span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// edition dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">try</span> <span class="none macro">async</span> <span class="none macro">await</span> <span class="none macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">try</span> <span class="none macro">async</span> <span class="none macro">await</span> <span class="none macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// edition and context dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// builtin custom syntax</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// contextual</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// reserved</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre> \ No newline at end of file
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre> \ No newline at end of file
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 a30d16d532..f8eb5d068a 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
@@ -53,22 +53,22 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">use</span> <span class="keyword crate_root public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
<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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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>
<span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// edition dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// edition and context dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// builtin custom syntax</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// contextual</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// reserved</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre> \ No newline at end of file
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre> \ No newline at end of file
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 a30d16d532..f8eb5d068a 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
@@ -53,22 +53,22 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">use</span> <span class="keyword crate_root public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
<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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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>
<span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// edition dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// edition and context dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// builtin custom syntax</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// contextual</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// reserved</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre> \ No newline at end of file
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre> \ No newline at end of file
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 b82a3f9f81..fca8401706 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
@@ -53,22 +53,22 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">use</span> <span class="keyword crate_root public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
<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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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>
<span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// edition dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="keyword macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="keyword macro">gen</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// edition and context dependent</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">dyn</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// builtin custom syntax</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// contextual</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// reserved</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre> \ No newline at end of file
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>'static 'self 'unsafe<span class="parenthesis macro">)</span></code></pre> \ No newline at end of file
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 06673d1a73..f640a5e6ca 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -53,34 +53,34 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="comma macro proc_macro">,</span><span class="builtin_type macro proc_macro">i32</span> <span class="colon macro proc_macro">:</span><span class="field declaration macro proc_macro public">y</span> <span class="keyword macro proc_macro">pub</span>
<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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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>
+<span class="macro public">def_fn</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
<span class="keyword macro">fn</span> <span class="function declaration macro">bar</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="operator macro">-</span><span class="operator macro">&gt;</span> <span class="builtin_type macro">u32</span> <span class="brace macro">{</span>
<span class="numeric_literal macro">100</span>
<span class="brace macro">}</span>
<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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">dont_color_me_braces</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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="operator">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="parenthesis">)</span>
<span class="brace">}</span>
@@ -94,7 +94,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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>
@@ -106,10 +106,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="keyword">struct</span> <span class="struct declaration">TestLocal</span><span class="semicolon">;</span>
<span class="comment">// regression test, TestLocal here used to not resolve</span>
- <span class="keyword">let</span> <span class="punctuation">_</span><span class="colon">:</span> <span class="struct">S</span><span class="angle">&lt;</span><span class="macro">id</span><span class="macro_bang">!</span><span class="bracket macro">[</span><span class="struct macro">TestLocal</span><span class="bracket macro">]</span><span class="angle">&gt;</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="punctuation">_</span><span class="colon">:</span> <span class="struct">S</span><span class="angle">&lt;</span><span class="macro public">id</span><span class="macro_bang">!</span><span class="bracket macro">[</span><span class="struct macro">TestLocal</span><span class="bracket macro">]</span><span class="angle">&gt;</span><span class="semicolon">;</span>
<span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">92</span><span class="comma macro">,</span><span class="parenthesis macro">)</span><span class="operator macro">.</span><span class="field library macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro">noop</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">noop</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro public">noop</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</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_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 1385ae0510..0a7e273950 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -45,14 +45,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
.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>
+<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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>
<span class="keyword">mod</span> <span class="module declaration">panic</span> <span class="brace">{</span>
- <span class="keyword">pub</span> <span class="keyword">macro</span> <span class="macro declaration">panic_2015</span> <span class="brace">{</span>
+ <span class="keyword">pub</span> <span class="keyword">macro</span> <span class="macro declaration public">panic_2015</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>
panic<span class="parenthesis">(</span><span class="string_literal">"explicit panic"</span><span class="parenthesis">)</span>
<span class="parenthesis">)</span><span class="comma">,</span>
@@ -73,12 +73,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">toho</span> <span class="brace">{</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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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>
@@ -95,74 +95,74 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="byte_literal">b'</span><span class="escape_sequence">\xFF</span><span class="byte_literal">'</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello, world!"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "The number is 1"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "(3, 4)"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "4"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "1 2"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1 1 2"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "test"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="variable declaration macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="variable declaration macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "a 3 b"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "{2}"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="variable declaration macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello"</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello, world!"</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "The number is 1"</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "(3, 4)"</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "4"</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "1 2"</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1 1 2"</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "test"</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1"</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="variable declaration macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="variable declaration macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "a 3 b"</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "{2}"</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="variable declaration macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// escape sequences</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal macro">World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal macro"> World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal macro">World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal macro"> World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="invalid_escape_sequence">\xFF</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// invalid non-UTF8 escape sequences</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">b"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="escape_sequence">\xFF</span><span class="invalid_escape_sequence">\u{FF}</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, invalid unicodes</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">c"</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\xFF</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, valid unicodes</span>
<span class="keyword">let</span> <span class="variable declaration reference">backslash</span> <span class="operator">=</span> <span class="string_literal">r"\\"</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span>
<span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
@@ -175,6 +175,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword const">const</span> <span class="constant const declaration">CONSTANT</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span>
<span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">m</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">CONSTANT</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">m</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">reuse_twice</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">CONSTANT</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">m</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro public">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public">reuse_twice</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</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_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 4e69c82f3d..d9beac3089 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -45,12 +45,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
.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>
+<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">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="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="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">unsafe_deref</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>
@@ -92,13 +92,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
- <span class="macro">id</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
- <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span>
+ <span class="macro public">id</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
+ <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span>
<span class="brace macro">}</span><span class="semicolon">;</span>
<span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
- <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro unsafe">id</span><span class="macro_bang">!</span> <span class="brace macro">{</span> <span class="macro macro unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span><span class="semicolon">;</span>
+ <span class="macro public unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro public unsafe">id</span><span class="macro_bang">!</span> <span class="brace macro">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span><span class="semicolon">;</span>
<span class="comment">// unsafe fn and method calls</span>
<span class="function unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index a20147add3..af52b33de6 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -990,7 +990,7 @@ impl t for foo {
fn test_injection() {
check_highlighting(
r##"
-fn fixture(ra_fixture: &str) {}
+fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
fn main() {
fixture(r#"
@@ -1188,7 +1188,11 @@ fn foo(x: &fn(&dyn Trait)) {}
/// Highlights the code given by the `ra_fixture` argument, renders the
/// result as HTML, and compares it with the HTML file given as `snapshot`.
/// Note that the `snapshot` file is overwritten by the rendered HTML.
-fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) {
+fn check_highlighting(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: ExpectFile,
+ rainbow: bool,
+) {
let (analysis, file_id) = fixture::file(ra_fixture.trim());
let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
expect.assert_eq(actual_html)
diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs
deleted file mode 100644
index e241cb82bd..0000000000
--- a/crates/ide/src/syntax_tree.rs
+++ /dev/null
@@ -1,338 +0,0 @@
-use hir::Semantics;
-use ide_db::{FileId, RootDatabase};
-use syntax::{
- AstNode, NodeOrToken, SourceFile, SyntaxKind::STRING, SyntaxToken, TextRange, TextSize,
-};
-
-// Feature: Show Syntax Tree
-//
-// Shows the parse tree of the current file. It exists mostly for debugging
-// rust-analyzer itself.
-//
-// |===
-// | Editor | Action Name
-//
-// | VS Code | **rust-analyzer: Show Syntax Tree**
-// |===
-// image::https://user-images.githubusercontent.com/48062697/113065586-068bdb80-91b1-11eb-9507-fee67f9f45a0.gif[]
-pub(crate) fn syntax_tree(
- db: &RootDatabase,
- file_id: FileId,
- text_range: Option<TextRange>,
-) -> String {
- let sema = Semantics::new(db);
- let parse = sema.parse_guess_edition(file_id);
- if let Some(text_range) = text_range {
- let node = match parse.syntax().covering_element(text_range) {
- NodeOrToken::Node(node) => node,
- NodeOrToken::Token(token) => {
- if let Some(tree) = syntax_tree_for_string(&token, text_range) {
- return tree;
- }
- token.parent().unwrap()
- }
- };
-
- format!("{node:#?}")
- } else {
- format!("{:#?}", parse.syntax())
- }
-}
-
-/// Attempts parsing the selected contents of a string literal
-/// as rust syntax and returns its syntax tree
-fn syntax_tree_for_string(token: &SyntaxToken, text_range: TextRange) -> Option<String> {
- // When the range is inside a string
- // we'll attempt parsing it as rust syntax
- // to provide the syntax tree of the contents of the string
- match token.kind() {
- STRING => syntax_tree_for_token(token, text_range),
- _ => None,
- }
-}
-
-fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<String> {
- // Range of the full node
- let node_range = node.text_range();
- let text = node.text().to_owned();
-
- // We start at some point inside the node
- // Either we have selected the whole string
- // or our selection is inside it
- let start = text_range.start() - node_range.start();
-
- // how many characters we have selected
- let len = text_range.len();
-
- let node_len = node_range.len();
-
- // We want to cap our length
- let len = len.min(node_len);
-
- // Ensure our slice is inside the actual string
- let end =
- if start + len < TextSize::of(&text) { start + len } else { TextSize::of(&text) - start };
-
- let text = &text[TextRange::new(start, end)];
-
- // Remove possible extra string quotes from the start
- // and the end of the string
- let text = text
- .trim_start_matches('r')
- .trim_start_matches('#')
- .trim_start_matches('"')
- .trim_end_matches('#')
- .trim_end_matches('"')
- .trim()
- // Remove custom markers
- .replace("$0", "");
-
- let parsed = SourceFile::parse(&text, span::Edition::CURRENT_FIXME);
-
- // If the "file" parsed without errors,
- // return its syntax
- if parsed.errors().is_empty() {
- return Some(format!("{:#?}", parsed.tree().syntax()));
- }
-
- None
-}
-
-#[cfg(test)]
-mod tests {
- use expect_test::expect;
-
- use crate::fixture;
-
- fn check(ra_fixture: &str, expect: expect_test::Expect) {
- let (analysis, file_id) = fixture::file(ra_fixture);
- let syn = analysis.syntax_tree(file_id, None).unwrap();
- expect.assert_eq(&syn)
- }
- fn check_range(ra_fixture: &str, expect: expect_test::Expect) {
- let (analysis, frange) = fixture::range(ra_fixture);
- let syn = analysis.syntax_tree(frange.file_id, Some(frange.range)).unwrap();
- expect.assert_eq(&syn)
- }
-
- #[test]
- fn test_syntax_tree_without_range() {
- // Basic syntax
- check(
- r#"fn foo() {}"#,
- expect![[r#"
- "#]],
- );
-
- check(
- r#"
-fn test() {
- assert!("
- fn foo() {
- }
- ", "");
-}"#,
- expect![[r#"
- [email protected] "\"\n fn foo() {\n ..."
- "#]],
- )
- }
-
- #[test]
- fn test_syntax_tree_with_range() {
- check_range(
- r#"$0fn foo() {}$0"#,
- expect![[r#"
- "#]],
- );
-
- check_range(
- r#"
-fn test() {
- $0assert!("
- fn foo() {
- }
- ", "");$0
-}"#,
- expect![[r#"
- [email protected] "\"\n fn foo() {\n ..."
- "#]],
- );
- }
-
- #[test]
- fn test_syntax_tree_inside_string() {
- check_range(
- r#"fn test() {
- assert!("
-$0fn foo() {
-}$0
-fn bar() {
-}
- ", "");
-}"#,
- expect![[r#"
- "#]],
- );
-
- // With a raw string
- check_range(
- r###"fn test() {
- assert!(r#"
-$0fn foo() {
-}$0
-fn bar() {
-}
- "#, "");
-}"###,
- expect![[r#"
- "#]],
- );
-
- // With a raw string
- check_range(
- r###"fn test() {
- assert!(r$0#"
-fn foo() {
-}
-fn bar() {
-}"$0#, "");
-}"###,
- expect![[r#"
- "#]],
- );
- }
-}
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index 8998934e0e..47d75f1c95 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -436,14 +436,18 @@ mod tests {
})
}
- fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) {
+ fn type_char(
+ char_typed: char,
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+ ) {
let actual = do_type_char(char_typed, ra_fixture_before)
.unwrap_or_else(|| panic!("typing `{char_typed}` did nothing"));
assert_eq_text!(ra_fixture_after, &actual);
}
- fn type_char_noop(char_typed: char, ra_fixture_before: &str) {
+ fn type_char_noop(char_typed: char, #[rust_analyzer::rust_fixture] ra_fixture_before: &str) {
let file_change = do_type_char(char_typed, ra_fixture_before);
assert_eq!(file_change, None)
}
@@ -889,7 +893,7 @@ fn main() {
type_char_noop(
'{',
r##"
-fn check_with(ra_fixture: &str, expect: Expect) {
+fn check_with(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let base = r#"
enum E { T(), R$0, C }
use self::E::X;
@@ -1191,7 +1195,7 @@ fn f(n: a<>b::<d>::c) {}
type_char_noop(
'(',
r##"
-fn check_with(ra_fixture: &str, expect: Expect) {
+fn check_with(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let base = r#"
enum E { T(), R$0, C }
use self::E::X;
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 773e352220..e249c38c73 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -208,7 +208,10 @@ mod tests {
Some(actual)
}
- fn do_check(ra_fixture_before: &str, ra_fixture_after: &str) {
+ fn do_check(
+ #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
+ ) {
let ra_fixture_after = &trim_indent(ra_fixture_after);
let actual = apply_on_enter(ra_fixture_before).unwrap();
assert_eq_text!(ra_fixture_after, &actual);
diff --git a/crates/ide/src/view_memory_layout.rs b/crates/ide/src/view_memory_layout.rs
index 830c39e21e..ff74e05e94 100644
--- a/crates/ide/src/view_memory_layout.rs
+++ b/crates/ide/src/view_memory_layout.rs
@@ -220,7 +220,9 @@ mod tests {
use crate::fixture;
use expect_test::expect;
- fn make_memory_layout(ra_fixture: &str) -> Option<RecursiveMemoryLayout> {
+ fn make_memory_layout(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ ) -> Option<RecursiveMemoryLayout> {
let (analysis, position, _) = fixture::annotations(ra_fixture);
view_memory_layout(&analysis.db, position)
diff --git a/crates/ide/src/view_syntax_tree.rs b/crates/ide/src/view_syntax_tree.rs
new file mode 100644
index 0000000000..218ee15a7d
--- /dev/null
+++ b/crates/ide/src/view_syntax_tree.rs
@@ -0,0 +1,226 @@
+use hir::Semantics;
+use ide_db::{FileId, RootDatabase};
+use span::TextRange;
+use stdx::format_to;
+use syntax::{
+ ast::{self, IsString},
+ AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, WalkEvent,
+};
+
+// Feature: Show Syntax Tree
+//
+// Shows a tree view with the syntax tree of the current file
+//
+// |===
+// | Editor | Panel Name
+//
+// | VS Code | **Rust Syntax Tree**
+// |===
+pub(crate) fn view_syntax_tree(db: &RootDatabase, file_id: FileId) -> String {
+ let sema = Semantics::new(db);
+ let parse = sema.parse_guess_edition(file_id);
+ syntax_node_to_json(parse.syntax(), None)
+}
+
+fn syntax_node_to_json(node: &SyntaxNode, ctx: Option<InStringCtx>) -> String {
+ let mut result = String::new();
+ for event in node.preorder_with_tokens() {
+ match event {
+ WalkEvent::Enter(it) => {
+ let kind = it.kind();
+ let (text_range, inner_range_str) = match &ctx {
+ Some(ctx) => {
+ let inner_start: u32 = it.text_range().start().into();
+ let inner_end: u32 = it.text_range().end().into();
+
+ let mut true_start = inner_start + ctx.offset;
+ let mut true_end = inner_end + ctx.offset;
+ for pos in &ctx.marker_positions {
+ if *pos >= inner_end {
+ break;
+ }
+
+ // We conditionally add to true_start in case
+ // the marker is between the start and end.
+ true_start += 2 * (*pos < inner_start) as u32;
+ true_end += 2;
+ }
+
+ let true_range = TextRange::new(true_start.into(), true_end.into());
+
+ (
+ true_range,
+ format!(
+ r#","istart":{:?},"iend":{:?}"#,
+ it.text_range().start(),
+ it.text_range().end()
+ ),
+ )
+ }
+ None => (it.text_range(), "".to_owned()),
+ };
+ let start = text_range.start();
+ let end = text_range.end();
+
+ match it {
+ NodeOrToken::Node(_) => {
+ format_to!(
+ result,
+ r#"{{"type":"Node","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str},"children":["#
+ );
+ }
+ NodeOrToken::Token(token) => {
+ let comma = if token.next_sibling_or_token().is_some() { "," } else { "" };
+ match parse_rust_string(token) {
+ Some(parsed) => {
+ format_to!(
+ result,
+ r#"{{"type":"Node","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str},"children":[{parsed}]}}{comma}"#
+ );
+ }
+ None => format_to!(
+ result,
+ r#"{{"type":"Token","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str}}}{comma}"#
+ ),
+ }
+ }
+ }
+ }
+ WalkEvent::Leave(it) => match it {
+ NodeOrToken::Node(node) => {
+ let comma = if node.next_sibling_or_token().is_some() { "," } else { "" };
+ format_to!(result, "]}}{comma}")
+ }
+ NodeOrToken::Token(_) => (),
+ },
+ }
+ }
+
+ result
+}
+
+fn parse_rust_string(token: SyntaxToken) -> Option<String> {
+ let string_node = ast::String::cast(token)?;
+ let text = string_node.value().ok()?;
+
+ let mut trim_result = String::new();
+ let mut marker_positions = Vec::new();
+ let mut skipped = 0;
+ let mut last_end = 0;
+ for (start, part) in text.match_indices("$0") {
+ marker_positions.push((start - skipped) as u32);
+ trim_result.push_str(&text[last_end..start]);
+ skipped += part.len();
+ last_end = start + part.len();
+ }
+ trim_result.push_str(&text[last_end..text.len()]);
+
+ let parsed = SourceFile::parse(&trim_result, span::Edition::CURRENT);
+
+ if !parsed.errors().is_empty() {
+ return None;
+ }
+
+ let node: &SyntaxNode = &parsed.syntax_node();
+
+ if node.children().count() == 0 {
+ // C'mon, you should have at least one node other than SOURCE_FILE
+ return None;
+ }
+
+ Some(syntax_node_to_json(
+ node,
+ Some(InStringCtx {
+ offset: string_node.text_range_between_quotes()?.start().into(),
+ marker_positions,
+ }),
+ ))
+}
+
+struct InStringCtx {
+ offset: u32,
+ marker_positions: Vec<u32>,
+}
+
+#[cfg(test)]
+mod tests {
+ use expect_test::expect;
+
+ use crate::fixture;
+
+ fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: expect_test::Expect) {
+ let (analysis, file_id) = fixture::file(ra_fixture);
+ let syn = analysis.view_syntax_tree(file_id).unwrap();
+ expect.assert_eq(&syn)
+ }
+
+ #[test]
+ fn view_syntax_tree() {
+ // Basic syntax
+ check(
+ r#"fn foo() {}"#,
+ expect![[
+ r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":11,"children":[{"type":"Node","kind":"FN","start":0,"end":11,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":6,"children":[{"type":"Token","kind":"IDENT","start":3,"end":6}]},{"type":"Node","kind":"PARAM_LIST","start":6,"end":8,"children":[{"type":"Token","kind":"L_PAREN","start":6,"end":7},{"type":"Token","kind":"R_PAREN","start":7,"end":8}]},{"type":"Token","kind":"WHITESPACE","start":8,"end":9},{"type":"Node","kind":"BLOCK_EXPR","start":9,"end":11,"children":[{"type":"Node","kind":"STMT_LIST","start":9,"end":11,"children":[{"type":"Token","kind":"L_CURLY","start":9,"end":10},{"type":"Token","kind":"R_CURLY","start":10,"end":11}]}]}]}]}"#
+ ]],
+ );
+
+ check(
+ r#"
+fn test() {
+ assert!("
+ fn foo() {
+ }
+ ", "");
+}"#,
+ expect![[
+ r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":60,"children":[{"type":"Node","kind":"FN","start":0,"end":60,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":60,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":60,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":58,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":57,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":57,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":57,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":52,"children":[{"type":"Node","kind":"SOURCE_FILE","start":25,"end":51,"istart":0,"iend":26,"children":[{"type":"Token","kind":"WHITESPACE","start":25,"end":30,"istart":0,"iend":5},{"type":"Node","kind":"FN","start":30,"end":46,"istart":5,"iend":21,"children":[{"type":"Token","kind":"FN_KW","start":30,"end":32,"istart":5,"iend":7},{"type":"Token","kind":"WHITESPACE","start":32,"end":33,"istart":7,"iend":8},{"type":"Node","kind":"NAME","start":33,"end":36,"istart":8,"iend":11,"children":[{"type":"Token","kind":"IDENT","start":33,"end":36,"istart":8,"iend":11}]},{"type":"Node","kind":"PARAM_LIST","start":36,"end":38,"istart":11,"iend":13,"children":[{"type":"Token","kind":"L_PAREN","start":36,"end":37,"istart":11,"iend":12},{"type":"Token","kind":"R_PAREN","start":37,"end":38,"istart":12,"iend":13}]},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":13,"iend":14},{"type":"Node","kind":"BLOCK_EXPR","start":39,"end":46,"istart":14,"iend":21,"children":[{"type":"Node","kind":"STMT_LIST","start":39,"end":46,"istart":14,"iend":21,"children":[{"type":"Token","kind":"L_CURLY","start":39,"end":40,"istart":14,"iend":15},{"type":"Token","kind":"WHITESPACE","start":40,"end":45,"istart":15,"iend":20},{"type":"Token","kind":"R_CURLY","start":45,"end":46,"istart":20,"iend":21}]}]}]},{"type":"Token","kind":"WHITESPACE","start":46,"end":51,"istart":21,"iend":26}]}]},{"type":"Token","kind":"COMMA","start":52,"end":53},{"type":"Token","kind":"WHITESPACE","start":53,"end":54},{"type":"Token","kind":"STRING","start":54,"end":56},{"type":"Token","kind":"R_PAREN","start":56,"end":57}]}]}]},{"type":"Token","kind":"SEMICOLON","start":57,"end":58}]},{"type":"Token","kind":"WHITESPACE","start":58,"end":59},{"type":"Token","kind":"R_CURLY","start":59,"end":60}]}]}]}]}"#
+ ]],
+ )
+ }
+
+ #[test]
+ fn view_syntax_tree_inside_string() {
+ check(
+ r#"fn test() {
+ assert!("
+$0fn foo() {
+}$0
+fn bar() {
+}
+ ", "");
+}"#,
+ expect![[
+ r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":65,"children":[{"type":"Node","kind":"FN","start":0,"end":65,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":65,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":65,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":63,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":62,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":62,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":62,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":57,"children":[{"type":"Node","kind":"SOURCE_FILE","start":25,"end":56,"istart":0,"iend":31,"children":[{"type":"Token","kind":"WHITESPACE","start":25,"end":26,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":26,"end":38,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":26,"end":28,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":28,"end":29,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":29,"end":32,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":29,"end":32,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":32,"end":34,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":32,"end":33,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":33,"end":34,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":34,"end":35,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":35,"end":38,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":35,"end":38,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":35,"end":36,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":37,"end":38,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":39,"end":51,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":39,"end":41,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":41,"end":42,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":42,"end":45,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":42,"end":45,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":45,"end":47,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":45,"end":46,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":46,"end":47,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":47,"end":48,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":48,"end":51,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":48,"end":51,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":48,"end":49,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":50,"end":51,"istart":25,"iend":26}]}]}]},{"type":"Token","kind":"WHITESPACE","start":51,"end":56,"istart":26,"iend":31}]}]},{"type":"Token","kind":"COMMA","start":57,"end":58},{"type":"Token","kind":"WHITESPACE","start":58,"end":59},{"type":"Token","kind":"STRING","start":59,"end":61},{"type":"Token","kind":"R_PAREN","start":61,"end":62}]}]}]},{"type":"Token","kind":"SEMICOLON","start":62,"end":63}]},{"type":"Token","kind":"WHITESPACE","start":63,"end":64},{"type":"Token","kind":"R_CURLY","start":64,"end":65}]}]}]}]}"#
+ ]],
+ );
+
+ // With a raw string
+ check(
+ r###"fn test() {
+ assert!(r#"
+$0fn foo() {
+}$0
+fn bar() {
+}
+ "#, "");
+}"###,
+ expect![[
+ r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":68,"children":[{"type":"Node","kind":"FN","start":0,"end":68,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":68,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":68,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":66,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":65,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":65,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":65,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":60,"children":[{"type":"Node","kind":"SOURCE_FILE","start":27,"end":58,"istart":0,"iend":31,"children":[{"type":"Token","kind":"WHITESPACE","start":27,"end":28,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":28,"end":40,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":28,"end":30,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":30,"end":31,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":31,"end":34,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":31,"end":34,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":34,"end":36,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":34,"end":35,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":35,"end":36,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":37,"end":38,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":39,"end":40,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":40,"end":41,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":41,"end":53,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":41,"end":43,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":43,"end":44,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":44,"end":47,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":44,"end":47,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":47,"end":49,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":47,"end":48,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":48,"end":49,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":50,"end":51,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":51,"end":52,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":52,"end":53,"istart":25,"iend":26}]}]}]},{"type":"Token","kind":"WHITESPACE","start":53,"end":58,"istart":26,"iend":31}]}]},{"type":"Token","kind":"COMMA","start":60,"end":61},{"type":"Token","kind":"WHITESPACE","start":61,"end":62},{"type":"Token","kind":"STRING","start":62,"end":64},{"type":"Token","kind":"R_PAREN","start":64,"end":65}]}]}]},{"type":"Token","kind":"SEMICOLON","start":65,"end":66}]},{"type":"Token","kind":"WHITESPACE","start":66,"end":67},{"type":"Token","kind":"R_CURLY","start":67,"end":68}]}]}]}]}"#
+ ]],
+ );
+
+ // With a raw string
+ check(
+ r###"fn test() {
+ assert!(r$0#"
+fn foo() {
+}
+fn bar() {
+}"$0#, "");
+}"###,
+ expect![[
+ r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":63,"children":[{"type":"Node","kind":"FN","start":0,"end":63,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":63,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":63,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":61,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":60,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":60,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":60,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":55,"children":[{"type":"Node","kind":"SOURCE_FILE","start":27,"end":53,"istart":0,"iend":26,"children":[{"type":"Token","kind":"WHITESPACE","start":27,"end":28,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":28,"end":40,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":28,"end":30,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":30,"end":31,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":31,"end":34,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":31,"end":34,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":34,"end":36,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":34,"end":35,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":35,"end":36,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":37,"end":38,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":39,"end":40,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":40,"end":41,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":41,"end":53,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":41,"end":43,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":43,"end":44,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":44,"end":47,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":44,"end":47,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":47,"end":49,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":47,"end":48,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":48,"end":49,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":50,"end":51,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":51,"end":52,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":52,"end":53,"istart":25,"iend":26}]}]}]}]}]},{"type":"Token","kind":"COMMA","start":55,"end":56},{"type":"Token","kind":"WHITESPACE","start":56,"end":57},{"type":"Token","kind":"STRING","start":57,"end":59},{"type":"Token","kind":"R_PAREN","start":59,"end":60}]}]}]},{"type":"Token","kind":"SEMICOLON","start":60,"end":61}]},{"type":"Token","kind":"WHITESPACE","start":61,"end":62},{"type":"Token","kind":"R_CURLY","start":62,"end":63}]}]}]}]}"#
+ ]],
+ );
+ }
+}
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 66b8900109..b3b46421b5 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -174,6 +174,7 @@ define_symbols! {
const_param_ty,
Context,
Continue,
+ convert,
copy,
Copy,
core_panic,
@@ -239,6 +240,8 @@ define_symbols! {
format_unsafe_arg,
format,
freeze,
+ From,
+ FromStr,
from_output,
from_residual,
from_usize,
@@ -429,6 +432,7 @@ define_symbols! {
shr,
simd,
sized,
+ skip,
slice_len_fn,
Some,
start,
@@ -456,6 +460,7 @@ define_symbols! {
transmute_trait,
transparent,
Try,
+ TryFrom,
tuple_trait,
u128,
u16,
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index 7389940865..00446b27cf 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -242,9 +242,6 @@ impl ProjectFolders {
}
}
- if dirs.include.is_empty() {
- continue;
- }
vfs::loader::Entry::Directories(dirs)
};
@@ -267,7 +264,7 @@ impl ProjectFolders {
};
let file_set_roots = vec![VfsPath::from(ratoml_path.to_owned())];
- let entry = vfs::loader::Entry::Files(vec![ratoml_path]);
+ let entry = vfs::loader::Entry::Files(vec![ratoml_path.to_owned()]);
res.watch.push(res.load.len());
res.load.push(entry);
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index 9255c5a689..7710ea7938 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -38,7 +38,10 @@ impl<'t> Bindings<'t> {
nesting_state.hit = true;
b = match b {
Binding::Fragment(_) => break,
- Binding::Missing(_) => break,
+ Binding::Missing(_) => {
+ nesting_state.at_end = true;
+ break;
+ }
Binding::Nested(bs) => bs.get(nesting_state.idx).ok_or_else(|| {
nesting_state.at_end = true;
binding_err!("could not find nested binding `{name}`")
@@ -445,6 +448,7 @@ fn expand_repeat(
let mut counter = 0;
let mut err = None;
+ let initial_restore_point = builder.restore_point();
let mut restore_point = builder.restore_point();
loop {
let ExpandResult { value: (), err: e } =
@@ -462,6 +466,10 @@ fn expand_repeat(
counter += 1;
if counter == limit {
+ // FIXME: This is a bug here, we get here when we shouldn't, see https://github.com/rust-lang/rust-analyzer/issues/18910.
+ // If we don't restore we emit a lot of nodes which causes a stack overflow down the road. For now just ignore them,
+ // there is always an error here anyway.
+ builder.restore(initial_restore_point);
err = Some(ExpandError::new(ctx.call_site, ExpandErrorKind::LimitExceeded));
break;
}
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 6abf56d4b3..bebd29ef74 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -369,7 +369,8 @@ pub fn expect_fragment<'t>(
) -> ExpandResult<tt::TokenTreesView<'t, Span>> {
use ::parser;
let buffer = tt_iter.remaining();
- let parser_input = to_parser_input(edition, buffer);
+ // FIXME: Pass the correct edition per token. Due to the split between mbe and hir-expand it's complicated.
+ let parser_input = to_parser_input(buffer, &mut |_ctx| edition);
let tree_traversal = entry_point.parse(&parser_input, edition);
let mut cursor = buffer.cursor();
let mut error = false;
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index e63ad113ff..fb68d35a4c 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -26,7 +26,7 @@ fn check_(
file_id: EditionedFileId::new(FileId::from_raw(0), def_edition),
ast_id: ErasedFileAstId::from_raw(0),
},
- SyntaxContextId::ROOT,
+ SyntaxContextId::root(Edition::CURRENT),
decl,
)
.unwrap();
@@ -39,16 +39,20 @@ fn check_(
file_id: EditionedFileId::new(FileId::from_raw(1), call_edition),
ast_id: ErasedFileAstId::from_raw(0),
};
- let arg_tt =
- syntax_bridge::parse_to_token_tree(call_edition, call_anchor, SyntaxContextId::ROOT, arg)
- .unwrap();
+ let arg_tt = syntax_bridge::parse_to_token_tree(
+ call_edition,
+ call_anchor,
+ SyntaxContextId::root(Edition::CURRENT),
+ arg,
+ )
+ .unwrap();
let res = mac.expand(
&arg_tt,
|_| (),
Span {
range: TextRange::up_to(TextSize::of(arg)),
anchor: call_anchor,
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
},
def_edition,
);
@@ -59,7 +63,12 @@ fn check_(
if render_debug {
format_to!(expect_res, "{:#?}\n\n", res.value.0);
}
- let (node, _) = syntax_bridge::token_tree_to_syntax_node(&res.value.0, parse, def_edition);
+ let (node, _) = syntax_bridge::token_tree_to_syntax_node(
+ &res.value.0,
+ parse,
+ &mut |_| def_edition,
+ def_edition,
+ );
format_to!(
expect_res,
"{}",
@@ -106,25 +115,25 @@ fn token_mapping_smoke_test() {
struct MyTraitMap2
"#,
expect![[r#"
- IDENT struct 0:[email protected]#0
- IDENT MyTraitMap2 1:[email protected]#0
- IDENT map 0:[email protected]#0
- PUNCH : [alone] 0:[email protected]#0
- PUNCH : [joint] 0:[email protected]#0
- PUNCH : [alone] 0:[email protected]#0
- IDENT std 0:[email protected]#0
- PUNCH : [joint] 0:[email protected]#0
- PUNCH : [alone] 0:[email protected]#0
- IDENT collections 0:[email protected]#0
- PUNCH : [joint] 0:[email protected]#0
- PUNCH : [alone] 0:[email protected]#0
- IDENT HashSet 0:[email protected]#0
- PUNCH < [alone] 0:[email protected]#0
- PUNCH > [joint] 0:[email protected]#0
- PUNCH , [alone] 0:[email protected]#0
+ IDENT struct 0:[email protected]#2
+ IDENT MyTraitMap2 1:[email protected]#2
+ IDENT map 0:[email protected]#2
+ PUNCH : [alone] 0:[email protected]#2
+ PUNCH : [joint] 0:[email protected]#2
+ PUNCH : [alone] 0:[email protected]#2
+ IDENT std 0:[email protected]#2
+ PUNCH : [joint] 0:[email protected]#2
+ PUNCH : [alone] 0:[email protected]#2
+ IDENT collections 0:[email protected]#2
+ PUNCH : [joint] 0:[email protected]#2
+ PUNCH : [alone] 0:[email protected]#2
+ IDENT HashSet 0:[email protected]#2
+ PUNCH < [alone] 0:[email protected]#2
+ PUNCH > [joint] 0:[email protected]#2
+ PUNCH , [alone] 0:[email protected]#2
struct MyTraitMap2 {
map: ::std::collections::HashSet<()>,
@@ -153,28 +162,28 @@ fn main() {
}
"#,
expect![[r#"
- IDENT fn 1:[email protected]#0
- IDENT main 1:[email protected]#0
- LITERAL Integer 1 1:[email protected]#0
- PUNCH ; [alone] 1:[email protected]#0
- LITERAL Float 1.0 1:[email protected]#0
- PUNCH ; [alone] 1:[email protected]#0
- LITERAL Integer 1 1:[email protected]#0
- PUNCH , [alone] 1:[email protected]#0
- PUNCH , [alone] 1:[email protected]#0
- PUNCH . [alone] 1:[email protected]#0
- LITERAL Float 0.0 1:[email protected]#0
- PUNCH ; [alone] 1:[email protected]#0
- IDENT let 1:[email protected]#0
- IDENT x 1:[email protected]#0
- PUNCH = [alone] 1:[email protected]#0
- LITERAL Integer 1 1:[email protected]#0
- PUNCH ; [alone] 1:[email protected]#0
+ IDENT fn 1:[email protected]#2
+ IDENT main 1:[email protected]#2
+ LITERAL Integer 1 1:[email protected]#2
+ PUNCH ; [alone] 1:[email protected]#2
+ LITERAL Float 1.0 1:[email protected]#2
+ PUNCH ; [alone] 1:[email protected]#2
+ LITERAL Integer 1 1:[email protected]#2
+ PUNCH , [alone] 1:[email protected]#2
+ PUNCH , [alone] 1:[email protected]#2
+ PUNCH . [alone] 1:[email protected]#2
+ LITERAL Float 0.0 1:[email protected]#2
+ PUNCH ; [alone] 1:[email protected]#2
+ IDENT let 1:[email protected]#2
+ IDENT x 1:[email protected]#2
+ PUNCH = [alone] 1:[email protected]#2
+ LITERAL Integer 1 1:[email protected]#2
+ PUNCH ; [alone] 1:[email protected]#2
fn main(){
1;
@@ -200,14 +209,14 @@ fn expr_2021() {
const { 1 },
"#,
expect![[r#"
- IDENT _ 1:[email protected]#0
- PUNCH ; [joint] 0:[email protected]#0
- IDENT const 1:[email protected]#0
- LITERAL Integer 1 1:[email protected]#0
- PUNCH ; [alone] 0:[email protected]#0
+ IDENT _ 1:[email protected]#2
+ PUNCH ; [joint] 0:[email protected]#2
+ IDENT const 1:[email protected]#2
+ LITERAL Integer 1 1:[email protected]#2
+ PUNCH ; [alone] 0:[email protected]#2
_;
(const {
@@ -228,13 +237,13 @@ fn expr_2021() {
expect![[r#"
ExpandError {
inner: (
NoMatchingRule,
),
}
- PUNCH ; [alone] 0:[email protected]#0
+ PUNCH ; [alone] 0:[email protected]#2
;"#]],
);
@@ -252,13 +261,13 @@ fn expr_2021() {
expect![[r#"
ExpandError {
inner: (
NoMatchingRule,
),
}
- PUNCH ; [alone] 0:[email protected]#0
+ PUNCH ; [alone] 0:[email protected]#2
;"#]],
);
@@ -278,26 +287,26 @@ fn expr_2021() {
break 'foo bar,
"#,
expect![[r#"
- LITERAL Integer 4 1:[email protected]#0
- PUNCH ; [joint] 0:[email protected]#0
- LITERAL Str literal 1:[email protected]#0
- PUNCH ; [joint] 0:[email protected]#0
- IDENT funcall 1:[email protected]#0
- PUNCH ; [joint] 0:[email protected]#0
- IDENT future 1:[email protected]#0
- PUNCH . [alone] 1:[email protected]#0
- IDENT await 1:[email protected]#0
- PUNCH ; [joint] 0:[email protected]#0
- IDENT break 1:[email protected]#0
- PUNCH ' [joint] 1:[email protected]#0
- IDENT foo 1:[email protected]#0
- IDENT bar 1:[email protected]#0
- PUNCH ; [alone] 0:[email protected]#0
+ LITERAL Integer 4 1:[email protected]#2
+ PUNCH ; [joint] 0:[email protected]#2
+ LITERAL Str literal 1:[email protected]#2
+ PUNCH ; [joint] 0:[email protected]#2
+ IDENT funcall 1:[email protected]#2
+ PUNCH ; [joint] 0:[email protected]#2
+ IDENT future 1:[email protected]#2
+ PUNCH . [alone] 1:[email protected]#2
+ IDENT await 1:[email protected]#2
+ PUNCH ; [joint] 0:[email protected]#2
+ IDENT break 1:[email protected]#2
+ PUNCH ' [joint] 1:[email protected]#2
+ IDENT foo 1:[email protected]#2
+ IDENT bar 1:[email protected]#2
+ PUNCH ; [alone] 0:[email protected]#2
4;
"literal";
@@ -319,13 +328,13 @@ fn expr_2021() {
expect![[r#"
ExpandError {
inner: (
NoMatchingRule,
),
}
- PUNCH ; [alone] 0:[email protected]#0
+ PUNCH ; [alone] 0:[email protected]#2
;"#]],
);
diff --git a/crates/parser/src/event.rs b/crates/parser/src/event.rs
index e38571dd3e..b197b086f3 100644
--- a/crates/parser/src/event.rs
+++ b/crates/parser/src/event.rs
@@ -12,7 +12,7 @@ use crate::{
/// `Parser` produces a flat list of `Event`s.
/// They are converted to a tree-structure in
/// a separate pass, via `TreeBuilder`.
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
pub(crate) enum Event {
/// This event signifies the start of the node.
/// It should be either abandoned (in which case the
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 3b3f11be13..389c01933c 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -134,10 +134,12 @@ pub(super) fn let_stmt(p: &mut Parser<'_>, with_semi: Semicolon) {
// test_err let_else_right_curly_brace
// fn func() { let Some(_) = {Some(1)} else { panic!("h") };}
if let Some(expr) = expr_after_eq {
- if BlockLike::is_blocklike(expr.kind()) {
- p.error(
- "right curly brace `}` before `else` in a `let...else` statement not allowed",
- )
+ if let Some(token) = expr.last_token(p) {
+ if token == T!['}'] {
+ p.error(
+ "right curly brace `}` before `else` in a `let...else` statement not allowed"
+ )
+ }
}
}
@@ -339,13 +341,20 @@ fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLik
// // raw reference operator
// let _ = &raw mut foo;
// let _ = &raw const foo;
+ // let _ = &raw foo;
// }
T![&] => {
m = p.start();
p.bump(T![&]);
- if p.at_contextual_kw(T![raw]) && [T![mut], T![const]].contains(&p.nth(1)) {
- p.bump_remap(T![raw]);
- p.bump_any();
+ if p.at_contextual_kw(T![raw]) {
+ if [T![mut], T![const]].contains(&p.nth(1)) {
+ p.bump_remap(T![raw]);
+ p.bump_any();
+ } else if p.nth_at(1, SyntaxKind::IDENT) {
+ // we treat raw as keyword in this case
+ // &raw foo;
+ p.bump_remap(T![raw]);
+ }
} else {
p.eat(T![mut]);
}
diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs
index 75a75f601c..2f6ba52574 100644
--- a/crates/parser/src/parser.rs
+++ b/crates/parser/src/parser.rs
@@ -318,7 +318,8 @@ impl Marker {
_ => unreachable!(),
}
p.push_event(Event::Finish);
- CompletedMarker::new(self.pos, kind)
+ let end_pos = p.events.len() as u32;
+ CompletedMarker::new(self.pos, end_pos, kind)
}
/// Abandons the syntax tree node. All its children
@@ -336,13 +337,14 @@ impl Marker {
}
pub(crate) struct CompletedMarker {
- pos: u32,
+ start_pos: u32,
+ end_pos: u32,
kind: SyntaxKind,
}
impl CompletedMarker {
- fn new(pos: u32, kind: SyntaxKind) -> Self {
- CompletedMarker { pos, kind }
+ fn new(start_pos: u32, end_pos: u32, kind: SyntaxKind) -> Self {
+ CompletedMarker { start_pos, end_pos, kind }
}
/// This method allows to create a new node which starts
@@ -360,10 +362,10 @@ impl CompletedMarker {
/// distance to `NEWSTART` into forward_parent(=2 in this case);
pub(crate) fn precede(self, p: &mut Parser<'_>) -> Marker {
let new_pos = p.start();
- let idx = self.pos as usize;
+ let idx = self.start_pos as usize;
match &mut p.events[idx] {
Event::Start { forward_parent, .. } => {
- *forward_parent = Some(new_pos.pos - self.pos);
+ *forward_parent = Some(new_pos.pos - self.start_pos);
}
_ => unreachable!(),
}
@@ -376,7 +378,7 @@ impl CompletedMarker {
let idx = m.pos as usize;
match &mut p.events[idx] {
Event::Start { forward_parent, .. } => {
- *forward_parent = Some(self.pos - m.pos);
+ *forward_parent = Some(self.start_pos - m.pos);
}
_ => unreachable!(),
}
@@ -386,4 +388,13 @@ impl CompletedMarker {
pub(crate) fn kind(&self) -> SyntaxKind {
self.kind
}
+
+ pub(crate) fn last_token(&self, p: &Parser<'_>) -> Option<SyntaxKind> {
+ let end_pos = self.end_pos as usize;
+ debug_assert_eq!(p.events[end_pos - 1], Event::Finish);
+ p.events[..end_pos].iter().rev().find_map(|event| match event {
+ Event::Token { kind, .. } => Some(*kind),
+ _ => None,
+ })
+ }
}
diff --git a/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast b/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast
new file mode 100644
index 0000000000..578dc2b0f9
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rast
@@ -0,0 +1,79 @@
+SOURCE_FILE
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "X"
+ WHITESPACE " "
+ RECORD_FIELD_LIST
+ L_CURLY "{"
+ RECORD_FIELD
+ NAME
+ IDENT "a"
+ COLON ":"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_CURLY "}"
+ WHITESPACE "\n"
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "f"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ RECORD_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "X"
+ WHITESPACE " "
+ RECORD_EXPR_FIELD_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ RECORD_EXPR_FIELD
+ NAME_REF
+ IDENT "a"
+ COLON ":"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE "\n "
+ R_CURLY "}"
+ WHITESPACE " "
+ LET_ELSE
+ ELSE_KW "else"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ RETURN_EXPR
+ RETURN_KW "return"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+error 63: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs b/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs
new file mode 100644
index 0000000000..c0c0edc983
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0056_let_else_right_curly_brace_struct.rs
@@ -0,0 +1,8 @@
+struct X {a: i32}
+fn f() {
+ let foo = X {
+ a: 1
+ } else {
+ return;
+ };
+} \ No newline at end of file
diff --git a/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast
new file mode 100644
index 0000000000..8e994f22d4
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rast
@@ -0,0 +1,42 @@
+SOURCE_FILE
+ ERROR
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE " "
+ LET_ELSE
+ ELSE_KW "else"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ RETURN_EXPR
+ RETURN_KW "return"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 0: expected an item
+error 23: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs
new file mode 100644
index 0000000000..c29ddcce1f
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_arithmetic.rs
@@ -0,0 +1,5 @@
+let foo = 1 + {
+ 1
+} else {
+ return;
+};
diff --git a/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast
new file mode 100644
index 0000000000..055b583ace
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rast
@@ -0,0 +1,90 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "r"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "ok"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ MACRO_EXPR
+ MACRO_CALL
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "format_args"
+ BANG "!"
+ TOKEN_TREE
+ L_PAREN "("
+ STRING "\"\""
+ R_PAREN ")"
+ WHITESPACE " "
+ LET_ELSE
+ ELSE_KW "else"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ EXPR_STMT
+ RETURN_EXPR
+ RETURN_KW "return"
+ SEMICOLON ";"
+ WHITESPACE " "
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "bad"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ MACRO_EXPR
+ MACRO_CALL
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "format_args"
+ BANG "!"
+ WHITESPACE " "
+ TOKEN_TREE
+ L_CURLY "{"
+ STRING "\"\""
+ R_CURLY "}"
+ WHITESPACE " "
+ LET_ELSE
+ ELSE_KW "else"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ EXPR_STMT
+ RETURN_EXPR
+ RETURN_KW "return"
+ SEMICOLON ";"
+ WHITESPACE " "
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
+error 89: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs
new file mode 100644
index 0000000000..5916fa07dc
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0057_let_else_right_curly_brace_format_args.rs
@@ -0,0 +1,5 @@
+fn r() {
+ let ok = format_args!("") else { return; };
+
+ let bad = format_args! {""} else { return; };
+}
diff --git a/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast b/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast
new file mode 100644
index 0000000000..8c7fd8c295
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rast
@@ -0,0 +1,40 @@
+SOURCE_FILE
+ ERROR
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ RANGE_EXPR
+ LITERAL
+ INT_NUMBER "1"
+ DOT2 ".."
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE " "
+ LET_ELSE
+ ELSE_KW "else"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ RETURN_EXPR
+ RETURN_KW "return"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 0: expected an item
+error 22: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs b/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs
new file mode 100644
index 0000000000..5417131d28
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0058_let_else_right_curly_brace_range.rs
@@ -0,0 +1,5 @@
+let foo = 1..{
+ 1
+} else {
+ return;
+};
diff --git a/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast b/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast
new file mode 100644
index 0000000000..57925a0d19
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rast
@@ -0,0 +1,55 @@
+SOURCE_FILE
+ ERROR
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ CLOSURE_EXPR
+ PARAM_LIST
+ PIPE "|"
+ PARAM
+ IDENT_PAT
+ NAME
+ IDENT "x"
+ COLON ":"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ PIPE "|"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "x"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE " "
+ LET_ELSE
+ ELSE_KW "else"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ RETURN_EXPR
+ RETURN_KW "return"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 0: expected an item
+error 28: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs b/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs
new file mode 100644
index 0000000000..89c7579b07
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0059_let_else_right_curly_brace_closure.rs
@@ -0,0 +1,5 @@
+let foo = |x: i32| {
+ x
+} else {
+ return;
+};
diff --git a/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast b/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast
new file mode 100644
index 0000000000..4fb70bd50e
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rast
@@ -0,0 +1,38 @@
+SOURCE_FILE
+ ERROR
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PREFIX_EXPR
+ MINUS "-"
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE " "
+ LET_ELSE
+ ELSE_KW "else"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ RETURN_EXPR
+ RETURN_KW "return"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 0: expected an item
+error 20: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs b/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs
new file mode 100644
index 0000000000..1ba7f7d761
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0060_let_else_right_curly_brace_unary.rs
@@ -0,0 +1,5 @@
+let foo = -{
+ 1
+} else {
+ return;
+};
diff --git a/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast b/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast
new file mode 100644
index 0000000000..e8eeeee695
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rast
@@ -0,0 +1,90 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "o"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ RET_TYPE
+ THIN_ARROW "->"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Result"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ TUPLE_TYPE
+ L_PAREN "("
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE " "
+ TYPE_ARG
+ TUPLE_TYPE
+ L_PAREN "("
+ R_PAREN ")"
+ R_ANGLE ">"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ YEET_EXPR
+ DO_KW "do"
+ WHITESPACE " "
+ YEET_KW "yeet"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE "\n "
+ R_CURLY "}"
+ WHITESPACE " "
+ LET_ELSE
+ ELSE_KW "else"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ RETURN_EXPR
+ RETURN_KW "return"
+ WHITESPACE " "
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Ok"
+ ARG_LIST
+ L_PAREN "("
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+error 67: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs b/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs
new file mode 100644
index 0000000000..188fb07d91
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0061_let_else_right_curly_brace_do_yeet.rs
@@ -0,0 +1,7 @@
+fn o() -> Result<(), ()> {
+ let foo = do yeet {
+ ()
+ } else {
+ return Ok(());
+ };
+} \ No newline at end of file
diff --git a/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast b/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast
new file mode 100644
index 0000000000..cc5e1278c3
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rast
@@ -0,0 +1,40 @@
+SOURCE_FILE
+ ERROR
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BECOME_EXPR
+ BECOME_KW "become"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE " "
+ LET_ELSE
+ ELSE_KW "else"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ RETURN_EXPR
+ RETURN_KW "return"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 0: expected an item
+error 27: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs b/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs
new file mode 100644
index 0000000000..622548b8f3
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0062_let_else_right_curly_brace_become.rs
@@ -0,0 +1,5 @@
+let foo = become {
+ ()
+} else {
+ return;
+};
diff --git a/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast b/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast
new file mode 100644
index 0000000000..ea2f4f28e2
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rast
@@ -0,0 +1,38 @@
+SOURCE_FILE
+ ERROR
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ REF_EXPR
+ AMP "&"
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE " "
+ LET_ELSE
+ ELSE_KW "else"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ RETURN_EXPR
+ RETURN_KW "return"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 0: expected an item
+error 20: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs b/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs
new file mode 100644
index 0000000000..9a00dca368
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0063_let_else_right_curly_brace_reference.rs
@@ -0,0 +1,5 @@
+let foo = &{
+ 1
+} else {
+ return;
+};
diff --git a/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast b/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast
new file mode 100644
index 0000000000..47396140c5
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rast
@@ -0,0 +1,45 @@
+SOURCE_FILE
+ ERROR
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "bar"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE " "
+ LET_ELSE
+ ELSE_KW "else"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ RETURN_EXPR
+ RETURN_KW "return"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 0: expected an item
+error 25: right curly brace `}` before `else` in a `let...else` statement not allowed
diff --git a/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs b/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs
new file mode 100644
index 0000000000..08e677416f
--- /dev/null
+++ b/crates/parser/test_data/parser/err/0064_let_else_right_curly_brace_assignment.rs
@@ -0,0 +1,5 @@
+let foo = bar = {
+ 1
+} else {
+ return;
+};
diff --git a/crates/parser/test_data/parser/inline/ok/ref_expr.rast b/crates/parser/test_data/parser/inline/ok/ref_expr.rast
index 108b0802c3..8dc916e5cc 100644
--- a/crates/parser/test_data/parser/inline/ok/ref_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/ref_expr.rast
@@ -134,6 +134,25 @@ SOURCE_FILE
NAME_REF
IDENT "foo"
SEMICOLON ";"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ REF_EXPR
+ AMP "&"
+ RAW_KW "raw"
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "foo"
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/ref_expr.rs b/crates/parser/test_data/parser/inline/ok/ref_expr.rs
index c5262f4469..31a2485b43 100644
--- a/crates/parser/test_data/parser/inline/ok/ref_expr.rs
+++ b/crates/parser/test_data/parser/inline/ok/ref_expr.rs
@@ -7,4 +7,5 @@ fn foo() {
// raw reference operator
let _ = &raw mut foo;
let _ = &raw const foo;
+ let _ = &raw foo;
}
diff --git a/crates/proc-macro-api/src/legacy_protocol/msg.rs b/crates/proc-macro-api/src/legacy_protocol/msg.rs
index 6ea8db9a90..4b831e4ace 100644
--- a/crates/proc-macro-api/src/legacy_protocol/msg.rs
+++ b/crates/proc-macro-api/src/legacy_protocol/msg.rs
@@ -159,7 +159,7 @@ type ProtocolWrite<W: Write> = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str)
#[cfg(test)]
mod tests {
use intern::{sym, Symbol};
- use span::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TextSize};
+ use span::{Edition, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TextSize};
use tt::{
Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree,
TopSubtreeBuilder,
@@ -180,12 +180,12 @@ mod tests {
open: Span {
range: TextRange::empty(TextSize::new(0)),
anchor,
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
},
close: Span {
range: TextRange::empty(TextSize::new(19)),
anchor,
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
},
kind: DelimiterKind::Invisible,
});
@@ -196,7 +196,7 @@ mod tests {
span: Span {
range: TextRange::at(TextSize::new(0), TextSize::of("struct")),
anchor,
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
},
is_raw: tt::IdentIsRaw::No,
}
@@ -208,7 +208,7 @@ mod tests {
span: Span {
range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")),
anchor,
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
},
is_raw: tt::IdentIsRaw::Yes,
}
@@ -219,7 +219,7 @@ mod tests {
span: Span {
range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")),
anchor,
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
},
kind: tt::LitKind::Str,
suffix: None,
@@ -229,7 +229,7 @@ mod tests {
span: Span {
range: TextRange::at(TextSize::new(13), TextSize::of('@')),
anchor,
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
},
spacing: Spacing::Joint,
}));
@@ -238,7 +238,7 @@ mod tests {
Span {
range: TextRange::at(TextSize::new(14), TextSize::of('{')),
anchor,
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
},
);
builder.push(Leaf::Literal(Literal {
@@ -246,7 +246,7 @@ mod tests {
span: Span {
range: TextRange::at(TextSize::new(15), TextSize::of("0u32")),
anchor,
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
},
kind: tt::LitKind::Integer,
suffix: Some(sym::u32.clone()),
@@ -254,7 +254,7 @@ mod tests {
builder.close(Span {
range: TextRange::at(TextSize::new(19), TextSize::of('}')),
anchor,
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
});
builder.build()
diff --git a/crates/proc-macro-srv/Cargo.toml b/crates/proc-macro-srv/Cargo.toml
index 00695c5473..191535ac55 100644
--- a/crates/proc-macro-srv/Cargo.toml
+++ b/crates/proc-macro-srv/Cargo.toml
@@ -14,6 +14,7 @@ doctest = false
[dependencies]
object.workspace = true
+libc.workspace = true
libloading.workspace = true
memmap2.workspace = true
diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs
index fe15d42b4e..cbf7a277bf 100644
--- a/crates/proc-macro-srv/src/dylib.rs
+++ b/crates/proc-macro-srv/src/dylib.rs
@@ -28,11 +28,16 @@ fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
#[cfg(unix)]
fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
+ // not defined by POSIX, different values on mips vs other targets
+ #[cfg(target_env = "gnu")]
+ use libc::RTLD_DEEPBIND;
use libloading::os::unix::Library as UnixLibrary;
- use std::os::raw::c_int;
+ // defined by POSIX
+ use libloading::os::unix::RTLD_NOW;
- const RTLD_NOW: c_int = 0x00002;
- const RTLD_DEEPBIND: c_int = 0x00008;
+ // MUSL and bionic don't have it..
+ #[cfg(not(target_env = "gnu"))]
+ const RTLD_DEEPBIND: std::os::raw::c_int = 0x0;
unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) }
}
diff --git a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index beaebf3330..c7614849e0 100644
--- a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -440,7 +440,7 @@ mod tests {
file_id: EditionedFileId::current_edition(FileId::from_raw(0)),
ast_id: span::ErasedFileAstId::from_raw(0),
},
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(span::Edition::CURRENT),
};
let s = TokenStream {
token_trees: vec![
@@ -482,7 +482,7 @@ mod tests {
file_id: EditionedFileId::current_edition(FileId::from_raw(0)),
ast_id: span::ErasedFileAstId::from_raw(0),
},
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(span::Edition::CURRENT),
};
let subtree_paren_a = vec![
tt::TokenTree::Subtree(tt::Subtree {
diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs
index dc6e71163b..15de88ea65 100644
--- a/crates/proc-macro-srv/src/tests/mod.rs
+++ b/crates/proc-macro-srv/src/tests/mod.rs
@@ -12,7 +12,7 @@ fn test_derive_empty() {
"DeriveEmpty",
r#"struct S;"#,
expect!["SUBTREE $$ 1 1"],
- expect!["SUBTREE $$ 42:[email protected]#0 42:[email protected]#0"],
+ expect!["SUBTREE $$ 42:[email protected]#2 42:[email protected]#2"],
);
}
@@ -29,12 +29,12 @@ fn test_derive_error() {
LITERAL Str #[derive(DeriveError)] struct S ; 1
PUNCH ; [alone] 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
- IDENT compile_error 42:[email protected]#0
- PUNCH ! [alone] 42:[email protected]#0
- SUBTREE () 42:[email protected]#0 42:[email protected]#0
- LITERAL Str #[derive(DeriveError)] struct S ; 42:[email protected]#0
- PUNCH ; [alone] 42:[email protected]#0"#]],
+ SUBTREE $$ 42:[email protected]#2 42:[email protected]#2
+ IDENT compile_error 42:[email protected]#2
+ PUNCH ! [alone] 42:[email protected]#2
+ SUBTREE () 42:[email protected]#2 42:[email protected]#2
+ LITERAL Str #[derive(DeriveError)] struct S ; 42:[email protected]#2
+ PUNCH ; [alone] 42:[email protected]#2"#]],
);
}
@@ -53,14 +53,14 @@ fn test_fn_like_macro_noop() {
PUNCH , [alone] 1
SUBTREE [] 1 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
- IDENT ident 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- LITERAL Integer 0 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- LITERAL Integer 1 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- SUBTREE [] 42:[email protected]#0 42:[email protected]#0"#]],
+ SUBTREE $$ 42:[email protected]#2 42:[email protected]#2
+ IDENT ident 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ LITERAL Integer 0 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ LITERAL Integer 1 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ SUBTREE [] 42:[email protected]#2 42:[email protected]#2"#]],
);
}
@@ -75,10 +75,10 @@ fn test_fn_like_macro_clone_ident_subtree() {
PUNCH , [alone] 1
SUBTREE [] 1 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
- IDENT ident 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- SUBTREE [] 42:[email protected]#0 42:[email protected]#0"#]],
+ SUBTREE $$ 42:[email protected]#2 42:[email protected]#2
+ IDENT ident 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ SUBTREE [] 42:[email protected]#2 42:[email protected]#2"#]],
);
}
@@ -91,8 +91,8 @@ fn test_fn_like_macro_clone_raw_ident() {
SUBTREE $$ 1 1
IDENT r#async 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
- IDENT r#async 42:[email protected]#0"#]],
+ SUBTREE $$ 42:[email protected]#2 42:[email protected]#2
+ IDENT r#async 42:[email protected]#2"#]],
);
}
@@ -105,8 +105,8 @@ fn test_fn_like_fn_like_span_join() {
SUBTREE $$ 1 1
IDENT r#joined 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
- IDENT r#joined 42:[email protected]#0"#]],
+ SUBTREE $$ 42:[email protected]#2 42:[email protected]#2
+ IDENT r#joined 42:[email protected]#2"#]],
);
}
@@ -121,10 +121,10 @@ fn test_fn_like_fn_like_span_ops() {
IDENT resolved_at_def_site 1
IDENT start_span 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
- IDENT set_def_site 41:[email protected]#0
- IDENT resolved_at_def_site 42:[email protected]#0
- IDENT start_span 42:[email protected]#0"#]],
+ SUBTREE $$ 42:[email protected]#2 42:[email protected]#2
+ IDENT set_def_site 41:[email protected]#2
+ IDENT resolved_at_def_site 42:[email protected]#2
+ IDENT start_span 42:[email protected]#2"#]],
);
}
@@ -143,14 +143,14 @@ fn test_fn_like_mk_literals() {
LITERAL Integer 123i64 1
LITERAL Integer 123 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
- LITERAL ByteStr byte_string 42:[email protected]#0
- LITERAL Char c 42:[email protected]#0
- LITERAL Str string 42:[email protected]#0
- LITERAL Float 3.14f64 42:[email protected]#0
- LITERAL Float 3.14 42:[email protected]#0
- LITERAL Integer 123i64 42:[email protected]#0
- LITERAL Integer 123 42:[email protected]#0"#]],
+ SUBTREE $$ 42:[email protected]#2 42:[email protected]#2
+ LITERAL ByteStr byte_string 42:[email protected]#2
+ LITERAL Char c 42:[email protected]#2
+ LITERAL Str string 42:[email protected]#2
+ LITERAL Float 3.14f64 42:[email protected]#2
+ LITERAL Float 3.14 42:[email protected]#2
+ LITERAL Integer 123i64 42:[email protected]#2
+ LITERAL Integer 123 42:[email protected]#2"#]],
);
}
@@ -164,9 +164,9 @@ fn test_fn_like_mk_idents() {
IDENT standard 1
IDENT r#raw 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
- IDENT standard 42:[email protected]#0
- IDENT r#raw 42:[email protected]#0"#]],
+ SUBTREE $$ 42:[email protected]#2 42:[email protected]#2
+ IDENT standard 42:[email protected]#2
+ IDENT r#raw 42:[email protected]#2"#]],
);
}
@@ -198,27 +198,27 @@ fn test_fn_like_macro_clone_literals() {
PUNCH , [alone] 1
LITERAL CStr null 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
- LITERAL Integer 1u16 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- LITERAL Integer 2_u32 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- PUNCH - [alone] 42:[email protected]#0
- LITERAL Integer 4i64 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- LITERAL Float 3.14f32 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- LITERAL Str hello bridge 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- LITERAL Str suffixedsuffix 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- LITERAL StrRaw(2) raw 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- LITERAL Char a 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- LITERAL Byte b 42:[email protected]#0
- PUNCH , [alone] 42:[email protected]#0
- LITERAL CStr null 42:[email protected]#0"#]],
+ SUBTREE $$ 42:[email protected]#2 42:[email protected]#2
+ LITERAL Integer 1u16 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ LITERAL Integer 2_u32 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ PUNCH - [alone] 42:[email protected]#2
+ LITERAL Integer 4i64 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ LITERAL Float 3.14f32 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ LITERAL Str hello bridge 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ LITERAL Str suffixedsuffix 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ LITERAL StrRaw(2) raw 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ LITERAL Char a 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ LITERAL Byte b 42:[email protected]#2
+ PUNCH , [alone] 42:[email protected]#2
+ LITERAL CStr null 42:[email protected]#2"#]],
);
}
@@ -239,12 +239,12 @@ fn test_attr_macro() {
LITERAL Str #[attr_error(some arguments)] mod m {} 1
PUNCH ; [alone] 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
- IDENT compile_error 42:[email protected]#0
- PUNCH ! [alone] 42:[email protected]#0
- SUBTREE () 42:[email protected]#0 42:[email protected]#0
- LITERAL Str #[attr_error(some arguments)] mod m {} 42:[email protected]#0
- PUNCH ; [alone] 42:[email protected]#0"#]],
+ SUBTREE $$ 42:[email protected]#2 42:[email protected]#2
+ IDENT compile_error 42:[email protected]#2
+ PUNCH ! [alone] 42:[email protected]#2
+ SUBTREE () 42:[email protected]#2 42:[email protected]#2
+ LITERAL Str #[attr_error(some arguments)] mod m {} 42:[email protected]#2
+ PUNCH ; [alone] 42:[email protected]#2"#]],
);
}
diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs
index 37d51050f3..4ce4544243 100644
--- a/crates/proc-macro-srv/src/tests/utils.rs
+++ b/crates/proc-macro-srv/src/tests/utils.rs
@@ -28,13 +28,18 @@ fn parse_string_spanned(
))
}
-pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect, expect_s: Expect) {
+pub fn assert_expand(
+ macro_name: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ expect: Expect,
+ expect_s: Expect,
+) {
assert_expand_impl(macro_name, ra_fixture, None, expect, expect_s);
}
pub fn assert_expand_attr(
macro_name: &str,
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
attr_args: &str,
expect: Expect,
expect_s: Expect,
@@ -76,7 +81,7 @@ fn assert_expand_impl(
file_id: EditionedFileId::current_edition(FileId::from_raw(41)),
ast_id: ErasedFileAstId::from_raw(1),
},
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(span::Edition::CURRENT),
};
let call_site = Span {
range: TextRange::new(0.into(), 100.into()),
@@ -84,7 +89,7 @@ fn assert_expand_impl(
file_id: EditionedFileId::current_edition(FileId::from_raw(42)),
ast_id: ErasedFileAstId::from_raw(2),
},
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(span::Edition::CURRENT),
};
let mixed_site = call_site;
diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml
index 2e3413f339..3179c810f6 100644
--- a/crates/profile/Cargo.toml
+++ b/crates/profile/Cargo.toml
@@ -21,7 +21,10 @@ jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = tr
perf-event = "=0.4.7"
[target.'cfg(windows)'.dependencies]
-windows-sys = { version = "0.52", features = ["Win32_System_Threading", "Win32_System_ProcessStatus"] }
+windows-sys = { version = "0.59", features = [
+ "Win32_System_Threading",
+ "Win32_System_ProcessStatus",
+] }
[features]
cpu_profiler = []
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index 4d906c2aeb..e4a6113462 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -92,6 +92,8 @@ pub struct CargoConfig {
pub sysroot_src: Option<AbsPathBuf>,
/// rustc private crate source
pub rustc_source: Option<RustLibSource>,
+ /// Extra includes to add to the VFS.
+ pub extra_includes: Vec<AbsPathBuf>,
pub cfg_overrides: CfgOverrides,
/// Invoke `cargo check` through the RUSTC_WRAPPER.
pub wrap_rustc_in_build_scripts: bool,
diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs
index 681bce3a5a..f111383112 100644
--- a/crates/project-model/src/tests.rs
+++ b/crates/project-model/src/tests.rs
@@ -49,6 +49,7 @@ fn load_workspace_from_metadata(file: &str) -> ProjectWorkspace {
rustc_cfg: Vec::new(),
toolchain: None,
target_layout: Err("target_data_layout not loaded".into()),
+ extra_includes: Vec::new(),
}
}
@@ -63,6 +64,7 @@ fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) {
toolchain: None,
target_layout: Err(Arc::from("test has no data layout")),
cfg_overrides: Default::default(),
+ extra_includes: Vec::new(),
};
to_crate_graph(project_workspace, &mut Default::default())
}
@@ -284,6 +286,7 @@ fn smoke_test_real_sysroot_cargo() {
cfg_overrides: Default::default(),
toolchain: None,
target_layout: Err("target_data_layout not loaded".into()),
+ extra_includes: Vec::new(),
};
project_workspace.to_crate_graph(
&mut {
diff --git a/crates/project-model/src/toolchain_info/rustc_cfg.rs b/crates/project-model/src/toolchain_info/rustc_cfg.rs
index afcc812079..4bf9b59e7d 100644
--- a/crates/project-model/src/toolchain_info/rustc_cfg.rs
+++ b/crates/project-model/src/toolchain_info/rustc_cfg.rs
@@ -66,7 +66,7 @@ fn rustc_print_cfg(
QueryConfig::Cargo(sysroot, cargo_toml) => {
let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent());
cmd.envs(extra_env);
- cmd.args(["rustc"]).args(RUSTC_ARGS);
+ cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
if let Some(target) = target {
cmd.args(["--target", target]);
}
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index a345c6bcce..f98d983ac0 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -11,9 +11,8 @@ use base_db::{
};
use cfg::{CfgAtom, CfgDiff, CfgOptions};
use intern::{sym, Symbol};
-use itertools::Itertools;
use paths::{AbsPath, AbsPathBuf};
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
use span::{Edition, FileId};
use tracing::instrument;
@@ -63,6 +62,8 @@ pub struct ProjectWorkspace {
pub target_layout: TargetLayoutLoadResult,
/// A set of cfg overrides for this workspace.
pub cfg_overrides: CfgOverrides,
+ /// Additional includes to add for the VFS.
+ pub extra_includes: Vec<AbsPathBuf>,
}
#[derive(Clone)]
@@ -104,7 +105,15 @@ pub enum ProjectWorkspaceKind {
impl fmt::Debug for ProjectWorkspace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Make sure this isn't too verbose.
- let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides } = self;
+ let Self {
+ kind,
+ sysroot,
+ rustc_cfg,
+ toolchain,
+ target_layout,
+ cfg_overrides,
+ extra_includes,
+ } = self;
match kind {
ProjectWorkspaceKind::Cargo { cargo, error: _, build_scripts, rustc, set_test } => f
.debug_struct("Cargo")
@@ -117,6 +126,7 @@ impl fmt::Debug for ProjectWorkspace {
)
.field("n_rustc_cfg", &rustc_cfg.len())
.field("n_cfg_overrides", &cfg_overrides.len())
+ .field("n_extra_includes", &extra_includes.len())
.field("toolchain", &toolchain)
.field("data_layout", &target_layout)
.field("set_test", set_test)
@@ -130,7 +140,8 @@ impl fmt::Debug for ProjectWorkspace {
.field("n_rustc_cfg", &rustc_cfg.len())
.field("toolchain", &toolchain)
.field("data_layout", &target_layout)
- .field("n_cfg_overrides", &cfg_overrides.len());
+ .field("n_cfg_overrides", &cfg_overrides.len())
+ .field("n_extra_includes", &extra_includes.len());
debug_struct.finish()
}
@@ -144,6 +155,7 @@ impl fmt::Debug for ProjectWorkspace {
.field("toolchain", &toolchain)
.field("data_layout", &target_layout)
.field("n_cfg_overrides", &cfg_overrides.len())
+ .field("n_extra_includes", &extra_includes.len())
.field("set_test", set_test)
.finish(),
}
@@ -320,6 +332,7 @@ impl ProjectWorkspace {
cfg_overrides,
toolchain,
target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
+ extra_includes: config.extra_includes.clone(),
})
}
@@ -340,6 +353,7 @@ impl ProjectWorkspace {
toolchain,
target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
cfg_overrides: config.cfg_overrides.clone(),
+ extra_includes: config.extra_includes.clone(),
}
}
@@ -399,6 +413,7 @@ impl ProjectWorkspace {
toolchain,
target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
cfg_overrides: config.cfg_overrides.clone(),
+ extra_includes: config.extra_includes.clone(),
})
}
@@ -565,13 +580,20 @@ impl ProjectWorkspace {
PackageRoot {
is_local: krate.is_workspace_member,
- include: krate.include.iter().cloned().chain(build_file).collect(),
+ include: krate
+ .include
+ .iter()
+ .cloned()
+ .chain(build_file)
+ .chain(self.extra_includes.iter().cloned())
+ .collect(),
exclude: krate.exclude.clone(),
}
})
+ .collect::<FxHashSet<_>>()
+ .into_iter()
.chain(mk_sysroot())
- .unique()
- .collect(),
+ .collect::<Vec<_>>(),
ProjectWorkspaceKind::Cargo { cargo, rustc, build_scripts, error: _, set_test: _ } => {
cargo
.packages()
@@ -603,6 +625,8 @@ impl ProjectWorkspace {
let mut exclude = vec![pkg_root.join(".git")];
if is_local {
+ include.extend(self.extra_includes.iter().cloned());
+
exclude.push(pkg_root.join("target"));
} else {
exclude.push(pkg_root.join("tests"));
@@ -619,11 +643,6 @@ impl ProjectWorkspace {
exclude: Vec::new(),
})
}))
- .chain(cargo.is_virtual_workspace().then(|| PackageRoot {
- is_local: true,
- include: vec![cargo.workspace_root().to_path_buf()],
- exclude: Vec::new(),
- }))
.collect()
}
ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => {
@@ -661,6 +680,8 @@ impl ProjectWorkspace {
let mut exclude = vec![pkg_root.join(".git")];
if is_local {
+ include.extend(self.extra_includes.iter().cloned());
+
exclude.push(pkg_root.join("target"));
} else {
exclude.push(pkg_root.join("tests"));
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index d06130ce8c..c24cbb4a31 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -76,7 +76,10 @@ vfs.workspace = true
paths.workspace = true
[target.'cfg(windows)'.dependencies]
-windows-sys = { version = "0.52", features = ["Win32_System_Diagnostics_Debug", "Win32_System_Threading"] }
+windows-sys = { version = "0.59", features = [
+ "Win32_System_Diagnostics_Debug",
+ "Win32_System_Threading",
+] }
[target.'cfg(not(target_env = "msvc"))'.dependencies]
jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = true }
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index afe3455b78..bcaec52019 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -1051,6 +1051,7 @@ impl flags::AnalysisStats {
&InlayHintsConfig {
render_colons: false,
type_hints: true,
+ sized_bound: false,
discriminant_hints: ide::DiscriminantHints::Always,
parameter_hints: true,
generic_parameter_hints: ide::GenericParameterHints {
diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs
index 6b0ce4db7c..199f61e70f 100644
--- a/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -93,6 +93,7 @@ impl Tester {
toolchain: None,
target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
cfg_overrides: Default::default(),
+ extra_includes: vec![],
};
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: false,
diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs
index 6ca7d9ac05..dc0f722aae 100644
--- a/crates/rust-analyzer/src/cli/scip.rs
+++ b/crates/rust-analyzer/src/cli/scip.rs
@@ -518,7 +518,7 @@ mod test {
use test_fixture::ChangeFixture;
use vfs::VfsPath;
- fn position(ra_fixture: &str) -> (AnalysisHost, FilePosition) {
+ fn position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (AnalysisHost, FilePosition) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
host.raw_database_mut().apply_change(change_fixture.change);
@@ -530,7 +530,7 @@ mod test {
/// If expected == "", then assert that there are no symbols (this is basically local symbol)
#[track_caller]
- fn check_symbol(ra_fixture: &str, expected: &str) {
+ fn check_symbol(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected: &str) {
let (host, position) = position(ra_fixture);
let analysis = host.analysis();
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 30f0031905..3dc4379258 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -185,6 +185,8 @@ config_data! {
inlayHints_genericParameterHints_type_enable: bool = false,
/// Whether to show implicit drop hints.
inlayHints_implicitDrops_enable: bool = false,
+ /// Whether to show inlay hints for the implied type parameter `Sized` bound.
+ inlayHints_implicitSizedBoundHints_enable: bool = false,
/// Whether to show inlay type hints for elided lifetimes in function signatures.
inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never,
/// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
@@ -324,8 +326,16 @@ config_data! {
/// Show documentation.
signatureInfo_documentation_enable: bool = true,
- /// Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`.
- typing_excludeChars: Option<String> = Some("|<".to_owned()),
+ /// Specify the characters allowed to invoke special on typing triggers.
+ /// - typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression
+ /// - typing `=` between two expressions adds `;` when in statement position
+ /// - typing `=` to turn an assignment into an equality comparison removes `;` when in expression position
+ /// - 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
+ /// - typing `<` in a path or type position inserts a closing `>` after the path or type.
+ typing_triggerChars: Option<String> = Some("=.".to_owned()),
/// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
@@ -571,11 +581,8 @@ config_data! {
/// avoid checking unnecessary things.
cargo_buildScripts_useRustcWrapper: bool = true,
/// List of cfg options to enable with the given values.
- cargo_cfgs: FxHashMap<String, Option<String>> = {
- let mut m = FxHashMap::default();
- m.insert("debug_assertions".to_owned(), None);
- m.insert("miri".to_owned(), None);
- m
+ cargo_cfgs: Vec<String> = {
+ vec!["debug_assertions".into(), "miri".into()]
},
/// Extra arguments that are passed to every cargo invocation.
cargo_extraArgs: Vec<String> = vec![],
@@ -728,6 +735,10 @@ config_data! {
/// available on a nightly build.
rustfmt_rangeFormatting_enable: bool = false,
+ /// Additional paths to include in the VFS. Generally for code that is
+ /// generated or otherwise managed by a build system outside of Cargo,
+ /// though Cargo might be the eventual consumer.
+ vfs_extraIncludes: Vec<String> = vec![],
/// Workspace symbol search kind.
workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes,
@@ -1620,6 +1631,7 @@ impl Config {
InlayHintsConfig {
render_colons: self.inlayHints_renderColons().to_owned(),
type_hints: self.inlayHints_typeHints_enable().to_owned(),
+ sized_bound: self.inlayHints_implicitSizedBoundHints_enable().to_owned(),
parameter_hints: self.inlayHints_parameterHints_enable().to_owned(),
generic_parameter_hints: GenericParameterHints {
type_hints: self.inlayHints_genericParameterHints_type_enable().to_owned(),
@@ -1926,6 +1938,13 @@ impl Config {
});
let sysroot_src =
self.cargo_sysrootSrc(source_root).as_ref().map(|sysroot| self.root_path.join(sysroot));
+ let extra_includes = self
+ .vfs_extraIncludes(source_root)
+ .iter()
+ .map(String::as_str)
+ .map(AbsPathBuf::try_from)
+ .filter_map(Result::ok)
+ .collect();
CargoConfig {
all_targets: *self.cargo_allTargets(source_root),
@@ -1940,10 +1959,18 @@ impl Config {
sysroot,
sysroot_src,
rustc_source,
+ extra_includes,
cfg_overrides: project_model::CfgOverrides {
global: CfgDiff::new(
self.cargo_cfgs(source_root)
.iter()
+ // parse any cfg setting formatted as key=value or just key (without value)
+ .filter_map(|s| {
+ let mut sp = s.splitn(2, "=");
+ let key = sp.next();
+ let val = sp.next();
+ key.map(|key| (key, val))
+ })
.map(|(key, val)| match val {
Some(val) => CfgAtom::KeyValue {
key: Symbol::intern(key),
@@ -2232,8 +2259,8 @@ impl Config {
}
}
- pub fn typing_exclude_chars(&self) -> Option<String> {
- self.typing_excludeChars().clone()
+ pub fn typing_trigger_chars(&self) -> &str {
+ self.typing_triggerChars().as_deref().unwrap_or_default()
}
// VSCode is our reference implementation, so we allow ourselves to work around issues by
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 7ac70efe2d..190015d7fa 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -136,15 +136,13 @@ pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> anyhow::Res
Ok(out)
}
-pub(crate) fn handle_syntax_tree(
+pub(crate) fn handle_view_syntax_tree(
snap: GlobalStateSnapshot,
- params: lsp_ext::SyntaxTreeParams,
+ params: lsp_ext::ViewSyntaxTreeParams,
) -> anyhow::Result<String> {
- let _p = tracing::info_span!("handle_syntax_tree").entered();
+ let _p = tracing::info_span!("handle_view_syntax_tree").entered();
let id = from_proto::file_id(&snap, &params.text_document.uri)?;
- let line_index = snap.file_line_index(id)?;
- let text_range = params.range.and_then(|r| from_proto::text_range(&line_index, r).ok());
- let res = snap.analysis.syntax_tree(id, text_range)?;
+ let res = snap.analysis.view_syntax_tree(id)?;
Ok(res)
}
@@ -436,29 +434,24 @@ pub(crate) fn handle_on_type_formatting(
params: lsp_types::DocumentOnTypeFormattingParams,
) -> anyhow::Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
let _p = tracing::info_span!("handle_on_type_formatting").entered();
+ let char_typed = params.ch.chars().next().unwrap_or('\0');
+ if !snap.config.typing_trigger_chars().contains(char_typed) {
+ return Ok(None);
+ }
+
let mut position = from_proto::file_position(&snap, params.text_document_position)?;
let line_index = snap.file_line_index(position.file_id)?;
// in `ide`, the `on_type` invariant is that
// `text.char_at(position) == typed_char`.
position.offset -= TextSize::of('.');
- let char_typed = params.ch.chars().next().unwrap_or('\0');
let text = snap.analysis.file_text(position.file_id)?;
if stdx::never!(!text[usize::from(position.offset)..].starts_with(char_typed)) {
return Ok(None);
}
- // We have an assist that inserts ` ` after typing `->` in `fn foo() ->{`,
- // but it requires precise cursor positioning to work, and one can't
- // position the cursor with on_type formatting. So, let's just toggle this
- // feature off here, hoping that we'll enable it one day, 😿.
- 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, chars_to_exclude)?;
+ let edit = snap.analysis.on_char_typed(position, char_typed)?;
let edit = match edit {
Some(it) => it,
None => return Ok(None),
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index e7f5a7f5e7..61ec576dd4 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -47,7 +47,8 @@ use self::lsp::ext as lsp_ext;
#[cfg(test)]
mod integrated_benchmarks;
-use ide::{CompletionItem, CompletionRelevance};
+use hir::Mutability;
+use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
use serde::de::DeserializeOwned;
use tenthash::TentHasher;
@@ -132,8 +133,13 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8;
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());
+ if let Some((ref_mode, text_size)) = &item.ref_match {
+ let prefix = match ref_mode {
+ CompletionItemRefMode::Reference(Mutability::Shared) => "&",
+ CompletionItemRefMode::Reference(Mutability::Mut) => "&mut ",
+ CompletionItemRefMode::Dereference => "*",
+ };
+ hasher.update(prefix);
hasher.update(u32::from(*text_size).to_le_bytes());
}
for (import_path, import_name) in &item.import_to_add {
diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs
index f50cbba7ac..134de92fea 100644
--- a/crates/rust-analyzer/src/lsp/ext.rs
+++ b/crates/rust-analyzer/src/lsp/ext.rs
@@ -108,19 +108,18 @@ impl Request for RebuildProcMacros {
const METHOD: &'static str = "rust-analyzer/rebuildProcMacros";
}
-pub enum SyntaxTree {}
+pub enum ViewSyntaxTree {}
-impl Request for SyntaxTree {
- type Params = SyntaxTreeParams;
+impl Request for ViewSyntaxTree {
+ type Params = ViewSyntaxTreeParams;
type Result = String;
- const METHOD: &'static str = "rust-analyzer/syntaxTree";
+ const METHOD: &'static str = "rust-analyzer/viewSyntaxTree";
}
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
-pub struct SyntaxTreeParams {
+pub struct ViewSyntaxTreeParams {
pub text_document: TextDocumentIdentifier,
- pub range: Option<Range>,
}
pub enum ViewHir {}
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index fe4d02dcb4..a5516e7f9d 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -547,7 +547,18 @@ pub(crate) fn inlay_hint(
file_id: FileId,
mut inlay_hint: InlayHint,
) -> Cancellable<lsp_types::InlayHint> {
- let resolve_range_and_hash = inlay_hint.needs_resolve().map(|range| {
+ let hint_needs_resolve = |hint: &InlayHint| -> Option<TextRange> {
+ hint.resolve_parent.filter(|_| {
+ hint.text_edit.is_some()
+ || hint
+ .label
+ .parts
+ .iter()
+ .any(|part| part.linked_location.is_some() || part.tooltip.is_some())
+ })
+ };
+
+ let resolve_range_and_hash = hint_needs_resolve(&inlay_hint).map(|range| {
(
range,
std::hash::BuildHasher::hash_one(
@@ -568,7 +579,11 @@ pub(crate) fn inlay_hint(
something_to_resolve |= inlay_hint.text_edit.is_some();
None
} else {
- inlay_hint.text_edit.take().map(|it| text_edit_vec(line_index, it))
+ inlay_hint
+ .text_edit
+ .take()
+ .and_then(|it| it.computed())
+ .map(|it| text_edit_vec(line_index, it))
};
let (label, tooltip) = inlay_hint_label(
snap,
@@ -626,7 +641,7 @@ fn inlay_hint_label(
*something_to_resolve |= tooltip.is_some();
None
} else {
- match tooltip {
+ match tooltip.and_then(|it| it.computed()) {
Some(ide::InlayTooltip::String(s)) => {
Some(lsp_types::InlayHintTooltip::String(s))
}
@@ -650,7 +665,7 @@ fn inlay_hint_label(
*something_to_resolve |= part.tooltip.is_some();
None
} else {
- match part.tooltip {
+ match part.tooltip.and_then(|it| it.computed()) {
Some(ide::InlayTooltip::String(s)) => {
Some(lsp_types::InlayHintLabelPartTooltip::String(s))
}
@@ -1993,7 +2008,7 @@ fn bar(_: usize) {}
#[track_caller]
fn check_rendered_snippets_in_source(
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
edit: TextEdit,
snippets: SnippetEdit,
expect: Expect,
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 97657b9265..d6dc8b521f 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -1145,7 +1145,7 @@ impl GlobalState {
.on::<RETRY, lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol)
.on::<NO_RETRY, lsp_ext::Ssr>(handlers::handle_ssr)
.on::<NO_RETRY, lsp_ext::ViewRecursiveMemoryLayout>(handlers::handle_view_recursive_memory_layout)
- .on::<NO_RETRY, lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
+ .on::<NO_RETRY, lsp_ext::ViewSyntaxTree>(handlers::handle_view_syntax_tree)
.on::<NO_RETRY, lsp_ext::ViewHir>(handlers::handle_view_hir)
.on::<NO_RETRY, lsp_ext::ViewMir>(handlers::handle_view_mir)
.on::<NO_RETRY, lsp_ext::InterpretFunction>(handlers::handle_interpret_function)
diff --git a/crates/rust-analyzer/src/test_runner.rs b/crates/rust-analyzer/src/test_runner.rs
index 503b3ee43a..3edfb812cf 100644
--- a/crates/rust-analyzer/src/test_runner.rs
+++ b/crates/rust-analyzer/src/test_runner.rs
@@ -18,7 +18,11 @@ pub(crate) enum TestState {
Started,
Ok,
Ignored,
- Failed { stdout: String },
+ Failed {
+ // the stdout field is not always present depending on cargo test flags
+ #[serde(skip_serializing_if = "String::is_empty", default)]
+ stdout: String,
+ },
}
#[derive(Debug, Deserialize)]
diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs
index 87a948df55..6becc8e41e 100644
--- a/crates/span/src/hygiene.rs
+++ b/crates/span/src/hygiene.rs
@@ -26,7 +26,7 @@ use crate::InternId;
#[cfg(feature = "ra-salsa")]
use ra_salsa::{InternId, InternValue};
-use crate::MacroCallId;
+use crate::{Edition, MacroCallId};
/// Interned [`SyntaxContextData`].
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -59,11 +59,20 @@ impl fmt::Display for SyntaxContextId {
}
impl SyntaxContextId {
+ #[inline]
+ pub fn remove_root_edition(&mut self) {
+ if self.is_root() {
+ *self = Self::root(Edition::Edition2015);
+ }
+ }
+
/// The root context, which is the parent of all other contexts. All [`FileId`]s have this context.
- pub const ROOT: Self = SyntaxContextId(unsafe { InternId::new_unchecked(0) });
+ pub const fn root(edition: Edition) -> Self {
+ SyntaxContextId(unsafe { InternId::new_unchecked(edition as u32) })
+ }
pub fn is_root(self) -> bool {
- self == Self::ROOT
+ self.into_u32() <= Edition::LATEST as u32
}
/// Deconstruct a `SyntaxContextId` into a raw `u32`.
@@ -89,6 +98,7 @@ pub struct SyntaxContextData {
// per crate. Though that is likely not a problem as `MacroCallId`s are already crate calling dependent.
pub outer_expn: Option<MacroCallId>,
pub outer_transparency: Transparency,
+ pub edition: Edition,
pub parent: SyntaxContextId,
/// This context, but with all transparent and semi-transparent expansions filtered away.
pub opaque: SyntaxContextId,
@@ -98,10 +108,10 @@ pub struct SyntaxContextData {
#[cfg(feature = "ra-salsa")]
impl InternValue for SyntaxContextData {
- type Key = (SyntaxContextId, Option<MacroCallId>, Transparency);
+ type Key = (SyntaxContextId, Option<MacroCallId>, Transparency, Edition);
fn into_key(&self) -> Self::Key {
- (self.parent, self.outer_expn, self.outer_transparency)
+ (self.parent, self.outer_expn, self.outer_transparency, self.edition)
}
}
@@ -118,13 +128,14 @@ impl std::fmt::Debug for SyntaxContextData {
}
impl SyntaxContextData {
- pub fn root() -> Self {
+ pub fn root(edition: Edition) -> Self {
SyntaxContextData {
outer_expn: None,
outer_transparency: Transparency::Opaque,
- parent: SyntaxContextId::ROOT,
- opaque: SyntaxContextId::ROOT,
- opaque_and_semitransparent: SyntaxContextId::ROOT,
+ parent: SyntaxContextId::root(edition),
+ opaque: SyntaxContextId::root(edition),
+ opaque_and_semitransparent: SyntaxContextId::root(edition),
+ edition,
}
}
}
diff --git a/crates/span/src/map.rs b/crates/span/src/map.rs
index 66bbce1859..dc35de67fd 100644
--- a/crates/span/src/map.rs
+++ b/crates/span/src/map.rs
@@ -208,7 +208,7 @@ impl RealSpanMap {
Span {
range: range - offset,
anchor: SpanAnchor { file_id: self.file_id, ast_id },
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(self.file_id.edition()),
}
}
}
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml
index bf0d6df9ad..1ebb48c577 100644
--- a/crates/stdx/Cargo.toml
+++ b/crates/stdx/Cargo.toml
@@ -23,7 +23,7 @@ itertools.workspace = true
[target.'cfg(windows)'.dependencies]
miow = "0.6.0"
-windows-sys = { version = "0.52", features = ["Win32_Foundation"] }
+windows-sys = { version = "0.59", features = ["Win32_Foundation"] }
[features]
# Uncomment to enable for the whole crate graph
diff --git a/crates/syntax-bridge/src/lib.rs b/crates/syntax-bridge/src/lib.rs
index ed8b1908d6..19801c49e4 100644
--- a/crates/syntax-bridge/src/lib.rs
+++ b/crates/syntax-bridge/src/lib.rs
@@ -1,6 +1,6 @@
//! Conversions between [`SyntaxNode`] and [`tt::TokenTree`].
-use std::fmt;
+use std::{fmt, hash::Hash};
use intern::Symbol;
use rustc_hash::{FxHashMap, FxHashSet};
@@ -58,7 +58,7 @@ pub mod dummy_test_span_utils {
),
ast_id: span::ROOT_ERASED_FILE_AST_ID,
},
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
};
pub struct DummyTestSpanMap;
@@ -74,7 +74,7 @@ pub mod dummy_test_span_utils {
),
ast_id: span::ROOT_ERASED_FILE_AST_ID,
},
- ctx: SyntaxContextId::ROOT,
+ ctx: SyntaxContextId::root(Edition::CURRENT),
}
}
}
@@ -141,15 +141,16 @@ where
pub fn token_tree_to_syntax_node<Ctx>(
tt: &tt::TopSubtree<SpanData<Ctx>>,
entry_point: parser::TopEntryPoint,
- edition: parser::Edition,
+ span_to_edition: &mut dyn FnMut(Ctx) -> Edition,
+ top_edition: Edition,
) -> (Parse<SyntaxNode>, SpanMap<Ctx>)
where
- SpanData<Ctx>: Copy + fmt::Debug,
- Ctx: PartialEq,
+ Ctx: Copy + fmt::Debug + PartialEq + PartialEq + Eq + Hash,
{
let buffer = tt.view().strip_invisible();
- let parser_input = to_parser_input(edition, buffer);
- let parser_output = entry_point.parse(&parser_input, edition);
+ let parser_input = to_parser_input(buffer, span_to_edition);
+ // It matters what edition we parse with even when we escape all identifiers correctly.
+ let parser_output = entry_point.parse(&parser_input, top_edition);
let mut tree_sink = TtTreeSink::new(buffer.cursor());
for event in parser_output.iter() {
match event {
diff --git a/crates/syntax-bridge/src/to_parser_input.rs b/crates/syntax-bridge/src/to_parser_input.rs
index 1bbb05f550..0dcb2be316 100644
--- a/crates/syntax-bridge/src/to_parser_input.rs
+++ b/crates/syntax-bridge/src/to_parser_input.rs
@@ -2,17 +2,20 @@
//! format that works for our parser.
use std::fmt;
+use std::hash::Hash;
-use span::Edition;
+use rustc_hash::FxHashMap;
+use span::{Edition, SpanData};
use syntax::{SyntaxKind, SyntaxKind::*, T};
-pub fn to_parser_input<S: Copy + fmt::Debug>(
- edition: Edition,
- buffer: tt::TokenTreesView<'_, S>,
+pub fn to_parser_input<Ctx: Copy + fmt::Debug + PartialEq + Eq + Hash>(
+ buffer: tt::TokenTreesView<'_, SpanData<Ctx>>,
+ span_to_edition: &mut dyn FnMut(Ctx) -> Edition,
) -> parser::Input {
let mut res = parser::Input::default();
let mut current = buffer.cursor();
+ let mut syntax_context_to_edition_cache = FxHashMap::default();
while !current.eof() {
let tt = current.token_tree();
@@ -57,20 +60,25 @@ pub fn to_parser_input<S: Copy + fmt::Debug>(
res.was_joint();
}
}
- tt::Leaf::Ident(ident) => match ident.sym.as_str() {
- "_" => res.push(T![_]),
- i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
- _ if ident.is_raw.yes() => res.push(IDENT),
- text => match SyntaxKind::from_keyword(text, edition) {
- Some(kind) => res.push(kind),
- None => {
- let contextual_keyword =
- SyntaxKind::from_contextual_keyword(text, edition)
- .unwrap_or(SyntaxKind::IDENT);
- res.push_ident(contextual_keyword);
- }
- },
- },
+ tt::Leaf::Ident(ident) => {
+ let edition = *syntax_context_to_edition_cache
+ .entry(ident.span.ctx)
+ .or_insert_with(|| span_to_edition(ident.span.ctx));
+ match ident.sym.as_str() {
+ "_" => res.push(T![_]),
+ i if i.starts_with('\'') => res.push(LIFETIME_IDENT),
+ _ if ident.is_raw.yes() => res.push(IDENT),
+ text => match SyntaxKind::from_keyword(text, edition) {
+ Some(kind) => res.push(kind),
+ None => {
+ let contextual_keyword =
+ SyntaxKind::from_contextual_keyword(text, edition)
+ .unwrap_or(SyntaxKind::IDENT);
+ res.push_ident(contextual_keyword);
+ }
+ },
+ }
+ }
tt::Leaf::Punct(punct) => {
let kind = SyntaxKind::from_char(punct.char)
.unwrap_or_else(|| panic!("{punct:#?} is not a valid punct"));
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index de40d638be..579f3ba8b4 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -153,8 +153,8 @@ impl<N: AstNode + Clone> AstNodeEdit for N {}
#[test]
fn test_increase_indent() {
let arm_list = {
- let arm = make::match_arm(iter::once(make::wildcard_pat().into()), None, make::expr_unit());
- make::match_arm_list(vec![arm.clone(), arm])
+ let arm = make::match_arm(make::wildcard_pat().into(), None, make::ext::expr_unit());
+ make::match_arm_list([arm.clone(), arm])
};
assert_eq!(
arm_list.syntax().to_string(),
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs
index 9466755576..93faeb40c3 100644
--- a/crates/syntax/src/ast/expr_ext.rs
+++ b/crates/syntax/src/ast/expr_ext.rs
@@ -10,7 +10,7 @@ use crate::{
FormatArgsArg, FormatArgsExpr, MacroDef, Static, TokenTree,
},
AstToken,
- SyntaxKind::*,
+ SyntaxKind::{self, *},
SyntaxNode, SyntaxToken, T,
};
@@ -50,6 +50,27 @@ impl From<ast::IfExpr> for ElseBranch {
}
}
+impl AstNode for ElseBranch {
+ fn can_cast(kind: SyntaxKind) -> bool {
+ ast::BlockExpr::can_cast(kind) || ast::IfExpr::can_cast(kind)
+ }
+
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if let Some(block_expr) = ast::BlockExpr::cast(syntax.clone()) {
+ Some(Self::Block(block_expr))
+ } else {
+ ast::IfExpr::cast(syntax).map(Self::IfExpr)
+ }
+ }
+
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ ElseBranch::Block(block_expr) => block_expr.syntax(),
+ ElseBranch::IfExpr(if_expr) => if_expr.syntax(),
+ }
+ }
+}
+
impl ast::IfExpr {
pub fn condition(&self) -> Option<ast::Expr> {
// If the condition is a BlockExpr, check if the then body is missing.
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 282cbc4b3a..dca231604f 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -63,6 +63,9 @@ pub mod ext {
Some(expr)
}
+ pub fn expr_unit() -> ast::Expr {
+ expr_tuple([]).into()
+ }
pub fn expr_unreachable() -> ast::Expr {
expr_from_text("unreachable!()")
}
@@ -546,10 +549,6 @@ pub fn hacky_block_expr(
ast_from_text(&format!("fn f() {buf}"))
}
-pub fn expr_unit() -> ast::Expr {
- expr_from_text("()")
-}
-
pub fn expr_literal(text: &str) -> ast::Literal {
assert_eq!(text.trim(), text);
ast_from_text(&format!("fn f() {{ let _ = {text}; }}"))
@@ -559,8 +558,8 @@ pub fn expr_const_value(text: &str) -> ast::ConstArg {
ast_from_text(&format!("trait Foo<const N: usize = {text}> {{}}"))
}
-pub fn expr_empty_block() -> ast::Expr {
- expr_from_text("{}")
+pub fn expr_empty_block() -> ast::BlockExpr {
+ ast_from_text("const C: () = {};")
}
pub fn expr_path(path: ast::Path) -> ast::Expr {
expr_from_text(&path.to_string())
@@ -600,14 +599,14 @@ pub fn expr_try(expr: ast::Expr) -> ast::Expr {
pub fn expr_await(expr: ast::Expr) -> ast::Expr {
expr_from_text(&format!("{expr}.await"))
}
-pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr {
+pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr {
expr_from_text(&format!("match {expr} {match_arm_list}"))
}
pub fn expr_if(
condition: ast::Expr,
then_branch: ast::BlockExpr,
else_branch: Option<ast::ElseBranch>,
-) -> ast::Expr {
+) -> ast::IfExpr {
let else_branch = match else_branch {
Some(ast::ElseBranch::Block(block)) => format!("else {block}"),
Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {if_expr}"),
@@ -623,7 +622,7 @@ pub fn expr_loop(block: ast::BlockExpr) -> ast::Expr {
expr_from_text(&format!("loop {block}"))
}
-pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
+pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr {
let token = token(op);
expr_from_text(&format!("{token}{expr}"))
}
@@ -656,14 +655,14 @@ pub fn expr_field(receiver: ast::Expr, field: &str) -> ast::Expr {
pub fn expr_paren(expr: ast::Expr) -> ast::Expr {
expr_from_text(&format!("({expr})"))
}
-pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::Expr {
+pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::TupleExpr {
let expr = elements.into_iter().format(", ");
expr_from_text(&format!("({expr})"))
}
pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
expr_from_text(&format!("{lhs} = {rhs}"))
}
-fn expr_from_text(text: &str) -> ast::Expr {
+fn expr_from_text<E: Into<ast::Expr> + AstNode>(text: &str) -> E {
ast_from_text(&format!("const C: () = {text};"))
}
pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
@@ -788,15 +787,21 @@ pub fn path_pat(path: ast::Path) -> ast::Pat {
}
}
-pub fn match_arm(
- pats: impl IntoIterator<Item = ast::Pat>,
- guard: Option<ast::Expr>,
- expr: ast::Expr,
-) -> ast::MatchArm {
- let pats_str = pats.into_iter().join(" | ");
+/// Returns a `Pat` if the path has just one segment, an `OrPat` otherwise.
+pub fn or_pat(pats: impl IntoIterator<Item = ast::Pat>, leading_pipe: bool) -> ast::Pat {
+ let leading_pipe = if leading_pipe { "| " } else { "" };
+ let pats = pats.into_iter().join(" | ");
+
+ return from_text(&format!("{leading_pipe}{pats}"));
+ fn from_text(text: &str) -> ast::Pat {
+ ast_from_text(&format!("fn f({text}: ())"))
+ }
+}
+
+pub fn match_arm(pat: ast::Pat, guard: Option<ast::MatchGuard>, expr: ast::Expr) -> ast::MatchArm {
return match guard {
- Some(guard) => from_text(&format!("{pats_str} if {guard} => {expr}")),
- None => from_text(&format!("{pats_str} => {expr}")),
+ Some(guard) => from_text(&format!("{pat} {guard} => {expr}")),
+ None => from_text(&format!("{pat} => {expr}")),
};
fn from_text(text: &str) -> ast::MatchArm {
@@ -817,6 +822,14 @@ pub fn match_arm_with_guard(
}
}
+pub fn match_guard(condition: ast::Expr) -> ast::MatchGuard {
+ return from_text(&format!("if {condition}"));
+
+ fn from_text(text: &str) -> ast::MatchGuard {
+ ast_from_text(&format!("fn f() {{ match () {{() {text} => () }}"))
+ }
+}
+
pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| {
let needs_comma = arm.expr().is_none_or(|it| !it.is_block_like());
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 81c7e15bcb..56f94b965e 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -185,6 +185,14 @@ impl ast::Attr {
Some((self.simple_name()?, tt))
}
+ pub fn as_simple_path(&self) -> Option<ast::Path> {
+ let meta = self.meta()?;
+ if meta.eq_token().is_some() || meta.token_tree().is_some() {
+ return None;
+ }
+ self.path()
+ }
+
pub fn simple_name(&self) -> Option<SmolStr> {
let path = self.meta()?.path()?;
match (path.segment(), path.qualifier()) {
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs
index bea6bfeafc..572622db54 100644
--- a/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -1,6 +1,9 @@
//! Wrappers over [`make`] constructors
use crate::{
- ast::{self, make, HasGenericParams, HasName, HasTypeBounds, HasVisibility},
+ ast::{
+ self, make, HasArgList, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds,
+ HasVisibility,
+ },
syntax_editor::SyntaxMappingBuilder,
AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken,
};
@@ -12,6 +15,14 @@ impl SyntaxFactory {
make::name(name).clone_for_update()
}
+ pub fn name_ref(&self, name: &str) -> ast::NameRef {
+ make::name_ref(name).clone_for_update()
+ }
+
+ pub fn lifetime(&self, text: &str) -> ast::Lifetime {
+ make::lifetime(text).clone_for_update()
+ }
+
pub fn ty(&self, text: &str) -> ast::Type {
make::ty(text).clone_for_update()
}
@@ -24,6 +35,20 @@ impl SyntaxFactory {
ast
}
+ pub fn ty_path(&self, path: ast::Path) -> ast::PathType {
+ let ast::Type::PathType(ast) = make::ty_path(path.clone()).clone_for_update() else {
+ unreachable!()
+ };
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn type_param(
&self,
name: ast::Name,
@@ -46,6 +71,71 @@ impl SyntaxFactory {
ast
}
+ pub fn path_segment(&self, name_ref: ast::NameRef) -> ast::PathSegment {
+ let ast = make::path_segment(name_ref.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn path_segment_generics(
+ &self,
+ name_ref: ast::NameRef,
+ generic_arg_list: ast::GenericArgList,
+ ) -> ast::PathSegment {
+ let ast::Type::PathType(path) = make::ty(&format!("{name_ref}{generic_arg_list}")) else {
+ unreachable!();
+ };
+
+ let ast = path.path().unwrap().segment().unwrap().clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone());
+ builder.map_node(
+ generic_arg_list.syntax().clone(),
+ ast.generic_arg_list().unwrap().syntax().clone(),
+ );
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn path_unqualified(&self, segment: ast::PathSegment) -> ast::Path {
+ let ast = make::path_unqualified(segment.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(segment.syntax().clone(), ast.segment().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn path_from_segments(
+ &self,
+ segments: impl IntoIterator<Item = ast::PathSegment>,
+ is_abs: bool,
+ ) -> ast::Path {
+ let (segments, input) = iterator_input(segments);
+ let ast = make::path_from_segments(segments, is_abs).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.segments().map(|it| it.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();
@@ -58,6 +148,32 @@ impl SyntaxFactory {
ast
}
+ pub fn wildcard_pat(&self) -> ast::WildcardPat {
+ make::wildcard_pat().clone_for_update()
+ }
+
+ pub fn literal_pat(&self, text: &str) -> ast::LiteralPat {
+ make::literal_pat(text).clone_for_update()
+ }
+
+ pub fn tuple_struct_pat(
+ &self,
+ path: ast::Path,
+ fields: impl IntoIterator<Item = ast::Pat>,
+ ) -> ast::TupleStructPat {
+ let (fields, input) = iterator_input(fields);
+ let ast = make::tuple_struct_pat(path.clone(), fields).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
+ builder.map_children(input.into_iter(), ast.fields().map(|it| it.syntax().clone()));
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn block_expr(
&self,
statements: impl IntoIterator<Item = ast::Stmt>,
@@ -95,7 +211,20 @@ impl SyntaxFactory {
}
pub fn expr_empty_block(&self) -> ast::BlockExpr {
- ast::BlockExpr { syntax: make::expr_empty_block().syntax().clone_for_update() }
+ make::expr_empty_block().clone_for_update()
+ }
+
+ pub fn expr_tuple(&self, fields: impl IntoIterator<Item = ast::Expr>) -> ast::TupleExpr {
+ let (fields, input) = iterator_input(fields);
+ let ast = make::expr_tuple(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 expr_bin(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::BinExpr {
@@ -115,6 +244,10 @@ impl SyntaxFactory {
ast
}
+ pub fn expr_literal(&self, text: &str) -> ast::Literal {
+ make::expr_literal(text).clone_for_update()
+ }
+
pub fn expr_path(&self, path: ast::Path) -> ast::Expr {
let ast::Expr::PathExpr(ast) = make::expr_path(path.clone()).clone_for_update() else {
unreachable!()
@@ -129,6 +262,49 @@ impl SyntaxFactory {
ast.into()
}
+ pub fn expr_prefix(&self, op: SyntaxKind, expr: ast::Expr) -> ast::PrefixExpr {
+ let ast = make::expr_prefix(op, expr.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn expr_call(&self, expr: ast::Expr, arg_list: ast::ArgList) -> ast::CallExpr {
+ // FIXME: `make::expr_call`` should return a `CallExpr`, not just an `Expr`
+ let ast::Expr::CallExpr(ast) =
+ make::expr_call(expr.clone(), arg_list.clone()).clone_for_update()
+ else {
+ unreachable!()
+ };
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+ builder.map_node(arg_list.syntax().clone(), ast.arg_list().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn arg_list(&self, args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
+ let (args, input) = iterator_input(args);
+ let ast = make::arg_list(args).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.args().map(|it| it.syntax().clone()));
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn expr_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr {
let ast::Expr::RefExpr(ast) = make::expr_ref(expr.clone(), exclusive).clone_for_update()
else {
@@ -160,6 +336,125 @@ impl SyntaxFactory {
ast
}
+ pub fn expr_if(
+ &self,
+ condition: ast::Expr,
+ then_branch: ast::BlockExpr,
+ else_branch: Option<ast::ElseBranch>,
+ ) -> ast::IfExpr {
+ let ast = make::expr_if(condition.clone(), then_branch.clone(), else_branch.clone())
+ .clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(condition.syntax().clone(), ast.condition().unwrap().syntax().clone());
+ builder.map_node(
+ then_branch.syntax().clone(),
+ ast.then_branch().unwrap().syntax().clone(),
+ );
+
+ if let Some(else_branch) = else_branch {
+ builder.map_node(
+ else_branch.syntax().clone(),
+ ast.else_branch().unwrap().syntax().clone(),
+ );
+ }
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn expr_let(&self, pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
+ let ast = make::expr_let(pattern.clone(), expr.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(pattern.syntax().clone(), ast.pat().unwrap().syntax().clone());
+ builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn expr_stmt(&self, expr: ast::Expr) -> ast::ExprStmt {
+ let ast = make::expr_stmt(expr.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn expr_match(&self, expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr {
+ let ast = make::expr_match(expr.clone(), match_arm_list.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+ builder.map_node(
+ match_arm_list.syntax().clone(),
+ ast.match_arm_list().unwrap().syntax().clone(),
+ );
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn match_arm(
+ &self,
+ pat: ast::Pat,
+ guard: Option<ast::MatchGuard>,
+ expr: ast::Expr,
+ ) -> ast::MatchArm {
+ let ast = make::match_arm(pat.clone(), guard.clone(), expr.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone());
+ if let Some(guard) = guard {
+ builder.map_node(guard.syntax().clone(), ast.guard().unwrap().syntax().clone());
+ }
+ builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn match_guard(&self, condition: ast::Expr) -> ast::MatchGuard {
+ let ast = make::match_guard(condition.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(condition.syntax().clone(), ast.condition().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn match_arm_list(
+ &self,
+ match_arms: impl IntoIterator<Item = ast::MatchArm>,
+ ) -> ast::MatchArmList {
+ let (match_arms, input) = iterator_input(match_arms);
+ let ast = make::match_arm_list(match_arms).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.arms().map(|it| it.syntax().clone()));
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn let_stmt(
&self,
pattern: ast::Pat,
@@ -185,6 +480,30 @@ impl SyntaxFactory {
ast
}
+ pub fn type_arg(&self, ty: ast::Type) -> ast::TypeArg {
+ let ast = make::type_arg(ty.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn lifetime_arg(&self, lifetime: ast::Lifetime) -> ast::LifetimeArg {
+ let ast = make::lifetime_arg(lifetime.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(lifetime.syntax().clone(), ast.lifetime().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn item_const(
&self,
visibility: Option<ast::Visibility>,
@@ -252,12 +571,17 @@ impl SyntaxFactory {
ast
}
- pub fn turbofish_generic_arg_list(
+ pub fn generic_arg_list(
&self,
generic_args: impl IntoIterator<Item = ast::GenericArg>,
+ is_turbo: bool,
) -> ast::GenericArgList {
let (generic_args, input) = iterator_input(generic_args);
- let ast = make::turbofish_generic_arg_list(generic_args.clone()).clone_for_update();
+ let ast = if is_turbo {
+ make::turbofish_generic_arg_list(generic_args).clone_for_update()
+ } else {
+ make::generic_arg_list(generic_args).clone_for_update()
+ };
if let Some(mut mapping) = self.mappings() {
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
@@ -503,11 +827,41 @@ impl SyntaxFactory {
make::token(kind)
}
- pub fn whitespace(&self, text: &str) -> ast::SyntaxToken {
+ pub fn whitespace(&self, text: &str) -> SyntaxToken {
make::tokens::whitespace(text)
}
}
+// `ext` constructors
+impl SyntaxFactory {
+ pub fn ident_path(&self, ident: &str) -> ast::Path {
+ self.path_unqualified(self.path_segment(self.name_ref(ident)))
+ }
+
+ pub fn expr_unit(&self) -> ast::Expr {
+ self.expr_tuple([]).into()
+ }
+
+ pub fn ty_option(&self, t: ast::Type) -> ast::PathType {
+ let generic_arg_list = self.generic_arg_list([self.type_arg(t).into()], false);
+ let path = self.path_unqualified(
+ self.path_segment_generics(self.name_ref("Option"), generic_arg_list),
+ );
+
+ self.ty_path(path)
+ }
+
+ pub fn ty_result(&self, t: ast::Type, e: ast::Type) -> ast::PathType {
+ let generic_arg_list =
+ self.generic_arg_list([self.type_arg(t).into(), self.type_arg(e).into()], false);
+ let path = self.path_unqualified(
+ self.path_segment_generics(self.name_ref("Result"), generic_arg_list),
+ );
+
+ self.ty_path(path)
+ }
+}
+
// We need to collect `input` here instead of taking `impl IntoIterator + Clone`,
// because if we took `impl IntoIterator + Clone`, that could be something like an
// `Iterator::map` with a closure that also makes use of a `SyntaxFactory` constructor.
diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs
index 992a847663..b82181ae13 100644
--- a/crates/syntax/src/syntax_editor.rs
+++ b/crates/syntax/src/syntax_editor.rs
@@ -335,7 +335,7 @@ mod tests {
#[test]
fn basic_usage() {
let root = make::match_arm(
- [make::wildcard_pat().into()],
+ make::wildcard_pat().into(),
None,
make::expr_tuple([
make::expr_bin_op(
@@ -344,7 +344,8 @@ mod tests {
make::expr_literal("2").into(),
),
make::expr_literal("true").into(),
- ]),
+ ])
+ .into(),
);
let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap();
@@ -549,7 +550,7 @@ mod tests {
None,
None,
make::param_list(None, []),
- make::block_expr([], Some(make::expr_unit())),
+ make::block_expr([], Some(make::ext::expr_unit())),
Some(make::ret_type(make::ty_unit())),
false,
false,
diff --git a/crates/test-fixture/Cargo.toml b/crates/test-fixture/Cargo.toml
index c860e7b118..95f4cb9d67 100644
--- a/crates/test-fixture/Cargo.toml
+++ b/crates/test-fixture/Cargo.toml
@@ -2,6 +2,8 @@
name = "test-fixture"
version = "0.0.0"
rust-version.workspace = true
+description = "Test fixtures for rust-analyzer."
+
edition.workspace = true
license.workspace = true
authors.workspace = true
diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs
index 0e72d79687..866379d940 100644
--- a/crates/test-fixture/src/lib.rs
+++ b/crates/test-fixture/src/lib.rs
@@ -30,7 +30,9 @@ pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0);
pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
#[track_caller]
- fn with_single_file(ra_fixture: &str) -> (Self, EditionedFileId) {
+ fn with_single_file(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ ) -> (Self, EditionedFileId) {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
fixture.change.apply(&mut db);
@@ -39,7 +41,9 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
}
#[track_caller]
- fn with_many_files(ra_fixture: &str) -> (Self, Vec<EditionedFileId>) {
+ fn with_many_files(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ ) -> (Self, Vec<EditionedFileId>) {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
fixture.change.apply(&mut db);
@@ -48,7 +52,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
}
#[track_caller]
- fn with_files(ra_fixture: &str) -> Self {
+ fn with_files(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
fixture.change.apply(&mut db);
@@ -58,7 +62,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
#[track_caller]
fn with_files_extra_proc_macros(
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
proc_macros: Vec<(String, ProcMacro)>,
) -> Self {
let fixture = ChangeFixture::parse_with_proc_macros(ra_fixture, proc_macros);
@@ -69,21 +73,23 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static {
}
#[track_caller]
- fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
+ fn with_position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FilePosition) {
let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
let offset = range_or_offset.expect_offset();
(db, FilePosition { file_id, offset })
}
#[track_caller]
- fn with_range(ra_fixture: &str) -> (Self, FileRange) {
+ fn with_range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FileRange) {
let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
let range = range_or_offset.expect_range();
(db, FileRange { file_id, range })
}
#[track_caller]
- fn with_range_or_offset(ra_fixture: &str) -> (Self, EditionedFileId, RangeOrOffset) {
+ fn with_range_or_offset(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ ) -> (Self, EditionedFileId, RangeOrOffset) {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
fixture.change.apply(&mut db);
@@ -116,12 +122,12 @@ pub struct ChangeFixture {
const SOURCE_ROOT_PREFIX: &str = "/";
impl ChangeFixture {
- pub fn parse(ra_fixture: &str) -> ChangeFixture {
+ pub fn parse(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> ChangeFixture {
Self::parse_with_proc_macros(ra_fixture, Vec::new())
}
pub fn parse_with_proc_macros(
- ra_fixture: &str,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
mut proc_macro_defs: Vec<(String, ProcMacro)>,
) -> ChangeFixture {
let FixtureWithProjectMeta {
@@ -376,8 +382,8 @@ impl ChangeFixture {
}
}
-fn default_test_proc_macros() -> [(String, ProcMacro); 8] {
- [
+fn default_test_proc_macros() -> Box<[(String, ProcMacro)]> {
+ Box::new([
(
r#"
#[proc_macro_attribute]
@@ -498,7 +504,22 @@ pub fn issue_17479(input: TokenStream) -> TokenStream {
disabled: false,
},
),
- ]
+ (
+ r#"
+#[proc_macro_attribute]
+pub fn issue_18898(_attr: TokenStream, input: TokenStream) -> TokenStream {
+ input
+}
+"#
+ .into(),
+ ProcMacro {
+ name: Symbol::intern("issue_18898"),
+ kind: ProcMacroKind::Bang,
+ expander: sync::Arc::new(Issue18898ProcMacroExpander),
+ disabled: false,
+ },
+ ),
+ ])
}
fn filter_test_proc_macros(
@@ -801,3 +822,54 @@ impl ProcMacroExpander for Issue17479ProcMacroExpander {
})
}
}
+
+// Reads ident type within string quotes, for issue #17479.
+#[derive(Debug)]
+struct Issue18898ProcMacroExpander;
+impl ProcMacroExpander for Issue18898ProcMacroExpander {
+ fn expand(
+ &self,
+ subtree: &TopSubtree,
+ _: Option<&TopSubtree>,
+ _: &Env,
+ def_site: Span,
+ _: Span,
+ _: Span,
+ _: Option<String>,
+ ) -> Result<TopSubtree, ProcMacroExpansionError> {
+ let span = subtree
+ .token_trees()
+ .flat_tokens()
+ .last()
+ .ok_or_else(|| ProcMacroExpansionError::Panic("malformed input".to_owned()))?
+ .first_span();
+ let overly_long_subtree = quote! {span =>
+ {
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ let a = 5;
+ }
+ };
+ Ok(quote! { def_site =>
+ fn foo() {
+ #overly_long_subtree
+ }
+ })
+ }
+}
diff --git a/crates/test-utils/src/fixture.rs b/crates/test-utils/src/fixture.rs
index 54c9db7aac..7fe26d53bf 100644
--- a/crates/test-utils/src/fixture.rs
+++ b/crates/test-utils/src/fixture.rs
@@ -168,7 +168,7 @@ impl FixtureWithProjectMeta {
/// That will set toolchain to nightly and include predefined proc macros and a subset of
/// `libcore` into the fixture, see `minicore.rs` for what's available. Note that toolchain
/// defaults to stable.
- pub fn parse(ra_fixture: &str) -> Self {
+ pub fn parse(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self {
let fixture = trim_indent(ra_fixture);
let mut fixture = fixture.as_str();
let mut toolchain = None;
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 4a2346193b..fd06736a25 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -32,7 +32,7 @@
//! error: fmt
//! fmt: option, result, transmute, coerce_unsized, copy, clone, derive
//! fn: tuple
-//! from: sized
+//! from: sized, result
//! future: pin
//! coroutine: pin
//! dispatch_from_dyn: unsize, pin
@@ -332,6 +332,25 @@ pub mod convert {
t
}
}
+
+ pub trait TryFrom<T>: Sized {
+ type Error;
+ fn try_from(value: T) -> Result<Self, Self::Error>;
+ }
+ pub trait TryInto<T>: Sized {
+ type Error;
+ fn try_into(self) -> Result<T, Self::Error>;
+ }
+
+ impl<T, U> TryInto<U> for T
+ where
+ U: TryFrom<T>,
+ {
+ type Error = U::Error;
+ fn try_into(self) -> Result<U, U::Error> {
+ U::try_from(self)
+ }
+ }
// endregion:from
// region:as_ref
@@ -1510,7 +1529,7 @@ pub mod iter {
impl<T, const N: usize> IntoIterator for [T; N] {
type Item = T;
type IntoIter = IntoIter<T, N>;
- fn into_iter(self) -> I {
+ fn into_iter(self) -> Self::IntoIter {
IntoIter { data: self, range: IndexRange { start: 0, end: loop {} } }
}
}
@@ -1520,6 +1539,29 @@ pub mod iter {
loop {}
}
}
+ pub struct Iter<'a, T> {
+ slice: &'a [T],
+ }
+ impl<'a, T> IntoIterator for &'a [T; N] {
+ type Item = &'a T;
+ type IntoIter = Iter<'a, T>;
+ fn into_iter(self) -> Self::IntoIter {
+ loop {}
+ }
+ }
+ impl<'a, T> IntoIterator for &'a [T] {
+ type Item = &'a T;
+ type IntoIter = Iter<'a, T>;
+ fn into_iter(self) -> Self::IntoIter {
+ loop {}
+ }
+ }
+ impl<'a, T> Iterator for Iter<'a, T> {
+ type Item = &'a T;
+ fn next(&mut self) -> Option<T> {
+ loop {}
+ }
+ }
}
pub use self::collect::IntoIterator;
}
@@ -1532,6 +1574,15 @@ pub mod str {
pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str {
""
}
+ pub trait FromStr: Sized {
+ type Err;
+ fn from_str(s: &str) -> Result<Self, Self::Err>;
+ }
+ impl str {
+ pub fn parse<F: FromStr>(&self) -> Result<F, F::Err> {
+ FromStr::from_str(self)
+ }
+ }
}
// endregion:str
@@ -1791,7 +1842,7 @@ pub mod prelude {
cmp::{Eq, PartialEq}, // :eq
cmp::{Ord, PartialOrd}, // :ord
convert::AsRef, // :as_ref
- convert::{From, Into}, // :from
+ convert::{From, Into, TryFrom, TryInto}, // :from
default::Default, // :default
iter::{IntoIterator, Iterator}, // :iterator
macros::builtin::{derive, derive_const}, // :derive
@@ -1806,6 +1857,7 @@ pub mod prelude {
option::Option::{self, None, Some}, // :option
panic, // :panic
result::Result::{self, Err, Ok}, // :result
+ str::FromStr, // :str
};
}
diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml
index 09296dc6dd..bc54d7168f 100644
--- a/crates/vfs-notify/Cargo.toml
+++ b/crates/vfs-notify/Cargo.toml
@@ -16,7 +16,7 @@ doctest = false
tracing.workspace = true
walkdir = "2.3.2"
crossbeam-channel.workspace = true
-notify = "6.1.1"
+notify = "8.0.0"
rayon = "1.10.0"
stdx.workspace = true
diff --git a/docs/dev/README.md b/docs/dev/README.md
index cd0f49174c..3ba492e095 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -154,19 +154,21 @@ There are also several VS Code commands which might be of interest:
* `rust-analyzer: Status` shows some memory-usage statistics.
-* `rust-analyzer: Syntax Tree` shows syntax tree of the current file/selection.
-
* `rust-analyzer: View Hir` shows the HIR expressions within the function containing the cursor.
- You can hover over syntax nodes in the opened text file to see the appropriate
- rust code that it refers to and the rust editor will also highlight the proper
- text range.
+* If `rust-analyzer.showSyntaxTree` is enabled in settings, `Rust Syntax Tree: Focus on Rust Syntax Tree View` shows the syntax tree of the current file.
+
+ You can click on nodes in the rust editor to go to the corresponding syntax node.
+
+ You can click on `Reveal Syntax Element` next to a syntax node to go to the corresponding rust code and highlight the proper text range.
If you trigger Go to Definition in the inspected Rust source file,
- the syntax tree read-only editor should scroll to and select the
+ the syntax tree view should scroll to and select the
appropriate syntax node token.
- ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png)
+ You can click on `Copy` next to a syntax node to copy a text representation of the node.
+
+ ![demo](https://github.com/user-attachments/assets/2d20ae87-0abf-495f-bee8-54aa2494a00d)
## Profiling
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 21ac3a5a26..a632fc6f5f 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
<!---
-lsp/ext.rs hash: 6dd762ae19630ec0
+lsp/ext.rs hash: 2d8604825c458288
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:
@@ -710,6 +710,23 @@ interface SyntaxTreeParams {
Returns textual representation of a parse tree for the file/selected region.
Primarily for debugging, but very useful for all people working on rust-analyzer itself.
+## View Syntax Tree
+
+**Method:** `rust-analyzer/viewSyntaxTree`
+
+**Request:**
+
+```typescript
+interface ViewSyntaxTreeParams {
+ textDocument: TextDocumentIdentifier,
+}
+```
+
+**Response:** `string`
+
+Returns json representation of the file's syntax tree.
+Used to create a treeView for debugging and working on rust-analyzer itself.
+
## View Hir
**Method:** `rust-analyzer/viewHir`
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 5b86766aa8..bd091db58d 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -94,10 +94,10 @@ avoid checking unnecessary things.
--
Default:
----
-{
- "miri": null,
- "debug_assertions": null
-}
+[
+ "debug_assertions",
+ "miri"
+]
----
List of cfg options to enable with the given values.
@@ -716,6 +716,11 @@ Whether to show generic type parameter name inlay hints.
--
Whether to show implicit drop hints.
--
+[[rust-analyzer.inlayHints.implicitSizedBoundHints.enable]]rust-analyzer.inlayHints.implicitSizedBoundHints.enable (default: `false`)::
++
+--
+Whether to show inlay hints for the implied type parameter `Sized` bound.
+--
[[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`)::
+
--
@@ -1046,10 +1051,25 @@ Show full signature of the callable. Only shows parameters if disabled.
--
Show documentation.
--
-[[rust-analyzer.typing.excludeChars]]rust-analyzer.typing.excludeChars (default: `"|<"`)::
+[[rust-analyzer.typing.triggerChars]]rust-analyzer.typing.triggerChars (default: `"=."`)::
++
+--
+Specify the characters allowed to invoke special on typing triggers.
+- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression
+- typing `=` between two expressions adds `;` when in statement position
+- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position
+- 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
+- typing `<` in a path or type position inserts a closing `>` after the path or type.
+--
+[[rust-analyzer.vfs.extraIncludes]]rust-analyzer.vfs.extraIncludes (default: `[]`)::
+
--
-Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`.
+Additional paths to include in the VFS. Generally for code that is
+generated or otherwise managed by a build system outside of Cargo,
+though Cargo might be the eventual consumer.
--
[[rust-analyzer.workspace.discoverConfig]]rust-analyzer.workspace.discoverConfig (default: `null`)::
+
diff --git a/editors/code/package.json b/editors/code/package.json
index 80246bf3fe..8b066377f2 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -109,11 +109,6 @@
],
"commands": [
{
- "command": "rust-analyzer.syntaxTree",
- "title": "Show Syntax Tree",
- "category": "rust-analyzer (debug command)"
- },
- {
"command": "rust-analyzer.viewHir",
"title": "View Hir",
"category": "rust-analyzer (debug command)"
@@ -289,6 +284,30 @@
"category": "rust-analyzer"
},
{
+ "command": "rust-analyzer.syntaxTreeReveal",
+ "title": "Reveal Syntax Element",
+ "icon": "$(search)",
+ "category": "rust-analyzer (syntax tree)"
+ },
+ {
+ "command": "rust-analyzer.syntaxTreeCopy",
+ "title": "Copy",
+ "icon": "$(copy)",
+ "category": "rust-analyzer (syntax tree)"
+ },
+ {
+ "command": "rust-analyzer.syntaxTreeHideWhitespace",
+ "title": "Hide Whitespace",
+ "icon": "$(filter)",
+ "category": "rust-analyzer (syntax tree)"
+ },
+ {
+ "command": "rust-analyzer.syntaxTreeShowWhitespace",
+ "title": "Show Whitespace",
+ "icon": "$(filter-filled)",
+ "category": "rust-analyzer (syntax tree)"
+ },
+ {
"command": "rust-analyzer.viewMemoryLayout",
"title": "View Memory Layout",
"category": "rust-analyzer"
@@ -345,6 +364,11 @@
"default": true,
"type": "boolean"
},
+ "rust-analyzer.showSyntaxTree": {
+ "markdownDescription": "Whether to show the syntax tree view.",
+ "default": false,
+ "type": "boolean"
+ },
"rust-analyzer.testExplorer": {
"markdownDescription": "Whether to show the test explorer.",
"default": false,
@@ -791,11 +815,14 @@
"properties": {
"rust-analyzer.cargo.cfgs": {
"markdownDescription": "List of cfg options to enable with the given values.",
- "default": {
- "miri": null,
- "debug_assertions": null
- },
- "type": "object"
+ "default": [
+ "debug_assertions",
+ "miri"
+ ],
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
}
}
},
@@ -2081,6 +2108,16 @@
{
"title": "inlayHints",
"properties": {
+ "rust-analyzer.inlayHints.implicitSizedBoundHints.enable": {
+ "markdownDescription": "Whether to show inlay hints for the implied type parameter `Sized` bound.",
+ "default": false,
+ "type": "boolean"
+ }
+ }
+ },
+ {
+ "title": "inlayHints",
+ "properties": {
"rust-analyzer.inlayHints.lifetimeElisionHints.enable": {
"markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.",
"default": "never",
@@ -2708,9 +2745,9 @@
{
"title": "typing",
"properties": {
- "rust-analyzer.typing.excludeChars": {
- "markdownDescription": "Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`.",
- "default": "|<",
+ "rust-analyzer.typing.triggerChars": {
+ "markdownDescription": "Specify the characters allowed to invoke special on typing triggers.\n- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression\n- typing `=` between two expressions adds `;` when in statement position\n- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position\n- typing `.` in a chain method call auto-indents\n- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression\n- typing `{` in a use item adds a closing `}` in the right place\n- typing `>` to complete a return type `->` will insert a whitespace after it\n- typing `<` in a path or type position inserts a closing `>` after the path or type.",
+ "default": "=.",
"type": [
"null",
"string"
@@ -2719,6 +2756,19 @@
}
},
{
+ "title": "vfs",
+ "properties": {
+ "rust-analyzer.vfs.extraIncludes": {
+ "markdownDescription": "Additional paths to include in the VFS. Generally for code that is\ngenerated or otherwise managed by a build system outside of Cargo,\nthough Cargo might be the eventual consumer.",
+ "default": [],
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ {
"title": "workspace",
"properties": {
"rust-analyzer.workspace.discoverConfig": {
@@ -2928,17 +2978,6 @@
"pattern": "$rustc"
}
],
- "colors": [
- {
- "id": "rust_analyzer.syntaxTreeBorder",
- "description": "Color of the border displayed in the Rust source code for the selected syntax node (see \"Show Syntax Tree\" command)",
- "defaults": {
- "dark": "#ffffff",
- "light": "#b700ff",
- "highContrast": "#b700ff"
- }
- }
- ],
"semanticTokenTypes": [
{
"id": "angle",
@@ -3259,10 +3298,6 @@
"menus": {
"commandPalette": [
{
- "command": "rust-analyzer.syntaxTree",
- "when": "inRustProject"
- },
- {
"command": "rust-analyzer.viewHir",
"when": "inRustProject"
},
@@ -3344,6 +3379,22 @@
},
{
"command": "rust-analyzer.openWalkthrough"
+ },
+ {
+ "command": "rust-analyzer.syntaxTreeReveal",
+ "when": "false"
+ },
+ {
+ "command": "rust-analyzer.syntaxTreeCopy",
+ "when": "false"
+ },
+ {
+ "command": "rust-analyzer.syntaxTreeHideWhitespace",
+ "when": "false"
+ },
+ {
+ "command": "rust-analyzer.syntaxTreeShowWhitespace",
+ "when": "false"
}
],
"editor/context": [
@@ -3357,6 +3408,30 @@
"when": "inRustProject && editorTextFocus && editorLangId == rust",
"group": "navigation@1001"
}
+ ],
+ "view/title": [
+ {
+ "command": "rust-analyzer.syntaxTreeHideWhitespace",
+ "group": "navigation",
+ "when": "view == rustSyntaxTree && !rustSyntaxTree.hideWhitespace"
+ },
+ {
+ "command": "rust-analyzer.syntaxTreeShowWhitespace",
+ "group": "navigation",
+ "when": "view == rustSyntaxTree && rustSyntaxTree.hideWhitespace"
+ }
+ ],
+ "view/item/context": [
+ {
+ "command": "rust-analyzer.syntaxTreeCopy",
+ "group": "inline",
+ "when": "view == rustSyntaxTree"
+ },
+ {
+ "command": "rust-analyzer.syntaxTreeReveal",
+ "group": "inline",
+ "when": "view == rustSyntaxTree"
+ }
]
},
"views": {
@@ -3366,6 +3441,22 @@
"name": "Rust Dependencies",
"when": "inRustProject && config.rust-analyzer.showDependenciesExplorer"
}
+ ],
+ "rustSyntaxTreeContainer": [
+ {
+ "id": "rustSyntaxTree",
+ "name": "Rust Syntax Tree",
+ "when": "inRustProject && config.rust-analyzer.showSyntaxTree"
+ }
+ ]
+ },
+ "viewsContainers": {
+ "activitybar": [
+ {
+ "id": "rustSyntaxTreeContainer",
+ "title": "Rust Syntax Tree",
+ "icon": "$(list-tree)"
+ }
]
},
"jsonValidation": [
diff --git a/editors/code/src/ast_inspector.ts b/editors/code/src/ast_inspector.ts
deleted file mode 100644
index 35b705c477..0000000000
--- a/editors/code/src/ast_inspector.ts
+++ /dev/null
@@ -1,216 +0,0 @@
-import * as vscode from "vscode";
-
-import type { Ctx, Disposable } from "./ctx";
-import { type RustEditor, isRustEditor, unwrapUndefinable } from "./util";
-
-// FIXME: consider implementing this via the Tree View API?
-// https://code.visualstudio.com/api/extension-guides/tree-view
-export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable {
- private readonly astDecorationType = vscode.window.createTextEditorDecorationType({
- borderColor: new vscode.ThemeColor("rust_analyzer.syntaxTreeBorder"),
- borderStyle: "solid",
- borderWidth: "2px",
- });
- private rustEditor: undefined | RustEditor;
-
- // Lazy rust token range -> syntax tree file range.
- private readonly rust2Ast = new Lazy(() => {
- const astEditor = this.findAstTextEditor();
- if (!this.rustEditor || !astEditor) return undefined;
-
- const buf: [vscode.Range, vscode.Range][] = [];
- for (let i = 0; i < astEditor.document.lineCount; ++i) {
- const astLine = astEditor.document.lineAt(i);
-
- // Heuristically look for nodes with quoted text (which are token nodes)
- const isTokenNode = astLine.text.lastIndexOf('"') >= 0;
- if (!isTokenNode) continue;
-
- const rustRange = this.parseRustTextRange(this.rustEditor.document, astLine.text);
- if (!rustRange) continue;
-
- buf.push([rustRange, this.findAstNodeRange(astLine)]);
- }
- return buf;
- });
-
- constructor(ctx: Ctx) {
- ctx.pushExtCleanup(
- vscode.languages.registerHoverProvider({ scheme: "rust-analyzer" }, this),
- );
- ctx.pushExtCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this));
- vscode.workspace.onDidCloseTextDocument(
- this.onDidCloseTextDocument,
- this,
- ctx.subscriptions,
- );
- vscode.workspace.onDidChangeTextDocument(
- this.onDidChangeTextDocument,
- this,
- ctx.subscriptions,
- );
- vscode.window.onDidChangeVisibleTextEditors(
- this.onDidChangeVisibleTextEditors,
- this,
- ctx.subscriptions,
- );
- }
- dispose() {
- this.setRustEditor(undefined);
- }
-
- private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
- if (
- this.rustEditor &&
- event.document.uri.toString() === this.rustEditor.document.uri.toString()
- ) {
- this.rust2Ast.reset();
- }
- }
-
- private onDidCloseTextDocument(doc: vscode.TextDocument) {
- if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) {
- this.setRustEditor(undefined);
- }
- }
-
- private onDidChangeVisibleTextEditors(editors: readonly vscode.TextEditor[]) {
- if (!this.findAstTextEditor()) {
- this.setRustEditor(undefined);
- return;
- }
- this.setRustEditor(editors.find(isRustEditor));
- }
-
- private findAstTextEditor(): undefined | vscode.TextEditor {
- return vscode.window.visibleTextEditors.find(
- (it) => it.document.uri.scheme === "rust-analyzer",
- );
- }
-
- private setRustEditor(newRustEditor: undefined | RustEditor) {
- if (this.rustEditor && this.rustEditor !== newRustEditor) {
- this.rustEditor.setDecorations(this.astDecorationType, []);
- this.rust2Ast.reset();
- }
- this.rustEditor = newRustEditor;
- }
-
- // additional positional params are omitted
- provideDefinition(
- doc: vscode.TextDocument,
- pos: vscode.Position,
- ): vscode.ProviderResult<vscode.DefinitionLink[]> {
- if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString()) {
- return;
- }
-
- const astEditor = this.findAstTextEditor();
- if (!astEditor) return;
-
- const rust2AstRanges = this.rust2Ast
- .get()
- ?.find(([rustRange, _]) => rustRange.contains(pos));
- if (!rust2AstRanges) return;
-
- const [rustFileRange, astFileRange] = rust2AstRanges;
-
- astEditor.revealRange(astFileRange);
- astEditor.selection = new vscode.Selection(astFileRange.start, astFileRange.end);
-
- return [
- {
- targetRange: astFileRange,
- targetUri: astEditor.document.uri,
- originSelectionRange: rustFileRange,
- targetSelectionRange: astFileRange,
- },
- ];
- }
-
- // additional positional params are omitted
- provideHover(
- doc: vscode.TextDocument,
- hoverPosition: vscode.Position,
- ): vscode.ProviderResult<vscode.Hover> {
- if (!this.rustEditor) return;
-
- const astFileLine = doc.lineAt(hoverPosition.line);
-
- const rustFileRange = this.parseRustTextRange(this.rustEditor.document, astFileLine.text);
- if (!rustFileRange) return;
-
- this.rustEditor.setDecorations(this.astDecorationType, [rustFileRange]);
- this.rustEditor.revealRange(rustFileRange);
-
- const rustSourceCode = this.rustEditor.document.getText(rustFileRange);
- const astFileRange = this.findAstNodeRange(astFileLine);
-
- return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astFileRange);
- }
-
- private findAstNodeRange(astLine: vscode.TextLine): vscode.Range {
- const lineOffset = astLine.range.start;
- const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex);
- const end = lineOffset.translate(undefined, astLine.text.trimEnd().length);
- return new vscode.Range(begin, end);
- }
-
- private parseRustTextRange(
- doc: vscode.TextDocument,
- astLine: string,
- ): undefined | vscode.Range {
- const parsedRange = /(\d+)\.\.(\d+)/.exec(astLine);
- if (!parsedRange) return;
-
- const [begin, end] = parsedRange.slice(1).map((off) => this.positionAt(doc, +off));
- const actualBegin = unwrapUndefinable(begin);
- const actualEnd = unwrapUndefinable(end);
- return new vscode.Range(actualBegin, actualEnd);
- }
-
- // Memoize the last value, otherwise the CPU is at 100% single core
- // with quadratic lookups when we build rust2Ast cache
- cache?: { doc: vscode.TextDocument; offset: number; line: number };
-
- positionAt(doc: vscode.TextDocument, targetOffset: number): vscode.Position {
- if (doc.eol === vscode.EndOfLine.LF) {
- return doc.positionAt(targetOffset);
- }
-
- // Dirty workaround for crlf line endings
- // We are still in this prehistoric era of carriage returns here...
-
- let line = 0;
- let offset = 0;
-
- const cache = this.cache;
- if (cache?.doc === doc && cache.offset <= targetOffset) {
- ({ line, offset } = cache);
- }
-
- while (true) {
- const lineLenWithLf = doc.lineAt(line).text.length + 1;
- if (offset + lineLenWithLf > targetOffset) {
- this.cache = { doc, offset, line };
- return doc.positionAt(targetOffset + line);
- }
- offset += lineLenWithLf;
- line += 1;
- }
- }
-}
-
-class Lazy<T> {
- val: undefined | T;
-
- constructor(private readonly compute: () => undefined | T) {}
-
- get() {
- return this.val ?? (this.val = this.compute());
- }
-
- reset() {
- this.val = undefined;
- }
-}
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 73e39c900e..b3aa04af7e 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -15,7 +15,6 @@ import {
createTaskFromRunnable,
createCargoArgs,
} from "./run";
-import { AstInspector } from "./ast_inspector";
import {
isRustDocument,
isCargoRunnableArgs,
@@ -31,8 +30,8 @@ import type { LanguageClient } from "vscode-languageclient/node";
import { HOVER_REFERENCE_COMMAND } from "./client";
import type { DependencyId } from "./dependencies_provider";
import { log } from "./util";
+import type { SyntaxElement } from "./syntax_tree_provider";
-export * from "./ast_inspector";
export * from "./run";
export function analyzerStatus(ctx: CtxInit): Cmd {
@@ -288,13 +287,13 @@ export function openCargoToml(ctx: CtxInit): Cmd {
export function revealDependency(ctx: CtxInit): Cmd {
return async (editor: RustEditor) => {
- if (!ctx.dependencies?.isInitialized()) {
+ if (!ctx.dependenciesProvider?.isInitialized()) {
return;
}
const documentPath = editor.document.uri.fsPath;
- const dep = ctx.dependencies?.getDependency(documentPath);
+ const dep = ctx.dependenciesProvider?.getDependency(documentPath);
if (dep) {
- await ctx.treeView?.reveal(dep, { select: true, expand: true });
+ await ctx.dependencyTreeView?.reveal(dep, { select: true, expand: true });
} else {
await revealParentChain(editor.document, ctx);
}
@@ -340,10 +339,10 @@ async function revealParentChain(document: RustDocument, ctx: CtxInit) {
// a open file referencing the old version
return;
}
- } while (!ctx.dependencies?.contains(documentPath));
+ } while (!ctx.dependenciesProvider?.contains(documentPath));
parentChain.reverse();
for (const idx in parentChain) {
- const treeView = ctx.treeView;
+ const treeView = ctx.dependencyTreeView;
if (!treeView) {
continue;
}
@@ -357,6 +356,77 @@ export async function execRevealDependency(e: RustEditor): Promise<void> {
await vscode.commands.executeCommand("rust-analyzer.revealDependency", e);
}
+export function syntaxTreeReveal(): Cmd {
+ return async (element: SyntaxElement) => {
+ const activeEditor = vscode.window.activeTextEditor;
+
+ if (activeEditor !== undefined) {
+ const start = activeEditor.document.positionAt(element.start);
+ const end = activeEditor.document.positionAt(element.end);
+
+ const newSelection = new vscode.Selection(start, end);
+
+ activeEditor.selection = newSelection;
+ activeEditor.revealRange(newSelection);
+ }
+ };
+}
+
+function elementToString(
+ activeDocument: vscode.TextDocument,
+ element: SyntaxElement,
+ depth: number = 0,
+): string {
+ let result = " ".repeat(depth);
+ const start = element.istart ?? element.start;
+ const end = element.iend ?? element.end;
+
+ result += `${element.kind}@${start}..${end}`;
+
+ if (element.type === "Token") {
+ const startPosition = activeDocument.positionAt(element.start);
+ const endPosition = activeDocument.positionAt(element.end);
+ const text = activeDocument.getText(new vscode.Range(startPosition, endPosition));
+ // JSON.stringify quotes and escapes the string for us.
+ result += ` ${JSON.stringify(text)}\n`;
+ } else {
+ result += "\n";
+ for (const child of element.children) {
+ result += elementToString(activeDocument, child, depth + 1);
+ }
+ }
+
+ return result;
+}
+
+export function syntaxTreeCopy(): Cmd {
+ return async (element: SyntaxElement) => {
+ const activeDocument = vscode.window.activeTextEditor?.document;
+ if (!activeDocument) {
+ return;
+ }
+
+ const result = elementToString(activeDocument, element);
+ await vscode.env.clipboard.writeText(result);
+ };
+}
+
+export function syntaxTreeHideWhitespace(ctx: CtxInit): Cmd {
+ return async () => {
+ if (ctx.syntaxTreeProvider !== undefined) {
+ await ctx.syntaxTreeProvider.toggleWhitespace();
+ }
+ };
+}
+
+export function syntaxTreeShowWhitespace(ctx: CtxInit): Cmd {
+ return async () => {
+ if (ctx.syntaxTreeProvider !== undefined) {
+ await ctx.syntaxTreeProvider.toggleWhitespace();
+ }
+ };
+}
+
export function ssr(ctx: CtxInit): Cmd {
return async () => {
const editor = vscode.window.activeTextEditor;
@@ -426,89 +496,6 @@ export function serverVersion(ctx: CtxInit): Cmd {
};
}
-// Opens the virtual file that will show the syntax tree
-//
-// The contents of the file come from the `TextDocumentContentProvider`
-export function syntaxTree(ctx: CtxInit): Cmd {
- const tdcp = new (class implements vscode.TextDocumentContentProvider {
- readonly uri = vscode.Uri.parse("rust-analyzer-syntax-tree://syntaxtree/tree.rast");
- readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
- constructor() {
- vscode.workspace.onDidChangeTextDocument(
- this.onDidChangeTextDocument,
- this,
- ctx.subscriptions,
- );
- vscode.window.onDidChangeActiveTextEditor(
- this.onDidChangeActiveTextEditor,
- this,
- ctx.subscriptions,
- );
- }
-
- private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
- if (isRustDocument(event.document)) {
- // We need to order this after language server updates, but there's no API for that.
- // Hence, good old sleep().
- void sleep(10).then(() => this.eventEmitter.fire(this.uri));
- }
- }
- private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
- if (editor && isRustEditor(editor)) {
- this.eventEmitter.fire(this.uri);
- }
- }
-
- async provideTextDocumentContent(
- uri: vscode.Uri,
- ct: vscode.CancellationToken,
- ): Promise<string> {
- const rustEditor = ctx.activeRustEditor;
- if (!rustEditor) return "";
- const client = ctx.client;
-
- // When the range based query is enabled we take the range of the selection
- const range =
- uri.query === "range=true" && !rustEditor.selection.isEmpty
- ? client.code2ProtocolConverter.asRange(rustEditor.selection)
- : null;
-
- const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range };
- return client.sendRequest(ra.syntaxTree, params, ct);
- }
-
- get onDidChange(): vscode.Event<vscode.Uri> {
- return this.eventEmitter.event;
- }
- })();
-
- ctx.pushExtCleanup(new AstInspector(ctx));
- ctx.pushExtCleanup(
- vscode.workspace.registerTextDocumentContentProvider("rust-analyzer-syntax-tree", tdcp),
- );
- ctx.pushExtCleanup(
- vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
- brackets: [["[", ")"]],
- }),
- );
-
- return async () => {
- const editor = vscode.window.activeTextEditor;
- const rangeEnabled = !!editor && !editor.selection.isEmpty;
-
- const uri = rangeEnabled ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`) : tdcp.uri;
-
- const document = await vscode.workspace.openTextDocument(uri);
-
- tdcp.eventEmitter.fire(uri);
-
- void (await vscode.window.showTextDocument(document, {
- viewColumn: vscode.ViewColumn.Two,
- preserveFocus: true,
- }));
- };
-}
-
function viewHirOrMir(ctx: CtxInit, xir: "hir" | "mir"): Cmd {
const viewXir = xir === "hir" ? "viewHir" : "viewMir";
const requestType = xir === "hir" ? ra.viewHir : ra.viewMir;
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 720c473c5b..d1467a4e82 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -351,6 +351,10 @@ export class Config {
return this.get<boolean>("showDependenciesExplorer");
}
+ get showSyntaxTree() {
+ return this.get<boolean>("showSyntaxTree");
+ }
+
get statusBarClickAction() {
return this.get<string>("statusBar.clickAction");
}
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 37a54abf71..5550bfa655 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -19,6 +19,7 @@ import {
RustDependenciesProvider,
type DependencyId,
} from "./dependencies_provider";
+import { SyntaxTreeProvider, type SyntaxElement } from "./syntax_tree_provider";
import { execRevealDependency } from "./commands";
import { PersistentState } from "./persistent_state";
import { bootstrap } from "./bootstrap";
@@ -84,8 +85,12 @@ export class Ctx implements RustAnalyzerExtensionApi {
private commandFactories: Record<string, CommandFactory>;
private commandDisposables: Disposable[];
private unlinkedFiles: vscode.Uri[];
- private _dependencies: RustDependenciesProvider | undefined;
- private _treeView: vscode.TreeView<Dependency | DependencyFile | DependencyId> | undefined;
+ private _dependenciesProvider: RustDependenciesProvider | undefined;
+ private _dependencyTreeView:
+ | vscode.TreeView<Dependency | DependencyFile | DependencyId>
+ | undefined;
+ private _syntaxTreeProvider: SyntaxTreeProvider | undefined;
+ private _syntaxTreeView: vscode.TreeView<SyntaxElement> | undefined;
private lastStatus: ServerStatusParams | { health: "stopped" } = { health: "stopped" };
private _serverVersion: string;
private statusBarActiveEditorListener: Disposable;
@@ -102,12 +107,20 @@ export class Ctx implements RustAnalyzerExtensionApi {
return this._client;
}
- get treeView() {
- return this._treeView;
+ get dependencyTreeView() {
+ return this._dependencyTreeView;
}
- get dependencies() {
- return this._dependencies;
+ get dependenciesProvider() {
+ return this._dependenciesProvider;
+ }
+
+ get syntaxTreeView() {
+ return this._syntaxTreeView;
+ }
+
+ get syntaxTreeProvider() {
+ return this._syntaxTreeProvider;
}
constructor(
@@ -278,6 +291,9 @@ export class Ctx implements RustAnalyzerExtensionApi {
if (this.config.showDependenciesExplorer) {
this.prepareTreeDependenciesView(client);
}
+ if (this.config.showSyntaxTree) {
+ this.prepareSyntaxTreeView(client);
+ }
}
private prepareTreeDependenciesView(client: lc.LanguageClient) {
@@ -285,13 +301,13 @@ export class Ctx implements RustAnalyzerExtensionApi {
...this,
client: client,
};
- this._dependencies = new RustDependenciesProvider(ctxInit);
- this._treeView = vscode.window.createTreeView("rustDependencies", {
- treeDataProvider: this._dependencies,
+ this._dependenciesProvider = new RustDependenciesProvider(ctxInit);
+ this._dependencyTreeView = vscode.window.createTreeView("rustDependencies", {
+ treeDataProvider: this._dependenciesProvider,
showCollapseAll: true,
});
- this.pushExtCleanup(this._treeView);
+ this.pushExtCleanup(this._dependencyTreeView);
vscode.window.onDidChangeActiveTextEditor(async (e) => {
// we should skip documents that belong to the current workspace
if (this.shouldRevealDependency(e)) {
@@ -303,7 +319,7 @@ export class Ctx implements RustAnalyzerExtensionApi {
}
});
- this.treeView?.onDidChangeVisibility(async (e) => {
+ this.dependencyTreeView?.onDidChangeVisibility(async (e) => {
if (e.visible) {
const activeEditor = vscode.window.activeTextEditor;
if (this.shouldRevealDependency(activeEditor)) {
@@ -322,10 +338,60 @@ export class Ctx implements RustAnalyzerExtensionApi {
e !== undefined &&
isRustEditor(e) &&
!isDocumentInWorkspace(e.document) &&
- (this.treeView?.visible || false)
+ (this.dependencyTreeView?.visible || false)
);
}
+ private prepareSyntaxTreeView(client: lc.LanguageClient) {
+ const ctxInit: CtxInit = {
+ ...this,
+ client: client,
+ };
+ this._syntaxTreeProvider = new SyntaxTreeProvider(ctxInit);
+ this._syntaxTreeView = vscode.window.createTreeView("rustSyntaxTree", {
+ treeDataProvider: this._syntaxTreeProvider,
+ showCollapseAll: true,
+ });
+
+ this.pushExtCleanup(this._syntaxTreeView);
+
+ vscode.window.onDidChangeActiveTextEditor(async () => {
+ if (this.syntaxTreeView?.visible) {
+ await this.syntaxTreeProvider?.refresh();
+ }
+ });
+
+ vscode.workspace.onDidChangeTextDocument(async () => {
+ if (this.syntaxTreeView?.visible) {
+ await this.syntaxTreeProvider?.refresh();
+ }
+ });
+
+ vscode.window.onDidChangeTextEditorSelection(async (e) => {
+ if (!this.syntaxTreeView?.visible || !isRustEditor(e.textEditor)) {
+ return;
+ }
+
+ const selection = e.selections[0];
+ if (selection === undefined) {
+ return;
+ }
+
+ const start = e.textEditor.document.offsetAt(selection.start);
+ const end = e.textEditor.document.offsetAt(selection.end);
+ const result = this.syntaxTreeProvider?.getElementByRange(start, end);
+ if (result !== undefined) {
+ await this.syntaxTreeView?.reveal(result);
+ }
+ });
+
+ this._syntaxTreeView.onDidChangeVisibility(async (e) => {
+ if (e.visible) {
+ await this.syntaxTreeProvider?.refresh();
+ }
+ });
+ }
+
async restart() {
// FIXME: We should re-use the client, that is ctx.deactivate() if none of the configs have changed
await this.stopAndDispose();
@@ -423,7 +489,8 @@ export class Ctx implements RustAnalyzerExtensionApi {
} else {
statusBar.command = "rust-analyzer.openLogs";
}
- this.dependencies?.refresh();
+ this.dependenciesProvider?.refresh();
+ void this.syntaxTreeProvider?.refresh();
break;
case "warning":
statusBar.color = new vscode.ThemeColor("statusBarItem.warningForeground");
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index d52e314e21..af86d9efd1 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -48,6 +48,9 @@ export const runFlycheck = new lc.NotificationType<{
export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>(
"rust-analyzer/syntaxTree",
);
+export const viewSyntaxTree = new lc.RequestType<ViewSyntaxTreeParams, string, void>(
+ "rust-analyzer/viewSyntaxTree",
+);
export const viewCrateGraph = new lc.RequestType<ViewCrateGraphParams, string, void>(
"rust-analyzer/viewCrateGraph",
);
@@ -157,6 +160,7 @@ export type SyntaxTreeParams = {
textDocument: lc.TextDocumentIdentifier;
range: lc.Range | null;
};
+export type ViewSyntaxTreeParams = { textDocument: lc.TextDocumentIdentifier };
export type ViewCrateGraphParams = { full: boolean };
export type ViewItemTreeParams = { textDocument: lc.TextDocumentIdentifier };
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index fdf43f66f9..c84b69b66c 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -158,7 +158,6 @@ function createCommands(): Record<string, CommandFactory> {
matchingBrace: { enabled: commands.matchingBrace },
joinLines: { enabled: commands.joinLines },
parentModule: { enabled: commands.parentModule },
- syntaxTree: { enabled: commands.syntaxTree },
viewHir: { enabled: commands.viewHir },
viewMir: { enabled: commands.viewMir },
interpretFunction: { enabled: commands.interpretFunction },
@@ -199,6 +198,10 @@ function createCommands(): Record<string, CommandFactory> {
rename: { enabled: commands.rename },
openLogs: { enabled: commands.openLogs },
revealDependency: { enabled: commands.revealDependency },
+ syntaxTreeReveal: { enabled: commands.syntaxTreeReveal },
+ syntaxTreeCopy: { enabled: commands.syntaxTreeCopy },
+ syntaxTreeHideWhitespace: { enabled: commands.syntaxTreeHideWhitespace },
+ syntaxTreeShowWhitespace: { enabled: commands.syntaxTreeShowWhitespace },
};
}
diff --git a/editors/code/src/syntax_tree_provider.ts b/editors/code/src/syntax_tree_provider.ts
new file mode 100644
index 0000000000..c7e8007e83
--- /dev/null
+++ b/editors/code/src/syntax_tree_provider.ts
@@ -0,0 +1,301 @@
+import * as vscode from "vscode";
+
+import { isRustEditor, setContextValue } from "./util";
+import type { CtxInit } from "./ctx";
+import * as ra from "./lsp_ext";
+
+export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement> {
+ private _onDidChangeTreeData: vscode.EventEmitter<SyntaxElement | undefined | void> =
+ new vscode.EventEmitter<SyntaxElement | undefined | void>();
+ readonly onDidChangeTreeData: vscode.Event<SyntaxElement | undefined | void> =
+ this._onDidChangeTreeData.event;
+ ctx: CtxInit;
+ root: SyntaxNode | undefined;
+ hideWhitespace: boolean = false;
+
+ constructor(ctx: CtxInit) {
+ this.ctx = ctx;
+ }
+
+ getTreeItem(element: SyntaxElement): vscode.TreeItem {
+ return new SyntaxTreeItem(element);
+ }
+
+ getChildren(element?: SyntaxElement): vscode.ProviderResult<SyntaxElement[]> {
+ return this.getRawChildren(element);
+ }
+
+ getParent(element: SyntaxElement): vscode.ProviderResult<SyntaxElement> {
+ return element.parent;
+ }
+
+ resolveTreeItem(
+ item: SyntaxTreeItem,
+ element: SyntaxElement,
+ _token: vscode.CancellationToken,
+ ): vscode.ProviderResult<SyntaxTreeItem> {
+ const editor = vscode.window.activeTextEditor;
+
+ if (editor !== undefined) {
+ const start = editor.document.positionAt(element.start);
+ const end = editor.document.positionAt(element.end);
+ const range = new vscode.Range(start, end);
+
+ const text = editor.document.getText(range);
+ item.tooltip = new vscode.MarkdownString().appendCodeblock(text, "rust");
+ }
+
+ return item;
+ }
+
+ private getRawChildren(element?: SyntaxElement): SyntaxElement[] {
+ if (element?.type === "Node") {
+ if (this.hideWhitespace) {
+ return element.children.filter((e) => e.kind !== "WHITESPACE");
+ }
+
+ return element.children;
+ }
+
+ if (element?.type === "Token") {
+ return [];
+ }
+
+ if (element === undefined && this.root !== undefined) {
+ return [this.root];
+ }
+
+ return [];
+ }
+
+ async refresh(): Promise<void> {
+ const editor = vscode.window.activeTextEditor;
+
+ if (editor && isRustEditor(editor)) {
+ const params = { textDocument: { uri: editor.document.uri.toString() }, range: null };
+ const fileText = await this.ctx.client.sendRequest(ra.viewSyntaxTree, params);
+ this.root = JSON.parse(fileText, (_key, value: SyntaxElement) => {
+ if (value.type === "Node") {
+ for (const child of value.children) {
+ child.parent = value;
+ }
+ }
+
+ return value;
+ });
+ } else {
+ this.root = undefined;
+ }
+
+ this._onDidChangeTreeData.fire();
+ }
+
+ getElementByRange(start: number, end: number): SyntaxElement | undefined {
+ if (this.root === undefined) {
+ return undefined;
+ }
+
+ let result: SyntaxElement = this.root;
+
+ if (this.root.start === start && this.root.end === end) {
+ return result;
+ }
+
+ let children = this.getRawChildren(this.root);
+
+ outer: while (true) {
+ for (const child of children) {
+ if (child.start <= start && child.end >= end) {
+ result = child;
+ if (start === end && start === child.end) {
+ // When the cursor is on the very end of a token,
+ // we assume the user wants the next token instead.
+ continue;
+ }
+
+ if (child.type === "Token") {
+ return result;
+ } else {
+ children = this.getRawChildren(child);
+ continue outer;
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+
+ async toggleWhitespace() {
+ this.hideWhitespace = !this.hideWhitespace;
+ this._onDidChangeTreeData.fire();
+ await setContextValue("rustSyntaxTree.hideWhitespace", this.hideWhitespace);
+ }
+}
+
+export type SyntaxNode = {
+ type: "Node";
+ kind: string;
+ start: number;
+ end: number;
+ istart?: number;
+ iend?: number;
+ children: SyntaxElement[];
+ parent?: SyntaxElement;
+};
+
+type SyntaxToken = {
+ type: "Token";
+ kind: string;
+ start: number;
+ end: number;
+ istart?: number;
+ iend?: number;
+ parent?: SyntaxElement;
+};
+
+export type SyntaxElement = SyntaxNode | SyntaxToken;
+
+export class SyntaxTreeItem extends vscode.TreeItem {
+ constructor(private readonly element: SyntaxElement) {
+ super(element.kind);
+ const icon = getIcon(element.kind);
+ if (element.type === "Node") {
+ this.contextValue = "syntaxNode";
+ this.iconPath = icon ?? new vscode.ThemeIcon("list-tree");
+ this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded;
+ } else {
+ this.contextValue = "syntaxToken";
+ this.iconPath = icon ?? new vscode.ThemeIcon("symbol-string");
+ this.collapsibleState = vscode.TreeItemCollapsibleState.None;
+ }
+
+ if (element.istart !== undefined && element.iend !== undefined) {
+ this.description = `${this.element.istart}..${this.element.iend}`;
+ } else {
+ this.description = `${this.element.start}..${this.element.end}`;
+ }
+ }
+}
+
+function getIcon(kind: string): vscode.ThemeIcon | undefined {
+ const icon = iconTable[kind];
+
+ if (icon !== undefined) {
+ return icon;
+ }
+
+ if (kind.endsWith("_KW")) {
+ return new vscode.ThemeIcon(
+ "symbol-keyword",
+ new vscode.ThemeColor("symbolIcon.keywordForeground"),
+ );
+ }
+
+ if (operators.includes(kind)) {
+ return new vscode.ThemeIcon(
+ "symbol-operator",
+ new vscode.ThemeColor("symbolIcon.operatorForeground"),
+ );
+ }
+
+ return undefined;
+}
+
+const iconTable: Record<string, vscode.ThemeIcon> = {
+ CALL_EXPR: new vscode.ThemeIcon("call-outgoing"),
+ COMMENT: new vscode.ThemeIcon("comment"),
+ ENUM: new vscode.ThemeIcon("symbol-enum", new vscode.ThemeColor("symbolIcon.enumForeground")),
+ FN: new vscode.ThemeIcon(
+ "symbol-function",
+ new vscode.ThemeColor("symbolIcon.functionForeground"),
+ ),
+ FLOAT_NUMBER: new vscode.ThemeIcon(
+ "symbol-number",
+ new vscode.ThemeColor("symbolIcon.numberForeground"),
+ ),
+ INDEX_EXPR: new vscode.ThemeIcon(
+ "symbol-array",
+ new vscode.ThemeColor("symbolIcon.arrayForeground"),
+ ),
+ INT_NUMBER: new vscode.ThemeIcon(
+ "symbol-number",
+ new vscode.ThemeColor("symbolIcon.numberForeground"),
+ ),
+ LITERAL: new vscode.ThemeIcon(
+ "symbol-misc",
+ new vscode.ThemeColor("symbolIcon.miscForeground"),
+ ),
+ MODULE: new vscode.ThemeIcon(
+ "symbol-module",
+ new vscode.ThemeColor("symbolIcon.moduleForeground"),
+ ),
+ METHOD_CALL_EXPR: new vscode.ThemeIcon("call-outgoing"),
+ PARAM: new vscode.ThemeIcon(
+ "symbol-parameter",
+ new vscode.ThemeColor("symbolIcon.parameterForeground"),
+ ),
+ RECORD_FIELD: new vscode.ThemeIcon(
+ "symbol-field",
+ new vscode.ThemeColor("symbolIcon.fieldForeground"),
+ ),
+ SOURCE_FILE: new vscode.ThemeIcon("file-code"),
+ STRING: new vscode.ThemeIcon("quote"),
+ STRUCT: new vscode.ThemeIcon(
+ "symbol-struct",
+ new vscode.ThemeColor("symbolIcon.structForeground"),
+ ),
+ TRAIT: new vscode.ThemeIcon(
+ "symbol-interface",
+ new vscode.ThemeColor("symbolIcon.interfaceForeground"),
+ ),
+ TYPE_PARAM: new vscode.ThemeIcon(
+ "symbol-type-parameter",
+ new vscode.ThemeColor("symbolIcon.typeParameterForeground"),
+ ),
+ VARIANT: new vscode.ThemeIcon(
+ "symbol-enum-member",
+ new vscode.ThemeColor("symbolIcon.enumMemberForeground"),
+ ),
+ WHITESPACE: new vscode.ThemeIcon("whitespace"),
+};
+
+const operators = [
+ "PLUS",
+ "PLUSEQ",
+ "MINUS",
+ "MINUSEQ",
+ "STAR",
+ "STAREQ",
+ "SLASH",
+ "SLASHEQ",
+ "PERCENT",
+ "PERCENTEQ",
+ "CARET",
+ "CARETEQ",
+ "AMP",
+ "AMPEQ",
+ "AMP2",
+ "PIPE",
+ "PIPEEQ",
+ "PIPE2",
+ "SHL",
+ "SHLEQ",
+ "SHR",
+ "SHREQ",
+ "EQ",
+ "EQ2",
+ "BANG",
+ "NEQ",
+ "L_ANGLE",
+ "LTEQ",
+ "R_ANGLE",
+ "GTEQ",
+ "COLON2",
+ "THIN_ARROW",
+ "FAT_ARROW",
+ "DOT",
+ "DOT2",
+ "DOT2EQ",
+ "AT",
+];
diff --git a/lib/lsp-server/src/msg.rs b/lib/lsp-server/src/msg.rs
index 11f98f5079..074bc43388 100644
--- a/lib/lsp-server/src/msg.rs
+++ b/lib/lsp-server/src/msg.rs
@@ -181,15 +181,15 @@ impl Message {
Ok(Some(msg))
}
- pub fn write(self, w: &mut impl Write) -> io::Result<()> {
+ pub fn write(&self, w: &mut impl Write) -> io::Result<()> {
self._write(w)
}
- fn _write(self, w: &mut dyn Write) -> io::Result<()> {
+ fn _write(&self, w: &mut dyn Write) -> io::Result<()> {
#[derive(Serialize)]
- struct JsonRpc {
+ struct JsonRpc<'a> {
jsonrpc: &'static str,
#[serde(flatten)]
- msg: Message,
+ msg: &'a Message,
}
let text = serde_json::to_string(&JsonRpc { jsonrpc: "2.0", msg: self })?;
write_msg_text(w, &text)
diff --git a/lib/lsp-server/src/socket.rs b/lib/lsp-server/src/socket.rs
index 36d728456f..48400abf22 100644
--- a/lib/lsp-server/src/socket.rs
+++ b/lib/lsp-server/src/socket.rs
@@ -15,8 +15,11 @@ pub(crate) fn socket_transport(
stream: TcpStream,
) -> (Sender<Message>, Receiver<Message>, IoThreads) {
let (reader_receiver, reader) = make_reader(stream.try_clone().unwrap());
- let (writer_sender, writer) = make_write(stream);
- let io_threads = make_io_threads(reader, writer);
+ let (writer_sender, writer, messages_to_drop) = make_write(stream);
+ let dropper = std::thread::spawn(move || {
+ messages_to_drop.into_iter().for_each(drop);
+ });
+ let io_threads = make_io_threads(reader, writer, dropper);
(writer_sender, reader_receiver, io_threads)
}
@@ -36,11 +39,21 @@ fn make_reader(stream: TcpStream) -> (Receiver<Message>, thread::JoinHandle<io::
(reader_receiver, reader)
}
-fn make_write(mut stream: TcpStream) -> (Sender<Message>, thread::JoinHandle<io::Result<()>>) {
+fn make_write(
+ mut stream: TcpStream,
+) -> (Sender<Message>, thread::JoinHandle<io::Result<()>>, Receiver<Message>) {
let (writer_sender, writer_receiver) = bounded::<Message>(0);
+ let (drop_sender, drop_receiver) = bounded::<Message>(0);
let writer = thread::spawn(move || {
- writer_receiver.into_iter().try_for_each(|it| it.write(&mut stream)).unwrap();
+ writer_receiver
+ .into_iter()
+ .try_for_each(|it| {
+ let result = it.write(&mut stream);
+ let _ = drop_sender.send(it);
+ result
+ })
+ .unwrap();
Ok(())
});
- (writer_sender, writer)
+ (writer_sender, writer, drop_receiver)
}
diff --git a/lib/lsp-server/src/stdio.rs b/lib/lsp-server/src/stdio.rs
index 279a6bce08..8344c9f56b 100644
--- a/lib/lsp-server/src/stdio.rs
+++ b/lib/lsp-server/src/stdio.rs
@@ -11,15 +11,24 @@ use crate::Message;
/// Creates an LSP connection via stdio.
pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThreads) {
+ let (drop_sender, drop_receiver) = bounded::<Message>(0);
let (writer_sender, writer_receiver) = bounded::<Message>(0);
let writer = thread::Builder::new()
.name("LspServerWriter".to_owned())
.spawn(move || {
let stdout = stdout();
let mut stdout = stdout.lock();
- writer_receiver.into_iter().try_for_each(|it| it.write(&mut stdout))
+ writer_receiver.into_iter().try_for_each(|it| {
+ let result = it.write(&mut stdout);
+ let _ = drop_sender.send(it);
+ result
+ })
})
.unwrap();
+ let dropper = thread::Builder::new()
+ .name("LspMessageDropper".to_owned())
+ .spawn(move || drop_receiver.into_iter().for_each(drop))
+ .unwrap();
let (reader_sender, reader_receiver) = bounded::<Message>(0);
let reader = thread::Builder::new()
.name("LspServerReader".to_owned())
@@ -41,7 +50,7 @@ pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThread
Ok(())
})
.unwrap();
- let threads = IoThreads { reader, writer };
+ let threads = IoThreads { reader, writer, dropper };
(writer_sender, reader_receiver, threads)
}
@@ -49,13 +58,15 @@ pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThread
pub(crate) fn make_io_threads(
reader: thread::JoinHandle<io::Result<()>>,
writer: thread::JoinHandle<io::Result<()>>,
+ dropper: thread::JoinHandle<()>,
) -> IoThreads {
- IoThreads { reader, writer }
+ IoThreads { reader, writer, dropper }
}
pub struct IoThreads {
reader: thread::JoinHandle<io::Result<()>>,
writer: thread::JoinHandle<io::Result<()>>,
+ dropper: thread::JoinHandle<()>,
}
impl IoThreads {
@@ -64,6 +75,12 @@ impl IoThreads {
Ok(r) => r?,
Err(err) => std::panic::panic_any(err),
}
+ match self.dropper.join() {
+ Ok(_) => (),
+ Err(err) => {
+ std::panic::panic_any(err);
+ }
+ }
match self.writer.join() {
Ok(r) => r,
Err(err) => {
diff --git a/rust-version b/rust-version
index 517fdfe18e..2d9a927c63 100644
--- a/rust-version
+++ b/rust-version
@@ -1 +1 @@
-fb546ee09b226bc4dd4b712d35a372d923c4fa54
+9a1d156f38c51441ee51e5a068f1d0caf4bb0f27