aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/Cargo.toml2
-rw-r--r--crates/assists/src/assist_config.rs32
-rw-r--r--crates/assists/src/assist_context.rs10
-rw-r--r--crates/assists/src/ast_transform.rs3
-rw-r--r--crates/assists/src/handlers/add_explicit_type.rs32
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs56
-rw-r--r--crates/assists/src/handlers/add_turbo_fish.rs12
-rw-r--r--crates/assists/src/handlers/apply_demorgan.rs12
-rw-r--r--crates/assists/src/handlers/auto_import.rs64
-rw-r--r--crates/assists/src/handlers/change_visibility.rs42
-rw-r--r--crates/assists/src/handlers/convert_integer_literal.rs32
-rw-r--r--crates/assists/src/handlers/early_return.rs32
-rw-r--r--crates/assists/src/handlers/expand_glob_import.rs34
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs103
-rw-r--r--crates/assists/src/handlers/extract_variable.rs52
-rw-r--r--crates/assists/src/handlers/fill_match_arms.rs46
-rw-r--r--crates/assists/src/handlers/fix_visibility.rs104
-rw-r--r--crates/assists/src/handlers/flip_binexpr.rs24
-rw-r--r--crates/assists/src/handlers/flip_comma.rs12
-rw-r--r--crates/assists/src/handlers/flip_trait_bound.rs20
-rw-r--r--crates/assists/src/handlers/generate_default_from_enum_variant.rs10
-rw-r--r--crates/assists/src/handlers/generate_derive.rs12
-rw-r--r--crates/assists/src/handlers/generate_from_impl_for_enum.rs16
-rw-r--r--crates/assists/src/handlers/generate_function.rs63
-rw-r--r--crates/assists/src/handlers/generate_impl.rs14
-rw-r--r--crates/assists/src/handlers/generate_new.rs32
-rw-r--r--crates/assists/src/handlers/infer_function_return_type.rs36
-rw-r--r--crates/assists/src/handlers/inline_function.rs202
-rw-r--r--crates/assists/src/handlers/inline_local_variable.rs155
-rw-r--r--crates/assists/src/handlers/introduce_named_lifetime.rs42
-rw-r--r--crates/assists/src/handlers/invert_if.rs20
-rw-r--r--crates/assists/src/handlers/merge_imports.rs42
-rw-r--r--crates/assists/src/handlers/merge_match_arms.rs12
-rw-r--r--crates/assists/src/handlers/move_bounds.rs10
-rw-r--r--crates/assists/src/handlers/move_guard.rs26
-rw-r--r--crates/assists/src/handlers/move_module_to_file.rs (renamed from crates/assists/src/handlers/extract_module_to_file.rs)50
-rw-r--r--crates/assists/src/handlers/pull_assignment_up.rs400
-rw-r--r--crates/assists/src/handlers/qualify_path.rs76
-rw-r--r--crates/assists/src/handlers/raw_string.rs54
-rw-r--r--crates/assists/src/handlers/remove_dbg.rs80
-rw-r--r--crates/assists/src/handlers/remove_mut.rs2
-rw-r--r--crates/assists/src/handlers/remove_unused_param.rs61
-rw-r--r--crates/assists/src/handlers/reorder_fields.rs12
-rw-r--r--crates/assists/src/handlers/reorder_impl.rs201
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs20
-rw-r--r--crates/assists/src/handlers/replace_if_let_with_match.rs32
-rw-r--r--crates/assists/src/handlers/replace_impl_trait_with_generic.rs18
-rw-r--r--crates/assists/src/handlers/replace_let_with_if_let.rs4
-rw-r--r--crates/assists/src/handlers/replace_qualified_name_with_use.rs58
-rw-r--r--crates/assists/src/handlers/replace_string_with_char.rs14
-rw-r--r--crates/assists/src/handlers/replace_unwrap_with_match.rs10
-rw-r--r--crates/assists/src/handlers/split_import.rs12
-rw-r--r--crates/assists/src/handlers/toggle_ignore.rs6
-rw-r--r--crates/assists/src/handlers/unwrap_block.rs34
-rw-r--r--crates/assists/src/handlers/wrap_return_type_in_result.rs78
-rw-r--r--crates/assists/src/lib.rs16
-rw-r--r--crates/assists/src/tests.rs44
-rw-r--r--crates/assists/src/tests/generated.rs223
-rw-r--r--crates/assists/src/utils.rs23
-rw-r--r--crates/base_db/src/fixture.rs16
-rw-r--r--crates/base_db/src/input.rs7
-rw-r--r--crates/cfg/Cargo.toml2
-rw-r--r--crates/completion/Cargo.toml4
-rw-r--r--crates/completion/src/completions.rs5
-rw-r--r--crates/completion/src/completions/attribute.rs73
-rw-r--r--crates/completion/src/completions/dot.rs48
-rw-r--r--crates/completion/src/completions/fn_param.rs8
-rw-r--r--crates/completion/src/completions/keyword.rs56
-rw-r--r--crates/completion/src/completions/macro_in_item_position.rs2
-rw-r--r--crates/completion/src/completions/mod_.rs87
-rw-r--r--crates/completion/src/completions/pattern.rs20
-rw-r--r--crates/completion/src/completions/postfix.rs79
-rw-r--r--crates/completion/src/completions/postfix/format_like.rs7
-rw-r--r--crates/completion/src/completions/qualified_path.rs70
-rw-r--r--crates/completion/src/completions/record.rs94
-rw-r--r--crates/completion/src/completions/snippet.rs14
-rw-r--r--crates/completion/src/completions/trait_impl.rs123
-rw-r--r--crates/completion/src/completions/unqualified_path.rs109
-rw-r--r--crates/completion/src/config.rs48
-rw-r--r--crates/completion/src/context.rs10
-rw-r--r--crates/completion/src/item.rs6
-rw-r--r--crates/completion/src/lib.rs23
-rw-r--r--crates/completion/src/patterns.rs56
-rw-r--r--crates/completion/src/render.rs61
-rw-r--r--crates/completion/src/render/const_.rs8
-rw-r--r--crates/completion/src/render/enum_variant.rs2
-rw-r--r--crates/completion/src/render/function.rs36
-rw-r--r--crates/completion/src/render/macro_.rs23
-rw-r--r--crates/completion/src/render/pattern.rs6
-rw-r--r--crates/completion/src/render/type_alias.rs8
-rw-r--r--crates/completion/src/test_utils.rs26
-rw-r--r--crates/flycheck/Cargo.toml3
-rw-r--r--crates/flycheck/src/lib.rs23
-rw-r--r--crates/hir/Cargo.toml2
-rw-r--r--crates/hir/src/attrs.rs28
-rw-r--r--crates/hir/src/code_model.rs138
-rw-r--r--crates/hir/src/db.rs4
-rw-r--r--crates/hir/src/diagnostics.rs2
-rw-r--r--crates/hir/src/from_id.rs31
-rw-r--r--crates/hir/src/has_source.rs77
-rw-r--r--crates/hir/src/lib.rs7
-rw-r--r--crates/hir/src/semantics.rs12
-rw-r--r--crates/hir/src/semantics/source_to_def.rs18
-rw-r--r--crates/hir/src/source_analyzer.rs1
-rw-r--r--crates/hir_def/Cargo.toml4
-rw-r--r--crates/hir_def/src/attr.rs90
-rw-r--r--crates/hir_def/src/body/lower.rs6
-rw-r--r--crates/hir_def/src/body/scope.rs30
-rw-r--r--crates/hir_def/src/db.rs11
-rw-r--r--crates/hir_def/src/diagnostics.rs4
-rw-r--r--crates/hir_def/src/expr.rs2
-rw-r--r--crates/hir_def/src/find_path.rs48
-rw-r--r--crates/hir_def/src/generics.rs44
-rw-r--r--crates/hir_def/src/import_map.rs285
-rw-r--r--crates/hir_def/src/item_scope.rs3
-rw-r--r--crates/hir_def/src/item_tree.rs9
-rw-r--r--crates/hir_def/src/keys.rs5
-rw-r--r--crates/hir_def/src/lib.rs29
-rw-r--r--crates/hir_def/src/nameres.rs2
-rw-r--r--crates/hir_def/src/nameres/collector.rs41
-rw-r--r--crates/hir_def/src/nameres/tests/incremental.rs4
-rw-r--r--crates/hir_def/src/path.rs1
-rw-r--r--crates/hir_def/src/path/lower.rs2
-rw-r--r--crates/hir_def/src/resolver.rs19
-rw-r--r--crates/hir_expand/src/builtin_derive.rs2
-rw-r--r--crates/hir_expand/src/builtin_macro.rs3
-rw-r--r--crates/hir_expand/src/db.rs16
-rw-r--r--crates/hir_expand/src/eager.rs6
-rw-r--r--crates/hir_expand/src/hygiene.rs210
-rw-r--r--crates/hir_expand/src/name.rs2
-rw-r--r--crates/hir_expand/src/proc_macro.rs10
-rw-r--r--crates/hir_ty/Cargo.toml10
-rw-r--r--crates/hir_ty/src/db.rs7
-rw-r--r--crates/hir_ty/src/diagnostics.rs15
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs57
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs4
-rw-r--r--crates/hir_ty/src/display.rs104
-rw-r--r--crates/hir_ty/src/infer/expr.rs2
-rw-r--r--crates/hir_ty/src/infer/path.rs1
-rw-r--r--crates/hir_ty/src/lib.rs4
-rw-r--r--crates/hir_ty/src/lower.rs21
-rw-r--r--crates/hir_ty/src/tests.rs2
-rw-r--r--crates/hir_ty/src/tests/display_source_code.rs15
-rw-r--r--crates/hir_ty/src/tests/macros.rs77
-rw-r--r--crates/hir_ty/src/tests/regression.rs18
-rw-r--r--crates/hir_ty/src/tests/simple.rs16
-rw-r--r--crates/hir_ty/src/tests/traits.rs8
-rw-r--r--crates/ide/Cargo.toml4
-rw-r--r--crates/ide/src/call_hierarchy.rs77
-rw-r--r--crates/ide/src/diagnostics.rs97
-rw-r--r--crates/ide/src/diagnostics/field_shorthand.rs8
-rw-r--r--crates/ide/src/diagnostics/fixes.rs16
-rw-r--r--crates/ide/src/display/navigation_target.rs145
-rw-r--r--crates/ide/src/display/short_label.rs11
-rw-r--r--crates/ide/src/doc_links.rs49
-rw-r--r--crates/ide/src/expand_macro.rs12
-rw-r--r--crates/ide/src/extend_selection.rs108
-rw-r--r--crates/ide/src/fixture.rs12
-rw-r--r--crates/ide/src/fn_references.rs6
-rw-r--r--crates/ide/src/goto_definition.rs237
-rw-r--r--crates/ide/src/goto_implementation.rs22
-rw-r--r--crates/ide/src/goto_type_definition.rs16
-rw-r--r--crates/ide/src/hover.rs576
-rw-r--r--crates/ide/src/inlay_hints.rs83
-rw-r--r--crates/ide/src/join_lines.rs136
-rw-r--r--crates/ide/src/lib.rs21
-rw-r--r--crates/ide/src/matching_brace.rs10
-rw-r--r--crates/ide/src/parent_module.rs8
-rw-r--r--crates/ide/src/references.rs181
-rw-r--r--crates/ide/src/references/rename.rs433
-rw-r--r--crates/ide/src/runnables.rs153
-rw-r--r--crates/ide/src/syntax_highlighting.rs820
-rw-r--r--crates/ide/src/syntax_highlighting/format.rs94
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs530
-rw-r--r--crates/ide/src/syntax_highlighting/highlights.rs92
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs28
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs158
-rw-r--r--crates/ide/src/syntax_highlighting/injection.rs183
-rw-r--r--crates/ide/src/syntax_highlighting/injector.rs78
-rw-r--r--crates/ide/src/syntax_highlighting/macro_rules.rs10
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs174
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html28
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html71
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html4
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_injection.html20
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html106
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html88
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html260
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/injection.html48
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html20
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs28
-rw-r--r--crates/ide/src/syntax_tree.rs20
-rw-r--r--crates/ide/src/typing.rs30
-rw-r--r--crates/ide/src/typing/on_enter.rs18
-rw-r--r--crates/ide/src/view_hir.rs25
-rw-r--r--crates/ide_db/Cargo.toml4
-rw-r--r--crates/ide_db/src/apply_change.rs1
-rw-r--r--crates/ide_db/src/call_info/tests.rs54
-rw-r--r--crates/ide_db/src/defs.rs35
-rw-r--r--crates/ide_db/src/helpers.rs109
-rw-r--r--crates/ide_db/src/helpers/famous_defs_fixture.rs120
-rw-r--r--crates/ide_db/src/imports_locator.rs23
-rw-r--r--crates/ide_db/src/search.rs164
-rw-r--r--crates/ide_db/src/traits/tests.rs18
-rw-r--r--crates/mbe/src/lib.rs16
-rw-r--r--crates/mbe/src/mbe_expander/matcher.rs10
-rw-r--r--crates/mbe/src/mbe_expander/transcriber.rs35
-rw-r--r--crates/mbe/src/parser.rs15
-rw-r--r--crates/mbe/src/subtree_source.rs152
-rw-r--r--crates/mbe/src/syntax_bridge.rs63
-rw-r--r--crates/mbe/src/tests.rs15
-rw-r--r--crates/parser/src/grammar.rs4
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs12
-rw-r--r--crates/parser/src/grammar/items.rs26
-rw-r--r--crates/parser/src/grammar/items/traits.rs2
-rw-r--r--crates/parser/src/grammar/items/use_item.rs2
-rw-r--r--crates/parser/src/grammar/patterns.rs2
-rw-r--r--crates/parser/src/grammar/type_args.rs6
-rw-r--r--crates/parser/src/grammar/type_params.rs4
-rw-r--r--crates/parser/src/lib.rs2
-rw-r--r--crates/proc_macro_api/src/msg.rs10
-rw-r--r--crates/proc_macro_api/src/process.rs7
-rw-r--r--crates/proc_macro_srv/Cargo.toml3
-rw-r--r--crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs2
-rw-r--r--crates/proc_macro_srv/src/rustc_server.rs45
-rw-r--r--crates/profile/src/stop_watch.rs6
-rw-r--r--crates/project_model/Cargo.toml4
-rw-r--r--crates/project_model/src/cargo_workspace.rs71
-rw-r--r--crates/project_model/src/lib.rs7
-rw-r--r--crates/project_model/src/project_json.rs13
-rw-r--r--crates/project_model/src/workspace.rs22
-rw-r--r--crates/rust-analyzer/Cargo.toml5
-rw-r--r--crates/rust-analyzer/src/bin/main.rs13
-rw-r--r--crates/rust-analyzer/src/caps.rs70
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs5
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs18
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs50
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs1
-rw-r--r--crates/rust-analyzer/src/config.rs705
-rw-r--r--crates/rust-analyzer/src/diff.rs53
-rw-r--r--crates/rust-analyzer/src/global_state.rs13
-rw-r--r--crates/rust-analyzer/src/handlers.rs247
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs8
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs12
-rw-r--r--crates/rust-analyzer/src/main_loop.rs106
-rw-r--r--crates/rust-analyzer/src/markdown.rs15
-rw-r--r--crates/rust-analyzer/src/op_queue.rs25
-rw-r--r--crates/rust-analyzer/src/reload.rs127
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs9
-rw-r--r--crates/rust-analyzer/src/to_proto.rs111
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/main.rs209
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs64
-rw-r--r--crates/ssr/Cargo.toml4
-rw-r--r--crates/ssr/src/matching.rs4
-rw-r--r--crates/ssr/src/parsing.rs15
-rw-r--r--crates/ssr/src/search.rs20
-rw-r--r--crates/ssr/src/tests.rs113
-rw-r--r--crates/stdx/src/lib.rs39
-rw-r--r--crates/syntax/Cargo.toml6
-rw-r--r--crates/syntax/src/algo.rs6
-rw-r--r--crates/syntax/src/ast/edit.rs4
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs2
-rw-r--r--crates/syntax/src/ast/make.rs2
-rw-r--r--crates/syntax/src/ast/node_ext.rs2
-rw-r--r--crates/syntax/src/lib.rs9
-rw-r--r--crates/syntax/src/parsing/lexer.rs8
-rw-r--r--crates/syntax/src/parsing/reparsing.rs56
-rw-r--r--crates/syntax/src/tests.rs9
-rw-r--r--crates/syntax/src/validation.rs6
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast9
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast69
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs5
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast11
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast12
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast21
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast21
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast22
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs3
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast10
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast11
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast98
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs2
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast28
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs2
-rw-r--r--crates/test_utils/Cargo.toml2
-rw-r--r--crates/test_utils/src/lib.rs36
-rw-r--r--crates/tt/src/buffer.rs105
-rw-r--r--crates/vfs/src/lib.rs51
306 files changed, 9122 insertions, 5700 deletions
diff --git a/crates/assists/Cargo.toml b/crates/assists/Cargo.toml
index 3fd8327d6..ed8ad666f 100644
--- a/crates/assists/Cargo.toml
+++ b/crates/assists/Cargo.toml
@@ -11,7 +11,7 @@ doctest = false
11 11
12[dependencies] 12[dependencies]
13rustc-hash = "1.1.0" 13rustc-hash = "1.1.0"
14itertools = "0.9.0" 14itertools = "0.10.0"
15either = "1.6.1" 15either = "1.6.1"
16 16
17stdx = { path = "../stdx", version = "0.0.0" } 17stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/assists/src/assist_config.rs b/crates/assists/src/assist_config.rs
index c458d9054..4fe8ea761 100644
--- a/crates/assists/src/assist_config.rs
+++ b/crates/assists/src/assist_config.rs
@@ -4,8 +4,7 @@
4//! module, and we use to statically check that we only produce snippet 4//! module, and we use to statically check that we only produce snippet
5//! assists if we are allowed to. 5//! assists if we are allowed to.
6 6
7use hir::PrefixKind; 7use ide_db::helpers::{insert_use::MergeBehavior, SnippetCap};
8use ide_db::helpers::insert_use::MergeBehavior;
9 8
10use crate::AssistKind; 9use crate::AssistKind;
11 10
@@ -16,35 +15,8 @@ pub struct AssistConfig {
16 pub insert_use: InsertUseConfig, 15 pub insert_use: InsertUseConfig,
17} 16}
18 17
19impl AssistConfig {
20 pub fn allow_snippets(&mut self, yes: bool) {
21 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
22 }
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub struct SnippetCap {
27 _private: (),
28}
29
30impl Default for AssistConfig {
31 fn default() -> Self {
32 AssistConfig {
33 snippet_cap: Some(SnippetCap { _private: () }),
34 allowed: None,
35 insert_use: InsertUseConfig::default(),
36 }
37 }
38}
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq)] 18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub struct InsertUseConfig { 19pub struct InsertUseConfig {
42 pub merge: Option<MergeBehavior>, 20 pub merge: Option<MergeBehavior>,
43 pub prefix_kind: PrefixKind, 21 pub prefix_kind: hir::PrefixKind,
44}
45
46impl Default for InsertUseConfig {
47 fn default() -> Self {
48 InsertUseConfig { merge: Some(MergeBehavior::Full), prefix_kind: PrefixKind::Plain }
49 }
50} 22}
diff --git a/crates/assists/src/assist_context.rs b/crates/assists/src/assist_context.rs
index 4f59d39a9..91cc63427 100644
--- a/crates/assists/src/assist_context.rs
+++ b/crates/assists/src/assist_context.rs
@@ -4,7 +4,10 @@ use std::mem;
4 4
5use algo::find_covering_element; 5use algo::find_covering_element;
6use hir::Semantics; 6use hir::Semantics;
7use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange}; 7use ide_db::{
8 base_db::{AnchoredPathBuf, FileId, FileRange},
9 helpers::SnippetCap,
10};
8use ide_db::{ 11use ide_db::{
9 label::Label, 12 label::Label,
10 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 13 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
@@ -17,10 +20,7 @@ use syntax::{
17}; 20};
18use text_edit::{TextEdit, TextEditBuilder}; 21use text_edit::{TextEdit, TextEditBuilder};
19 22
20use crate::{ 23use crate::{assist_config::AssistConfig, Assist, AssistId, AssistKind, GroupLabel};
21 assist_config::{AssistConfig, SnippetCap},
22 Assist, AssistId, AssistKind, GroupLabel,
23};
24 24
25/// `AssistContext` allows to apply an assist or check if it could be applied. 25/// `AssistContext` allows to apply an assist or check if it could be applied.
26/// 26///
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs
index da94e9987..4a3ed7783 100644
--- a/crates/assists/src/ast_transform.rs
+++ b/crates/assists/src/ast_transform.rs
@@ -204,7 +204,8 @@ impl<'a> AstTransform<'a> for QualifyPaths<'a> {
204 } 204 }
205 PathResolution::Local(_) 205 PathResolution::Local(_)
206 | PathResolution::TypeParam(_) 206 | PathResolution::TypeParam(_)
207 | PathResolution::SelfType(_) => None, 207 | PathResolution::SelfType(_)
208 | PathResolution::ConstParam(_) => None,
208 PathResolution::Macro(_) => None, 209 PathResolution::Macro(_) => None,
209 PathResolution::AssocItem(_) => None, 210 PathResolution::AssocItem(_) => None,
210 } 211 }
diff --git a/crates/assists/src/handlers/add_explicit_type.rs b/crates/assists/src/handlers/add_explicit_type.rs
index 563cbf505..cb1548cef 100644
--- a/crates/assists/src/handlers/add_explicit_type.rs
+++ b/crates/assists/src/handlers/add_explicit_type.rs
@@ -12,7 +12,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
12// 12//
13// ``` 13// ```
14// fn main() { 14// fn main() {
15// let x<|> = 92; 15// let x$0 = 92;
16// } 16// }
17// ``` 17// ```
18// -> 18// ->
@@ -81,21 +81,17 @@ mod tests {
81 81
82 #[test] 82 #[test]
83 fn add_explicit_type_target() { 83 fn add_explicit_type_target() {
84 check_assist_target(add_explicit_type, "fn f() { let a<|> = 1; }", "a"); 84 check_assist_target(add_explicit_type, "fn f() { let a$0 = 1; }", "a");
85 } 85 }
86 86
87 #[test] 87 #[test]
88 fn add_explicit_type_works_for_simple_expr() { 88 fn add_explicit_type_works_for_simple_expr() {
89 check_assist(add_explicit_type, "fn f() { let a<|> = 1; }", "fn f() { let a: i32 = 1; }"); 89 check_assist(add_explicit_type, "fn f() { let a$0 = 1; }", "fn f() { let a: i32 = 1; }");
90 } 90 }
91 91
92 #[test] 92 #[test]
93 fn add_explicit_type_works_for_underscore() { 93 fn add_explicit_type_works_for_underscore() {
94 check_assist( 94 check_assist(add_explicit_type, "fn f() { let a$0: _ = 1; }", "fn f() { let a: i32 = 1; }");
95 add_explicit_type,
96 "fn f() { let a<|>: _ = 1; }",
97 "fn f() { let a: i32 = 1; }",
98 );
99 } 95 }
100 96
101 #[test] 97 #[test]
@@ -109,7 +105,7 @@ mod tests {
109 } 105 }
110 106
111 fn f() { 107 fn f() {
112 let a<|>: Option<_> = Option::Some(1); 108 let a$0: Option<_> = Option::Some(1);
113 }"#, 109 }"#,
114 r#" 110 r#"
115 enum Option<T> { 111 enum Option<T> {
@@ -127,7 +123,7 @@ mod tests {
127 fn add_explicit_type_works_for_macro_call() { 123 fn add_explicit_type_works_for_macro_call() {
128 check_assist( 124 check_assist(
129 add_explicit_type, 125 add_explicit_type,
130 r"macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }", 126 r"macro_rules! v { () => {0u64} } fn f() { let a$0 = v!(); }",
131 r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }", 127 r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }",
132 ); 128 );
133 } 129 }
@@ -136,31 +132,31 @@ mod tests {
136 fn add_explicit_type_works_for_macro_call_recursive() { 132 fn add_explicit_type_works_for_macro_call_recursive() {
137 check_assist( 133 check_assist(
138 add_explicit_type, 134 add_explicit_type,
139 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }"#, 135 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a$0 = v!(); }"#,
140 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a: u64 = v!(); }"#, 136 r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a: u64 = v!(); }"#,
141 ); 137 );
142 } 138 }
143 139
144 #[test] 140 #[test]
145 fn add_explicit_type_not_applicable_if_ty_not_inferred() { 141 fn add_explicit_type_not_applicable_if_ty_not_inferred() {
146 check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }"); 142 check_assist_not_applicable(add_explicit_type, "fn f() { let a$0 = None; }");
147 } 143 }
148 144
149 #[test] 145 #[test]
150 fn add_explicit_type_not_applicable_if_ty_already_specified() { 146 fn add_explicit_type_not_applicable_if_ty_already_specified() {
151 check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: i32 = 1; }"); 147 check_assist_not_applicable(add_explicit_type, "fn f() { let a$0: i32 = 1; }");
152 } 148 }
153 149
154 #[test] 150 #[test]
155 fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() { 151 fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() {
156 check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: (i32, i32) = (3, 4); }"); 152 check_assist_not_applicable(add_explicit_type, "fn f() { let a$0: (i32, i32) = (3, 4); }");
157 } 153 }
158 154
159 #[test] 155 #[test]
160 fn add_explicit_type_not_applicable_if_cursor_after_equals() { 156 fn add_explicit_type_not_applicable_if_cursor_after_equals() {
161 check_assist_not_applicable( 157 check_assist_not_applicable(
162 add_explicit_type, 158 add_explicit_type,
163 "fn f() {let a =<|> match 1 {2 => 3, 3 => 5};}", 159 "fn f() {let a =$0 match 1 {2 => 3, 3 => 5};}",
164 ) 160 )
165 } 161 }
166 162
@@ -168,7 +164,7 @@ mod tests {
168 fn add_explicit_type_not_applicable_if_cursor_before_let() { 164 fn add_explicit_type_not_applicable_if_cursor_before_let() {
169 check_assist_not_applicable( 165 check_assist_not_applicable(
170 add_explicit_type, 166 add_explicit_type,
171 "fn f() <|>{let a = match 1 {2 => 3, 3 => 5};}", 167 "fn f() $0{let a = match 1 {2 => 3, 3 => 5};}",
172 ) 168 )
173 } 169 }
174 170
@@ -178,7 +174,7 @@ mod tests {
178 add_explicit_type, 174 add_explicit_type,
179 r#" 175 r#"
180fn main() { 176fn main() {
181 let multiply_by_two<|> = |i| i * 3; 177 let multiply_by_two$0 = |i| i * 3;
182 let six = multiply_by_two(2); 178 let six = multiply_by_two(2);
183}"#, 179}"#,
184 ) 180 )
@@ -195,7 +191,7 @@ struct Test<K, T = u8> {
195} 191}
196 192
197fn main() { 193fn main() {
198 let test<|> = Test { t: 23u8, k: 33 }; 194 let test$0 = Test { t: 23u8, k: 33 };
199}"#, 195}"#,
200 r#" 196 r#"
201struct Test<K, T = u8> { 197struct Test<K, T = u8> {
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs
index 7df05b841..63cea754d 100644
--- a/crates/assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/assists/src/handlers/add_missing_impl_members.rs
@@ -20,7 +20,7 @@ use crate::{
20// fn bar(&self) {} 20// fn bar(&self) {}
21// } 21// }
22// 22//
23// impl Trait<u32> for () {<|> 23// impl Trait<u32> for () {$0
24// 24//
25// } 25// }
26// ``` 26// ```
@@ -63,7 +63,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -
63// 63//
64// impl Trait for () { 64// impl Trait for () {
65// type X = (); 65// type X = ();
66// fn foo(&self) {}<|> 66// fn foo(&self) {}$0
67// 67//
68// } 68// }
69// ``` 69// ```
@@ -166,7 +166,7 @@ struct S;
166 166
167impl Foo for S { 167impl Foo for S {
168 fn bar(&self) {} 168 fn bar(&self) {}
169<|> 169$0
170}"#, 170}"#,
171 r#" 171 r#"
172trait Foo { 172trait Foo {
@@ -214,7 +214,7 @@ struct S;
214 214
215impl Foo for S { 215impl Foo for S {
216 fn bar(&self) {} 216 fn bar(&self) {}
217<|> 217$0
218}"#, 218}"#,
219 r#" 219 r#"
220trait Foo { 220trait Foo {
@@ -242,7 +242,7 @@ impl Foo for S {
242 r#" 242 r#"
243trait Foo { fn foo(&self); } 243trait Foo { fn foo(&self); }
244struct S; 244struct S;
245impl Foo for S { <|> }"#, 245impl Foo for S { $0 }"#,
246 r#" 246 r#"
247trait Foo { fn foo(&self); } 247trait Foo { fn foo(&self); }
248struct S; 248struct S;
@@ -261,7 +261,7 @@ impl Foo for S {
261 r#" 261 r#"
262trait Foo { fn foo(&self); } 262trait Foo { fn foo(&self); }
263struct S; 263struct S;
264impl Foo for S<|>"#, 264impl Foo for S$0"#,
265 r#" 265 r#"
266trait Foo { fn foo(&self); } 266trait Foo { fn foo(&self); }
267struct S; 267struct S;
@@ -280,7 +280,7 @@ impl Foo for S {
280 r#" 280 r#"
281trait Foo<T> { fn foo(&self, t: T) -> &T; } 281trait Foo<T> { fn foo(&self, t: T) -> &T; }
282struct S; 282struct S;
283impl Foo<u32> for S { <|> }"#, 283impl Foo<u32> for S { $0 }"#,
284 r#" 284 r#"
285trait Foo<T> { fn foo(&self, t: T) -> &T; } 285trait Foo<T> { fn foo(&self, t: T) -> &T; }
286struct S; 286struct S;
@@ -299,7 +299,7 @@ impl Foo<u32> for S {
299 r#" 299 r#"
300trait Foo<T> { fn foo(&self, t: T) -> &T; } 300trait Foo<T> { fn foo(&self, t: T) -> &T; }
301struct S; 301struct S;
302impl<U> Foo<U> for S { <|> }"#, 302impl<U> Foo<U> for S { $0 }"#,
303 r#" 303 r#"
304trait Foo<T> { fn foo(&self, t: T) -> &T; } 304trait Foo<T> { fn foo(&self, t: T) -> &T; }
305struct S; 305struct S;
@@ -318,7 +318,7 @@ impl<U> Foo<U> for S {
318 r#" 318 r#"
319trait Foo { fn foo(&self); } 319trait Foo { fn foo(&self); }
320struct S; 320struct S;
321impl Foo for S {}<|>"#, 321impl Foo for S {}$0"#,
322 r#" 322 r#"
323trait Foo { fn foo(&self); } 323trait Foo { fn foo(&self); }
324struct S; 324struct S;
@@ -340,7 +340,7 @@ mod foo {
340 trait Foo { fn foo(&self, bar: Bar); } 340 trait Foo { fn foo(&self, bar: Bar); }
341} 341}
342struct S; 342struct S;
343impl foo::Foo for S { <|> }"#, 343impl foo::Foo for S { $0 }"#,
344 r#" 344 r#"
345mod foo { 345mod foo {
346 pub struct Bar; 346 pub struct Bar;
@@ -370,7 +370,7 @@ mod foo {
370use foo::bar; 370use foo::bar;
371 371
372struct S; 372struct S;
373impl bar::Foo for S { <|> }"#, 373impl bar::Foo for S { $0 }"#,
374 r#" 374 r#"
375mod foo { 375mod foo {
376 pub mod bar { 376 pub mod bar {
@@ -400,7 +400,7 @@ mod foo {
400 trait Foo { fn foo(&self, bar: Bar<u32>); } 400 trait Foo { fn foo(&self, bar: Bar<u32>); }
401} 401}
402struct S; 402struct S;
403impl foo::Foo for S { <|> }"#, 403impl foo::Foo for S { $0 }"#,
404 r#" 404 r#"
405mod foo { 405mod foo {
406 pub struct Bar<T>; 406 pub struct Bar<T>;
@@ -425,7 +425,7 @@ mod foo {
425 trait Foo<T> { fn foo(&self, bar: Bar<T>); } 425 trait Foo<T> { fn foo(&self, bar: Bar<T>); }
426} 426}
427struct S; 427struct S;
428impl foo::Foo<u32> for S { <|> }"#, 428impl foo::Foo<u32> for S { $0 }"#,
429 r#" 429 r#"
430mod foo { 430mod foo {
431 pub struct Bar<T>; 431 pub struct Bar<T>;
@@ -452,7 +452,7 @@ mod foo {
452} 452}
453struct Param; 453struct Param;
454struct S; 454struct S;
455impl foo::Foo<Param> for S { <|> }"#, 455impl foo::Foo<Param> for S { $0 }"#,
456 r#" 456 r#"
457mod foo { 457mod foo {
458 trait Foo<T> { fn foo(&self, bar: T); } 458 trait Foo<T> { fn foo(&self, bar: T); }
@@ -479,7 +479,7 @@ mod foo {
479 trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); } 479 trait Foo { fn foo(&self, bar: Bar<u32>::Assoc); }
480} 480}
481struct S; 481struct S;
482impl foo::Foo for S { <|> }"#, 482impl foo::Foo for S { $0 }"#,
483 r#" 483 r#"
484mod foo { 484mod foo {
485 pub struct Bar<T>; 485 pub struct Bar<T>;
@@ -506,7 +506,7 @@ mod foo {
506 trait Foo { fn foo(&self, bar: Bar<Baz>); } 506 trait Foo { fn foo(&self, bar: Bar<Baz>); }
507} 507}
508struct S; 508struct S;
509impl foo::Foo for S { <|> }"#, 509impl foo::Foo for S { $0 }"#,
510 r#" 510 r#"
511mod foo { 511mod foo {
512 pub struct Bar<T>; 512 pub struct Bar<T>;
@@ -532,7 +532,7 @@ mod foo {
532 trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); } 532 trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); }
533} 533}
534struct S; 534struct S;
535impl foo::Foo for S { <|> }"#, 535impl foo::Foo for S { $0 }"#,
536 r#" 536 r#"
537mod foo { 537mod foo {
538 pub trait Fn<Args> { type Output; } 538 pub trait Fn<Args> { type Output; }
@@ -554,7 +554,7 @@ impl foo::Foo for S {
554 r#" 554 r#"
555trait Foo; 555trait Foo;
556struct S; 556struct S;
557impl Foo for S { <|> }"#, 557impl Foo for S { $0 }"#,
558 ) 558 )
559 } 559 }
560 560
@@ -568,7 +568,7 @@ trait Foo {
568 fn valid(some: u32) -> bool { false } 568 fn valid(some: u32) -> bool { false }
569} 569}
570struct S; 570struct S;
571impl Foo for S { <|> }"#, 571impl Foo for S { $0 }"#,
572 ) 572 )
573 } 573 }
574 574
@@ -586,7 +586,7 @@ trait Foo {
586 fn foo(&self); 586 fn foo(&self);
587} 587}
588struct S; 588struct S;
589impl Foo for S {}<|>"#, 589impl Foo for S {}$0"#,
590 r#" 590 r#"
591#[doc(alias = "test alias")] 591#[doc(alias = "test alias")]
592trait Foo { 592trait Foo {
@@ -621,7 +621,7 @@ trait Foo {
621 fn foo(some: u32) -> bool; 621 fn foo(some: u32) -> bool;
622} 622}
623struct S; 623struct S;
624impl Foo for S { <|> }"#, 624impl Foo for S { $0 }"#,
625 r#" 625 r#"
626trait Foo { 626trait Foo {
627 type Output; 627 type Output;
@@ -648,7 +648,7 @@ trait Foo<T = Self> {
648} 648}
649 649
650struct S; 650struct S;
651impl Foo for S { <|> }"#, 651impl Foo for S { $0 }"#,
652 r#" 652 r#"
653trait Foo<T = Self> { 653trait Foo<T = Self> {
654 fn bar(&self, other: &T); 654 fn bar(&self, other: &T);
@@ -673,7 +673,7 @@ trait Foo<T1, T2 = Self> {
673} 673}
674 674
675struct S<T>; 675struct S<T>;
676impl Foo<T> for S<T> { <|> }"#, 676impl Foo<T> for S<T> { $0 }"#,
677 r#" 677 r#"
678trait Foo<T1, T2 = Self> { 678trait Foo<T1, T2 = Self> {
679 fn bar(&self, this: &T1, that: &T2); 679 fn bar(&self, this: &T1, that: &T2);
@@ -697,7 +697,7 @@ trait Tr {
697 type Ty: Copy + 'static; 697 type Ty: Copy + 'static;
698} 698}
699 699
700impl Tr for ()<|> { 700impl Tr for ()$0 {
701}"#, 701}"#,
702 r#" 702 r#"
703trait Tr { 703trait Tr {
@@ -719,7 +719,7 @@ trait Tr {
719 fn foo(); 719 fn foo();
720} 720}
721 721
722impl Tr for ()<|> { 722impl Tr for ()$0 {
723 +++ 723 +++
724}"#, 724}"#,
725 r#" 725 r#"
@@ -745,7 +745,7 @@ trait Tr {
745 fn foo(); 745 fn foo();
746} 746}
747 747
748impl Tr for ()<|> { 748impl Tr for ()$0 {
749 // very important 749 // very important
750}"#, 750}"#,
751 r#" 751 r#"
@@ -771,7 +771,7 @@ trait Test {
771 fn foo(&self, x: crate) 771 fn foo(&self, x: crate)
772} 772}
773impl Test for () { 773impl Test for () {
774 <|> 774 $0
775} 775}
776"#, 776"#,
777 r#" 777 r#"
@@ -796,7 +796,7 @@ trait Foo<BAR> {
796 fn foo(&self, bar: BAR); 796 fn foo(&self, bar: BAR);
797} 797}
798impl Foo for () { 798impl Foo for () {
799 <|> 799 $0
800} 800}
801"#, 801"#,
802 r#" 802 r#"
diff --git a/crates/assists/src/handlers/add_turbo_fish.rs b/crates/assists/src/handlers/add_turbo_fish.rs
index 1f486c013..8e9ea4fad 100644
--- a/crates/assists/src/handlers/add_turbo_fish.rs
+++ b/crates/assists/src/handlers/add_turbo_fish.rs
@@ -14,7 +14,7 @@ use crate::{
14// ``` 14// ```
15// fn make<T>() -> T { todo!() } 15// fn make<T>() -> T { todo!() }
16// fn main() { 16// fn main() {
17// let x = make<|>(); 17// let x = make$0();
18// } 18// }
19// ``` 19// ```
20// -> 20// ->
@@ -77,7 +77,7 @@ mod tests {
77 r#" 77 r#"
78fn make<T>() -> T {} 78fn make<T>() -> T {}
79fn main() { 79fn main() {
80 make<|>(); 80 make$0();
81} 81}
82"#, 82"#,
83 r#" 83 r#"
@@ -97,7 +97,7 @@ fn main() {
97 r#" 97 r#"
98fn make<T>() -> T {} 98fn make<T>() -> T {}
99fn main() { 99fn main() {
100 make()<|>; 100 make()$0;
101} 101}
102"#, 102"#,
103 r#" 103 r#"
@@ -119,7 +119,7 @@ impl S {
119 fn make<T>(&self) -> T {} 119 fn make<T>(&self) -> T {}
120} 120}
121fn main() { 121fn main() {
122 S.make<|>(); 122 S.make$0();
123} 123}
124"#, 124"#,
125 r#" 125 r#"
@@ -142,7 +142,7 @@ fn main() {
142 r#" 142 r#"
143fn make<T>() -> T {} 143fn make<T>() -> T {}
144fn main() { 144fn main() {
145 make<|>::<()>(); 145 make$0::<()>();
146} 146}
147"#, 147"#,
148 ); 148 );
@@ -156,7 +156,7 @@ fn main() {
156 r#" 156 r#"
157fn make() -> () {} 157fn make() -> () {}
158fn main() { 158fn main() {
159 make<|>(); 159 make$0();
160} 160}
161"#, 161"#,
162 ); 162 );
diff --git a/crates/assists/src/handlers/apply_demorgan.rs b/crates/assists/src/handlers/apply_demorgan.rs
index 1a6fdafda..ed4d11455 100644
--- a/crates/assists/src/handlers/apply_demorgan.rs
+++ b/crates/assists/src/handlers/apply_demorgan.rs
@@ -12,7 +12,7 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin
12// 12//
13// ``` 13// ```
14// fn main() { 14// fn main() {
15// if x != 4 ||<|> !y {} 15// if x != 4 ||$0 !y {}
16// } 16// }
17// ``` 17// ```
18// -> 18// ->
@@ -68,26 +68,26 @@ mod tests {
68 68
69 #[test] 69 #[test]
70 fn demorgan_turns_and_into_or() { 70 fn demorgan_turns_and_into_or() {
71 check_assist(apply_demorgan, "fn f() { !x &&<|> !x }", "fn f() { !(x || x) }") 71 check_assist(apply_demorgan, "fn f() { !x &&$0 !x }", "fn f() { !(x || x) }")
72 } 72 }
73 73
74 #[test] 74 #[test]
75 fn demorgan_turns_or_into_and() { 75 fn demorgan_turns_or_into_and() {
76 check_assist(apply_demorgan, "fn f() { !x ||<|> !x }", "fn f() { !(x && x) }") 76 check_assist(apply_demorgan, "fn f() { !x ||$0 !x }", "fn f() { !(x && x) }")
77 } 77 }
78 78
79 #[test] 79 #[test]
80 fn demorgan_removes_inequality() { 80 fn demorgan_removes_inequality() {
81 check_assist(apply_demorgan, "fn f() { x != x ||<|> !x }", "fn f() { !(x == x && x) }") 81 check_assist(apply_demorgan, "fn f() { x != x ||$0 !x }", "fn f() { !(x == x && x) }")
82 } 82 }
83 83
84 #[test] 84 #[test]
85 fn demorgan_general_case() { 85 fn demorgan_general_case() {
86 check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x && !x) }") 86 check_assist(apply_demorgan, "fn f() { x ||$0 x }", "fn f() { !(!x && !x) }")
87 } 87 }
88 88
89 #[test] 89 #[test]
90 fn demorgan_doesnt_apply_with_cursor_not_on_op() { 90 fn demorgan_doesnt_apply_with_cursor_not_on_op() {
91 check_assist_not_applicable(apply_demorgan, "fn f() { <|> !x || !x }") 91 check_assist_not_applicable(apply_demorgan, "fn f() { $0 !x || !x }")
92 } 92 }
93} 93}
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index bd5bba646..55620f0f3 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -70,7 +70,7 @@ use crate::{
70// 70//
71// ``` 71// ```
72// fn main() { 72// fn main() {
73// let map = HashMap<|>::new(); 73// let map = HashMap$0::new();
74// } 74// }
75// # pub mod std { pub mod collections { pub struct HashMap { } } } 75// # pub mod std { pub mod collections { pub struct HashMap { } } }
76// ``` 76// ```
@@ -151,7 +151,7 @@ mod tests {
151 151
152 use std::fmt; 152 use std::fmt;
153 153
154 <|>Formatter 154 $0Formatter
155 ", 155 ",
156 r" 156 r"
157 mod std { 157 mod std {
@@ -172,7 +172,7 @@ mod tests {
172 check_assist( 172 check_assist(
173 auto_import, 173 auto_import,
174 r" 174 r"
175 <|>PubStruct 175 $0PubStruct
176 176
177 pub mod PubMod { 177 pub mod PubMod {
178 pub struct PubStruct; 178 pub struct PubStruct;
@@ -198,7 +198,7 @@ mod tests {
198 macro_rules! foo { 198 macro_rules! foo {
199 ($i:ident) => { fn foo(a: $i) {} } 199 ($i:ident) => { fn foo(a: $i) {} }
200 } 200 }
201 foo!(Pub<|>Struct); 201 foo!(Pub$0Struct);
202 202
203 pub mod PubMod { 203 pub mod PubMod {
204 pub struct PubStruct; 204 pub struct PubStruct;
@@ -227,7 +227,7 @@ mod tests {
227 use PubMod::PubStruct1; 227 use PubMod::PubStruct1;
228 228
229 struct Test { 229 struct Test {
230 test: Pub<|>Struct2<u8>, 230 test: Pub$0Struct2<u8>,
231 } 231 }
232 232
233 pub mod PubMod { 233 pub mod PubMod {
@@ -259,7 +259,7 @@ mod tests {
259 check_assist( 259 check_assist(
260 auto_import, 260 auto_import,
261 r" 261 r"
262 PubSt<|>ruct 262 PubSt$0ruct
263 263
264 pub mod PubMod1 { 264 pub mod PubMod1 {
265 pub struct PubStruct; 265 pub struct PubStruct;
@@ -296,7 +296,7 @@ mod tests {
296 r" 296 r"
297 use PubMod::PubStruct; 297 use PubMod::PubStruct;
298 298
299 PubStruct<|> 299 PubStruct$0
300 300
301 pub mod PubMod { 301 pub mod PubMod {
302 pub struct PubStruct; 302 pub struct PubStruct;
@@ -310,7 +310,7 @@ mod tests {
310 check_assist_not_applicable( 310 check_assist_not_applicable(
311 auto_import, 311 auto_import,
312 r" 312 r"
313 PrivateStruct<|> 313 PrivateStruct$0
314 314
315 pub mod PubMod { 315 pub mod PubMod {
316 struct PrivateStruct; 316 struct PrivateStruct;
@@ -324,7 +324,7 @@ mod tests {
324 check_assist_not_applicable( 324 check_assist_not_applicable(
325 auto_import, 325 auto_import,
326 " 326 "
327 PubStruct<|>", 327 PubStruct$0",
328 ); 328 );
329 } 329 }
330 330
@@ -333,7 +333,7 @@ mod tests {
333 check_assist_not_applicable( 333 check_assist_not_applicable(
334 auto_import, 334 auto_import,
335 r" 335 r"
336 use PubStruct<|>; 336 use PubStruct$0;
337 337
338 pub mod PubMod { 338 pub mod PubMod {
339 pub struct PubStruct; 339 pub struct PubStruct;
@@ -346,7 +346,7 @@ mod tests {
346 check_assist( 346 check_assist(
347 auto_import, 347 auto_import,
348 r" 348 r"
349 test_function<|> 349 test_function$0
350 350
351 pub mod PubMod { 351 pub mod PubMod {
352 pub fn test_function() {}; 352 pub fn test_function() {};
@@ -377,7 +377,7 @@ macro_rules! foo {
377 377
378//- /main.rs crate:main deps:crate_with_macro 378//- /main.rs crate:main deps:crate_with_macro
379fn main() { 379fn main() {
380 foo<|> 380 foo$0
381} 381}
382", 382",
383 r"use crate_with_macro::foo; 383 r"use crate_with_macro::foo;
@@ -395,7 +395,7 @@ fn main() {
395 auto_import, 395 auto_import,
396 r" 396 r"
397 struct AssistInfo { 397 struct AssistInfo {
398 group_label: Option<<|>GroupLabel>, 398 group_label: Option<$0GroupLabel>,
399 } 399 }
400 400
401 mod m { pub struct GroupLabel; } 401 mod m { pub struct GroupLabel; }
@@ -419,7 +419,7 @@ fn main() {
419 419
420 use mod1::mod2; 420 use mod1::mod2;
421 fn main() { 421 fn main() {
422 mod2::mod3::TestStruct<|> 422 mod2::mod3::TestStruct$0
423 } 423 }
424 ", 424 ",
425 ); 425 );
@@ -436,7 +436,7 @@ fn main() {
436 436
437 use test_mod::test_function; 437 use test_mod::test_function;
438 fn main() { 438 fn main() {
439 test_function<|> 439 test_function$0
440 } 440 }
441 ", 441 ",
442 ); 442 );
@@ -455,7 +455,7 @@ fn main() {
455 } 455 }
456 456
457 fn main() { 457 fn main() {
458 TestStruct::test_function<|> 458 TestStruct::test_function$0
459 } 459 }
460 ", 460 ",
461 r" 461 r"
@@ -488,7 +488,7 @@ fn main() {
488 } 488 }
489 489
490 fn main() { 490 fn main() {
491 TestStruct::TEST_CONST<|> 491 TestStruct::TEST_CONST$0
492 } 492 }
493 ", 493 ",
494 r" 494 r"
@@ -524,7 +524,7 @@ fn main() {
524 } 524 }
525 525
526 fn main() { 526 fn main() {
527 test_mod::TestStruct::test_function<|> 527 test_mod::TestStruct::test_function$0
528 } 528 }
529 ", 529 ",
530 r" 530 r"
@@ -573,7 +573,7 @@ fn main() {
573 573
574 use test_mod::TestTrait2; 574 use test_mod::TestTrait2;
575 fn main() { 575 fn main() {
576 test_mod::TestEnum::test_function<|>; 576 test_mod::TestEnum::test_function$0;
577 } 577 }
578 ", 578 ",
579 ) 579 )
@@ -595,7 +595,7 @@ fn main() {
595 } 595 }
596 596
597 fn main() { 597 fn main() {
598 test_mod::TestStruct::TEST_CONST<|> 598 test_mod::TestStruct::TEST_CONST$0
599 } 599 }
600 ", 600 ",
601 r" 601 r"
@@ -644,7 +644,7 @@ fn main() {
644 644
645 use test_mod::TestTrait2; 645 use test_mod::TestTrait2;
646 fn main() { 646 fn main() {
647 test_mod::TestEnum::TEST_CONST<|>; 647 test_mod::TestEnum::TEST_CONST$0;
648 } 648 }
649 ", 649 ",
650 ) 650 )
@@ -667,7 +667,7 @@ fn main() {
667 667
668 fn main() { 668 fn main() {
669 let test_struct = test_mod::TestStruct {}; 669 let test_struct = test_mod::TestStruct {};
670 test_struct.test_meth<|>od() 670 test_struct.test_meth$0od()
671 } 671 }
672 ", 672 ",
673 r" 673 r"
@@ -699,7 +699,7 @@ fn main() {
699 //- /main.rs crate:main deps:dep 699 //- /main.rs crate:main deps:dep
700 fn main() { 700 fn main() {
701 let test_struct = dep::test_mod::TestStruct {}; 701 let test_struct = dep::test_mod::TestStruct {};
702 test_struct.test_meth<|>od() 702 test_struct.test_meth$0od()
703 } 703 }
704 //- /dep.rs crate:dep 704 //- /dep.rs crate:dep
705 pub mod test_mod { 705 pub mod test_mod {
@@ -730,7 +730,7 @@ fn main() {
730 r" 730 r"
731 //- /main.rs crate:main deps:dep 731 //- /main.rs crate:main deps:dep
732 fn main() { 732 fn main() {
733 dep::test_mod::TestStruct::test_func<|>tion 733 dep::test_mod::TestStruct::test_func$0tion
734 } 734 }
735 //- /dep.rs crate:dep 735 //- /dep.rs crate:dep
736 pub mod test_mod { 736 pub mod test_mod {
@@ -760,7 +760,7 @@ fn main() {
760 r" 760 r"
761 //- /main.rs crate:main deps:dep 761 //- /main.rs crate:main deps:dep
762 fn main() { 762 fn main() {
763 dep::test_mod::TestStruct::CONST<|> 763 dep::test_mod::TestStruct::CONST$0
764 } 764 }
765 //- /dep.rs crate:dep 765 //- /dep.rs crate:dep
766 pub mod test_mod { 766 pub mod test_mod {
@@ -791,7 +791,7 @@ fn main() {
791 //- /main.rs crate:main deps:dep 791 //- /main.rs crate:main deps:dep
792 fn main() { 792 fn main() {
793 let test_struct = dep::test_mod::TestStruct {}; 793 let test_struct = dep::test_mod::TestStruct {};
794 test_struct.test_func<|>tion() 794 test_struct.test_func$0tion()
795 } 795 }
796 //- /dep.rs crate:dep 796 //- /dep.rs crate:dep
797 pub mod test_mod { 797 pub mod test_mod {
@@ -815,7 +815,7 @@ fn main() {
815 //- /main.rs crate:main deps:dep 815 //- /main.rs crate:main deps:dep
816 fn main() { 816 fn main() {
817 let test_struct = dep::test_mod::TestStruct {}; 817 let test_struct = dep::test_mod::TestStruct {};
818 test_struct.test_meth<|>od() 818 test_struct.test_meth$0od()
819 } 819 }
820 //- /dep.rs crate:dep 820 //- /dep.rs crate:dep
821 pub mod test_mod { 821 pub mod test_mod {
@@ -858,7 +858,7 @@ fn main() {
858 use test_mod::TestTrait2; 858 use test_mod::TestTrait2;
859 fn main() { 859 fn main() {
860 let one = test_mod::TestEnum::One; 860 let one = test_mod::TestEnum::One;
861 one.test<|>_method(); 861 one.test$0_method();
862 } 862 }
863 ", 863 ",
864 ) 864 )
@@ -874,7 +874,7 @@ pub struct Struct;
874 874
875//- /main.rs crate:main deps:dep 875//- /main.rs crate:main deps:dep
876fn main() { 876fn main() {
877 Struct<|> 877 Struct$0
878} 878}
879", 879",
880 r"use dep::Struct; 880 r"use dep::Struct;
@@ -902,7 +902,7 @@ pub fn panic_fmt() {}
902//- /main.rs crate:main deps:dep 902//- /main.rs crate:main deps:dep
903struct S; 903struct S;
904 904
905impl f<|>mt::Display for S {} 905impl f$0mt::Display for S {}
906", 906",
907 r"use dep::fmt; 907 r"use dep::fmt;
908 908
@@ -930,7 +930,7 @@ mac!();
930 930
931//- /main.rs crate:main deps:dep 931//- /main.rs crate:main deps:dep
932fn main() { 932fn main() {
933 Cheese<|>; 933 Cheese$0;
934} 934}
935", 935",
936 r"use dep::Cheese; 936 r"use dep::Cheese;
@@ -954,7 +954,7 @@ pub struct fmt;
954 954
955//- /main.rs crate:main deps:dep 955//- /main.rs crate:main deps:dep
956fn main() { 956fn main() {
957 FMT<|>; 957 FMT$0;
958} 958}
959", 959",
960 r"use dep::FMT; 960 r"use dep::FMT;
diff --git a/crates/assists/src/handlers/change_visibility.rs b/crates/assists/src/handlers/change_visibility.rs
index 22d7c95d9..ac8c44124 100644
--- a/crates/assists/src/handlers/change_visibility.rs
+++ b/crates/assists/src/handlers/change_visibility.rs
@@ -13,7 +13,7 @@ use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
13// Adds or changes existing visibility specifier. 13// Adds or changes existing visibility specifier.
14// 14//
15// ``` 15// ```
16// <|>fn frobnicate() {} 16// $0fn frobnicate() {}
17// ``` 17// ```
18// -> 18// ->
19// ``` 19// ```
@@ -118,23 +118,23 @@ mod tests {
118 118
119 #[test] 119 #[test]
120 fn change_visibility_adds_pub_crate_to_items() { 120 fn change_visibility_adds_pub_crate_to_items() {
121 check_assist(change_visibility, "<|>fn foo() {}", "pub(crate) fn foo() {}"); 121 check_assist(change_visibility, "$0fn foo() {}", "pub(crate) fn foo() {}");
122 check_assist(change_visibility, "f<|>n foo() {}", "pub(crate) fn foo() {}"); 122 check_assist(change_visibility, "f$0n foo() {}", "pub(crate) fn foo() {}");
123 check_assist(change_visibility, "<|>struct Foo {}", "pub(crate) struct Foo {}"); 123 check_assist(change_visibility, "$0struct Foo {}", "pub(crate) struct Foo {}");
124 check_assist(change_visibility, "<|>mod foo {}", "pub(crate) mod foo {}"); 124 check_assist(change_visibility, "$0mod foo {}", "pub(crate) mod foo {}");
125 check_assist(change_visibility, "<|>trait Foo {}", "pub(crate) trait Foo {}"); 125 check_assist(change_visibility, "$0trait Foo {}", "pub(crate) trait Foo {}");
126 check_assist(change_visibility, "m<|>od {}", "pub(crate) mod {}"); 126 check_assist(change_visibility, "m$0od {}", "pub(crate) mod {}");
127 check_assist(change_visibility, "unsafe f<|>n foo() {}", "pub(crate) unsafe fn foo() {}"); 127 check_assist(change_visibility, "unsafe f$0n foo() {}", "pub(crate) unsafe fn foo() {}");
128 } 128 }
129 129
130 #[test] 130 #[test]
131 fn change_visibility_works_with_struct_fields() { 131 fn change_visibility_works_with_struct_fields() {
132 check_assist( 132 check_assist(
133 change_visibility, 133 change_visibility,
134 r"struct S { <|>field: u32 }", 134 r"struct S { $0field: u32 }",
135 r"struct S { pub(crate) field: u32 }", 135 r"struct S { pub(crate) field: u32 }",
136 ); 136 );
137 check_assist(change_visibility, r"struct S ( <|>u32 )", r"struct S ( pub(crate) u32 )"); 137 check_assist(change_visibility, r"struct S ( $0u32 )", r"struct S ( pub(crate) u32 )");
138 } 138 }
139 139
140 #[test] 140 #[test]
@@ -142,33 +142,33 @@ mod tests {
142 mark::check!(change_visibility_field_false_positive); 142 mark::check!(change_visibility_field_false_positive);
143 check_assist_not_applicable( 143 check_assist_not_applicable(
144 change_visibility, 144 change_visibility,
145 r"struct S { field: [(); { let <|>x = ();}] }", 145 r"struct S { field: [(); { let $0x = ();}] }",
146 ) 146 )
147 } 147 }
148 148
149 #[test] 149 #[test]
150 fn change_visibility_pub_to_pub_crate() { 150 fn change_visibility_pub_to_pub_crate() {
151 check_assist(change_visibility, "<|>pub fn foo() {}", "pub(crate) fn foo() {}") 151 check_assist(change_visibility, "$0pub fn foo() {}", "pub(crate) fn foo() {}")
152 } 152 }
153 153
154 #[test] 154 #[test]
155 fn change_visibility_pub_crate_to_pub() { 155 fn change_visibility_pub_crate_to_pub() {
156 check_assist(change_visibility, "<|>pub(crate) fn foo() {}", "pub fn foo() {}") 156 check_assist(change_visibility, "$0pub(crate) fn foo() {}", "pub fn foo() {}")
157 } 157 }
158 158
159 #[test] 159 #[test]
160 fn change_visibility_const() { 160 fn change_visibility_const() {
161 check_assist(change_visibility, "<|>const FOO = 3u8;", "pub(crate) const FOO = 3u8;"); 161 check_assist(change_visibility, "$0const FOO = 3u8;", "pub(crate) const FOO = 3u8;");
162 } 162 }
163 163
164 #[test] 164 #[test]
165 fn change_visibility_static() { 165 fn change_visibility_static() {
166 check_assist(change_visibility, "<|>static FOO = 3u8;", "pub(crate) static FOO = 3u8;"); 166 check_assist(change_visibility, "$0static FOO = 3u8;", "pub(crate) static FOO = 3u8;");
167 } 167 }
168 168
169 #[test] 169 #[test]
170 fn change_visibility_type_alias() { 170 fn change_visibility_type_alias() {
171 check_assist(change_visibility, "<|>type T = ();", "pub(crate) type T = ();"); 171 check_assist(change_visibility, "$0type T = ();", "pub(crate) type T = ();");
172 } 172 }
173 173
174 #[test] 174 #[test]
@@ -181,7 +181,7 @@ mod tests {
181 // comments 181 // comments
182 182
183 #[derive(Debug)] 183 #[derive(Debug)]
184 <|>struct Foo; 184 $0struct Foo;
185 ", 185 ",
186 r" 186 r"
187 /// docs 187 /// docs
@@ -199,14 +199,14 @@ mod tests {
199 check_assist_not_applicable( 199 check_assist_not_applicable(
200 change_visibility, 200 change_visibility,
201 r"mod foo { pub enum Foo {Foo1} } 201 r"mod foo { pub enum Foo {Foo1} }
202 fn main() { foo::Foo::Foo1<|> } ", 202 fn main() { foo::Foo::Foo1$0 } ",
203 ); 203 );
204 } 204 }
205 205
206 #[test] 206 #[test]
207 fn change_visibility_target() { 207 fn change_visibility_target() {
208 check_assist_target(change_visibility, "<|>fn foo() {}", "fn"); 208 check_assist_target(change_visibility, "$0fn foo() {}", "fn");
209 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)"); 209 check_assist_target(change_visibility, "pub(crate)$0 fn foo() {}", "pub(crate)");
210 check_assist_target(change_visibility, "struct S { <|>field: u32 }", "field"); 210 check_assist_target(change_visibility, "struct S { $0field: u32 }", "field");
211 } 211 }
212} 212}
diff --git a/crates/assists/src/handlers/convert_integer_literal.rs b/crates/assists/src/handlers/convert_integer_literal.rs
index 667115382..a8a819cfc 100644
--- a/crates/assists/src/handlers/convert_integer_literal.rs
+++ b/crates/assists/src/handlers/convert_integer_literal.rs
@@ -7,7 +7,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
7// Converts the base of integer literals to other bases. 7// Converts the base of integer literals to other bases.
8// 8//
9// ``` 9// ```
10// const _: i32 = 10<|>; 10// const _: i32 = 10$0;
11// ``` 11// ```
12// -> 12// ->
13// ``` 13// ```
@@ -65,47 +65,47 @@ mod tests {
65 65
66 #[test] 66 #[test]
67 fn binary_target() { 67 fn binary_target() {
68 check_assist_target(convert_integer_literal, "const _: i32 = 0b1010<|>;", "0b1010"); 68 check_assist_target(convert_integer_literal, "const _: i32 = 0b1010$0;", "0b1010");
69 } 69 }
70 70
71 #[test] 71 #[test]
72 fn octal_target() { 72 fn octal_target() {
73 check_assist_target(convert_integer_literal, "const _: i32 = 0o12<|>;", "0o12"); 73 check_assist_target(convert_integer_literal, "const _: i32 = 0o12$0;", "0o12");
74 } 74 }
75 75
76 #[test] 76 #[test]
77 fn decimal_target() { 77 fn decimal_target() {
78 check_assist_target(convert_integer_literal, "const _: i32 = 10<|>;", "10"); 78 check_assist_target(convert_integer_literal, "const _: i32 = 10$0;", "10");
79 } 79 }
80 80
81 #[test] 81 #[test]
82 fn hexadecimal_target() { 82 fn hexadecimal_target() {
83 check_assist_target(convert_integer_literal, "const _: i32 = 0xA<|>;", "0xA"); 83 check_assist_target(convert_integer_literal, "const _: i32 = 0xA$0;", "0xA");
84 } 84 }
85 85
86 #[test] 86 #[test]
87 fn binary_target_with_underscores() { 87 fn binary_target_with_underscores() {
88 check_assist_target(convert_integer_literal, "const _: i32 = 0b10_10<|>;", "0b10_10"); 88 check_assist_target(convert_integer_literal, "const _: i32 = 0b10_10$0;", "0b10_10");
89 } 89 }
90 90
91 #[test] 91 #[test]
92 fn octal_target_with_underscores() { 92 fn octal_target_with_underscores() {
93 check_assist_target(convert_integer_literal, "const _: i32 = 0o1_2<|>;", "0o1_2"); 93 check_assist_target(convert_integer_literal, "const _: i32 = 0o1_2$0;", "0o1_2");
94 } 94 }
95 95
96 #[test] 96 #[test]
97 fn decimal_target_with_underscores() { 97 fn decimal_target_with_underscores() {
98 check_assist_target(convert_integer_literal, "const _: i32 = 1_0<|>;", "1_0"); 98 check_assist_target(convert_integer_literal, "const _: i32 = 1_0$0;", "1_0");
99 } 99 }
100 100
101 #[test] 101 #[test]
102 fn hexadecimal_target_with_underscores() { 102 fn hexadecimal_target_with_underscores() {
103 check_assist_target(convert_integer_literal, "const _: i32 = 0x_A<|>;", "0x_A"); 103 check_assist_target(convert_integer_literal, "const _: i32 = 0x_A$0;", "0x_A");
104 } 104 }
105 105
106 #[test] 106 #[test]
107 fn convert_decimal_integer() { 107 fn convert_decimal_integer() {
108 let before = "const _: i32 = 1000<|>;"; 108 let before = "const _: i32 = 1000$0;";
109 109
110 check_assist_by_label( 110 check_assist_by_label(
111 convert_integer_literal, 111 convert_integer_literal,
@@ -131,7 +131,7 @@ mod tests {
131 131
132 #[test] 132 #[test]
133 fn convert_hexadecimal_integer() { 133 fn convert_hexadecimal_integer() {
134 let before = "const _: i32 = 0xFF<|>;"; 134 let before = "const _: i32 = 0xFF$0;";
135 135
136 check_assist_by_label( 136 check_assist_by_label(
137 convert_integer_literal, 137 convert_integer_literal,
@@ -157,7 +157,7 @@ mod tests {
157 157
158 #[test] 158 #[test]
159 fn convert_binary_integer() { 159 fn convert_binary_integer() {
160 let before = "const _: i32 = 0b11111111<|>;"; 160 let before = "const _: i32 = 0b11111111$0;";
161 161
162 check_assist_by_label( 162 check_assist_by_label(
163 convert_integer_literal, 163 convert_integer_literal,
@@ -183,7 +183,7 @@ mod tests {
183 183
184 #[test] 184 #[test]
185 fn convert_octal_integer() { 185 fn convert_octal_integer() {
186 let before = "const _: i32 = 0o377<|>;"; 186 let before = "const _: i32 = 0o377$0;";
187 187
188 check_assist_by_label( 188 check_assist_by_label(
189 convert_integer_literal, 189 convert_integer_literal,
@@ -209,7 +209,7 @@ mod tests {
209 209
210 #[test] 210 #[test]
211 fn convert_integer_with_underscores() { 211 fn convert_integer_with_underscores() {
212 let before = "const _: i32 = 1_00_0<|>;"; 212 let before = "const _: i32 = 1_00_0$0;";
213 213
214 check_assist_by_label( 214 check_assist_by_label(
215 convert_integer_literal, 215 convert_integer_literal,
@@ -235,7 +235,7 @@ mod tests {
235 235
236 #[test] 236 #[test]
237 fn convert_integer_with_suffix() { 237 fn convert_integer_with_suffix() {
238 let before = "const _: i32 = 1000i32<|>;"; 238 let before = "const _: i32 = 1000i32$0;";
239 239
240 check_assist_by_label( 240 check_assist_by_label(
241 convert_integer_literal, 241 convert_integer_literal,
@@ -262,7 +262,7 @@ mod tests {
262 #[test] 262 #[test]
263 fn convert_overflowing_literal() { 263 fn convert_overflowing_literal() {
264 let before = "const _: i32 = 264 let before = "const _: i32 =
265 111111111111111111111111111111111111111111111111111111111111111111111111<|>;"; 265 111111111111111111111111111111111111111111111111111111111111111111111111$0;";
266 check_assist_not_applicable(convert_integer_literal, before); 266 check_assist_not_applicable(convert_integer_literal, before);
267 } 267 }
268} 268}
diff --git a/crates/assists/src/handlers/early_return.rs b/crates/assists/src/handlers/early_return.rs
index 7bcc318a9..8bbbb7ed5 100644
--- a/crates/assists/src/handlers/early_return.rs
+++ b/crates/assists/src/handlers/early_return.rs
@@ -24,7 +24,7 @@ use crate::{
24// 24//
25// ``` 25// ```
26// fn main() { 26// fn main() {
27// <|>if cond { 27// $0if cond {
28// foo(); 28// foo();
29// bar(); 29// bar();
30// } 30// }
@@ -69,7 +69,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
69 69
70 let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; 70 let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
71 71
72 if parent_block.expr()? != if_expr.clone().into() { 72 if parent_block.tail_expr()? != if_expr.clone().into() {
73 return None; 73 return None;
74 } 74 }
75 75
@@ -200,7 +200,7 @@ mod tests {
200 r#" 200 r#"
201 fn main() { 201 fn main() {
202 bar(); 202 bar();
203 if<|> true { 203 if$0 true {
204 foo(); 204 foo();
205 205
206 //comment 206 //comment
@@ -230,7 +230,7 @@ mod tests {
230 r#" 230 r#"
231 fn main(n: Option<String>) { 231 fn main(n: Option<String>) {
232 bar(); 232 bar();
233 if<|> let Some(n) = n { 233 if$0 let Some(n) = n {
234 foo(n); 234 foo(n);
235 235
236 //comment 236 //comment
@@ -260,7 +260,7 @@ mod tests {
260 convert_to_guarded_return, 260 convert_to_guarded_return,
261 r#" 261 r#"
262 fn main() { 262 fn main() {
263 if<|> let Ok(x) = Err(92) { 263 if$0 let Ok(x) = Err(92) {
264 foo(x); 264 foo(x);
265 } 265 }
266 } 266 }
@@ -284,7 +284,7 @@ mod tests {
284 r#" 284 r#"
285 fn main(n: Option<String>) { 285 fn main(n: Option<String>) {
286 bar(); 286 bar();
287 if<|> let Ok(n) = n { 287 if$0 let Ok(n) = n {
288 foo(n); 288 foo(n);
289 289
290 //comment 290 //comment
@@ -315,7 +315,7 @@ mod tests {
315 r#" 315 r#"
316 fn main() { 316 fn main() {
317 while true { 317 while true {
318 if<|> true { 318 if$0 true {
319 foo(); 319 foo();
320 bar(); 320 bar();
321 } 321 }
@@ -343,7 +343,7 @@ mod tests {
343 r#" 343 r#"
344 fn main() { 344 fn main() {
345 while true { 345 while true {
346 if<|> let Some(n) = n { 346 if$0 let Some(n) = n {
347 foo(n); 347 foo(n);
348 bar(); 348 bar();
349 } 349 }
@@ -372,7 +372,7 @@ mod tests {
372 r#" 372 r#"
373 fn main() { 373 fn main() {
374 loop { 374 loop {
375 if<|> true { 375 if$0 true {
376 foo(); 376 foo();
377 bar(); 377 bar();
378 } 378 }
@@ -400,7 +400,7 @@ mod tests {
400 r#" 400 r#"
401 fn main() { 401 fn main() {
402 loop { 402 loop {
403 if<|> let Some(n) = n { 403 if$0 let Some(n) = n {
404 foo(n); 404 foo(n);
405 bar(); 405 bar();
406 } 406 }
@@ -428,7 +428,7 @@ mod tests {
428 convert_to_guarded_return, 428 convert_to_guarded_return,
429 r#" 429 r#"
430 fn main() { 430 fn main() {
431 if<|> true { 431 if$0 true {
432 return; 432 return;
433 } 433 }
434 } 434 }
@@ -443,7 +443,7 @@ mod tests {
443 r#" 443 r#"
444 fn main() { 444 fn main() {
445 loop { 445 loop {
446 if<|> true { 446 if$0 true {
447 continue; 447 continue;
448 } 448 }
449 } 449 }
@@ -458,7 +458,7 @@ mod tests {
458 convert_to_guarded_return, 458 convert_to_guarded_return,
459 r#" 459 r#"
460 fn main() { 460 fn main() {
461 if<|> true { 461 if$0 true {
462 return 462 return
463 } 463 }
464 } 464 }
@@ -472,7 +472,7 @@ mod tests {
472 convert_to_guarded_return, 472 convert_to_guarded_return,
473 r#" 473 r#"
474 fn main() { 474 fn main() {
475 if<|> true { 475 if$0 true {
476 foo(); 476 foo();
477 } else { 477 } else {
478 bar() 478 bar()
@@ -488,7 +488,7 @@ mod tests {
488 convert_to_guarded_return, 488 convert_to_guarded_return,
489 r#" 489 r#"
490 fn main() { 490 fn main() {
491 if<|> true { 491 if$0 true {
492 foo(); 492 foo();
493 } 493 }
494 bar(); 494 bar();
@@ -504,7 +504,7 @@ mod tests {
504 r#" 504 r#"
505 fn main() { 505 fn main() {
506 if false { 506 if false {
507 if<|> true { 507 if$0 true {
508 foo(); 508 foo();
509 } 509 }
510 } 510 }
diff --git a/crates/assists/src/handlers/expand_glob_import.rs b/crates/assists/src/handlers/expand_glob_import.rs
index f51a9a4ad..5fe617ba4 100644
--- a/crates/assists/src/handlers/expand_glob_import.rs
+++ b/crates/assists/src/handlers/expand_glob_import.rs
@@ -25,7 +25,7 @@ use crate::{
25// pub struct Baz; 25// pub struct Baz;
26// } 26// }
27// 27//
28// use foo::*<|>; 28// use foo::*$0;
29// 29//
30// fn qux(bar: Bar, baz: Baz) {} 30// fn qux(bar: Bar, baz: Baz) {}
31// ``` 31// ```
@@ -201,7 +201,7 @@ fn is_mod_visible_from(ctx: &AssistContext, module: Module, from: Module) -> boo
201// } 201// }
202// 202//
203// ↓ --------------- 203// ↓ ---------------
204// use foo::*<|>; 204// use foo::*$0;
205// use baz::Baz; 205// use baz::Baz;
206// ↑ --------------- 206// ↑ ---------------
207fn find_imported_defs(ctx: &AssistContext, star: SyntaxToken) -> Option<Vec<Def>> { 207fn find_imported_defs(ctx: &AssistContext, star: SyntaxToken) -> Option<Vec<Def>> {
@@ -303,7 +303,7 @@ mod foo {
303 pub fn f() {} 303 pub fn f() {}
304} 304}
305 305
306use foo::*<|>; 306use foo::*$0;
307 307
308fn qux(bar: Bar, baz: Baz) { 308fn qux(bar: Bar, baz: Baz) {
309 f(); 309 f();
@@ -340,7 +340,7 @@ mod foo {
340 pub fn f() {} 340 pub fn f() {}
341} 341}
342 342
343use foo::{*<|>, f}; 343use foo::{*$0, f};
344 344
345fn qux(bar: Bar, baz: Baz) { 345fn qux(bar: Bar, baz: Baz) {
346 f(); 346 f();
@@ -378,7 +378,7 @@ mod foo {
378} 378}
379 379
380use foo::Bar; 380use foo::Bar;
381use foo::{*<|>, f}; 381use foo::{*$0, f};
382 382
383fn qux(bar: Bar, baz: Baz) { 383fn qux(bar: Bar, baz: Baz) {
384 f(); 384 f();
@@ -422,7 +422,7 @@ mod foo {
422 } 422 }
423} 423}
424 424
425use foo::{bar::{*<|>, f}, baz::*}; 425use foo::{bar::{*$0, f}, baz::*};
426 426
427fn qux(bar: Bar, baz: Baz) { 427fn qux(bar: Bar, baz: Baz) {
428 f(); 428 f();
@@ -470,7 +470,7 @@ mod foo {
470 } 470 }
471} 471}
472 472
473use foo::{bar::{Bar, Baz, f}, baz::*<|>}; 473use foo::{bar::{Bar, Baz, f}, baz::*$0};
474 474
475fn qux(bar: Bar, baz: Baz) { 475fn qux(bar: Bar, baz: Baz) {
476 f(); 476 f();
@@ -529,7 +529,7 @@ mod foo {
529 529
530use foo::{ 530use foo::{
531 bar::{*, f}, 531 bar::{*, f},
532 baz::{g, qux::*<|>} 532 baz::{g, qux::*$0}
533}; 533};
534 534
535fn qux(bar: Bar, baz: Baz) { 535fn qux(bar: Bar, baz: Baz) {
@@ -605,7 +605,7 @@ mod foo {
605 605
606use foo::{ 606use foo::{
607 bar::{*, f}, 607 bar::{*, f},
608 baz::{g, qux::{h, q::*<|>}} 608 baz::{g, qux::{h, q::*$0}}
609}; 609};
610 610
611fn qux(bar: Bar, baz: Baz) { 611fn qux(bar: Bar, baz: Baz) {
@@ -681,7 +681,7 @@ mod foo {
681 681
682use foo::{ 682use foo::{
683 bar::{*, f}, 683 bar::{*, f},
684 baz::{g, qux::{q::j, *<|>}} 684 baz::{g, qux::{q::j, *$0}}
685}; 685};
686 686
687fn qux(bar: Bar, baz: Baz) { 687fn qux(bar: Bar, baz: Baz) {
@@ -747,7 +747,7 @@ fn qux(bar: Bar, baz: Baz) {
747 // pub fn baz() {} 747 // pub fn baz() {}
748 748
749 // //- /main.rs crate:main deps:foo 749 // //- /main.rs crate:main deps:foo
750 // use foo::*<|>; 750 // use foo::*$0;
751 751
752 // fn main() { 752 // fn main() {
753 // bar!(); 753 // bar!();
@@ -777,7 +777,7 @@ pub trait Tr {
777impl Tr for () {} 777impl Tr for () {}
778 778
779//- /main.rs crate:main deps:foo 779//- /main.rs crate:main deps:foo
780use foo::*<|>; 780use foo::*$0;
781 781
782fn main() { 782fn main() {
783 ().method(); 783 ().method();
@@ -807,7 +807,7 @@ pub trait Tr2 {
807impl Tr2 for () {} 807impl Tr2 for () {}
808 808
809//- /main.rs crate:main deps:foo 809//- /main.rs crate:main deps:foo
810use foo::*<|>; 810use foo::*$0;
811 811
812fn main() { 812fn main() {
813 ().method(); 813 ().method();
@@ -834,7 +834,7 @@ mod foo {
834 } 834 }
835} 835}
836 836
837use foo::bar::*<|>; 837use foo::bar::*$0;
838 838
839fn baz(bar: Bar) {} 839fn baz(bar: Bar) {}
840", 840",
@@ -851,7 +851,7 @@ mod foo {
851 } 851 }
852} 852}
853 853
854use foo::bar::baz::*<|>; 854use foo::bar::baz::*$0;
855 855
856fn qux(baz: Baz) {} 856fn qux(baz: Baz) {}
857", 857",
@@ -869,7 +869,7 @@ fn qux(baz: Baz) {}
869 pub struct Qux; 869 pub struct Qux;
870 } 870 }
871 871
872 use foo::Bar<|>; 872 use foo::Bar$0;
873 873
874 fn qux(bar: Bar, baz: Baz) {} 874 fn qux(bar: Bar, baz: Baz) {}
875 ", 875 ",
@@ -885,7 +885,7 @@ mod foo {
885 pub struct Bar; 885 pub struct Bar;
886} 886}
887 887
888use foo::{*<|>}; 888use foo::{*$0};
889 889
890struct Baz { 890struct Baz {
891 bar: Bar 891 bar: Bar
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index 030b9cd0c..e3ef04932 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -2,12 +2,16 @@ use std::iter;
2 2
3use either::Either; 3use either::Either;
4use hir::{AsName, Module, ModuleDef, Name, Variant}; 4use hir::{AsName, Module, ModuleDef, Name, Variant};
5use ide_db::helpers::{ 5use ide_db::{
6 insert_use::{insert_use, ImportScope}, 6 defs::Definition,
7 mod_path_to_ast, 7 helpers::{
8 insert_use::{insert_use, ImportScope},
9 mod_path_to_ast,
10 },
11 search::FileReference,
12 RootDatabase,
8}; 13};
9use ide_db::{defs::Definition, search::Reference, RootDatabase}; 14use rustc_hash::FxHashSet;
10use rustc_hash::{FxHashMap, FxHashSet};
11use syntax::{ 15use syntax::{
12 algo::{find_node_at_offset, SyntaxRewriter}, 16 algo::{find_node_at_offset, SyntaxRewriter},
13 ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner}, 17 ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner},
@@ -21,7 +25,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
21// Extracts a struct from enum variant. 25// Extracts a struct from enum variant.
22// 26//
23// ``` 27// ```
24// enum A { <|>One(u32, u32) } 28// enum A { $0One(u32, u32) }
25// ``` 29// ```
26// -> 30// ->
27// ``` 31// ```
@@ -58,29 +62,29 @@ pub(crate) fn extract_struct_from_enum_variant(
58 let mut visited_modules_set = FxHashSet::default(); 62 let mut visited_modules_set = FxHashSet::default();
59 let current_module = enum_hir.module(ctx.db()); 63 let current_module = enum_hir.module(ctx.db());
60 visited_modules_set.insert(current_module); 64 visited_modules_set.insert(current_module);
61 let mut rewriters = FxHashMap::default(); 65 let mut def_rewriter = None;
62 for reference in usages { 66 for (file_id, references) in usages {
63 let rewriter = rewriters 67 let mut rewriter = SyntaxRewriter::default();
64 .entry(reference.file_range.file_id) 68 let source_file = ctx.sema.parse(file_id);
65 .or_insert_with(SyntaxRewriter::default); 69 for reference in references {
66 let source_file = ctx.sema.parse(reference.file_range.file_id); 70 update_reference(
67 update_reference( 71 ctx,
68 ctx, 72 &mut rewriter,
69 rewriter, 73 reference,
70 reference, 74 &source_file,
71 &source_file, 75 &enum_module_def,
72 &enum_module_def, 76 &variant_hir_name,
73 &variant_hir_name, 77 &mut visited_modules_set,
74 &mut visited_modules_set, 78 );
75 ); 79 }
76 } 80 if file_id == ctx.frange.file_id {
77 let mut rewriter = 81 def_rewriter = Some(rewriter);
78 rewriters.remove(&ctx.frange.file_id).unwrap_or_else(SyntaxRewriter::default); 82 continue;
79 for (file_id, rewriter) in rewriters { 83 }
80 builder.edit_file(file_id); 84 builder.edit_file(file_id);
81 builder.rewrite(rewriter); 85 builder.rewrite(rewriter);
82 } 86 }
83 builder.edit_file(ctx.frange.file_id); 87 let mut rewriter = def_rewriter.unwrap_or_default();
84 update_variant(&mut rewriter, &variant); 88 update_variant(&mut rewriter, &variant);
85 extract_struct_def( 89 extract_struct_def(
86 &mut rewriter, 90 &mut rewriter,
@@ -90,6 +94,7 @@ pub(crate) fn extract_struct_from_enum_variant(
90 &variant.parent_enum().syntax().clone().into(), 94 &variant.parent_enum().syntax().clone().into(),
91 enum_ast.visibility(), 95 enum_ast.visibility(),
92 ); 96 );
97 builder.edit_file(ctx.frange.file_id);
93 builder.rewrite(rewriter); 98 builder.rewrite(rewriter);
94 }, 99 },
95 ) 100 )
@@ -117,10 +122,14 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
117 .into_iter() 122 .into_iter()
118 .filter(|(_, def)| match def { 123 .filter(|(_, def)| match def {
119 // only check type-namespace 124 // only check type-namespace
120 hir::ScopeDef::ModuleDef(def) => matches!(def, 125 hir::ScopeDef::ModuleDef(def) => matches!(
121 ModuleDef::Module(_) | ModuleDef::Adt(_) | 126 def,
122 ModuleDef::Variant(_) | ModuleDef::Trait(_) | 127 ModuleDef::Module(_)
123 ModuleDef::TypeAlias(_) | ModuleDef::BuiltinType(_) 128 | ModuleDef::Adt(_)
129 | ModuleDef::Variant(_)
130 | ModuleDef::Trait(_)
131 | ModuleDef::TypeAlias(_)
132 | ModuleDef::BuiltinType(_)
124 ), 133 ),
125 _ => false, 134 _ => false,
126 }) 135 })
@@ -201,13 +210,13 @@ fn update_variant(rewriter: &mut SyntaxRewriter, variant: &ast::Variant) -> Opti
201fn update_reference( 210fn update_reference(
202 ctx: &AssistContext, 211 ctx: &AssistContext,
203 rewriter: &mut SyntaxRewriter, 212 rewriter: &mut SyntaxRewriter,
204 reference: Reference, 213 reference: FileReference,
205 source_file: &SourceFile, 214 source_file: &SourceFile,
206 enum_module_def: &ModuleDef, 215 enum_module_def: &ModuleDef,
207 variant_hir_name: &Name, 216 variant_hir_name: &Name,
208 visited_modules_set: &mut FxHashSet<Module>, 217 visited_modules_set: &mut FxHashSet<Module>,
209) -> Option<()> { 218) -> Option<()> {
210 let offset = reference.file_range.range.start(); 219 let offset = reference.range.start();
211 let (segment, expr) = if let Some(path_expr) = 220 let (segment, expr) = if let Some(path_expr) =
212 find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset) 221 find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset)
213 { 222 {
@@ -247,7 +256,7 @@ mod tests {
247 fn test_extract_struct_several_fields_tuple() { 256 fn test_extract_struct_several_fields_tuple() {
248 check_assist( 257 check_assist(
249 extract_struct_from_enum_variant, 258 extract_struct_from_enum_variant,
250 "enum A { <|>One(u32, u32) }", 259 "enum A { $0One(u32, u32) }",
251 r#"struct One(pub u32, pub u32); 260 r#"struct One(pub u32, pub u32);
252 261
253enum A { One(One) }"#, 262enum A { One(One) }"#,
@@ -258,7 +267,7 @@ enum A { One(One) }"#,
258 fn test_extract_struct_several_fields_named() { 267 fn test_extract_struct_several_fields_named() {
259 check_assist( 268 check_assist(
260 extract_struct_from_enum_variant, 269 extract_struct_from_enum_variant,
261 "enum A { <|>One { foo: u32, bar: u32 } }", 270 "enum A { $0One { foo: u32, bar: u32 } }",
262 r#"struct One{ pub foo: u32, pub bar: u32 } 271 r#"struct One{ pub foo: u32, pub bar: u32 }
263 272
264enum A { One(One) }"#, 273enum A { One(One) }"#,
@@ -269,7 +278,7 @@ enum A { One(One) }"#,
269 fn test_extract_struct_one_field_named() { 278 fn test_extract_struct_one_field_named() {
270 check_assist( 279 check_assist(
271 extract_struct_from_enum_variant, 280 extract_struct_from_enum_variant,
272 "enum A { <|>One { foo: u32 } }", 281 "enum A { $0One { foo: u32 } }",
273 r#"struct One{ pub foo: u32 } 282 r#"struct One{ pub foo: u32 }
274 283
275enum A { One(One) }"#, 284enum A { One(One) }"#,
@@ -281,7 +290,7 @@ enum A { One(One) }"#,
281 check_assist( 290 check_assist(
282 extract_struct_from_enum_variant, 291 extract_struct_from_enum_variant,
283 r#"const One: () = (); 292 r#"const One: () = ();
284enum A { <|>One(u32, u32) }"#, 293enum A { $0One(u32, u32) }"#,
285 r#"const One: () = (); 294 r#"const One: () = ();
286struct One(pub u32, pub u32); 295struct One(pub u32, pub u32);
287 296
@@ -293,7 +302,7 @@ enum A { One(One) }"#,
293 fn test_extract_struct_pub_visibility() { 302 fn test_extract_struct_pub_visibility() {
294 check_assist( 303 check_assist(
295 extract_struct_from_enum_variant, 304 extract_struct_from_enum_variant,
296 "pub enum A { <|>One(u32, u32) }", 305 "pub enum A { $0One(u32, u32) }",
297 r#"pub struct One(pub u32, pub u32); 306 r#"pub struct One(pub u32, pub u32);
298 307
299pub enum A { One(One) }"#, 308pub enum A { One(One) }"#,
@@ -315,7 +324,7 @@ pub enum A { One(One) }"#,
315 } 324 }
316 325
317 pub enum MyEnum { 326 pub enum MyEnum {
318 <|>MyField(u8, u8), 327 $0MyField(u8, u8),
319 } 328 }
320 } 329 }
321} 330}
@@ -357,7 +366,7 @@ fn another_fn() {
357 extract_struct_from_enum_variant, 366 extract_struct_from_enum_variant,
358 r#" 367 r#"
359enum E { 368enum E {
360 <|>V { i: i32, j: i32 } 369 $0V { i: i32, j: i32 }
361} 370}
362 371
363fn f() { 372fn f() {
@@ -385,7 +394,7 @@ fn f() {
385 r#" 394 r#"
386//- /main.rs 395//- /main.rs
387enum E { 396enum E {
388 <|>V(i32, i32) 397 $0V(i32, i32)
389} 398}
390mod foo; 399mod foo;
391 400
@@ -420,7 +429,7 @@ fn f() {
420 r#" 429 r#"
421//- /main.rs 430//- /main.rs
422enum E { 431enum E {
423 <|>V { i: i32, j: i32 } 432 $0V { i: i32, j: i32 }
424} 433}
425mod foo; 434mod foo;
426 435
@@ -453,7 +462,7 @@ fn f() {
453 check_assist( 462 check_assist(
454 extract_struct_from_enum_variant, 463 extract_struct_from_enum_variant,
455 r#" 464 r#"
456enum A { <|>One { a: u32, b: u32 } } 465enum A { $0One { a: u32, b: u32 } }
457 466
458struct B(A); 467struct B(A);
459 468
@@ -483,29 +492,29 @@ fn foo() {
483 492
484 #[test] 493 #[test]
485 fn test_extract_enum_not_applicable_for_element_with_no_fields() { 494 fn test_extract_enum_not_applicable_for_element_with_no_fields() {
486 check_not_applicable("enum A { <|>One }"); 495 check_not_applicable("enum A { $0One }");
487 } 496 }
488 497
489 #[test] 498 #[test]
490 fn test_extract_enum_not_applicable_if_struct_exists() { 499 fn test_extract_enum_not_applicable_if_struct_exists() {
491 check_not_applicable( 500 check_not_applicable(
492 r#"struct One; 501 r#"struct One;
493 enum A { <|>One(u8, u32) }"#, 502 enum A { $0One(u8, u32) }"#,
494 ); 503 );
495 } 504 }
496 505
497 #[test] 506 #[test]
498 fn test_extract_not_applicable_one_field() { 507 fn test_extract_not_applicable_one_field() {
499 check_not_applicable(r"enum A { <|>One(u32) }"); 508 check_not_applicable(r"enum A { $0One(u32) }");
500 } 509 }
501 510
502 #[test] 511 #[test]
503 fn test_extract_not_applicable_no_field_tuple() { 512 fn test_extract_not_applicable_no_field_tuple() {
504 check_not_applicable(r"enum A { <|>None() }"); 513 check_not_applicable(r"enum A { $0None() }");
505 } 514 }
506 515
507 #[test] 516 #[test]
508 fn test_extract_not_applicable_no_field_named() { 517 fn test_extract_not_applicable_no_field_named() {
509 check_not_applicable(r"enum A { <|>None {} }"); 518 check_not_applicable(r"enum A { $0None {} }");
510 } 519 }
511} 520}
diff --git a/crates/assists/src/handlers/extract_variable.rs b/crates/assists/src/handlers/extract_variable.rs
index 9957012fe..98f3dc6ca 100644
--- a/crates/assists/src/handlers/extract_variable.rs
+++ b/crates/assists/src/handlers/extract_variable.rs
@@ -16,7 +16,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
16// 16//
17// ``` 17// ```
18// fn main() { 18// fn main() {
19// <|>(1 + 2)<|> * 4; 19// $0(1 + 2)$0 * 4;
20// } 20// }
21// ``` 21// ```
22// -> 22// ->
@@ -139,7 +139,7 @@ impl Anchor {
139 fn from(to_extract: &ast::Expr) -> Option<Anchor> { 139 fn from(to_extract: &ast::Expr) -> Option<Anchor> {
140 to_extract.syntax().ancestors().find_map(|node| { 140 to_extract.syntax().ancestors().find_map(|node| {
141 if let Some(expr) = 141 if let Some(expr) =
142 node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) 142 node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr())
143 { 143 {
144 if expr.syntax() == &node { 144 if expr.syntax() == &node {
145 mark::hit!(test_extract_var_last_expr); 145 mark::hit!(test_extract_var_last_expr);
@@ -187,7 +187,7 @@ mod tests {
187 extract_variable, 187 extract_variable,
188 r#" 188 r#"
189fn foo() { 189fn foo() {
190 foo(<|>1 + 1<|>); 190 foo($01 + 1$0);
191}"#, 191}"#,
192 r#" 192 r#"
193fn foo() { 193fn foo() {
@@ -200,7 +200,7 @@ fn foo() {
200 #[test] 200 #[test]
201 fn extract_var_in_comment_is_not_applicable() { 201 fn extract_var_in_comment_is_not_applicable() {
202 mark::check!(extract_var_in_comment_is_not_applicable); 202 mark::check!(extract_var_in_comment_is_not_applicable);
203 check_assist_not_applicable(extract_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }"); 203 check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }");
204 } 204 }
205 205
206 #[test] 206 #[test]
@@ -210,7 +210,7 @@ fn foo() {
210 extract_variable, 210 extract_variable,
211 r#" 211 r#"
212fn foo() { 212fn foo() {
213 <|>1 + 1<|>; 213 $01 + 1$0;
214}"#, 214}"#,
215 r#" 215 r#"
216fn foo() { 216fn foo() {
@@ -221,7 +221,7 @@ fn foo() {
221 extract_variable, 221 extract_variable,
222 " 222 "
223fn foo() { 223fn foo() {
224 <|>{ let x = 0; x }<|> 224 $0{ let x = 0; x }$0
225 something_else(); 225 something_else();
226}", 226}",
227 " 227 "
@@ -238,7 +238,7 @@ fn foo() {
238 extract_variable, 238 extract_variable,
239 " 239 "
240fn foo() { 240fn foo() {
241 <|>1<|> + 1; 241 $01$0 + 1;
242}", 242}",
243 " 243 "
244fn foo() { 244fn foo() {
@@ -255,7 +255,7 @@ fn foo() {
255 extract_variable, 255 extract_variable,
256 r#" 256 r#"
257fn foo() { 257fn foo() {
258 bar(<|>1 + 1<|>) 258 bar($01 + 1$0)
259} 259}
260"#, 260"#,
261 r#" 261 r#"
@@ -269,7 +269,7 @@ fn foo() {
269 extract_variable, 269 extract_variable,
270 r#" 270 r#"
271fn foo() { 271fn foo() {
272 <|>bar(1 + 1)<|> 272 $0bar(1 + 1)$0
273} 273}
274"#, 274"#,
275 r#" 275 r#"
@@ -289,7 +289,7 @@ fn foo() {
289fn main() { 289fn main() {
290 let x = true; 290 let x = true;
291 let tuple = match x { 291 let tuple = match x {
292 true => (<|>2 + 2<|>, true) 292 true => ($02 + 2$0, true)
293 _ => (0, false) 293 _ => (0, false)
294 }; 294 };
295} 295}
@@ -316,7 +316,7 @@ fn main() {
316 let tuple = match x { 316 let tuple = match x {
317 true => { 317 true => {
318 let y = 1; 318 let y = 1;
319 (<|>2 + y<|>, true) 319 ($02 + y$0, true)
320 } 320 }
321 _ => (0, false) 321 _ => (0, false)
322 }; 322 };
@@ -344,7 +344,7 @@ fn main() {
344 extract_variable, 344 extract_variable,
345 " 345 "
346fn main() { 346fn main() {
347 let lambda = |x: u32| <|>x * 2<|>; 347 let lambda = |x: u32| $0x * 2$0;
348} 348}
349", 349",
350 " 350 "
@@ -361,7 +361,7 @@ fn main() {
361 extract_variable, 361 extract_variable,
362 " 362 "
363fn main() { 363fn main() {
364 let lambda = |x: u32| { <|>x * 2<|> }; 364 let lambda = |x: u32| { $0x * 2$0 };
365} 365}
366", 366",
367 " 367 "
@@ -378,7 +378,7 @@ fn main() {
378 extract_variable, 378 extract_variable,
379 " 379 "
380fn main() { 380fn main() {
381 let o = <|>Some(true)<|>; 381 let o = $0Some(true)$0;
382} 382}
383", 383",
384 " 384 "
@@ -396,7 +396,7 @@ fn main() {
396 extract_variable, 396 extract_variable,
397 " 397 "
398fn main() { 398fn main() {
399 let v = <|>bar.foo()<|>; 399 let v = $0bar.foo()$0;
400} 400}
401", 401",
402 " 402 "
@@ -414,7 +414,7 @@ fn main() {
414 extract_variable, 414 extract_variable,
415 " 415 "
416fn foo() -> u32 { 416fn foo() -> u32 {
417 <|>return 2 + 2<|>; 417 $0return 2 + 2$0;
418} 418}
419", 419",
420 " 420 "
@@ -434,7 +434,7 @@ fn foo() -> u32 {
434fn foo() -> u32 { 434fn foo() -> u32 {
435 435
436 436
437 <|>return 2 + 2<|>; 437 $0return 2 + 2$0;
438} 438}
439", 439",
440 " 440 "
@@ -452,7 +452,7 @@ fn foo() -> u32 {
452 " 452 "
453fn foo() -> u32 { 453fn foo() -> u32 {
454 454
455 <|>return 2 + 2<|>; 455 $0return 2 + 2$0;
456} 456}
457", 457",
458 " 458 "
@@ -473,7 +473,7 @@ fn foo() -> u32 {
473 // bar 473 // bar
474 474
475 475
476 <|>return 2 + 2<|>; 476 $0return 2 + 2$0;
477} 477}
478", 478",
479 " 479 "
@@ -497,7 +497,7 @@ fn foo() -> u32 {
497 " 497 "
498fn main() { 498fn main() {
499 let result = loop { 499 let result = loop {
500 <|>break 2 + 2<|>; 500 $0break 2 + 2$0;
501 }; 501 };
502} 502}
503", 503",
@@ -518,7 +518,7 @@ fn main() {
518 extract_variable, 518 extract_variable,
519 " 519 "
520fn main() { 520fn main() {
521 let v = <|>0f32 as u32<|>; 521 let v = $00f32 as u32$0;
522} 522}
523", 523",
524 " 524 "
@@ -540,7 +540,7 @@ struct S {
540} 540}
541 541
542fn main() { 542fn main() {
543 S { foo: <|>1 + 1<|> } 543 S { foo: $01 + 1$0 }
544} 544}
545"#, 545"#,
546 r#" 546 r#"
@@ -558,18 +558,18 @@ fn main() {
558 558
559 #[test] 559 #[test]
560 fn test_extract_var_for_return_not_applicable() { 560 fn test_extract_var_for_return_not_applicable() {
561 check_assist_not_applicable(extract_variable, "fn foo() { <|>return<|>; } "); 561 check_assist_not_applicable(extract_variable, "fn foo() { $0return$0; } ");
562 } 562 }
563 563
564 #[test] 564 #[test]
565 fn test_extract_var_for_break_not_applicable() { 565 fn test_extract_var_for_break_not_applicable() {
566 check_assist_not_applicable(extract_variable, "fn main() { loop { <|>break<|>; }; }"); 566 check_assist_not_applicable(extract_variable, "fn main() { loop { $0break$0; }; }");
567 } 567 }
568 568
569 // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic 569 // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic
570 #[test] 570 #[test]
571 fn extract_var_target() { 571 fn extract_var_target() {
572 check_assist_target(extract_variable, "fn foo() -> u32 { <|>return 2 + 2<|>; }", "2 + 2"); 572 check_assist_target(extract_variable, "fn foo() -> u32 { $0return 2 + 2$0; }", "2 + 2");
573 573
574 check_assist_target( 574 check_assist_target(
575 extract_variable, 575 extract_variable,
@@ -577,7 +577,7 @@ fn main() {
577fn main() { 577fn main() {
578 let x = true; 578 let x = true;
579 let tuple = match x { 579 let tuple = match x {
580 true => (<|>2 + 2<|>, true) 580 true => ($02 + 2$0, true)
581 _ => (0, false) 581 _ => (0, false)
582 }; 582 };
583} 583}
diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs
index cb60a3128..da47187e4 100644
--- a/crates/assists/src/handlers/fill_match_arms.rs
+++ b/crates/assists/src/handlers/fill_match_arms.rs
@@ -21,7 +21,7 @@ use crate::{
21// 21//
22// fn handle(action: Action) { 22// fn handle(action: Action) {
23// match action { 23// match action {
24// <|> 24// $0
25// } 25// }
26// } 26// }
27// ``` 27// ```
@@ -196,7 +196,7 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Optio
196 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); 196 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
197 197
198 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 198 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
199 let pat: ast::Pat = match var.source(db).value.kind() { 199 let pat: ast::Pat = match var.source(db)?.value.kind() {
200 ast::StructKind::Tuple(field_list) => { 200 ast::StructKind::Tuple(field_list) => {
201 let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); 201 let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
202 make::tuple_struct_pat(path, pats).into() 202 make::tuple_struct_pat(path, pats).into()
@@ -231,7 +231,7 @@ mod tests {
231 Cs(i32, Option<i32>), 231 Cs(i32, Option<i32>),
232 } 232 }
233 fn main() { 233 fn main() {
234 match A::As<|> { 234 match A::As$0 {
235 A::As, 235 A::As,
236 A::Bs{x,y:Some(_)} => {} 236 A::Bs{x,y:Some(_)} => {}
237 A::Cs(_, Some(_)) => {} 237 A::Cs(_, Some(_)) => {}
@@ -249,7 +249,7 @@ mod tests {
249 fill_match_arms, 249 fill_match_arms,
250 r#" 250 r#"
251 fn main() { 251 fn main() {
252 match (0, false)<|> { 252 match (0, false)$0 {
253 } 253 }
254 } 254 }
255 "#, 255 "#,
@@ -267,7 +267,7 @@ mod tests {
267 Cs(i32, Option<i32>), 267 Cs(i32, Option<i32>),
268 } 268 }
269 fn main() { 269 fn main() {
270 match A::As<|> { 270 match A::As$0 {
271 A::Bs { x, y: Some(_) } => {} 271 A::Bs { x, y: Some(_) } => {}
272 A::Cs(_, Some(_)) => {} 272 A::Cs(_, Some(_)) => {}
273 } 273 }
@@ -297,7 +297,7 @@ mod tests {
297 r#" 297 r#"
298enum A { As, Bs, Cs(Option<i32>) } 298enum A { As, Bs, Cs(Option<i32>) }
299fn main() { 299fn main() {
300 match A::As<|> { 300 match A::As$0 {
301 A::Cs(_) | A::Bs => {} 301 A::Cs(_) | A::Bs => {}
302 } 302 }
303} 303}
@@ -322,7 +322,7 @@ fn main() {
322enum A { As, Bs, Cs, Ds(String), Es(B) } 322enum A { As, Bs, Cs, Ds(String), Es(B) }
323enum B { Xs, Ys } 323enum B { Xs, Ys }
324fn main() { 324fn main() {
325 match A::As<|> { 325 match A::As$0 {
326 A::Bs if 0 < 1 => {} 326 A::Bs if 0 < 1 => {}
327 A::Ds(_value) => { let x = 1; } 327 A::Ds(_value) => { let x = 1; }
328 A::Es(B::Xs) => (), 328 A::Es(B::Xs) => (),
@@ -352,7 +352,7 @@ fn main() {
352 r#" 352 r#"
353enum A { As, Bs, Cs(Option<i32>) } 353enum A { As, Bs, Cs(Option<i32>) }
354fn main() { 354fn main() {
355 match A::As<|> { 355 match A::As$0 {
356 A::As(_) => {} 356 A::As(_) => {}
357 a @ A::Bs(_) => {} 357 a @ A::Bs(_) => {}
358 } 358 }
@@ -380,7 +380,7 @@ enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
380 380
381fn main() { 381fn main() {
382 let a = A::As; 382 let a = A::As;
383 match a<|> {} 383 match a$0 {}
384} 384}
385"#, 385"#,
386 r#" 386 r#"
@@ -411,7 +411,7 @@ fn main() {
411 fn main() { 411 fn main() {
412 let a = A::One; 412 let a = A::One;
413 let b = B::One; 413 let b = B::One;
414 match (a<|>, b) {} 414 match (a$0, b) {}
415 } 415 }
416 "#, 416 "#,
417 r#" 417 r#"
@@ -443,7 +443,7 @@ fn main() {
443 fn main() { 443 fn main() {
444 let a = A::One; 444 let a = A::One;
445 let b = B::One; 445 let b = B::One;
446 match (&a<|>, &b) {} 446 match (&a$0, &b) {}
447 } 447 }
448 "#, 448 "#,
449 r#" 449 r#"
@@ -475,7 +475,7 @@ fn main() {
475 fn main() { 475 fn main() {
476 let a = A::One; 476 let a = A::One;
477 let b = B::One; 477 let b = B::One;
478 match (a<|>, b) { 478 match (a$0, b) {
479 (A::Two, B::One) => {} 479 (A::Two, B::One) => {}
480 } 480 }
481 } 481 }
@@ -494,7 +494,7 @@ fn main() {
494 fn main() { 494 fn main() {
495 let a = A::One; 495 let a = A::One;
496 let b = B::One; 496 let b = B::One;
497 match (a<|>, b) { 497 match (a$0, b) {
498 (A::Two, B::One) => {} 498 (A::Two, B::One) => {}
499 (A::One, B::One) => {} 499 (A::One, B::One) => {}
500 (A::One, B::Two) => {} 500 (A::One, B::Two) => {}
@@ -517,7 +517,7 @@ fn main() {
517 517
518 fn main() { 518 fn main() {
519 let a = A::One; 519 let a = A::One;
520 match (a<|>, ) { 520 match (a$0, ) {
521 } 521 }
522 } 522 }
523 "#, 523 "#,
@@ -532,7 +532,7 @@ fn main() {
532 enum A { As } 532 enum A { As }
533 533
534 fn foo(a: &A) { 534 fn foo(a: &A) {
535 match a<|> { 535 match a$0 {
536 } 536 }
537 } 537 }
538 "#, 538 "#,
@@ -555,7 +555,7 @@ fn main() {
555 } 555 }
556 556
557 fn foo(a: &mut A) { 557 fn foo(a: &mut A) {
558 match a<|> { 558 match a$0 {
559 } 559 }
560 } 560 }
561 "#, 561 "#,
@@ -581,7 +581,7 @@ fn main() {
581 enum E { X, Y } 581 enum E { X, Y }
582 582
583 fn main() { 583 fn main() {
584 match E::X<|> {} 584 match E::X$0 {}
585 } 585 }
586 "#, 586 "#,
587 "match E::X {}", 587 "match E::X {}",
@@ -597,7 +597,7 @@ fn main() {
597 597
598 fn main() { 598 fn main() {
599 match E::X { 599 match E::X {
600 <|>_ => {} 600 $0_ => {}
601 } 601 }
602 } 602 }
603 "#, 603 "#,
@@ -624,7 +624,7 @@ fn main() {
624 624
625 fn main() { 625 fn main() {
626 match X { 626 match X {
627 <|> 627 $0
628 } 628 }
629 } 629 }
630 "#, 630 "#,
@@ -650,7 +650,7 @@ fn main() {
650 enum A { One, Two } 650 enum A { One, Two }
651 fn foo(a: A) { 651 fn foo(a: A) {
652 match a { 652 match a {
653 // foo bar baz<|> 653 // foo bar baz$0
654 A::One => {} 654 A::One => {}
655 // This is where the rest should be 655 // This is where the rest should be
656 } 656 }
@@ -678,7 +678,7 @@ fn main() {
678 enum A { One, Two } 678 enum A { One, Two }
679 fn foo(a: A) { 679 fn foo(a: A) {
680 match a { 680 match a {
681 // foo bar baz<|> 681 // foo bar baz$0
682 } 682 }
683 } 683 }
684 "#, 684 "#,
@@ -702,7 +702,7 @@ fn main() {
702 r#" 702 r#"
703 enum A { One, Two, } 703 enum A { One, Two, }
704 fn foo(a: A) { 704 fn foo(a: A) {
705 match a<|> { 705 match a$0 {
706 _ => (), 706 _ => (),
707 } 707 }
708 } 708 }
@@ -724,7 +724,7 @@ fn main() {
724 mark::check!(option_order); 724 mark::check!(option_order);
725 let before = r#" 725 let before = r#"
726fn foo(opt: Option<i32>) { 726fn foo(opt: Option<i32>) {
727 match opt<|> { 727 match opt$0 {
728 } 728 }
729} 729}
730"#; 730"#;
diff --git a/crates/assists/src/handlers/fix_visibility.rs b/crates/assists/src/handlers/fix_visibility.rs
index 8558a8ff0..6c7824e55 100644
--- a/crates/assists/src/handlers/fix_visibility.rs
+++ b/crates/assists/src/handlers/fix_visibility.rs
@@ -18,7 +18,7 @@ use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
18// fn frobnicate() {} 18// fn frobnicate() {}
19// } 19// }
20// fn main() { 20// fn main() {
21// m::frobnicate<|>() {} 21// m::frobnicate$0() {}
22// } 22// }
23// ``` 23// ```
24// -> 24// ->
@@ -97,7 +97,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
97 let parent_name = parent.name(ctx.db()); 97 let parent_name = parent.name(ctx.db());
98 let target_module = parent.module(ctx.db()); 98 let target_module = parent.module(ctx.db());
99 99
100 let in_file_source = record_field_def.source(ctx.db()); 100 let in_file_source = record_field_def.source(ctx.db())?;
101 let (offset, current_visibility, target) = match in_file_source.value { 101 let (offset, current_visibility, target) = match in_file_source.value {
102 hir::FieldSource::Named(it) => { 102 hir::FieldSource::Named(it) => {
103 let s = it.syntax(); 103 let s = it.syntax();
@@ -145,53 +145,53 @@ fn target_data_for_def(
145 fn offset_target_and_file_id<S, Ast>( 145 fn offset_target_and_file_id<S, Ast>(
146 db: &dyn HirDatabase, 146 db: &dyn HirDatabase,
147 x: S, 147 x: S,
148 ) -> (TextSize, Option<ast::Visibility>, TextRange, FileId) 148 ) -> Option<(TextSize, Option<ast::Visibility>, TextRange, FileId)>
149 where 149 where
150 S: HasSource<Ast = Ast>, 150 S: HasSource<Ast = Ast>,
151 Ast: AstNode + ast::VisibilityOwner, 151 Ast: AstNode + ast::VisibilityOwner,
152 { 152 {
153 let source = x.source(db); 153 let source = x.source(db)?;
154 let in_file_syntax = source.syntax(); 154 let in_file_syntax = source.syntax();
155 let file_id = in_file_syntax.file_id; 155 let file_id = in_file_syntax.file_id;
156 let syntax = in_file_syntax.value; 156 let syntax = in_file_syntax.value;
157 let current_visibility = source.value.visibility(); 157 let current_visibility = source.value.visibility();
158 ( 158 Some((
159 vis_offset(syntax), 159 vis_offset(syntax),
160 current_visibility, 160 current_visibility,
161 syntax.text_range(), 161 syntax.text_range(),
162 file_id.original_file(db.upcast()), 162 file_id.original_file(db.upcast()),
163 ) 163 ))
164 } 164 }
165 165
166 let target_name; 166 let target_name;
167 let (offset, current_visibility, target, target_file) = match def { 167 let (offset, current_visibility, target, target_file) = match def {
168 hir::ModuleDef::Function(f) => { 168 hir::ModuleDef::Function(f) => {
169 target_name = Some(f.name(db)); 169 target_name = Some(f.name(db));
170 offset_target_and_file_id(db, f) 170 offset_target_and_file_id(db, f)?
171 } 171 }
172 hir::ModuleDef::Adt(adt) => { 172 hir::ModuleDef::Adt(adt) => {
173 target_name = Some(adt.name(db)); 173 target_name = Some(adt.name(db));
174 match adt { 174 match adt {
175 hir::Adt::Struct(s) => offset_target_and_file_id(db, s), 175 hir::Adt::Struct(s) => offset_target_and_file_id(db, s)?,
176 hir::Adt::Union(u) => offset_target_and_file_id(db, u), 176 hir::Adt::Union(u) => offset_target_and_file_id(db, u)?,
177 hir::Adt::Enum(e) => offset_target_and_file_id(db, e), 177 hir::Adt::Enum(e) => offset_target_and_file_id(db, e)?,
178 } 178 }
179 } 179 }
180 hir::ModuleDef::Const(c) => { 180 hir::ModuleDef::Const(c) => {
181 target_name = c.name(db); 181 target_name = c.name(db);
182 offset_target_and_file_id(db, c) 182 offset_target_and_file_id(db, c)?
183 } 183 }
184 hir::ModuleDef::Static(s) => { 184 hir::ModuleDef::Static(s) => {
185 target_name = s.name(db); 185 target_name = s.name(db);
186 offset_target_and_file_id(db, s) 186 offset_target_and_file_id(db, s)?
187 } 187 }
188 hir::ModuleDef::Trait(t) => { 188 hir::ModuleDef::Trait(t) => {
189 target_name = Some(t.name(db)); 189 target_name = Some(t.name(db));
190 offset_target_and_file_id(db, t) 190 offset_target_and_file_id(db, t)?
191 } 191 }
192 hir::ModuleDef::TypeAlias(t) => { 192 hir::ModuleDef::TypeAlias(t) => {
193 target_name = Some(t.name(db)); 193 target_name = Some(t.name(db));
194 offset_target_and_file_id(db, t) 194 offset_target_and_file_id(db, t)?
195 } 195 }
196 hir::ModuleDef::Module(m) => { 196 hir::ModuleDef::Module(m) => {
197 target_name = m.name(db); 197 target_name = m.name(db);
@@ -218,14 +218,14 @@ mod tests {
218 check_assist( 218 check_assist(
219 fix_visibility, 219 fix_visibility,
220 r"mod foo { fn foo() {} } 220 r"mod foo { fn foo() {} }
221 fn main() { foo::foo<|>() } ", 221 fn main() { foo::foo$0() } ",
222 r"mod foo { $0pub(crate) fn foo() {} } 222 r"mod foo { $0pub(crate) fn foo() {} }
223 fn main() { foo::foo() } ", 223 fn main() { foo::foo() } ",
224 ); 224 );
225 check_assist_not_applicable( 225 check_assist_not_applicable(
226 fix_visibility, 226 fix_visibility,
227 r"mod foo { pub fn foo() {} } 227 r"mod foo { pub fn foo() {} }
228 fn main() { foo::foo<|>() } ", 228 fn main() { foo::foo$0() } ",
229 ) 229 )
230 } 230 }
231 231
@@ -234,38 +234,38 @@ mod tests {
234 check_assist( 234 check_assist(
235 fix_visibility, 235 fix_visibility,
236 r"mod foo { struct Foo; } 236 r"mod foo { struct Foo; }
237 fn main() { foo::Foo<|> } ", 237 fn main() { foo::Foo$0 } ",
238 r"mod foo { $0pub(crate) struct Foo; } 238 r"mod foo { $0pub(crate) struct Foo; }
239 fn main() { foo::Foo } ", 239 fn main() { foo::Foo } ",
240 ); 240 );
241 check_assist_not_applicable( 241 check_assist_not_applicable(
242 fix_visibility, 242 fix_visibility,
243 r"mod foo { pub struct Foo; } 243 r"mod foo { pub struct Foo; }
244 fn main() { foo::Foo<|> } ", 244 fn main() { foo::Foo$0 } ",
245 ); 245 );
246 check_assist( 246 check_assist(
247 fix_visibility, 247 fix_visibility,
248 r"mod foo { enum Foo; } 248 r"mod foo { enum Foo; }
249 fn main() { foo::Foo<|> } ", 249 fn main() { foo::Foo$0 } ",
250 r"mod foo { $0pub(crate) enum Foo; } 250 r"mod foo { $0pub(crate) enum Foo; }
251 fn main() { foo::Foo } ", 251 fn main() { foo::Foo } ",
252 ); 252 );
253 check_assist_not_applicable( 253 check_assist_not_applicable(
254 fix_visibility, 254 fix_visibility,
255 r"mod foo { pub enum Foo; } 255 r"mod foo { pub enum Foo; }
256 fn main() { foo::Foo<|> } ", 256 fn main() { foo::Foo$0 } ",
257 ); 257 );
258 check_assist( 258 check_assist(
259 fix_visibility, 259 fix_visibility,
260 r"mod foo { union Foo; } 260 r"mod foo { union Foo; }
261 fn main() { foo::Foo<|> } ", 261 fn main() { foo::Foo$0 } ",
262 r"mod foo { $0pub(crate) union Foo; } 262 r"mod foo { $0pub(crate) union Foo; }
263 fn main() { foo::Foo } ", 263 fn main() { foo::Foo } ",
264 ); 264 );
265 check_assist_not_applicable( 265 check_assist_not_applicable(
266 fix_visibility, 266 fix_visibility,
267 r"mod foo { pub union Foo; } 267 r"mod foo { pub union Foo; }
268 fn main() { foo::Foo<|> } ", 268 fn main() { foo::Foo$0 } ",
269 ); 269 );
270 } 270 }
271 271
@@ -276,7 +276,7 @@ mod tests {
276 r" 276 r"
277//- /main.rs 277//- /main.rs
278mod foo; 278mod foo;
279fn main() { foo::Foo<|> } 279fn main() { foo::Foo$0 }
280 280
281//- /foo.rs 281//- /foo.rs
282struct Foo; 282struct Foo;
@@ -291,7 +291,7 @@ struct Foo;
291 check_assist( 291 check_assist(
292 fix_visibility, 292 fix_visibility,
293 r"mod foo { pub struct Foo { bar: (), } } 293 r"mod foo { pub struct Foo { bar: (), } }
294 fn main() { foo::Foo { <|>bar: () }; } ", 294 fn main() { foo::Foo { $0bar: () }; } ",
295 r"mod foo { pub struct Foo { $0pub(crate) bar: (), } } 295 r"mod foo { pub struct Foo { $0pub(crate) bar: (), } }
296 fn main() { foo::Foo { bar: () }; } ", 296 fn main() { foo::Foo { bar: () }; } ",
297 ); 297 );
@@ -300,7 +300,7 @@ struct Foo;
300 r" 300 r"
301//- /lib.rs 301//- /lib.rs
302mod foo; 302mod foo;
303fn main() { foo::Foo { <|>bar: () }; } 303fn main() { foo::Foo { $0bar: () }; }
304//- /foo.rs 304//- /foo.rs
305pub struct Foo { bar: () } 305pub struct Foo { bar: () }
306", 306",
@@ -310,14 +310,14 @@ pub struct Foo { bar: () }
310 check_assist_not_applicable( 310 check_assist_not_applicable(
311 fix_visibility, 311 fix_visibility,
312 r"mod foo { pub struct Foo { pub bar: (), } } 312 r"mod foo { pub struct Foo { pub bar: (), } }
313 fn main() { foo::Foo { <|>bar: () }; } ", 313 fn main() { foo::Foo { $0bar: () }; } ",
314 ); 314 );
315 check_assist_not_applicable( 315 check_assist_not_applicable(
316 fix_visibility, 316 fix_visibility,
317 r" 317 r"
318//- /lib.rs 318//- /lib.rs
319mod foo; 319mod foo;
320fn main() { foo::Foo { <|>bar: () }; } 320fn main() { foo::Foo { $0bar: () }; }
321//- /foo.rs 321//- /foo.rs
322pub struct Foo { pub bar: () } 322pub struct Foo { pub bar: () }
323", 323",
@@ -331,14 +331,14 @@ pub struct Foo { pub bar: () }
331 check_assist_not_applicable( 331 check_assist_not_applicable(
332 fix_visibility, 332 fix_visibility,
333 r"mod foo { pub enum Foo { Bar { bar: () } } } 333 r"mod foo { pub enum Foo { Bar { bar: () } } }
334 fn main() { foo::Foo::Bar { <|>bar: () }; } ", 334 fn main() { foo::Foo::Bar { $0bar: () }; } ",
335 ); 335 );
336 check_assist_not_applicable( 336 check_assist_not_applicable(
337 fix_visibility, 337 fix_visibility,
338 r" 338 r"
339//- /lib.rs 339//- /lib.rs
340mod foo; 340mod foo;
341fn main() { foo::Foo::Bar { <|>bar: () }; } 341fn main() { foo::Foo::Bar { $0bar: () }; }
342//- /foo.rs 342//- /foo.rs
343pub enum Foo { Bar { bar: () } } 343pub enum Foo { Bar { bar: () } }
344", 344",
@@ -346,14 +346,14 @@ pub enum Foo { Bar { bar: () } }
346 check_assist_not_applicable( 346 check_assist_not_applicable(
347 fix_visibility, 347 fix_visibility,
348 r"mod foo { pub struct Foo { pub bar: (), } } 348 r"mod foo { pub struct Foo { pub bar: (), } }
349 fn main() { foo::Foo { <|>bar: () }; } ", 349 fn main() { foo::Foo { $0bar: () }; } ",
350 ); 350 );
351 check_assist_not_applicable( 351 check_assist_not_applicable(
352 fix_visibility, 352 fix_visibility,
353 r" 353 r"
354//- /lib.rs 354//- /lib.rs
355mod foo; 355mod foo;
356fn main() { foo::Foo { <|>bar: () }; } 356fn main() { foo::Foo { $0bar: () }; }
357//- /foo.rs 357//- /foo.rs
358pub struct Foo { pub bar: () } 358pub struct Foo { pub bar: () }
359", 359",
@@ -367,7 +367,7 @@ pub struct Foo { pub bar: () }
367 check_assist( 367 check_assist(
368 fix_visibility, 368 fix_visibility,
369 r"mod foo { pub union Foo { bar: (), } } 369 r"mod foo { pub union Foo { bar: (), } }
370 fn main() { foo::Foo { <|>bar: () }; } ", 370 fn main() { foo::Foo { $0bar: () }; } ",
371 r"mod foo { pub union Foo { $0pub(crate) bar: (), } } 371 r"mod foo { pub union Foo { $0pub(crate) bar: (), } }
372 fn main() { foo::Foo { bar: () }; } ", 372 fn main() { foo::Foo { bar: () }; } ",
373 ); 373 );
@@ -376,7 +376,7 @@ pub struct Foo { pub bar: () }
376 r" 376 r"
377//- /lib.rs 377//- /lib.rs
378mod foo; 378mod foo;
379fn main() { foo::Foo { <|>bar: () }; } 379fn main() { foo::Foo { $0bar: () }; }
380//- /foo.rs 380//- /foo.rs
381pub union Foo { bar: () } 381pub union Foo { bar: () }
382", 382",
@@ -386,14 +386,14 @@ pub union Foo { bar: () }
386 check_assist_not_applicable( 386 check_assist_not_applicable(
387 fix_visibility, 387 fix_visibility,
388 r"mod foo { pub union Foo { pub bar: (), } } 388 r"mod foo { pub union Foo { pub bar: (), } }
389 fn main() { foo::Foo { <|>bar: () }; } ", 389 fn main() { foo::Foo { $0bar: () }; } ",
390 ); 390 );
391 check_assist_not_applicable( 391 check_assist_not_applicable(
392 fix_visibility, 392 fix_visibility,
393 r" 393 r"
394//- /lib.rs 394//- /lib.rs
395mod foo; 395mod foo;
396fn main() { foo::Foo { <|>bar: () }; } 396fn main() { foo::Foo { $0bar: () }; }
397//- /foo.rs 397//- /foo.rs
398pub union Foo { pub bar: () } 398pub union Foo { pub bar: () }
399", 399",
@@ -405,14 +405,14 @@ pub union Foo { pub bar: () }
405 check_assist( 405 check_assist(
406 fix_visibility, 406 fix_visibility,
407 r"mod foo { const FOO: () = (); } 407 r"mod foo { const FOO: () = (); }
408 fn main() { foo::FOO<|> } ", 408 fn main() { foo::FOO$0 } ",
409 r"mod foo { $0pub(crate) const FOO: () = (); } 409 r"mod foo { $0pub(crate) const FOO: () = (); }
410 fn main() { foo::FOO } ", 410 fn main() { foo::FOO } ",
411 ); 411 );
412 check_assist_not_applicable( 412 check_assist_not_applicable(
413 fix_visibility, 413 fix_visibility,
414 r"mod foo { pub const FOO: () = (); } 414 r"mod foo { pub const FOO: () = (); }
415 fn main() { foo::FOO<|> } ", 415 fn main() { foo::FOO$0 } ",
416 ); 416 );
417 } 417 }
418 418
@@ -421,14 +421,14 @@ pub union Foo { pub bar: () }
421 check_assist( 421 check_assist(
422 fix_visibility, 422 fix_visibility,
423 r"mod foo { static FOO: () = (); } 423 r"mod foo { static FOO: () = (); }
424 fn main() { foo::FOO<|> } ", 424 fn main() { foo::FOO$0 } ",
425 r"mod foo { $0pub(crate) static FOO: () = (); } 425 r"mod foo { $0pub(crate) static FOO: () = (); }
426 fn main() { foo::FOO } ", 426 fn main() { foo::FOO } ",
427 ); 427 );
428 check_assist_not_applicable( 428 check_assist_not_applicable(
429 fix_visibility, 429 fix_visibility,
430 r"mod foo { pub static FOO: () = (); } 430 r"mod foo { pub static FOO: () = (); }
431 fn main() { foo::FOO<|> } ", 431 fn main() { foo::FOO$0 } ",
432 ); 432 );
433 } 433 }
434 434
@@ -437,14 +437,14 @@ pub union Foo { pub bar: () }
437 check_assist( 437 check_assist(
438 fix_visibility, 438 fix_visibility,
439 r"mod foo { trait Foo { fn foo(&self) {} } } 439 r"mod foo { trait Foo { fn foo(&self) {} } }
440 fn main() { let x: &dyn foo::<|>Foo; } ", 440 fn main() { let x: &dyn foo::$0Foo; } ",
441 r"mod foo { $0pub(crate) trait Foo { fn foo(&self) {} } } 441 r"mod foo { $0pub(crate) trait Foo { fn foo(&self) {} } }
442 fn main() { let x: &dyn foo::Foo; } ", 442 fn main() { let x: &dyn foo::Foo; } ",
443 ); 443 );
444 check_assist_not_applicable( 444 check_assist_not_applicable(
445 fix_visibility, 445 fix_visibility,
446 r"mod foo { pub trait Foo { fn foo(&self) {} } } 446 r"mod foo { pub trait Foo { fn foo(&self) {} } }
447 fn main() { let x: &dyn foo::Foo<|>; } ", 447 fn main() { let x: &dyn foo::Foo$0; } ",
448 ); 448 );
449 } 449 }
450 450
@@ -453,14 +453,14 @@ pub union Foo { pub bar: () }
453 check_assist( 453 check_assist(
454 fix_visibility, 454 fix_visibility,
455 r"mod foo { type Foo = (); } 455 r"mod foo { type Foo = (); }
456 fn main() { let x: foo::Foo<|>; } ", 456 fn main() { let x: foo::Foo$0; } ",
457 r"mod foo { $0pub(crate) type Foo = (); } 457 r"mod foo { $0pub(crate) type Foo = (); }
458 fn main() { let x: foo::Foo; } ", 458 fn main() { let x: foo::Foo; } ",
459 ); 459 );
460 check_assist_not_applicable( 460 check_assist_not_applicable(
461 fix_visibility, 461 fix_visibility,
462 r"mod foo { pub type Foo = (); } 462 r"mod foo { pub type Foo = (); }
463 fn main() { let x: foo::Foo<|>; } ", 463 fn main() { let x: foo::Foo$0; } ",
464 ); 464 );
465 } 465 }
466 466
@@ -469,7 +469,7 @@ pub union Foo { pub bar: () }
469 check_assist( 469 check_assist(
470 fix_visibility, 470 fix_visibility,
471 r"mod foo { mod bar { fn bar() {} } } 471 r"mod foo { mod bar { fn bar() {} } }
472 fn main() { foo::bar<|>::bar(); } ", 472 fn main() { foo::bar$0::bar(); } ",
473 r"mod foo { $0pub(crate) mod bar { fn bar() {} } } 473 r"mod foo { $0pub(crate) mod bar { fn bar() {} } }
474 fn main() { foo::bar::bar(); } ", 474 fn main() { foo::bar::bar(); } ",
475 ); 475 );
@@ -479,7 +479,7 @@ pub union Foo { pub bar: () }
479 r" 479 r"
480//- /main.rs 480//- /main.rs
481mod foo; 481mod foo;
482fn main() { foo::bar<|>::baz(); } 482fn main() { foo::bar$0::baz(); }
483 483
484//- /foo.rs 484//- /foo.rs
485mod bar { 485mod bar {
@@ -495,7 +495,7 @@ mod bar {
495 check_assist_not_applicable( 495 check_assist_not_applicable(
496 fix_visibility, 496 fix_visibility,
497 r"mod foo { pub mod bar { pub fn bar() {} } } 497 r"mod foo { pub mod bar { pub fn bar() {} } }
498 fn main() { foo::bar<|>::bar(); } ", 498 fn main() { foo::bar$0::bar(); } ",
499 ); 499 );
500 } 500 }
501 501
@@ -506,7 +506,7 @@ mod bar {
506 r" 506 r"
507//- /main.rs 507//- /main.rs
508mod foo; 508mod foo;
509fn main() { foo::bar<|>::baz(); } 509fn main() { foo::bar$0::baz(); }
510 510
511//- /foo.rs 511//- /foo.rs
512mod bar; 512mod bar;
@@ -525,7 +525,7 @@ pub fn baz() {}
525 r" 525 r"
526//- /main.rs 526//- /main.rs
527mod foo; 527mod foo;
528fn main() { foo::bar<|>>::baz(); } 528fn main() { foo::bar$0>::baz(); }
529 529
530//- /foo.rs 530//- /foo.rs
531mod bar { 531mod bar {
@@ -545,7 +545,7 @@ mod bar {
545 fix_visibility, 545 fix_visibility,
546 r" 546 r"
547//- /main.rs crate:a deps:foo 547//- /main.rs crate:a deps:foo
548foo::Bar<|> 548foo::Bar$0
549//- /lib.rs crate:foo 549//- /lib.rs crate:foo
550struct Bar; 550struct Bar;
551", 551",
@@ -560,7 +560,7 @@ struct Bar;
560 fix_visibility, 560 fix_visibility,
561 r" 561 r"
562//- /main.rs crate:a deps:foo 562//- /main.rs crate:a deps:foo
563foo::Bar<|> 563foo::Bar$0
564//- /lib.rs crate:foo 564//- /lib.rs crate:foo
565pub(crate) struct Bar; 565pub(crate) struct Bar;
566", 566",
@@ -572,7 +572,7 @@ pub(crate) struct Bar;
572 r" 572 r"
573//- /main.rs crate:a deps:foo 573//- /main.rs crate:a deps:foo
574fn main() { 574fn main() {
575 foo::Foo { <|>bar: () }; 575 foo::Foo { $0bar: () };
576} 576}
577//- /lib.rs crate:foo 577//- /lib.rs crate:foo
578pub struct Foo { pub(crate) bar: () } 578pub struct Foo { pub(crate) bar: () }
@@ -593,7 +593,7 @@ pub struct Foo { pub(crate) bar: () }
593 use bar::Baz; 593 use bar::Baz;
594 mod bar { pub(super) struct Baz; } 594 mod bar { pub(super) struct Baz; }
595 } 595 }
596 foo::Baz<|> 596 foo::Baz$0
597 ", 597 ",
598 r" 598 r"
599 mod foo { 599 mod foo {
diff --git a/crates/assists/src/handlers/flip_binexpr.rs b/crates/assists/src/handlers/flip_binexpr.rs
index 404f06133..209e5d43c 100644
--- a/crates/assists/src/handlers/flip_binexpr.rs
+++ b/crates/assists/src/handlers/flip_binexpr.rs
@@ -8,7 +8,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
8// 8//
9// ``` 9// ```
10// fn main() { 10// fn main() {
11// let _ = 90 +<|> 2; 11// let _ = 90 +$0 2;
12// } 12// }
13// ``` 13// ```
14// -> 14// ->
@@ -77,42 +77,34 @@ mod tests {
77 77
78 #[test] 78 #[test]
79 fn flip_binexpr_target_is_the_op() { 79 fn flip_binexpr_target_is_the_op() {
80 check_assist_target(flip_binexpr, "fn f() { let res = 1 ==<|> 2; }", "==") 80 check_assist_target(flip_binexpr, "fn f() { let res = 1 ==$0 2; }", "==")
81 } 81 }
82 82
83 #[test] 83 #[test]
84 fn flip_binexpr_not_applicable_for_assignment() { 84 fn flip_binexpr_not_applicable_for_assignment() {
85 check_assist_not_applicable(flip_binexpr, "fn f() { let mut _x = 1; _x +=<|> 2 }") 85 check_assist_not_applicable(flip_binexpr, "fn f() { let mut _x = 1; _x +=$0 2 }")
86 } 86 }
87 87
88 #[test] 88 #[test]
89 fn flip_binexpr_works_for_eq() { 89 fn flip_binexpr_works_for_eq() {
90 check_assist( 90 check_assist(flip_binexpr, "fn f() { let res = 1 ==$0 2; }", "fn f() { let res = 2 == 1; }")
91 flip_binexpr,
92 "fn f() { let res = 1 ==<|> 2; }",
93 "fn f() { let res = 2 == 1; }",
94 )
95 } 91 }
96 92
97 #[test] 93 #[test]
98 fn flip_binexpr_works_for_gt() { 94 fn flip_binexpr_works_for_gt() {
99 check_assist(flip_binexpr, "fn f() { let res = 1 ><|> 2; }", "fn f() { let res = 2 < 1; }") 95 check_assist(flip_binexpr, "fn f() { let res = 1 >$0 2; }", "fn f() { let res = 2 < 1; }")
100 } 96 }
101 97
102 #[test] 98 #[test]
103 fn flip_binexpr_works_for_lteq() { 99 fn flip_binexpr_works_for_lteq() {
104 check_assist( 100 check_assist(flip_binexpr, "fn f() { let res = 1 <=$0 2; }", "fn f() { let res = 2 >= 1; }")
105 flip_binexpr,
106 "fn f() { let res = 1 <=<|> 2; }",
107 "fn f() { let res = 2 >= 1; }",
108 )
109 } 101 }
110 102
111 #[test] 103 #[test]
112 fn flip_binexpr_works_for_complex_expr() { 104 fn flip_binexpr_works_for_complex_expr() {
113 check_assist( 105 check_assist(
114 flip_binexpr, 106 flip_binexpr,
115 "fn f() { let res = (1 + 1) ==<|> (2 + 2); }", 107 "fn f() { let res = (1 + 1) ==$0 (2 + 2); }",
116 "fn f() { let res = (2 + 2) == (1 + 1); }", 108 "fn f() { let res = (2 + 2) == (1 + 1); }",
117 ) 109 )
118 } 110 }
@@ -125,7 +117,7 @@ mod tests {
125 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool { 117 fn dyn_eq(&self, other: &dyn Diagnostic) -> bool {
126 match other.downcast_ref::<Self>() { 118 match other.downcast_ref::<Self>() {
127 None => false, 119 None => false,
128 Some(it) => it ==<|> self, 120 Some(it) => it ==$0 self,
129 } 121 }
130 } 122 }
131 "#, 123 "#,
diff --git a/crates/assists/src/handlers/flip_comma.rs b/crates/assists/src/handlers/flip_comma.rs
index 64b4b1a76..18cf64a34 100644
--- a/crates/assists/src/handlers/flip_comma.rs
+++ b/crates/assists/src/handlers/flip_comma.rs
@@ -8,7 +8,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
8// 8//
9// ``` 9// ```
10// fn main() { 10// fn main() {
11// ((1, 2),<|> (3, 4)); 11// ((1, 2),$0 (3, 4));
12// } 12// }
13// ``` 13// ```
14// -> 14// ->
@@ -49,14 +49,14 @@ mod tests {
49 fn flip_comma_works_for_function_parameters() { 49 fn flip_comma_works_for_function_parameters() {
50 check_assist( 50 check_assist(
51 flip_comma, 51 flip_comma,
52 "fn foo(x: i32,<|> y: Result<(), ()>) {}", 52 r#"fn foo(x: i32,$0 y: Result<(), ()>) {}"#,
53 "fn foo(y: Result<(), ()>, x: i32) {}", 53 r#"fn foo(y: Result<(), ()>, x: i32) {}"#,
54 ) 54 )
55 } 55 }
56 56
57 #[test] 57 #[test]
58 fn flip_comma_target() { 58 fn flip_comma_target() {
59 check_assist_target(flip_comma, "fn foo(x: i32,<|> y: Result<(), ()>) {}", ",") 59 check_assist_target(flip_comma, r#"fn foo(x: i32,$0 y: Result<(), ()>) {}"#, ",")
60 } 60 }
61 61
62 #[test] 62 #[test]
@@ -68,7 +68,7 @@ mod tests {
68 check_assist_target( 68 check_assist_target(
69 flip_comma, 69 flip_comma,
70 "pub enum Test { \ 70 "pub enum Test { \
71 A,<|> \ 71 A,$0 \
72 }", 72 }",
73 ",", 73 ",",
74 ); 74 );
@@ -76,7 +76,7 @@ mod tests {
76 check_assist_target( 76 check_assist_target(
77 flip_comma, 77 flip_comma,
78 "pub struct Test { \ 78 "pub struct Test { \
79 foo: usize,<|> \ 79 foo: usize,$0 \
80 }", 80 }",
81 ",", 81 ",",
82 ); 82 );
diff --git a/crates/assists/src/handlers/flip_trait_bound.rs b/crates/assists/src/handlers/flip_trait_bound.rs
index 92ee42181..d419d263e 100644
--- a/crates/assists/src/handlers/flip_trait_bound.rs
+++ b/crates/assists/src/handlers/flip_trait_bound.rs
@@ -11,7 +11,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
11// Flips two trait bounds. 11// Flips two trait bounds.
12// 12//
13// ``` 13// ```
14// fn foo<T: Clone +<|> Copy>() { } 14// fn foo<T: Clone +$0 Copy>() { }
15// ``` 15// ```
16// -> 16// ->
17// ``` 17// ```
@@ -52,19 +52,19 @@ mod tests {
52 52
53 #[test] 53 #[test]
54 fn flip_trait_bound_assist_available() { 54 fn flip_trait_bound_assist_available() {
55 check_assist_target(flip_trait_bound, "struct S<T> where T: A <|>+ B + C { }", "+") 55 check_assist_target(flip_trait_bound, "struct S<T> where T: A $0+ B + C { }", "+")
56 } 56 }
57 57
58 #[test] 58 #[test]
59 fn flip_trait_bound_not_applicable_for_single_trait_bound() { 59 fn flip_trait_bound_not_applicable_for_single_trait_bound() {
60 check_assist_not_applicable(flip_trait_bound, "struct S<T> where T: <|>A { }") 60 check_assist_not_applicable(flip_trait_bound, "struct S<T> where T: $0A { }")
61 } 61 }
62 62
63 #[test] 63 #[test]
64 fn flip_trait_bound_works_for_struct() { 64 fn flip_trait_bound_works_for_struct() {
65 check_assist( 65 check_assist(
66 flip_trait_bound, 66 flip_trait_bound,
67 "struct S<T> where T: A <|>+ B { }", 67 "struct S<T> where T: A $0+ B { }",
68 "struct S<T> where T: B + A { }", 68 "struct S<T> where T: B + A { }",
69 ) 69 )
70 } 70 }
@@ -73,21 +73,21 @@ mod tests {
73 fn flip_trait_bound_works_for_trait_impl() { 73 fn flip_trait_bound_works_for_trait_impl() {
74 check_assist( 74 check_assist(
75 flip_trait_bound, 75 flip_trait_bound,
76 "impl X for S<T> where T: A +<|> B { }", 76 "impl X for S<T> where T: A +$0 B { }",
77 "impl X for S<T> where T: B + A { }", 77 "impl X for S<T> where T: B + A { }",
78 ) 78 )
79 } 79 }
80 80
81 #[test] 81 #[test]
82 fn flip_trait_bound_works_for_fn() { 82 fn flip_trait_bound_works_for_fn() {
83 check_assist(flip_trait_bound, "fn f<T: A <|>+ B>(t: T) { }", "fn f<T: B + A>(t: T) { }") 83 check_assist(flip_trait_bound, "fn f<T: A $0+ B>(t: T) { }", "fn f<T: B + A>(t: T) { }")
84 } 84 }
85 85
86 #[test] 86 #[test]
87 fn flip_trait_bound_works_for_fn_where_clause() { 87 fn flip_trait_bound_works_for_fn_where_clause() {
88 check_assist( 88 check_assist(
89 flip_trait_bound, 89 flip_trait_bound,
90 "fn f<T>(t: T) where T: A +<|> B { }", 90 "fn f<T>(t: T) where T: A +$0 B { }",
91 "fn f<T>(t: T) where T: B + A { }", 91 "fn f<T>(t: T) where T: B + A { }",
92 ) 92 )
93 } 93 }
@@ -96,7 +96,7 @@ mod tests {
96 fn flip_trait_bound_works_for_lifetime() { 96 fn flip_trait_bound_works_for_lifetime() {
97 check_assist( 97 check_assist(
98 flip_trait_bound, 98 flip_trait_bound,
99 "fn f<T>(t: T) where T: A <|>+ 'static { }", 99 "fn f<T>(t: T) where T: A $0+ 'static { }",
100 "fn f<T>(t: T) where T: 'static + A { }", 100 "fn f<T>(t: T) where T: 'static + A { }",
101 ) 101 )
102 } 102 }
@@ -105,7 +105,7 @@ mod tests {
105 fn flip_trait_bound_works_for_complex_bounds() { 105 fn flip_trait_bound_works_for_complex_bounds() {
106 check_assist( 106 check_assist(
107 flip_trait_bound, 107 flip_trait_bound,
108 "struct S<T> where T: A<T> <|>+ b_mod::B<T> + C<T> { }", 108 "struct S<T> where T: A<T> $0+ b_mod::B<T> + C<T> { }",
109 "struct S<T> where T: b_mod::B<T> + A<T> + C<T> { }", 109 "struct S<T> where T: b_mod::B<T> + A<T> + C<T> { }",
110 ) 110 )
111 } 111 }
@@ -114,7 +114,7 @@ mod tests {
114 fn flip_trait_bound_works_for_long_bounds() { 114 fn flip_trait_bound_works_for_long_bounds() {
115 check_assist( 115 check_assist(
116 flip_trait_bound, 116 flip_trait_bound,
117 "struct S<T> where T: A + B + C + D + E + F +<|> G + H + I + J { }", 117 "struct S<T> where T: A + B + C + D + E + F +$0 G + H + I + J { }",
118 "struct S<T> where T: A + B + C + D + E + G + F + H + I + J { }", 118 "struct S<T> where T: A + B + C + D + E + G + F + H + I + J { }",
119 ) 119 )
120 } 120 }
diff --git a/crates/assists/src/handlers/generate_default_from_enum_variant.rs b/crates/assists/src/handlers/generate_default_from_enum_variant.rs
index bcea46735..6a2ab9596 100644
--- a/crates/assists/src/handlers/generate_default_from_enum_variant.rs
+++ b/crates/assists/src/handlers/generate_default_from_enum_variant.rs
@@ -12,7 +12,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
12// ``` 12// ```
13// enum Version { 13// enum Version {
14// Undefined, 14// Undefined,
15// Minor<|>, 15// Minor$0,
16// Major, 16// Major,
17// } 17// }
18// ``` 18// ```
@@ -108,7 +108,7 @@ mod tests {
108 r#" 108 r#"
109enum Variant { 109enum Variant {
110 Undefined, 110 Undefined,
111 Minor<|>, 111 Minor$0,
112 Major, 112 Major,
113}"#, 113}"#,
114 r#"enum Variant { 114 r#"enum Variant {
@@ -132,7 +132,7 @@ impl Default for Variant {
132 r#" 132 r#"
133enum Variant { 133enum Variant {
134 Undefined, 134 Undefined,
135 Minor<|>, 135 Minor$0,
136 Major, 136 Major,
137} 137}
138 138
@@ -151,7 +151,7 @@ impl Default for Variant {
151 r#" 151 r#"
152enum Variant { 152enum Variant {
153 Undefined, 153 Undefined,
154 Minor(u32)<|>, 154 Minor(u32)$0,
155 Major, 155 Major,
156}"#, 156}"#,
157 ); 157 );
@@ -161,7 +161,7 @@ enum Variant {
161 fn test_generate_default_from_variant_with_one_variant() { 161 fn test_generate_default_from_variant_with_one_variant() {
162 check_assist( 162 check_assist(
163 generate_default_from_enum_variant, 163 generate_default_from_enum_variant,
164 r#"enum Variant { Undefi<|>ned }"#, 164 r#"enum Variant { Undefi$0ned }"#,
165 r#" 165 r#"
166enum Variant { Undefined } 166enum Variant { Undefined }
167 167
diff --git a/crates/assists/src/handlers/generate_derive.rs b/crates/assists/src/handlers/generate_derive.rs
index 314504e15..f876b7684 100644
--- a/crates/assists/src/handlers/generate_derive.rs
+++ b/crates/assists/src/handlers/generate_derive.rs
@@ -13,7 +13,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
13// ``` 13// ```
14// struct Point { 14// struct Point {
15// x: u32, 15// x: u32,
16// y: u32,<|> 16// y: u32,$0
17// } 17// }
18// ``` 18// ```
19// -> 19// ->
@@ -76,12 +76,12 @@ mod tests {
76 fn add_derive_new() { 76 fn add_derive_new() {
77 check_assist( 77 check_assist(
78 generate_derive, 78 generate_derive,
79 "struct Foo { a: i32, <|>}", 79 "struct Foo { a: i32, $0}",
80 "#[derive($0)]\nstruct Foo { a: i32, }", 80 "#[derive($0)]\nstruct Foo { a: i32, }",
81 ); 81 );
82 check_assist( 82 check_assist(
83 generate_derive, 83 generate_derive,
84 "struct Foo { <|> a: i32, }", 84 "struct Foo { $0 a: i32, }",
85 "#[derive($0)]\nstruct Foo { a: i32, }", 85 "#[derive($0)]\nstruct Foo { a: i32, }",
86 ); 86 );
87 } 87 }
@@ -90,7 +90,7 @@ mod tests {
90 fn add_derive_existing() { 90 fn add_derive_existing() {
91 check_assist( 91 check_assist(
92 generate_derive, 92 generate_derive,
93 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", 93 "#[derive(Clone)]\nstruct Foo { a: i32$0, }",
94 "#[derive(Clone$0)]\nstruct Foo { a: i32, }", 94 "#[derive(Clone$0)]\nstruct Foo { a: i32, }",
95 ); 95 );
96 } 96 }
@@ -102,7 +102,7 @@ mod tests {
102 " 102 "
103/// `Foo` is a pretty important struct. 103/// `Foo` is a pretty important struct.
104/// It does stuff. 104/// It does stuff.
105struct Foo { a: i32<|>, } 105struct Foo { a: i32$0, }
106 ", 106 ",
107 " 107 "
108/// `Foo` is a pretty important struct. 108/// `Foo` is a pretty important struct.
@@ -121,7 +121,7 @@ struct Foo { a: i32, }
121struct SomeThingIrrelevant; 121struct SomeThingIrrelevant;
122/// `Foo` is a pretty important struct. 122/// `Foo` is a pretty important struct.
123/// It does stuff. 123/// It does stuff.
124struct Foo { a: i32<|>, } 124struct Foo { a: i32$0, }
125struct EvenMoreIrrelevant; 125struct EvenMoreIrrelevant;
126 ", 126 ",
127 "/// `Foo` is a pretty important struct. 127 "/// `Foo` is a pretty important struct.
diff --git a/crates/assists/src/handlers/generate_from_impl_for_enum.rs b/crates/assists/src/handlers/generate_from_impl_for_enum.rs
index 3c374e5d9..d9af6ab11 100644
--- a/crates/assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/crates/assists/src/handlers/generate_from_impl_for_enum.rs
@@ -10,7 +10,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
10// Adds a From impl for an enum variant with one tuple field. 10// Adds a From impl for an enum variant with one tuple field.
11// 11//
12// ``` 12// ```
13// enum A { <|>One(u32) } 13// enum A { $0One(u32) }
14// ``` 14// ```
15// -> 15// ->
16// ``` 16// ```
@@ -101,7 +101,7 @@ mod tests {
101 fn test_generate_from_impl_for_enum() { 101 fn test_generate_from_impl_for_enum() {
102 check_assist( 102 check_assist(
103 generate_from_impl_for_enum, 103 generate_from_impl_for_enum,
104 "enum A { <|>One(u32) }", 104 "enum A { $0One(u32) }",
105 r#"enum A { One(u32) } 105 r#"enum A { One(u32) }
106 106
107impl From<u32> for A { 107impl From<u32> for A {
@@ -116,7 +116,7 @@ impl From<u32> for A {
116 fn test_generate_from_impl_for_enum_complicated_path() { 116 fn test_generate_from_impl_for_enum_complicated_path() {
117 check_assist( 117 check_assist(
118 generate_from_impl_for_enum, 118 generate_from_impl_for_enum,
119 r#"enum A { <|>One(foo::bar::baz::Boo) }"#, 119 r#"enum A { $0One(foo::bar::baz::Boo) }"#,
120 r#"enum A { One(foo::bar::baz::Boo) } 120 r#"enum A { One(foo::bar::baz::Boo) }
121 121
122impl From<foo::bar::baz::Boo> for A { 122impl From<foo::bar::baz::Boo> for A {
@@ -135,17 +135,17 @@ impl From<foo::bar::baz::Boo> for A {
135 135
136 #[test] 136 #[test]
137 fn test_add_from_impl_no_element() { 137 fn test_add_from_impl_no_element() {
138 check_not_applicable("enum A { <|>One }"); 138 check_not_applicable("enum A { $0One }");
139 } 139 }
140 140
141 #[test] 141 #[test]
142 fn test_add_from_impl_more_than_one_element_in_tuple() { 142 fn test_add_from_impl_more_than_one_element_in_tuple() {
143 check_not_applicable("enum A { <|>One(u32, String) }"); 143 check_not_applicable("enum A { $0One(u32, String) }");
144 } 144 }
145 145
146 #[test] 146 #[test]
147 fn test_add_from_impl_struct_variant() { 147 fn test_add_from_impl_struct_variant() {
148 check_not_applicable("enum A { <|>One { x: u32 } }"); 148 check_not_applicable("enum A { $0One { x: u32 } }");
149 } 149 }
150 150
151 #[test] 151 #[test]
@@ -153,7 +153,7 @@ impl From<foo::bar::baz::Boo> for A {
153 mark::check!(test_add_from_impl_already_exists); 153 mark::check!(test_add_from_impl_already_exists);
154 check_not_applicable( 154 check_not_applicable(
155 r#" 155 r#"
156enum A { <|>One(u32), } 156enum A { $0One(u32), }
157 157
158impl From<u32> for A { 158impl From<u32> for A {
159 fn from(v: u32) -> Self { 159 fn from(v: u32) -> Self {
@@ -168,7 +168,7 @@ impl From<u32> for A {
168 fn test_add_from_impl_different_variant_impl_exists() { 168 fn test_add_from_impl_different_variant_impl_exists() {
169 check_assist( 169 check_assist(
170 generate_from_impl_for_enum, 170 generate_from_impl_for_enum,
171 r#"enum A { <|>One(u32), Two(String), } 171 r#"enum A { $0One(u32), Two(String), }
172 172
173impl From<String> for A { 173impl From<String> for A {
174 fn from(v: String) -> Self { 174 fn from(v: String) -> Self {
diff --git a/crates/assists/src/handlers/generate_function.rs b/crates/assists/src/handlers/generate_function.rs
index f4cf155b6..06ac85f67 100644
--- a/crates/assists/src/handlers/generate_function.rs
+++ b/crates/assists/src/handlers/generate_function.rs
@@ -1,5 +1,5 @@
1use hir::HirDisplay; 1use hir::HirDisplay;
2use ide_db::base_db::FileId; 2use ide_db::{base_db::FileId, helpers::SnippetCap};
3use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::{FxHashMap, FxHashSet};
4use syntax::{ 4use syntax::{
5 ast::{ 5 ast::{
@@ -11,7 +11,6 @@ use syntax::{
11}; 11};
12 12
13use crate::{ 13use crate::{
14 assist_config::SnippetCap,
15 utils::{render_snippet, Cursor}, 14 utils::{render_snippet, Cursor},
16 AssistContext, AssistId, AssistKind, Assists, 15 AssistContext, AssistId, AssistKind, Assists,
17}; 16};
@@ -24,7 +23,7 @@ use crate::{
24// struct Baz; 23// struct Baz;
25// fn baz() -> Baz { Baz } 24// fn baz() -> Baz { Baz }
26// fn foo() { 25// fn foo() {
27// bar<|>("", baz()); 26// bar$0("", baz());
28// } 27// }
29// 28//
30// ``` 29// ```
@@ -343,7 +342,7 @@ mod tests {
343 generate_function, 342 generate_function,
344 r" 343 r"
345fn foo() { 344fn foo() {
346 bar<|>(); 345 bar$0();
347} 346}
348", 347",
349 r" 348 r"
@@ -367,7 +366,7 @@ fn bar() ${0:-> ()} {
367 r" 366 r"
368impl Foo { 367impl Foo {
369 fn foo() { 368 fn foo() {
370 bar<|>(); 369 bar$0();
371 } 370 }
372} 371}
373", 372",
@@ -392,7 +391,7 @@ fn bar() ${0:-> ()} {
392 generate_function, 391 generate_function,
393 r" 392 r"
394fn foo1() { 393fn foo1() {
395 bar<|>(); 394 bar$0();
396} 395}
397 396
398fn foo2() {} 397fn foo2() {}
@@ -418,7 +417,7 @@ fn foo2() {}
418 r" 417 r"
419mod baz { 418mod baz {
420 fn foo() { 419 fn foo() {
421 bar<|>(); 420 bar$0();
422 } 421 }
423} 422}
424", 423",
@@ -444,7 +443,7 @@ mod baz {
444struct Baz; 443struct Baz;
445fn baz() -> Baz { todo!() } 444fn baz() -> Baz { todo!() }
446fn foo() { 445fn foo() {
447 bar<|>(baz()); 446 bar$0(baz());
448} 447}
449", 448",
450 r" 449 r"
@@ -469,7 +468,7 @@ fn bar(baz: Baz) ${0:-> ()} {
469struct Baz; 468struct Baz;
470impl Baz { 469impl Baz {
471 fn foo(&self) -> Baz { 470 fn foo(&self) -> Baz {
472 ba<|>r(self.baz()) 471 ba$0r(self.baz())
473 } 472 }
474 fn baz(&self) -> Baz { 473 fn baz(&self) -> Baz {
475 Baz 474 Baz
@@ -500,7 +499,7 @@ fn bar(baz: Baz) ${0:-> ()} {
500 generate_function, 499 generate_function,
501 r#" 500 r#"
502fn foo() { 501fn foo() {
503 <|>bar("bar") 502 $0bar("bar")
504} 503}
505"#, 504"#,
506 r#" 505 r#"
@@ -521,7 +520,7 @@ fn bar(arg: &str) ${0:-> ()} {
521 generate_function, 520 generate_function,
522 r#" 521 r#"
523fn foo() { 522fn foo() {
524 <|>bar('x') 523 $0bar('x')
525} 524}
526"#, 525"#,
527 r#" 526 r#"
@@ -542,7 +541,7 @@ fn bar(arg: char) ${0:-> ()} {
542 generate_function, 541 generate_function,
543 r" 542 r"
544fn foo() { 543fn foo() {
545 <|>bar(42) 544 $0bar(42)
546} 545}
547", 546",
548 r" 547 r"
@@ -563,7 +562,7 @@ fn bar(arg: i32) ${0:-> ()} {
563 generate_function, 562 generate_function,
564 r" 563 r"
565fn foo() { 564fn foo() {
566 <|>bar(42 as u8) 565 $0bar(42 as u8)
567} 566}
568", 567",
569 r" 568 r"
@@ -587,7 +586,7 @@ fn bar(arg: u8) ${0:-> ()} {
587 r" 586 r"
588fn foo() { 587fn foo() {
589 let x = 42; 588 let x = 42;
590 bar<|>(x as u8) 589 bar$0(x as u8)
591} 590}
592", 591",
593 r" 592 r"
@@ -610,7 +609,7 @@ fn bar(x: u8) ${0:-> ()} {
610 r" 609 r"
611fn foo() { 610fn foo() {
612 let worble = (); 611 let worble = ();
613 <|>bar(worble) 612 $0bar(worble)
614} 613}
615", 614",
616 r" 615 r"
@@ -636,7 +635,7 @@ fn foo() -> impl Foo {
636 todo!() 635 todo!()
637} 636}
638fn baz() { 637fn baz() {
639 <|>bar(foo()) 638 $0bar(foo())
640} 639}
641", 640",
642 r" 641 r"
@@ -664,7 +663,7 @@ struct Baz;
664fn baz() -> Baz { todo!() } 663fn baz() -> Baz { todo!() }
665 664
666fn foo() { 665fn foo() {
667 bar<|>(&baz()) 666 bar$0(&baz())
668} 667}
669", 668",
670 r" 669 r"
@@ -692,7 +691,7 @@ mod Baz {
692 pub fn baz() -> Bof { Bof } 691 pub fn baz() -> Bof { Bof }
693} 692}
694fn foo() { 693fn foo() {
695 <|>bar(Baz::baz()) 694 $0bar(Baz::baz())
696} 695}
697", 696",
698 r" 697 r"
@@ -719,7 +718,7 @@ fn bar(baz: Baz::Bof) ${0:-> ()} {
719 generate_function, 718 generate_function,
720 r" 719 r"
721fn foo<T>(t: T) { 720fn foo<T>(t: T) {
722 <|>bar(t) 721 $0bar(t)
723} 722}
724", 723",
725 r" 724 r"
@@ -746,7 +745,7 @@ impl Baz {
746 fn new() -> Self { Baz } 745 fn new() -> Self { Baz }
747} 746}
748fn foo() { 747fn foo() {
749 <|>bar(Baz::new); 748 $0bar(Baz::new);
750} 749}
751", 750",
752 r" 751 r"
@@ -774,7 +773,7 @@ fn bar(arg: fn() -> Baz) ${0:-> ()} {
774 r" 773 r"
775fn foo() { 774fn foo() {
776 let closure = |x: i64| x - 1; 775 let closure = |x: i64| x - 1;
777 <|>bar(closure) 776 $0bar(closure)
778} 777}
779", 778",
780 r" 779 r"
@@ -796,7 +795,7 @@ fn bar(closure: impl Fn(i64) -> i64) ${0:-> ()} {
796 generate_function, 795 generate_function,
797 r" 796 r"
798fn foo() { 797fn foo() {
799 <|>bar(baz) 798 $0bar(baz)
800} 799}
801", 800",
802 r" 801 r"
@@ -819,7 +818,7 @@ fn bar(baz: ()) ${0:-> ()} {
819struct Baz; 818struct Baz;
820fn baz() -> Baz { Baz } 819fn baz() -> Baz { Baz }
821fn foo() { 820fn foo() {
822 <|>bar(baz(), baz()) 821 $0bar(baz(), baz())
823} 822}
824", 823",
825 r" 824 r"
@@ -844,7 +843,7 @@ fn bar(baz_1: Baz, baz_2: Baz) ${0:-> ()} {
844struct Baz; 843struct Baz;
845fn baz() -> Baz { Baz } 844fn baz() -> Baz { Baz }
846fn foo() { 845fn foo() {
847 <|>bar(baz(), baz(), "foo", "bar") 846 $0bar(baz(), baz(), "foo", "bar")
848} 847}
849"#, 848"#,
850 r#" 849 r#"
@@ -869,7 +868,7 @@ fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) ${0:-> ()} {
869mod bar {} 868mod bar {}
870 869
871fn foo() { 870fn foo() {
872 bar::my_fn<|>() 871 bar::my_fn$0()
873} 872}
874", 873",
875 r" 874 r"
@@ -900,7 +899,7 @@ mod foo {
900fn bar() { 899fn bar() {
901 use foo::Foo; 900 use foo::Foo;
902 let foo = Foo; 901 let foo = Foo;
903 baz<|>(foo) 902 baz$0(foo)
904} 903}
905", 904",
906 " 905 "
@@ -930,7 +929,7 @@ mod bar {
930} 929}
931 930
932fn foo() { 931fn foo() {
933 bar::my_fn<|>() 932 bar::my_fn$0()
934} 933}
935", 934",
936 r" 935 r"
@@ -959,7 +958,7 @@ mod bar {
959} 958}
960 959
961fn foo() { 960fn foo() {
962 bar::baz::my_fn<|>() 961 bar::baz::my_fn$0()
963} 962}
964", 963",
965 r" 964 r"
@@ -987,7 +986,7 @@ fn foo() {
987mod foo; 986mod foo;
988 987
989fn main() { 988fn main() {
990 foo::bar<|>() 989 foo::bar$0()
991} 990}
992//- /foo.rs 991//- /foo.rs
993", 992",
@@ -1006,7 +1005,7 @@ pub(crate) fn bar() ${0:-> ()} {
1006 generate_function, 1005 generate_function,
1007 r" 1006 r"
1008fn foo() { 1007fn foo() {
1009 bar<|>(); 1008 bar$0();
1010} 1009}
1011 1010
1012fn bar() {} 1011fn bar() {}
@@ -1023,7 +1022,7 @@ fn bar() {}
1023 generate_function, 1022 generate_function,
1024 r" 1023 r"
1025fn foo() { 1024fn foo() {
1026 bar(b<|>az); 1025 bar(b$0az);
1027} 1026}
1028 1027
1029fn bar(baz: ()) {} 1028fn bar(baz: ()) {}
@@ -1040,7 +1039,7 @@ fn bar(baz: ()) {}
1040struct Foo; 1039struct Foo;
1041impl Foo { 1040impl Foo {
1042 fn foo(&self) { 1041 fn foo(&self) {
1043 self.bar()<|>; 1042 self.bar()$0;
1044 } 1043 }
1045} 1044}
1046 ", 1045 ",
diff --git a/crates/assists/src/handlers/generate_impl.rs b/crates/assists/src/handlers/generate_impl.rs
index 960af5ab3..9af45192b 100644
--- a/crates/assists/src/handlers/generate_impl.rs
+++ b/crates/assists/src/handlers/generate_impl.rs
@@ -10,7 +10,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
10// 10//
11// ``` 11// ```
12// struct Ctx<T: Clone> { 12// struct Ctx<T: Clone> {
13// data: T,<|> 13// data: T,$0
14// } 14// }
15// ``` 15// ```
16// -> 16// ->
@@ -87,24 +87,24 @@ mod tests {
87 fn test_add_impl() { 87 fn test_add_impl() {
88 check_assist( 88 check_assist(
89 generate_impl, 89 generate_impl,
90 "struct Foo {<|>}\n", 90 "struct Foo {$0}\n",
91 "struct Foo {}\n\nimpl Foo {\n $0\n}\n", 91 "struct Foo {}\n\nimpl Foo {\n $0\n}\n",
92 ); 92 );
93 check_assist( 93 check_assist(
94 generate_impl, 94 generate_impl,
95 "struct Foo<T: Clone> {<|>}", 95 "struct Foo<T: Clone> {$0}",
96 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}", 96 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}",
97 ); 97 );
98 check_assist( 98 check_assist(
99 generate_impl, 99 generate_impl,
100 "struct Foo<'a, T: Foo<'a>> {<|>}", 100 "struct Foo<'a, T: Foo<'a>> {$0}",
101 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}", 101 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}",
102 ); 102 );
103 check_assist( 103 check_assist(
104 generate_impl, 104 generate_impl,
105 r#" 105 r#"
106 #[cfg(feature = "foo")] 106 #[cfg(feature = "foo")]
107 struct Foo<'a, T: Foo<'a>> {<|>}"#, 107 struct Foo<'a, T: Foo<'a>> {$0}"#,
108 r#" 108 r#"
109 #[cfg(feature = "foo")] 109 #[cfg(feature = "foo")]
110 struct Foo<'a, T: Foo<'a>> {} 110 struct Foo<'a, T: Foo<'a>> {}
@@ -119,7 +119,7 @@ mod tests {
119 generate_impl, 119 generate_impl,
120 r#" 120 r#"
121 #[cfg(not(feature = "foo"))] 121 #[cfg(not(feature = "foo"))]
122 struct Foo<'a, T: Foo<'a>> {<|>}"#, 122 struct Foo<'a, T: Foo<'a>> {$0}"#,
123 r#" 123 r#"
124 #[cfg(not(feature = "foo"))] 124 #[cfg(not(feature = "foo"))]
125 struct Foo<'a, T: Foo<'a>> {} 125 struct Foo<'a, T: Foo<'a>> {}
@@ -138,7 +138,7 @@ mod tests {
138 " 138 "
139struct SomeThingIrrelevant; 139struct SomeThingIrrelevant;
140/// Has a lifetime parameter 140/// Has a lifetime parameter
141struct Foo<'a, T: Foo<'a>> {<|>} 141struct Foo<'a, T: Foo<'a>> {$0}
142struct EvenMoreIrrelevant; 142struct EvenMoreIrrelevant;
143", 143",
144 "/// Has a lifetime parameter 144 "/// Has a lifetime parameter
diff --git a/crates/assists/src/handlers/generate_new.rs b/crates/assists/src/handlers/generate_new.rs
index c5fec4e0a..5c52b2bc8 100644
--- a/crates/assists/src/handlers/generate_new.rs
+++ b/crates/assists/src/handlers/generate_new.rs
@@ -14,7 +14,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
14// 14//
15// ``` 15// ```
16// struct Ctx<T: Clone> { 16// struct Ctx<T: Clone> {
17// data: T,<|> 17// data: T,$0
18// } 18// }
19// ``` 19// ```
20// -> 20// ->
@@ -182,7 +182,7 @@ mod tests {
182 // Check output of generation 182 // Check output of generation
183 check_assist( 183 check_assist(
184 generate_new, 184 generate_new,
185"struct Foo {<|>}", 185"struct Foo {$0}",
186"struct Foo {} 186"struct Foo {}
187 187
188impl Foo { 188impl Foo {
@@ -192,7 +192,7 @@ impl Foo {
192 ); 192 );
193 check_assist( 193 check_assist(
194 generate_new, 194 generate_new,
195"struct Foo<T: Clone> {<|>}", 195"struct Foo<T: Clone> {$0}",
196"struct Foo<T: Clone> {} 196"struct Foo<T: Clone> {}
197 197
198impl<T: Clone> Foo<T> { 198impl<T: Clone> Foo<T> {
@@ -202,7 +202,7 @@ impl<T: Clone> Foo<T> {
202 ); 202 );
203 check_assist( 203 check_assist(
204 generate_new, 204 generate_new,
205"struct Foo<'a, T: Foo<'a>> {<|>}", 205"struct Foo<'a, T: Foo<'a>> {$0}",
206"struct Foo<'a, T: Foo<'a>> {} 206"struct Foo<'a, T: Foo<'a>> {}
207 207
208impl<'a, T: Foo<'a>> Foo<'a, T> { 208impl<'a, T: Foo<'a>> Foo<'a, T> {
@@ -212,7 +212,7 @@ impl<'a, T: Foo<'a>> Foo<'a, T> {
212 ); 212 );
213 check_assist( 213 check_assist(
214 generate_new, 214 generate_new,
215"struct Foo { baz: String <|>}", 215"struct Foo { baz: String $0}",
216"struct Foo { baz: String } 216"struct Foo { baz: String }
217 217
218impl Foo { 218impl Foo {
@@ -222,7 +222,7 @@ impl Foo {
222 ); 222 );
223 check_assist( 223 check_assist(
224 generate_new, 224 generate_new,
225"struct Foo { baz: String, qux: Vec<i32> <|>}", 225"struct Foo { baz: String, qux: Vec<i32> $0}",
226"struct Foo { baz: String, qux: Vec<i32> } 226"struct Foo { baz: String, qux: Vec<i32> }
227 227
228impl Foo { 228impl Foo {
@@ -234,7 +234,7 @@ impl Foo {
234 // Check that visibility modifiers don't get brought in for fields 234 // Check that visibility modifiers don't get brought in for fields
235 check_assist( 235 check_assist(
236 generate_new, 236 generate_new,
237"struct Foo { pub baz: String, pub qux: Vec<i32> <|>}", 237"struct Foo { pub baz: String, pub qux: Vec<i32> $0}",
238"struct Foo { pub baz: String, pub qux: Vec<i32> } 238"struct Foo { pub baz: String, pub qux: Vec<i32> }
239 239
240impl Foo { 240impl Foo {
@@ -246,7 +246,7 @@ impl Foo {
246 // Check that it reuses existing impls 246 // Check that it reuses existing impls
247 check_assist( 247 check_assist(
248 generate_new, 248 generate_new,
249"struct Foo {<|>} 249"struct Foo {$0}
250 250
251impl Foo {} 251impl Foo {}
252", 252",
@@ -259,7 +259,7 @@ impl Foo {
259 ); 259 );
260 check_assist( 260 check_assist(
261 generate_new, 261 generate_new,
262"struct Foo {<|>} 262"struct Foo {$0}
263 263
264impl Foo { 264impl Foo {
265 fn qux(&self) {} 265 fn qux(&self) {}
@@ -277,7 +277,7 @@ impl Foo {
277 277
278 check_assist( 278 check_assist(
279 generate_new, 279 generate_new,
280"struct Foo {<|>} 280"struct Foo {$0}
281 281
282impl Foo { 282impl Foo {
283 fn qux(&self) {} 283 fn qux(&self) {}
@@ -302,7 +302,7 @@ impl Foo {
302 // Check visibility of new fn based on struct 302 // Check visibility of new fn based on struct
303 check_assist( 303 check_assist(
304 generate_new, 304 generate_new,
305"pub struct Foo {<|>}", 305"pub struct Foo {$0}",
306"pub struct Foo {} 306"pub struct Foo {}
307 307
308impl Foo { 308impl Foo {
@@ -312,7 +312,7 @@ impl Foo {
312 ); 312 );
313 check_assist( 313 check_assist(
314 generate_new, 314 generate_new,
315"pub(crate) struct Foo {<|>}", 315"pub(crate) struct Foo {$0}",
316"pub(crate) struct Foo {} 316"pub(crate) struct Foo {}
317 317
318impl Foo { 318impl Foo {
@@ -327,7 +327,7 @@ impl Foo {
327 check_assist_not_applicable( 327 check_assist_not_applicable(
328 generate_new, 328 generate_new,
329 " 329 "
330struct Foo {<|>} 330struct Foo {$0}
331 331
332impl Foo { 332impl Foo {
333 fn new() -> Self { 333 fn new() -> Self {
@@ -339,7 +339,7 @@ impl Foo {
339 check_assist_not_applicable( 339 check_assist_not_applicable(
340 generate_new, 340 generate_new,
341 " 341 "
342struct Foo {<|>} 342struct Foo {$0}
343 343
344impl Foo { 344impl Foo {
345 fn New() -> Self { 345 fn New() -> Self {
@@ -356,7 +356,7 @@ impl Foo {
356 " 356 "
357struct SomeThingIrrelevant; 357struct SomeThingIrrelevant;
358/// Has a lifetime parameter 358/// Has a lifetime parameter
359struct Foo<'a, T: Foo<'a>> {<|>} 359struct Foo<'a, T: Foo<'a>> {$0}
360struct EvenMoreIrrelevant; 360struct EvenMoreIrrelevant;
361", 361",
362 "/// Has a lifetime parameter 362 "/// Has a lifetime parameter
@@ -381,7 +381,7 @@ impl<N: AstNode> AstId<N> {
381} 381}
382 382
383pub struct Source<T> { 383pub struct Source<T> {
384 pub file_id: HirFileId,<|> 384 pub file_id: HirFileId,$0
385 pub ast: T, 385 pub ast: T,
386} 386}
387 387
diff --git a/crates/assists/src/handlers/infer_function_return_type.rs b/crates/assists/src/handlers/infer_function_return_type.rs
index aa584eb03..5279af1f3 100644
--- a/crates/assists/src/handlers/infer_function_return_type.rs
+++ b/crates/assists/src/handlers/infer_function_return_type.rs
@@ -10,7 +10,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
10// type specified. This assists is useable in a functions or closures tail expression or return type position. 10// type specified. This assists is useable in a functions or closures tail expression or return type position.
11// 11//
12// ``` 12// ```
13// fn foo() { 4<|>2i32 } 13// fn foo() { 4$02i32 }
14// ``` 14// ```
15// -> 15// ->
16// ``` 16// ```
@@ -89,7 +89,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrRepla
89 let body = closure.body()?; 89 let body = closure.body()?;
90 let body_start = body.syntax().first_token()?.text_range().start(); 90 let body_start = body.syntax().first_token()?.text_range().start();
91 let (tail_expr, wrap_expr) = match body { 91 let (tail_expr, wrap_expr) = match body {
92 ast::Expr::BlockExpr(block) => (block.expr()?, false), 92 ast::Expr::BlockExpr(block) => (block.tail_expr()?, false),
93 body => (body, true), 93 body => (body, true),
94 }; 94 };
95 95
@@ -101,7 +101,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrRepla
101 let action = ret_ty_to_action(func.ret_type(), rparen_pos)?; 101 let action = ret_ty_to_action(func.ret_type(), rparen_pos)?;
102 102
103 let body = func.body()?; 103 let body = func.body()?;
104 let tail_expr = body.expr()?; 104 let tail_expr = body.tail_expr()?;
105 105
106 let ret_range_end = body.l_curly_token()?.text_range().start(); 106 let ret_range_end = body.l_curly_token()?.text_range().start();
107 let ret_range = TextRange::new(rparen_pos, ret_range_end); 107 let ret_range = TextRange::new(rparen_pos, ret_range_end);
@@ -131,7 +131,7 @@ mod tests {
131 mark::check!(existing_infer_ret_type); 131 mark::check!(existing_infer_ret_type);
132 check_assist( 132 check_assist(
133 infer_function_return_type, 133 infer_function_return_type,
134 r#"fn foo() -> <|>_ { 134 r#"fn foo() -> $0_ {
135 45 135 45
136}"#, 136}"#,
137 r#"fn foo() -> i32 { 137 r#"fn foo() -> i32 {
@@ -146,7 +146,7 @@ mod tests {
146 check_assist( 146 check_assist(
147 infer_function_return_type, 147 infer_function_return_type,
148 r#"fn foo() { 148 r#"fn foo() {
149 || -> _ {<|>45}; 149 || -> _ {$045};
150}"#, 150}"#,
151 r#"fn foo() { 151 r#"fn foo() {
152 || -> i32 {45}; 152 || -> i32 {45};
@@ -159,7 +159,7 @@ mod tests {
159 mark::check!(cursor_in_ret_position); 159 mark::check!(cursor_in_ret_position);
160 check_assist( 160 check_assist(
161 infer_function_return_type, 161 infer_function_return_type,
162 r#"fn foo() <|>{ 162 r#"fn foo() $0{
163 45 163 45
164}"#, 164}"#,
165 r#"fn foo() -> i32 { 165 r#"fn foo() -> i32 {
@@ -174,7 +174,7 @@ mod tests {
174 check_assist( 174 check_assist(
175 infer_function_return_type, 175 infer_function_return_type,
176 r#"fn foo() { 176 r#"fn foo() {
177 || <|>45 177 || $045
178}"#, 178}"#,
179 r#"fn foo() { 179 r#"fn foo() {
180 || -> i32 {45} 180 || -> i32 {45}
@@ -188,7 +188,7 @@ mod tests {
188 check_assist( 188 check_assist(
189 infer_function_return_type, 189 infer_function_return_type,
190 r#"fn foo() { 190 r#"fn foo() {
191 45<|> 191 45$0
192}"#, 192}"#,
193 r#"fn foo() -> i32 { 193 r#"fn foo() -> i32 {
194 45 194 45
@@ -202,7 +202,7 @@ mod tests {
202 infer_function_return_type, 202 infer_function_return_type,
203 r#"fn foo() { 203 r#"fn foo() {
204 if true { 204 if true {
205 3<|> 205 3$0
206 } else { 206 } else {
207 5 207 5
208 } 208 }
@@ -223,7 +223,7 @@ mod tests {
223 check_assist_not_applicable( 223 check_assist_not_applicable(
224 infer_function_return_type, 224 infer_function_return_type,
225 r#"fn foo() -> i32 { 225 r#"fn foo() -> i32 {
226 ( 45<|> + 32 ) * 123 226 ( 45$0 + 32 ) * 123
227}"#, 227}"#,
228 ); 228 );
229 } 229 }
@@ -233,7 +233,7 @@ mod tests {
233 check_assist_not_applicable( 233 check_assist_not_applicable(
234 infer_function_return_type, 234 infer_function_return_type,
235 r#"fn foo() { 235 r#"fn foo() {
236 let x = <|>3; 236 let x = $03;
237 ( 45 + 32 ) * 123 237 ( 45 + 32 ) * 123
238}"#, 238}"#,
239 ); 239 );
@@ -244,7 +244,7 @@ mod tests {
244 check_assist_not_applicable( 244 check_assist_not_applicable(
245 infer_function_return_type, 245 infer_function_return_type,
246 r#"fn foo() { 246 r#"fn foo() {
247 (<|>) 247 ($0)
248}"#, 248}"#,
249 ); 249 );
250 } 250 }
@@ -256,7 +256,7 @@ mod tests {
256 infer_function_return_type, 256 infer_function_return_type,
257 r#"fn foo() { 257 r#"fn foo() {
258 |x: i32| { 258 |x: i32| {
259 x<|> 259 x$0
260 }; 260 };
261}"#, 261}"#,
262 r#"fn foo() { 262 r#"fn foo() {
@@ -272,7 +272,7 @@ mod tests {
272 check_assist( 272 check_assist(
273 infer_function_return_type, 273 infer_function_return_type,
274 r#"fn foo() { 274 r#"fn foo() {
275 |x: i32| { x<|> }; 275 |x: i32| { x$0 };
276}"#, 276}"#,
277 r#"fn foo() { 277 r#"fn foo() {
278 |x: i32| -> i32 { x }; 278 |x: i32| -> i32 { x };
@@ -286,7 +286,7 @@ mod tests {
286 check_assist( 286 check_assist(
287 infer_function_return_type, 287 infer_function_return_type,
288 r#"fn foo() { 288 r#"fn foo() {
289 |x: i32| x<|>; 289 |x: i32| x$0;
290}"#, 290}"#,
291 r#"fn foo() { 291 r#"fn foo() {
292 |x: i32| -> i32 {x}; 292 |x: i32| -> i32 {x};
@@ -301,7 +301,7 @@ mod tests {
301 r#"fn foo() { 301 r#"fn foo() {
302 || { 302 || {
303 if true { 303 if true {
304 3<|> 304 3$0
305 } else { 305 } else {
306 5 306 5
307 } 307 }
@@ -325,7 +325,7 @@ mod tests {
325 check_assist_not_applicable( 325 check_assist_not_applicable(
326 infer_function_return_type, 326 infer_function_return_type,
327 r#"fn foo() { 327 r#"fn foo() {
328 || -> i32 { 3<|> } 328 || -> i32 { 3$0 }
329}"#, 329}"#,
330 ); 330 );
331 } 331 }
@@ -336,7 +336,7 @@ mod tests {
336 infer_function_return_type, 336 infer_function_return_type,
337 r#"fn foo() { 337 r#"fn foo() {
338 || -> i32 { 338 || -> i32 {
339 let x = 3<|>; 339 let x = 3$0;
340 6 340 6
341 } 341 }
342}"#, 342}"#,
diff --git a/crates/assists/src/handlers/inline_function.rs b/crates/assists/src/handlers/inline_function.rs
new file mode 100644
index 000000000..6ec99b09b
--- /dev/null
+++ b/crates/assists/src/handlers/inline_function.rs
@@ -0,0 +1,202 @@
1use ast::make;
2use hir::{HasSource, PathResolution};
3use syntax::{
4 ast::{self, edit::AstNodeEdit, ArgListOwner},
5 AstNode,
6};
7use test_utils::mark;
8
9use crate::{
10 assist_context::{AssistContext, Assists},
11 AssistId, AssistKind,
12};
13
14// Assist: inline_function
15//
16// Inlines a function body.
17//
18// ```
19// fn add(a: u32, b: u32) -> u32 { a + b }
20// fn main() {
21// let x = add$0(1, 2);
22// }
23// ```
24// ->
25// ```
26// fn add(a: u32, b: u32) -> u32 { a + b }
27// fn main() {
28// let x = {
29// let a = 1;
30// let b = 2;
31// a + b
32// };
33// }
34// ```
35pub(crate) fn inline_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
36 let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
37 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
38 let path = path_expr.path()?;
39
40 let function = match ctx.sema.resolve_path(&path)? {
41 PathResolution::Def(hir::ModuleDef::Function(f)) => f,
42 _ => return None,
43 };
44
45 let function_source = function.source(ctx.db())?;
46 let arguments: Vec<_> = call.arg_list()?.args().collect();
47 let parameters = function_parameter_patterns(&function_source.value)?;
48
49 if arguments.len() != parameters.len() {
50 // Can't inline the function because they've passed the wrong number of
51 // arguments to this function
52 mark::hit!(inline_function_incorrect_number_of_arguments);
53 return None;
54 }
55
56 let new_bindings = parameters.into_iter().zip(arguments);
57
58 let body = function_source.value.body()?;
59
60 acc.add(
61 AssistId("inline_function", AssistKind::RefactorInline),
62 format!("Inline `{}`", path),
63 call.syntax().text_range(),
64 |builder| {
65 let mut statements: Vec<ast::Stmt> = Vec::new();
66
67 for (pattern, value) in new_bindings {
68 statements.push(make::let_stmt(pattern, Some(value)).into());
69 }
70
71 statements.extend(body.statements());
72
73 let original_indentation = call.indent_level();
74 let replacement = make::block_expr(statements, body.tail_expr())
75 .reset_indent()
76 .indent(original_indentation);
77
78 builder.replace_ast(ast::Expr::CallExpr(call), ast::Expr::BlockExpr(replacement));
79 },
80 )
81}
82
83fn function_parameter_patterns(value: &ast::Fn) -> Option<Vec<ast::Pat>> {
84 let mut patterns = Vec::new();
85
86 for param in value.param_list()?.params() {
87 let pattern = param.pat()?;
88 patterns.push(pattern);
89 }
90
91 Some(patterns)
92}
93
94#[cfg(test)]
95mod tests {
96 use crate::tests::{check_assist, check_assist_not_applicable};
97
98 use super::*;
99
100 #[test]
101 fn no_args_or_return_value_gets_inlined_without_block() {
102 check_assist(
103 inline_function,
104 r#"
105fn foo() { println!("Hello, World!"); }
106fn main() {
107 fo$0o();
108}
109"#,
110 r#"
111fn foo() { println!("Hello, World!"); }
112fn main() {
113 {
114 println!("Hello, World!");
115 };
116}
117"#,
118 );
119 }
120
121 #[test]
122 fn args_with_side_effects() {
123 check_assist(
124 inline_function,
125 r#"
126fn foo(name: String) { println!("Hello, {}!", name); }
127fn main() {
128 foo$0(String::from("Michael"));
129}
130"#,
131 r#"
132fn foo(name: String) { println!("Hello, {}!", name); }
133fn main() {
134 {
135 let name = String::from("Michael");
136 println!("Hello, {}!", name);
137 };
138}
139"#,
140 );
141 }
142
143 #[test]
144 fn method_inlining_isnt_supported() {
145 check_assist_not_applicable(
146 inline_function,
147 r"
148struct Foo;
149impl Foo { fn bar(&self) {} }
150
151fn main() { Foo.bar$0(); }
152",
153 );
154 }
155
156 #[test]
157 fn not_applicable_when_incorrect_number_of_parameters_are_provided() {
158 mark::check!(inline_function_incorrect_number_of_arguments);
159 check_assist_not_applicable(
160 inline_function,
161 r#"
162fn add(a: u32, b: u32) -> u32 { a + b }
163fn main() { let x = add$0(42); }
164"#,
165 );
166 }
167
168 #[test]
169 fn function_with_multiple_statements() {
170 check_assist(
171 inline_function,
172 r#"
173fn foo(a: u32, b: u32) -> u32 {
174 let x = a + b;
175 let y = x - b;
176 x * y
177}
178
179fn main() {
180 let x = foo$0(1, 2);
181}
182"#,
183 r#"
184fn foo(a: u32, b: u32) -> u32 {
185 let x = a + b;
186 let y = x - b;
187 x * y
188}
189
190fn main() {
191 let x = {
192 let a = 1;
193 let b = 2;
194 let x = a + b;
195 let y = x - b;
196 x * y
197 };
198}
199"#,
200 );
201 }
202}
diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs
index 587eb5feb..dc798daaa 100644
--- a/crates/assists/src/handlers/inline_local_variable.rs
+++ b/crates/assists/src/handlers/inline_local_variable.rs
@@ -1,4 +1,7 @@
1use ide_db::{defs::Definition, search::ReferenceKind}; 1use ide_db::{
2 defs::Definition,
3 search::{FileReference, ReferenceKind},
4};
2use syntax::{ 5use syntax::{
3 ast::{self, AstNode, AstToken}, 6 ast::{self, AstNode, AstToken},
4 TextRange, 7 TextRange,
@@ -16,7 +19,7 @@ use crate::{
16// 19//
17// ``` 20// ```
18// fn main() { 21// fn main() {
19// let x<|> = 1 + 2; 22// let x$0 = 1 + 2;
20// x * 4; 23// x * 4;
21// } 24// }
22// ``` 25// ```
@@ -44,8 +47,8 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
44 47
45 let def = ctx.sema.to_def(&bind_pat)?; 48 let def = ctx.sema.to_def(&bind_pat)?;
46 let def = Definition::Local(def); 49 let def = Definition::Local(def);
47 let refs = def.usages(&ctx.sema).all(); 50 let usages = def.usages(&ctx.sema).all();
48 if refs.is_empty() { 51 if usages.is_empty() {
49 mark::hit!(test_not_applicable_if_variable_unused); 52 mark::hit!(test_not_applicable_if_variable_unused);
50 return None; 53 return None;
51 }; 54 };
@@ -63,48 +66,45 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
63 let_stmt.syntax().text_range() 66 let_stmt.syntax().text_range()
64 }; 67 };
65 68
66 let mut wrap_in_parens = vec![true; refs.len()]; 69 let wrap_in_parens = usages
67 70 .references
68 for (i, desc) in refs.iter().enumerate() { 71 .values()
69 let usage_node = ctx 72 .flatten()
70 .covering_node_for_range(desc.file_range.range) 73 .map(|&FileReference { range, .. }| {
71 .ancestors() 74 let usage_node =
72 .find_map(ast::PathExpr::cast)?; 75 ctx.covering_node_for_range(range).ancestors().find_map(ast::PathExpr::cast)?;
73 let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast); 76 let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast);
74 let usage_parent = match usage_parent_option { 77 let usage_parent = match usage_parent_option {
75 Some(u) => u, 78 Some(u) => u,
76 None => { 79 None => return Ok(false),
77 wrap_in_parens[i] = false; 80 };
78 continue; 81
79 } 82 Ok(!matches!((&initializer_expr, usage_parent),
80 }; 83 (ast::Expr::CallExpr(_), _)
81 84 | (ast::Expr::IndexExpr(_), _)
82 wrap_in_parens[i] = match (&initializer_expr, usage_parent) { 85 | (ast::Expr::MethodCallExpr(_), _)
83 (ast::Expr::CallExpr(_), _) 86 | (ast::Expr::FieldExpr(_), _)
84 | (ast::Expr::IndexExpr(_), _) 87 | (ast::Expr::TryExpr(_), _)
85 | (ast::Expr::MethodCallExpr(_), _) 88 | (ast::Expr::RefExpr(_), _)
86 | (ast::Expr::FieldExpr(_), _) 89 | (ast::Expr::Literal(_), _)
87 | (ast::Expr::TryExpr(_), _) 90 | (ast::Expr::TupleExpr(_), _)
88 | (ast::Expr::RefExpr(_), _) 91 | (ast::Expr::ArrayExpr(_), _)
89 | (ast::Expr::Literal(_), _) 92 | (ast::Expr::ParenExpr(_), _)
90 | (ast::Expr::TupleExpr(_), _) 93 | (ast::Expr::PathExpr(_), _)
91 | (ast::Expr::ArrayExpr(_), _) 94 | (ast::Expr::BlockExpr(_), _)
92 | (ast::Expr::ParenExpr(_), _) 95 | (ast::Expr::EffectExpr(_), _)
93 | (ast::Expr::PathExpr(_), _) 96 | (_, ast::Expr::CallExpr(_))
94 | (ast::Expr::BlockExpr(_), _) 97 | (_, ast::Expr::TupleExpr(_))
95 | (ast::Expr::EffectExpr(_), _) 98 | (_, ast::Expr::ArrayExpr(_))
96 | (_, ast::Expr::CallExpr(_)) 99 | (_, ast::Expr::ParenExpr(_))
97 | (_, ast::Expr::TupleExpr(_)) 100 | (_, ast::Expr::ForExpr(_))
98 | (_, ast::Expr::ArrayExpr(_)) 101 | (_, ast::Expr::WhileExpr(_))
99 | (_, ast::Expr::ParenExpr(_)) 102 | (_, ast::Expr::BreakExpr(_))
100 | (_, ast::Expr::ForExpr(_)) 103 | (_, ast::Expr::ReturnExpr(_))
101 | (_, ast::Expr::WhileExpr(_)) 104 | (_, ast::Expr::MatchExpr(_))
102 | (_, ast::Expr::BreakExpr(_)) 105 ))
103 | (_, ast::Expr::ReturnExpr(_)) 106 })
104 | (_, ast::Expr::MatchExpr(_)) => false, 107 .collect::<Result<Vec<_>, _>>()?;
105 _ => true,
106 };
107 }
108 108
109 let init_str = initializer_expr.syntax().text().to_string(); 109 let init_str = initializer_expr.syntax().text().to_string();
110 let init_in_paren = format!("({})", &init_str); 110 let init_in_paren = format!("({})", &init_str);
@@ -116,15 +116,16 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
116 target, 116 target,
117 move |builder| { 117 move |builder| {
118 builder.delete(delete_range); 118 builder.delete(delete_range);
119 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { 119 for (reference, should_wrap) in usages.references.values().flatten().zip(wrap_in_parens)
120 {
120 let replacement = 121 let replacement =
121 if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 122 if should_wrap { init_in_paren.clone() } else { init_str.clone() };
122 match desc.kind { 123 match reference.kind {
123 ReferenceKind::FieldShorthandForLocal => { 124 ReferenceKind::FieldShorthandForLocal => {
124 mark::hit!(inline_field_shorthand); 125 mark::hit!(inline_field_shorthand);
125 builder.insert(desc.file_range.range.end(), format!(": {}", replacement)) 126 builder.insert(reference.range.end(), format!(": {}", replacement))
126 } 127 }
127 _ => builder.replace(desc.file_range.range, replacement), 128 _ => builder.replace(reference.range, replacement),
128 } 129 }
129 } 130 }
130 }, 131 },
@@ -146,7 +147,7 @@ mod tests {
146 r" 147 r"
147fn bar(a: usize) {} 148fn bar(a: usize) {}
148fn foo() { 149fn foo() {
149 let a<|> = 1; 150 let a$0 = 1;
150 a + 1; 151 a + 1;
151 if a > 10 { 152 if a > 10 {
152 } 153 }
@@ -180,7 +181,7 @@ fn foo() {
180 r" 181 r"
181fn bar(a: usize) {} 182fn bar(a: usize) {}
182fn foo() { 183fn foo() {
183 let a<|> = 1 + 1; 184 let a$0 = 1 + 1;
184 a + 1; 185 a + 1;
185 if a > 10 { 186 if a > 10 {
186 } 187 }
@@ -214,7 +215,7 @@ fn foo() {
214 r" 215 r"
215fn bar(a: usize) {} 216fn bar(a: usize) {}
216fn foo() { 217fn foo() {
217 let a<|> = bar(1); 218 let a$0 = bar(1);
218 a + 1; 219 a + 1;
219 if a > 10 { 220 if a > 10 {
220 } 221 }
@@ -248,7 +249,7 @@ fn foo() {
248 r" 249 r"
249fn bar(a: usize): usize { a } 250fn bar(a: usize): usize { a }
250fn foo() { 251fn foo() {
251 let a<|> = bar(1) as u64; 252 let a$0 = bar(1) as u64;
252 a + 1; 253 a + 1;
253 if a > 10 { 254 if a > 10 {
254 } 255 }
@@ -281,7 +282,7 @@ fn foo() {
281 inline_local_variable, 282 inline_local_variable,
282 r" 283 r"
283fn foo() { 284fn foo() {
284 let a<|> = { 10 + 1 }; 285 let a$0 = { 10 + 1 };
285 a + 1; 286 a + 1;
286 if a > 10 { 287 if a > 10 {
287 } 288 }
@@ -313,7 +314,7 @@ fn foo() {
313 inline_local_variable, 314 inline_local_variable,
314 r" 315 r"
315fn foo() { 316fn foo() {
316 let a<|> = ( 10 + 1 ); 317 let a$0 = ( 10 + 1 );
317 a + 1; 318 a + 1;
318 if a > 10 { 319 if a > 10 {
319 } 320 }
@@ -346,7 +347,7 @@ fn foo() {
346 inline_local_variable, 347 inline_local_variable,
347 r" 348 r"
348fn foo() { 349fn foo() {
349 let mut a<|> = 1 + 1; 350 let mut a$0 = 1 + 1;
350 a + 1; 351 a + 1;
351}", 352}",
352 ); 353 );
@@ -358,7 +359,7 @@ fn foo() {
358 inline_local_variable, 359 inline_local_variable,
359 r" 360 r"
360fn foo() { 361fn foo() {
361 let a<|> = bar(10 + 1); 362 let a$0 = bar(10 + 1);
362 let b = a * 10; 363 let b = a * 10;
363 let c = a as usize; 364 let c = a as usize;
364}", 365}",
@@ -377,7 +378,7 @@ fn foo() {
377 r" 378 r"
378fn foo() { 379fn foo() {
379 let x = vec![1, 2, 3]; 380 let x = vec![1, 2, 3];
380 let a<|> = x[0]; 381 let a$0 = x[0];
381 let b = a * 10; 382 let b = a * 10;
382 let c = a as usize; 383 let c = a as usize;
383}", 384}",
@@ -397,7 +398,7 @@ fn foo() {
397 r" 398 r"
398fn foo() { 399fn foo() {
399 let bar = vec![1]; 400 let bar = vec![1];
400 let a<|> = bar.len(); 401 let a$0 = bar.len();
401 let b = a * 10; 402 let b = a * 10;
402 let c = a as usize; 403 let c = a as usize;
403}", 404}",
@@ -421,7 +422,7 @@ struct Bar {
421 422
422fn foo() { 423fn foo() {
423 let bar = Bar { foo: 1 }; 424 let bar = Bar { foo: 1 };
424 let a<|> = bar.foo; 425 let a$0 = bar.foo;
425 let b = a * 10; 426 let b = a * 10;
426 let c = a as usize; 427 let c = a as usize;
427}", 428}",
@@ -445,7 +446,7 @@ fn foo() {
445 r" 446 r"
446fn foo() -> Option<usize> { 447fn foo() -> Option<usize> {
447 let bar = Some(1); 448 let bar = Some(1);
448 let a<|> = bar?; 449 let a$0 = bar?;
449 let b = a * 10; 450 let b = a * 10;
450 let c = a as usize; 451 let c = a as usize;
451 None 452 None
@@ -467,7 +468,7 @@ fn foo() -> Option<usize> {
467 r" 468 r"
468fn foo() { 469fn foo() {
469 let bar = 10; 470 let bar = 10;
470 let a<|> = &bar; 471 let a$0 = &bar;
471 let b = a * 10; 472 let b = a * 10;
472}", 473}",
473 r" 474 r"
@@ -484,7 +485,7 @@ fn foo() {
484 inline_local_variable, 485 inline_local_variable,
485 r" 486 r"
486fn foo() { 487fn foo() {
487 let a<|> = (10, 20); 488 let a$0 = (10, 20);
488 let b = a[0]; 489 let b = a[0];
489}", 490}",
490 r" 491 r"
@@ -500,7 +501,7 @@ fn foo() {
500 inline_local_variable, 501 inline_local_variable,
501 r" 502 r"
502fn foo() { 503fn foo() {
503 let a<|> = [1, 2, 3]; 504 let a$0 = [1, 2, 3];
504 let b = a.len(); 505 let b = a.len();
505}", 506}",
506 r" 507 r"
@@ -516,7 +517,7 @@ fn foo() {
516 inline_local_variable, 517 inline_local_variable,
517 r" 518 r"
518fn foo() { 519fn foo() {
519 let a<|> = (10 + 20); 520 let a$0 = (10 + 20);
520 let b = a * 10; 521 let b = a * 10;
521 let c = a as usize; 522 let c = a as usize;
522}", 523}",
@@ -535,7 +536,7 @@ fn foo() {
535 r" 536 r"
536fn foo() { 537fn foo() {
537 let d = 10; 538 let d = 10;
538 let a<|> = d; 539 let a$0 = d;
539 let b = a * 10; 540 let b = a * 10;
540 let c = a as usize; 541 let c = a as usize;
541}", 542}",
@@ -554,7 +555,7 @@ fn foo() {
554 inline_local_variable, 555 inline_local_variable,
555 r" 556 r"
556fn foo() { 557fn foo() {
557 let a<|> = { 10 }; 558 let a$0 = { 10 };
558 let b = a * 10; 559 let b = a * 10;
559 let c = a as usize; 560 let c = a as usize;
560}", 561}",
@@ -572,7 +573,7 @@ fn foo() {
572 inline_local_variable, 573 inline_local_variable,
573 r" 574 r"
574fn foo() { 575fn foo() {
575 let a<|> = 10 + 20; 576 let a$0 = 10 + 20;
576 let b = a * 10; 577 let b = a * 10;
577 let c = (a, 20); 578 let c = (a, 20);
578 let d = [a, 10]; 579 let d = [a, 10];
@@ -594,7 +595,7 @@ fn foo() {
594 inline_local_variable, 595 inline_local_variable,
595 r" 596 r"
596fn foo() { 597fn foo() {
597 let a<|> = vec![10, 20]; 598 let a$0 = vec![10, 20];
598 for i in a {} 599 for i in a {}
599}", 600}",
600 r" 601 r"
@@ -610,7 +611,7 @@ fn foo() {
610 inline_local_variable, 611 inline_local_variable,
611 r" 612 r"
612fn foo() { 613fn foo() {
613 let a<|> = 1 > 0; 614 let a$0 = 1 > 0;
614 while a {} 615 while a {}
615}", 616}",
616 r" 617 r"
@@ -626,7 +627,7 @@ fn foo() {
626 inline_local_variable, 627 inline_local_variable,
627 r" 628 r"
628fn foo() { 629fn foo() {
629 let a<|> = 1 + 1; 630 let a$0 = 1 + 1;
630 loop { 631 loop {
631 break a; 632 break a;
632 } 633 }
@@ -646,7 +647,7 @@ fn foo() {
646 inline_local_variable, 647 inline_local_variable,
647 r" 648 r"
648fn foo() { 649fn foo() {
649 let a<|> = 1 > 0; 650 let a$0 = 1 > 0;
650 return a; 651 return a;
651}", 652}",
652 r" 653 r"
@@ -662,7 +663,7 @@ fn foo() {
662 inline_local_variable, 663 inline_local_variable,
663 r" 664 r"
664fn foo() { 665fn foo() {
665 let a<|> = 1 > 0; 666 let a$0 = 1 > 0;
666 match a {} 667 match a {}
667}", 668}",
668 r" 669 r"
@@ -680,7 +681,7 @@ fn foo() {
680 r" 681 r"
681struct S { foo: i32} 682struct S { foo: i32}
682fn main() { 683fn main() {
683 let <|>foo = 92; 684 let $0foo = 92;
684 S { foo } 685 S { foo }
685} 686}
686", 687",
@@ -700,7 +701,7 @@ fn main() {
700 inline_local_variable, 701 inline_local_variable,
701 r" 702 r"
702fn foo() { 703fn foo() {
703 let <|>a = 0; 704 let $0a = 0;
704} 705}
705 ", 706 ",
706 ) 707 )
@@ -713,7 +714,7 @@ fn foo() {
713 inline_local_variable, 714 inline_local_variable,
714 r" 715 r"
715fn main() { 716fn main() {
716 let x = <|>1 + 2; 717 let x = $01 + 2;
717 x * 4; 718 x * 4;
718} 719}
719", 720",
diff --git a/crates/assists/src/handlers/introduce_named_lifetime.rs b/crates/assists/src/handlers/introduce_named_lifetime.rs
index ab8fe3ea9..3f5f44d69 100644
--- a/crates/assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/assists/src/handlers/introduce_named_lifetime.rs
@@ -14,7 +14,7 @@ static ASSIST_LABEL: &str = "Introduce named lifetime";
14// Change an anonymous lifetime to a named lifetime. 14// Change an anonymous lifetime to a named lifetime.
15// 15//
16// ``` 16// ```
17// impl Cursor<'_<|>> { 17// impl Cursor<'_$0> {
18// fn node(self) -> &SyntaxNode { 18// fn node(self) -> &SyntaxNode {
19// match self { 19// match self {
20// Cursor::Replace(node) | Cursor::Before(node) => node, 20// Cursor::Replace(node) | Cursor::Before(node) => node,
@@ -33,7 +33,7 @@ static ASSIST_LABEL: &str = "Introduce named lifetime";
33// } 33// }
34// ``` 34// ```
35// FIXME: How can we handle renaming any one of multiple anonymous lifetimes? 35// FIXME: How can we handle renaming any one of multiple anonymous lifetimes?
36// FIXME: should also add support for the case fun(f: &Foo) -> &<|>Foo 36// FIXME: should also add support for the case fun(f: &Foo) -> &$0Foo
37pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 37pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
38 let lifetime = 38 let lifetime =
39 ctx.find_node_at_offset::<ast::Lifetime>().filter(|lifetime| lifetime.text() == "'_")?; 39 ctx.find_node_at_offset::<ast::Lifetime>().filter(|lifetime| lifetime.text() == "'_")?;
@@ -150,7 +150,7 @@ mod tests {
150 fn test_example_case() { 150 fn test_example_case() {
151 check_assist( 151 check_assist(
152 introduce_named_lifetime, 152 introduce_named_lifetime,
153 r#"impl Cursor<'_<|>> { 153 r#"impl Cursor<'_$0> {
154 fn node(self) -> &SyntaxNode { 154 fn node(self) -> &SyntaxNode {
155 match self { 155 match self {
156 Cursor::Replace(node) | Cursor::Before(node) => node, 156 Cursor::Replace(node) | Cursor::Before(node) => node,
@@ -171,7 +171,7 @@ mod tests {
171 fn test_example_case_simplified() { 171 fn test_example_case_simplified() {
172 check_assist( 172 check_assist(
173 introduce_named_lifetime, 173 introduce_named_lifetime,
174 r#"impl Cursor<'_<|>> {"#, 174 r#"impl Cursor<'_$0> {"#,
175 r#"impl<'a> Cursor<'a> {"#, 175 r#"impl<'a> Cursor<'a> {"#,
176 ); 176 );
177 } 177 }
@@ -180,7 +180,7 @@ mod tests {
180 fn test_example_case_cursor_after_tick() { 180 fn test_example_case_cursor_after_tick() {
181 check_assist( 181 check_assist(
182 introduce_named_lifetime, 182 introduce_named_lifetime,
183 r#"impl Cursor<'<|>_> {"#, 183 r#"impl Cursor<'$0_> {"#,
184 r#"impl<'a> Cursor<'a> {"#, 184 r#"impl<'a> Cursor<'a> {"#,
185 ); 185 );
186 } 186 }
@@ -189,7 +189,7 @@ mod tests {
189 fn test_impl_with_other_type_param() { 189 fn test_impl_with_other_type_param() {
190 check_assist( 190 check_assist(
191 introduce_named_lifetime, 191 introduce_named_lifetime,
192 "impl<I> fmt::Display for SepByBuilder<'_<|>, I> 192 "impl<I> fmt::Display for SepByBuilder<'_$0, I>
193 where 193 where
194 I: Iterator, 194 I: Iterator,
195 I::Item: fmt::Display, 195 I::Item: fmt::Display,
@@ -206,28 +206,28 @@ mod tests {
206 fn test_example_case_cursor_before_tick() { 206 fn test_example_case_cursor_before_tick() {
207 check_assist( 207 check_assist(
208 introduce_named_lifetime, 208 introduce_named_lifetime,
209 r#"impl Cursor<<|>'_> {"#, 209 r#"impl Cursor<$0'_> {"#,
210 r#"impl<'a> Cursor<'a> {"#, 210 r#"impl<'a> Cursor<'a> {"#,
211 ); 211 );
212 } 212 }
213 213
214 #[test] 214 #[test]
215 fn test_not_applicable_cursor_position() { 215 fn test_not_applicable_cursor_position() {
216 check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<'_><|> {"#); 216 check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<'_>$0 {"#);
217 check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<|><'_> {"#); 217 check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor$0<'_> {"#);
218 } 218 }
219 219
220 #[test] 220 #[test]
221 fn test_not_applicable_lifetime_already_name() { 221 fn test_not_applicable_lifetime_already_name() {
222 check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<'a<|>> {"#); 222 check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<'a$0> {"#);
223 check_assist_not_applicable(introduce_named_lifetime, r#"fn my_fun<'a>() -> X<'a<|>>"#); 223 check_assist_not_applicable(introduce_named_lifetime, r#"fn my_fun<'a>() -> X<'a$0>"#);
224 } 224 }
225 225
226 #[test] 226 #[test]
227 fn test_with_type_parameter() { 227 fn test_with_type_parameter() {
228 check_assist( 228 check_assist(
229 introduce_named_lifetime, 229 introduce_named_lifetime,
230 r#"impl<T> Cursor<T, '_<|>>"#, 230 r#"impl<T> Cursor<T, '_$0>"#,
231 r#"impl<T, 'a> Cursor<T, 'a>"#, 231 r#"impl<T, 'a> Cursor<T, 'a>"#,
232 ); 232 );
233 } 233 }
@@ -236,7 +236,7 @@ mod tests {
236 fn test_with_existing_lifetime_name_conflict() { 236 fn test_with_existing_lifetime_name_conflict() {
237 check_assist( 237 check_assist(
238 introduce_named_lifetime, 238 introduce_named_lifetime,
239 r#"impl<'a, 'b> Cursor<'a, 'b, '_<|>>"#, 239 r#"impl<'a, 'b> Cursor<'a, 'b, '_$0>"#,
240 r#"impl<'a, 'b, 'c> Cursor<'a, 'b, 'c>"#, 240 r#"impl<'a, 'b, 'c> Cursor<'a, 'b, 'c>"#,
241 ); 241 );
242 } 242 }
@@ -245,7 +245,7 @@ mod tests {
245 fn test_function_return_value_anon_lifetime_param() { 245 fn test_function_return_value_anon_lifetime_param() {
246 check_assist( 246 check_assist(
247 introduce_named_lifetime, 247 introduce_named_lifetime,
248 r#"fn my_fun() -> X<'_<|>>"#, 248 r#"fn my_fun() -> X<'_$0>"#,
249 r#"fn my_fun<'a>() -> X<'a>"#, 249 r#"fn my_fun<'a>() -> X<'a>"#,
250 ); 250 );
251 } 251 }
@@ -254,7 +254,7 @@ mod tests {
254 fn test_function_return_value_anon_reference_lifetime() { 254 fn test_function_return_value_anon_reference_lifetime() {
255 check_assist( 255 check_assist(
256 introduce_named_lifetime, 256 introduce_named_lifetime,
257 r#"fn my_fun() -> &'_<|> X"#, 257 r#"fn my_fun() -> &'_$0 X"#,
258 r#"fn my_fun<'a>() -> &'a X"#, 258 r#"fn my_fun<'a>() -> &'a X"#,
259 ); 259 );
260 } 260 }
@@ -263,7 +263,7 @@ mod tests {
263 fn test_function_param_anon_lifetime() { 263 fn test_function_param_anon_lifetime() {
264 check_assist( 264 check_assist(
265 introduce_named_lifetime, 265 introduce_named_lifetime,
266 r#"fn my_fun(x: X<'_<|>>)"#, 266 r#"fn my_fun(x: X<'_$0>)"#,
267 r#"fn my_fun<'a>(x: X<'a>)"#, 267 r#"fn my_fun<'a>(x: X<'a>)"#,
268 ); 268 );
269 } 269 }
@@ -272,7 +272,7 @@ mod tests {
272 fn test_function_add_lifetime_to_params() { 272 fn test_function_add_lifetime_to_params() {
273 check_assist( 273 check_assist(
274 introduce_named_lifetime, 274 introduce_named_lifetime,
275 r#"fn my_fun(f: &Foo) -> X<'_<|>>"#, 275 r#"fn my_fun(f: &Foo) -> X<'_$0>"#,
276 r#"fn my_fun<'a>(f: &'a Foo) -> X<'a>"#, 276 r#"fn my_fun<'a>(f: &'a Foo) -> X<'a>"#,
277 ); 277 );
278 } 278 }
@@ -281,7 +281,7 @@ mod tests {
281 fn test_function_add_lifetime_to_params_in_presence_of_other_lifetime() { 281 fn test_function_add_lifetime_to_params_in_presence_of_other_lifetime() {
282 check_assist( 282 check_assist(
283 introduce_named_lifetime, 283 introduce_named_lifetime,
284 r#"fn my_fun<'other>(f: &Foo, b: &'other Bar) -> X<'_<|>>"#, 284 r#"fn my_fun<'other>(f: &Foo, b: &'other Bar) -> X<'_$0>"#,
285 r#"fn my_fun<'other, 'a>(f: &'a Foo, b: &'other Bar) -> X<'a>"#, 285 r#"fn my_fun<'other, 'a>(f: &'a Foo, b: &'other Bar) -> X<'a>"#,
286 ); 286 );
287 } 287 }
@@ -291,7 +291,7 @@ mod tests {
291 // this is not permitted under lifetime elision rules 291 // this is not permitted under lifetime elision rules
292 check_assist_not_applicable( 292 check_assist_not_applicable(
293 introduce_named_lifetime, 293 introduce_named_lifetime,
294 r#"fn my_fun(f: &Foo, b: &Bar) -> X<'_<|>>"#, 294 r#"fn my_fun(f: &Foo, b: &Bar) -> X<'_$0>"#,
295 ); 295 );
296 } 296 }
297 297
@@ -299,7 +299,7 @@ mod tests {
299 fn test_function_add_lifetime_to_self_ref_param() { 299 fn test_function_add_lifetime_to_self_ref_param() {
300 check_assist( 300 check_assist(
301 introduce_named_lifetime, 301 introduce_named_lifetime,
302 r#"fn my_fun<'other>(&self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#, 302 r#"fn my_fun<'other>(&self, f: &Foo, b: &'other Bar) -> X<'_$0>"#,
303 r#"fn my_fun<'other, 'a>(&'a self, f: &Foo, b: &'other Bar) -> X<'a>"#, 303 r#"fn my_fun<'other, 'a>(&'a self, f: &Foo, b: &'other Bar) -> X<'a>"#,
304 ); 304 );
305 } 305 }
@@ -308,7 +308,7 @@ mod tests {
308 fn test_function_add_lifetime_to_param_with_non_ref_self() { 308 fn test_function_add_lifetime_to_param_with_non_ref_self() {
309 check_assist( 309 check_assist(
310 introduce_named_lifetime, 310 introduce_named_lifetime,
311 r#"fn my_fun<'other>(self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#, 311 r#"fn my_fun<'other>(self, f: &Foo, b: &'other Bar) -> X<'_$0>"#,
312 r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#, 312 r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#,
313 ); 313 );
314 } 314 }
diff --git a/crates/assists/src/handlers/invert_if.rs b/crates/assists/src/handlers/invert_if.rs
index f9c33b3f7..5b69dafd4 100644
--- a/crates/assists/src/handlers/invert_if.rs
+++ b/crates/assists/src/handlers/invert_if.rs
@@ -18,7 +18,7 @@ use crate::{
18// 18//
19// ``` 19// ```
20// fn main() { 20// fn main() {
21// if<|> !y { A } else { B } 21// if$0 !y { A } else { B }
22// } 22// }
23// ``` 23// ```
24// -> 24// ->
@@ -72,7 +72,7 @@ mod tests {
72 fn invert_if_composite_condition() { 72 fn invert_if_composite_condition() {
73 check_assist( 73 check_assist(
74 invert_if, 74 invert_if,
75 "fn f() { i<|>f x == 3 || x == 4 || x == 5 { 1 } else { 3 * 2 } }", 75 "fn f() { i$0f x == 3 || x == 4 || x == 5 { 1 } else { 3 * 2 } }",
76 "fn f() { if !(x == 3 || x == 4 || x == 5) { 3 * 2 } else { 1 } }", 76 "fn f() { if !(x == 3 || x == 4 || x == 5) { 3 * 2 } else { 1 } }",
77 ) 77 )
78 } 78 }
@@ -81,7 +81,7 @@ mod tests {
81 fn invert_if_remove_not_parentheses() { 81 fn invert_if_remove_not_parentheses() {
82 check_assist( 82 check_assist(
83 invert_if, 83 invert_if,
84 "fn f() { i<|>f !(x == 3 || x == 4 || x == 5) { 3 * 2 } else { 1 } }", 84 "fn f() { i$0f !(x == 3 || x == 4 || x == 5) { 3 * 2 } else { 1 } }",
85 "fn f() { if x == 3 || x == 4 || x == 5 { 1 } else { 3 * 2 } }", 85 "fn f() { if x == 3 || x == 4 || x == 5 { 1 } else { 3 * 2 } }",
86 ) 86 )
87 } 87 }
@@ -90,7 +90,7 @@ mod tests {
90 fn invert_if_remove_inequality() { 90 fn invert_if_remove_inequality() {
91 check_assist( 91 check_assist(
92 invert_if, 92 invert_if,
93 "fn f() { i<|>f x != 3 { 1 } else { 3 + 2 } }", 93 "fn f() { i$0f x != 3 { 1 } else { 3 + 2 } }",
94 "fn f() { if x == 3 { 3 + 2 } else { 1 } }", 94 "fn f() { if x == 3 { 3 + 2 } else { 1 } }",
95 ) 95 )
96 } 96 }
@@ -99,7 +99,7 @@ mod tests {
99 fn invert_if_remove_not() { 99 fn invert_if_remove_not() {
100 check_assist( 100 check_assist(
101 invert_if, 101 invert_if,
102 "fn f() { <|>if !cond { 3 * 2 } else { 1 } }", 102 "fn f() { $0if !cond { 3 * 2 } else { 1 } }",
103 "fn f() { if cond { 1 } else { 3 * 2 } }", 103 "fn f() { if cond { 1 } else { 3 * 2 } }",
104 ) 104 )
105 } 105 }
@@ -108,21 +108,21 @@ mod tests {
108 fn invert_if_general_case() { 108 fn invert_if_general_case() {
109 check_assist( 109 check_assist(
110 invert_if, 110 invert_if,
111 "fn f() { i<|>f cond { 3 * 2 } else { 1 } }", 111 "fn f() { i$0f cond { 3 * 2 } else { 1 } }",
112 "fn f() { if !cond { 1 } else { 3 * 2 } }", 112 "fn f() { if !cond { 1 } else { 3 * 2 } }",
113 ) 113 )
114 } 114 }
115 115
116 #[test] 116 #[test]
117 fn invert_if_doesnt_apply_with_cursor_not_on_if() { 117 fn invert_if_doesnt_apply_with_cursor_not_on_if() {
118 check_assist_not_applicable(invert_if, "fn f() { if !<|>cond { 3 * 2 } else { 1 } }") 118 check_assist_not_applicable(invert_if, "fn f() { if !$0cond { 3 * 2 } else { 1 } }")
119 } 119 }
120 120
121 #[test] 121 #[test]
122 fn invert_if_doesnt_apply_with_if_let() { 122 fn invert_if_doesnt_apply_with_if_let() {
123 check_assist_not_applicable( 123 check_assist_not_applicable(
124 invert_if, 124 invert_if,
125 "fn f() { i<|>f let Some(_) = Some(1) { 1 } else { 0 } }", 125 "fn f() { i$0f let Some(_) = Some(1) { 1 } else { 0 } }",
126 ) 126 )
127 } 127 }
128 128
@@ -130,7 +130,7 @@ mod tests {
130 fn invert_if_option_case() { 130 fn invert_if_option_case() {
131 check_assist( 131 check_assist(
132 invert_if, 132 invert_if,
133 "fn f() { if<|> doc_style.is_some() { Class::DocComment } else { Class::Comment } }", 133 "fn f() { if$0 doc_style.is_some() { Class::DocComment } else { Class::Comment } }",
134 "fn f() { if doc_style.is_none() { Class::Comment } else { Class::DocComment } }", 134 "fn f() { if doc_style.is_none() { Class::Comment } else { Class::DocComment } }",
135 ) 135 )
136 } 136 }
@@ -139,7 +139,7 @@ mod tests {
139 fn invert_if_result_case() { 139 fn invert_if_result_case() {
140 check_assist( 140 check_assist(
141 invert_if, 141 invert_if,
142 "fn f() { i<|>f doc_style.is_err() { Class::Err } else { Class::Ok } }", 142 "fn f() { i$0f doc_style.is_err() { Class::Err } else { Class::Ok } }",
143 "fn f() { if doc_style.is_ok() { Class::Ok } else { Class::Err } }", 143 "fn f() { if doc_style.is_ok() { Class::Ok } else { Class::Err } }",
144 ) 144 )
145 } 145 }
diff --git a/crates/assists/src/handlers/merge_imports.rs b/crates/assists/src/handlers/merge_imports.rs
index 2f0dc7831..7bd7e1e36 100644
--- a/crates/assists/src/handlers/merge_imports.rs
+++ b/crates/assists/src/handlers/merge_imports.rs
@@ -15,7 +15,7 @@ use crate::{
15// Merges two imports with a common prefix. 15// Merges two imports with a common prefix.
16// 16//
17// ``` 17// ```
18// use std::<|>fmt::Formatter; 18// use std::$0fmt::Formatter;
19// use std::io; 19// use std::io;
20// ``` 20// ```
21// -> 21// ->
@@ -75,7 +75,7 @@ mod tests {
75 check_assist( 75 check_assist(
76 merge_imports, 76 merge_imports,
77 r" 77 r"
78use std::fmt<|>::{Display, Debug}; 78use std::fmt$0::{Display, Debug};
79use std::fmt::{Display, Debug}; 79use std::fmt::{Display, Debug};
80", 80",
81 r" 81 r"
@@ -89,7 +89,7 @@ use std::fmt::{Debug, Display};
89 check_assist( 89 check_assist(
90 merge_imports, 90 merge_imports,
91 r" 91 r"
92use std::fmt<|>::Debug; 92use std::fmt$0::Debug;
93use std::fmt::Display; 93use std::fmt::Display;
94", 94",
95 r" 95 r"
@@ -104,7 +104,7 @@ use std::fmt::{Debug, Display};
104 merge_imports, 104 merge_imports,
105 r" 105 r"
106use std::fmt::Debug; 106use std::fmt::Debug;
107use std::fmt<|>::Display; 107use std::fmt$0::Display;
108", 108",
109 r" 109 r"
110use std::fmt::{Debug, Display}; 110use std::fmt::{Debug, Display};
@@ -117,7 +117,7 @@ use std::fmt::{Debug, Display};
117 check_assist( 117 check_assist(
118 merge_imports, 118 merge_imports,
119 r" 119 r"
120use std::fmt<|>; 120use std::fmt$0;
121use std::fmt::Display; 121use std::fmt::Display;
122", 122",
123 r" 123 r"
@@ -131,7 +131,7 @@ use std::fmt::{self, Display};
131 check_assist( 131 check_assist(
132 merge_imports, 132 merge_imports,
133 r" 133 r"
134use std::{fmt, <|>fmt::Display}; 134use std::{fmt, $0fmt::Display};
135", 135",
136 r" 136 r"
137use std::{fmt::{self, Display}}; 137use std::{fmt::{self, Display}};
@@ -144,7 +144,7 @@ use std::{fmt::{self, Display}};
144 check_assist_not_applicable( 144 check_assist_not_applicable(
145 merge_imports, 145 merge_imports,
146 r" 146 r"
147pub use std::fmt<|>::Debug; 147pub use std::fmt$0::Debug;
148use std::fmt::Display; 148use std::fmt::Display;
149", 149",
150 ); 150 );
@@ -155,7 +155,7 @@ use std::fmt::Display;
155 check_assist_not_applicable( 155 check_assist_not_applicable(
156 merge_imports, 156 merge_imports,
157 r" 157 r"
158use std::fmt<|>::Debug; 158use std::fmt$0::Debug;
159pub use std::fmt::Display; 159pub use std::fmt::Display;
160", 160",
161 ); 161 );
@@ -166,7 +166,7 @@ pub use std::fmt::Display;
166 check_assist_not_applicable( 166 check_assist_not_applicable(
167 merge_imports, 167 merge_imports,
168 r" 168 r"
169pub(crate) use std::fmt<|>::Debug; 169pub(crate) use std::fmt$0::Debug;
170pub use std::fmt::Display; 170pub use std::fmt::Display;
171", 171",
172 ); 172 );
@@ -177,7 +177,7 @@ pub use std::fmt::Display;
177 check_assist_not_applicable( 177 check_assist_not_applicable(
178 merge_imports, 178 merge_imports,
179 r" 179 r"
180pub use std::fmt<|>::Debug; 180pub use std::fmt$0::Debug;
181pub(crate) use std::fmt::Display; 181pub(crate) use std::fmt::Display;
182", 182",
183 ); 183 );
@@ -188,7 +188,7 @@ pub(crate) use std::fmt::Display;
188 check_assist( 188 check_assist(
189 merge_imports, 189 merge_imports,
190 r" 190 r"
191pub use std::fmt<|>::Debug; 191pub use std::fmt$0::Debug;
192pub use std::fmt::Display; 192pub use std::fmt::Display;
193", 193",
194 r" 194 r"
@@ -202,7 +202,7 @@ pub use std::fmt::{Debug, Display};
202 check_assist( 202 check_assist(
203 merge_imports, 203 merge_imports,
204 r" 204 r"
205pub(crate) use std::fmt<|>::Debug; 205pub(crate) use std::fmt$0::Debug;
206pub(crate) use std::fmt::Display; 206pub(crate) use std::fmt::Display;
207", 207",
208 r" 208 r"
@@ -216,7 +216,7 @@ pub(crate) use std::fmt::{Debug, Display};
216 check_assist( 216 check_assist(
217 merge_imports, 217 merge_imports,
218 r" 218 r"
219use std::{fmt<|>::Debug, fmt::Display}; 219use std::{fmt$0::Debug, fmt::Display};
220", 220",
221 r" 221 r"
222use std::{fmt::{Debug, Display}}; 222use std::{fmt::{Debug, Display}};
@@ -229,7 +229,7 @@ use std::{fmt::{Debug, Display}};
229 check_assist( 229 check_assist(
230 merge_imports, 230 merge_imports,
231 r" 231 r"
232use std::{fmt::Debug, fmt<|>::Display}; 232use std::{fmt::Debug, fmt$0::Display};
233", 233",
234 r" 234 r"
235use std::{fmt::{Debug, Display}}; 235use std::{fmt::{Debug, Display}};
@@ -242,7 +242,7 @@ use std::{fmt::{Debug, Display}};
242 check_assist( 242 check_assist(
243 merge_imports, 243 merge_imports,
244 r" 244 r"
245use std<|>::cell::*; 245use std$0::cell::*;
246use std::str; 246use std::str;
247", 247",
248 r" 248 r"
@@ -256,7 +256,7 @@ use std::{cell::*, str};
256 check_assist( 256 check_assist(
257 merge_imports, 257 merge_imports,
258 r" 258 r"
259use std<|>::cell::*; 259use std$0::cell::*;
260use std::str::*; 260use std::str::*;
261", 261",
262 r" 262 r"
@@ -270,7 +270,7 @@ use std::{cell::*, str::*};
270 check_assist( 270 check_assist(
271 merge_imports, 271 merge_imports,
272 r" 272 r"
273use foo<|>::bar; 273use foo$0::bar;
274use foo::baz; 274use foo::baz;
275 275
276/// Doc comment 276/// Doc comment
@@ -289,7 +289,7 @@ use foo::{bar, baz};
289 merge_imports, 289 merge_imports,
290 r" 290 r"
291use { 291use {
292 foo<|>::bar, 292 foo$0::bar,
293 foo::baz, 293 foo::baz,
294}; 294};
295", 295",
@@ -304,7 +304,7 @@ use {
304 r" 304 r"
305use { 305use {
306 foo::baz, 306 foo::baz,
307 foo<|>::bar, 307 foo$0::bar,
308}; 308};
309", 309",
310 r" 310 r"
@@ -321,7 +321,7 @@ use {
321 merge_imports, 321 merge_imports,
322 r" 322 r"
323use foo::bar::baz; 323use foo::bar::baz;
324use foo::<|>{ 324use foo::$0{
325 FooBar, 325 FooBar,
326}; 326};
327", 327",
@@ -336,7 +336,7 @@ use foo::{FooBar, bar::baz};
336 check_assist_not_applicable( 336 check_assist_not_applicable(
337 merge_imports, 337 merge_imports,
338 r" 338 r"
339use std::<|> 339use std::$0
340fn main() {}", 340fn main() {}",
341 ); 341 );
342 } 342 }
diff --git a/crates/assists/src/handlers/merge_match_arms.rs b/crates/assists/src/handlers/merge_match_arms.rs
index c347eb40e..9bf076cb9 100644
--- a/crates/assists/src/handlers/merge_match_arms.rs
+++ b/crates/assists/src/handlers/merge_match_arms.rs
@@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, TextRange};
17// 17//
18// fn handle(action: Action) { 18// fn handle(action: Action) {
19// match action { 19// match action {
20// <|>Action::Move(..) => foo(), 20// $0Action::Move(..) => foo(),
21// Action::Stop => foo(), 21// Action::Stop => foo(),
22// } 22// }
23// } 23// }
@@ -106,7 +106,7 @@ mod tests {
106 fn main() { 106 fn main() {
107 let x = X::A; 107 let x = X::A;
108 let y = match x { 108 let y = match x {
109 X::A => { 1i32<|> } 109 X::A => { 1i32$0 }
110 X::B => { 1i32 } 110 X::B => { 1i32 }
111 X::C => { 2i32 } 111 X::C => { 2i32 }
112 } 112 }
@@ -138,7 +138,7 @@ mod tests {
138 fn main() { 138 fn main() {
139 let x = X::A; 139 let x = X::A;
140 let y = match x { 140 let y = match x {
141 X::A | X::B => {<|> 1i32 }, 141 X::A | X::B => {$0 1i32 },
142 X::C | X::D => { 1i32 }, 142 X::C | X::D => { 1i32 },
143 X::E => { 2i32 }, 143 X::E => { 2i32 },
144 } 144 }
@@ -171,7 +171,7 @@ mod tests {
171 let x = X::A; 171 let x = X::A;
172 let y = match x { 172 let y = match x {
173 X::A => { 1i32 }, 173 X::A => { 1i32 },
174 X::B => { 2i<|>32 }, 174 X::B => { 2i$032 },
175 _ => { 2i32 } 175 _ => { 2i32 }
176 } 176 }
177 } 177 }
@@ -200,7 +200,7 @@ mod tests {
200 200
201 fn main() { 201 fn main() {
202 match X::A { 202 match X::A {
203 X::A<|> => 92, 203 X::A$0 => 92,
204 X::B => 92, 204 X::B => 92,
205 X::C => 92, 205 X::C => 92,
206 X::D => 62, 206 X::D => 62,
@@ -237,7 +237,7 @@ mod tests {
237 fn main() { 237 fn main() {
238 let x = X::A; 238 let x = X::A;
239 let y = match x { 239 let y = match x {
240 X::A(a) if a > 5 => { <|>1i32 }, 240 X::A(a) if a > 5 => { $01i32 },
241 X::B => { 1i32 }, 241 X::B => { 1i32 },
242 X::C => { 2i32 } 242 X::C => { 2i32 }
243 } 243 }
diff --git a/crates/assists/src/handlers/move_bounds.rs b/crates/assists/src/handlers/move_bounds.rs
index e2e461520..cf260c6f8 100644
--- a/crates/assists/src/handlers/move_bounds.rs
+++ b/crates/assists/src/handlers/move_bounds.rs
@@ -12,7 +12,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
12// Moves inline type bounds to a where clause. 12// Moves inline type bounds to a where clause.
13// 13//
14// ``` 14// ```
15// fn apply<T, U, <|>F: FnOnce(T) -> U>(f: F, x: T) -> U { 15// fn apply<T, U, $0F: FnOnce(T) -> U>(f: F, x: T) -> U {
16// f(x) 16// f(x)
17// } 17// }
18// ``` 18// ```
@@ -103,7 +103,7 @@ mod tests {
103 check_assist( 103 check_assist(
104 move_bounds_to_where_clause, 104 move_bounds_to_where_clause,
105 r#" 105 r#"
106 fn foo<T: u32, <|>F: FnOnce(T) -> T>() {} 106 fn foo<T: u32, $0F: FnOnce(T) -> T>() {}
107 "#, 107 "#,
108 r#" 108 r#"
109 fn foo<T, F>() where T: u32, F: FnOnce(T) -> T {} 109 fn foo<T, F>() where T: u32, F: FnOnce(T) -> T {}
@@ -116,7 +116,7 @@ mod tests {
116 check_assist( 116 check_assist(
117 move_bounds_to_where_clause, 117 move_bounds_to_where_clause,
118 r#" 118 r#"
119 impl<U: u32, <|>T> A<U, T> {} 119 impl<U: u32, $0T> A<U, T> {}
120 "#, 120 "#,
121 r#" 121 r#"
122 impl<U, T> A<U, T> where U: u32 {} 122 impl<U, T> A<U, T> where U: u32 {}
@@ -129,7 +129,7 @@ mod tests {
129 check_assist( 129 check_assist(
130 move_bounds_to_where_clause, 130 move_bounds_to_where_clause,
131 r#" 131 r#"
132 struct A<<|>T: Iterator<Item = u32>> {} 132 struct A<$0T: Iterator<Item = u32>> {}
133 "#, 133 "#,
134 r#" 134 r#"
135 struct A<T> where T: Iterator<Item = u32> {} 135 struct A<T> where T: Iterator<Item = u32> {}
@@ -142,7 +142,7 @@ mod tests {
142 check_assist( 142 check_assist(
143 move_bounds_to_where_clause, 143 move_bounds_to_where_clause,
144 r#" 144 r#"
145 struct Pair<<|>T: u32>(T, T); 145 struct Pair<$0T: u32>(T, T);
146 "#, 146 "#,
147 r#" 147 r#"
148 struct Pair<T>(T, T) where T: u32; 148 struct Pair<T>(T, T) where T: u32;
diff --git a/crates/assists/src/handlers/move_guard.rs b/crates/assists/src/handlers/move_guard.rs
index eaffd80ce..3f22302a9 100644
--- a/crates/assists/src/handlers/move_guard.rs
+++ b/crates/assists/src/handlers/move_guard.rs
@@ -14,7 +14,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
14// 14//
15// fn handle(action: Action) { 15// fn handle(action: Action) {
16// match action { 16// match action {
17// Action::Move { distance } <|>if distance > 10 => foo(), 17// Action::Move { distance } $0if distance > 10 => foo(),
18// _ => (), 18// _ => (),
19// } 19// }
20// } 20// }
@@ -74,7 +74,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
74// 74//
75// fn handle(action: Action) { 75// fn handle(action: Action) {
76// match action { 76// match action {
77// Action::Move { distance } => <|>if distance > 10 { foo() }, 77// Action::Move { distance } => $0if distance > 10 { foo() },
78// _ => (), 78// _ => (),
79// } 79// }
80// } 80// }
@@ -98,7 +98,7 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
98 let mut replace_node = None; 98 let mut replace_node = None;
99 let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| { 99 let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| {
100 let block_expr = BlockExpr::cast(arm_body.syntax().clone())?; 100 let block_expr = BlockExpr::cast(arm_body.syntax().clone())?;
101 if let Expr::IfExpr(e) = block_expr.expr()? { 101 if let Expr::IfExpr(e) = block_expr.tail_expr()? {
102 replace_node = Some(block_expr.syntax().clone()); 102 replace_node = Some(block_expr.syntax().clone());
103 Some(e) 103 Some(e)
104 } else { 104 } else {
@@ -128,7 +128,7 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
128 |edit| { 128 |edit| {
129 let then_only_expr = then_block.statements().next().is_none(); 129 let then_only_expr = then_block.statements().next().is_none();
130 130
131 match &then_block.expr() { 131 match &then_block.tail_expr() {
132 Some(then_expr) if then_only_expr => { 132 Some(then_expr) if then_only_expr => {
133 edit.replace(replace_node.text_range(), then_expr.syntax().text()) 133 edit.replace(replace_node.text_range(), then_expr.syntax().text())
134 } 134 }
@@ -158,7 +158,7 @@ mod tests {
158 r#" 158 r#"
159fn main() { 159fn main() {
160 match 92 { 160 match 92 {
161 x <|>if x > 10 => false, 161 x $0if x > 10 => false,
162 _ => true 162 _ => true
163 } 163 }
164} 164}
@@ -174,7 +174,7 @@ fn main() {
174 r#" 174 r#"
175fn main() { 175fn main() {
176 match 92 { 176 match 92 {
177 x <|>if x > 10 => false, 177 x $0if x > 10 => false,
178 _ => true 178 _ => true
179 } 179 }
180} 180}
@@ -199,7 +199,7 @@ fn main() {
199 r#" 199 r#"
200fn main() { 200fn main() {
201 match 92 { 201 match 92 {
202 <|>x @ 4 | x @ 5 if x > 5 => true, 202 $0x @ 4 | x @ 5 if x > 5 => true,
203 _ => false 203 _ => false
204 } 204 }
205} 205}
@@ -224,7 +224,7 @@ fn main() {
224 r#" 224 r#"
225fn main() { 225fn main() {
226 match 92 { 226 match 92 {
227 x => if x > 10 { <|>false }, 227 x => if x > 10 { $0false },
228 _ => true 228 _ => true
229 } 229 }
230} 230}
@@ -248,7 +248,7 @@ fn main() {
248fn main() { 248fn main() {
249 match 92 { 249 match 92 {
250 x => { 250 x => {
251 <|>if x > 10 { 251 $0if x > 10 {
252 false 252 false
253 } 253 }
254 }, 254 },
@@ -274,7 +274,7 @@ fn main() {
274 r#" 274 r#"
275fn main() { 275fn main() {
276 match 92 { 276 match 92 {
277 x => if let 62 = x { <|>false }, 277 x => if let 62 = x { $0false },
278 _ => true 278 _ => true
279 } 279 }
280} 280}
@@ -289,7 +289,7 @@ fn main() {
289 r#" 289 r#"
290fn main() { 290fn main() {
291 match 92 { 291 match 92 {
292 x => if x > 10 { <|> }, 292 x => if x > 10 { $0 },
293 _ => true 293 _ => true
294 } 294 }
295} 295}
@@ -313,7 +313,7 @@ fn main() {
313fn main() { 313fn main() {
314 match 92 { 314 match 92 {
315 x => if x > 10 { 315 x => if x > 10 {
316 92;<|> 316 92;$0
317 false 317 false
318 }, 318 },
319 _ => true 319 _ => true
@@ -343,7 +343,7 @@ fn main() {
343 match 92 { 343 match 92 {
344 x => { 344 x => {
345 if x > 10 { 345 if x > 10 {
346 92;<|> 346 92;$0
347 false 347 false
348 } 348 }
349 } 349 }
diff --git a/crates/assists/src/handlers/extract_module_to_file.rs b/crates/assists/src/handlers/move_module_to_file.rs
index 50bf67ef7..9d8579f47 100644
--- a/crates/assists/src/handlers/extract_module_to_file.rs
+++ b/crates/assists/src/handlers/move_module_to_file.rs
@@ -2,17 +2,18 @@ use ast::edit::IndentLevel;
2use ide_db::base_db::AnchoredPathBuf; 2use ide_db::base_db::AnchoredPathBuf;
3use syntax::{ 3use syntax::{
4 ast::{self, edit::AstNodeEdit, NameOwner}, 4 ast::{self, edit::AstNodeEdit, NameOwner},
5 AstNode, 5 AstNode, TextRange,
6}; 6};
7use test_utils::mark;
7 8
8use crate::{AssistContext, AssistId, AssistKind, Assists}; 9use crate::{AssistContext, AssistId, AssistKind, Assists};
9 10
10// Assist: extract_module_to_file 11// Assist: move_module_to_file
11// 12//
12// This assist extract module to file. 13// Moves inline module's contents to a separate file.
13// 14//
14// ``` 15// ```
15// mod foo {<|> 16// mod $0foo {
16// fn t() {} 17// fn t() {}
17// } 18// }
18// ``` 19// ```
@@ -20,19 +21,24 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
20// ``` 21// ```
21// mod foo; 22// mod foo;
22// ``` 23// ```
23pub(crate) fn extract_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 24pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
24 let module_ast = ctx.find_node_at_offset::<ast::Module>()?; 25 let module_ast = ctx.find_node_at_offset::<ast::Module>()?;
26 let module_items = module_ast.item_list()?;
27
28 let l_curly_offset = module_items.syntax().text_range().start();
29 if l_curly_offset <= ctx.offset() {
30 mark::hit!(available_before_curly);
31 return None;
32 }
33 let target = TextRange::new(module_ast.syntax().text_range().start(), l_curly_offset);
34
25 let module_name = module_ast.name()?; 35 let module_name = module_ast.name()?;
26 36
27 let module_def = ctx.sema.to_def(&module_ast)?; 37 let module_def = ctx.sema.to_def(&module_ast)?;
28 let parent_module = module_def.parent(ctx.db())?; 38 let parent_module = module_def.parent(ctx.db())?;
29 39
30 let module_items = module_ast.item_list()?;
31 let target = module_ast.syntax().text_range();
32 let anchor_file_id = ctx.frange.file_id;
33
34 acc.add( 40 acc.add(
35 AssistId("extract_module_to_file", AssistKind::RefactorExtract), 41 AssistId("move_module_to_file", AssistKind::RefactorExtract),
36 "Extract module to file", 42 "Extract module to file",
37 target, 43 target,
38 |builder| { 44 |builder| {
@@ -53,9 +59,9 @@ pub(crate) fn extract_module_to_file(acc: &mut Assists, ctx: &AssistContext) ->
53 items 59 items
54 }; 60 };
55 61
56 builder.replace(target, format!("mod {};", module_name)); 62 builder.replace(module_ast.syntax().text_range(), format!("mod {};", module_name));
57 63
58 let dst = AnchoredPathBuf { anchor: anchor_file_id, path }; 64 let dst = AnchoredPathBuf { anchor: ctx.frange.file_id, path };
59 builder.create_file(dst, contents); 65 builder.create_file(dst, contents);
60 }, 66 },
61 ) 67 )
@@ -63,16 +69,16 @@ pub(crate) fn extract_module_to_file(acc: &mut Assists, ctx: &AssistContext) ->
63 69
64#[cfg(test)] 70#[cfg(test)]
65mod tests { 71mod tests {
66 use crate::tests::check_assist; 72 use crate::tests::{check_assist, check_assist_not_applicable};
67 73
68 use super::*; 74 use super::*;
69 75
70 #[test] 76 #[test]
71 fn extract_from_root() { 77 fn extract_from_root() {
72 check_assist( 78 check_assist(
73 extract_module_to_file, 79 move_module_to_file,
74 r#" 80 r#"
75mod tests {<|> 81mod $0tests {
76 #[test] fn t() {} 82 #[test] fn t() {}
77} 83}
78"#, 84"#,
@@ -88,12 +94,12 @@ mod tests;
88 #[test] 94 #[test]
89 fn extract_from_submodule() { 95 fn extract_from_submodule() {
90 check_assist( 96 check_assist(
91 extract_module_to_file, 97 move_module_to_file,
92 r#" 98 r#"
93//- /main.rs 99//- /main.rs
94mod submod; 100mod submod;
95//- /submod.rs 101//- /submod.rs
96mod inner<|> { 102$0mod inner {
97 fn f() {} 103 fn f() {}
98} 104}
99fn g() {} 105fn g() {}
@@ -111,12 +117,12 @@ fn f() {}
111 #[test] 117 #[test]
112 fn extract_from_mod_rs() { 118 fn extract_from_mod_rs() {
113 check_assist( 119 check_assist(
114 extract_module_to_file, 120 move_module_to_file,
115 r#" 121 r#"
116//- /main.rs 122//- /main.rs
117mod submodule; 123mod submodule;
118//- /submodule/mod.rs 124//- /submodule/mod.rs
119mod inner<|> { 125mod inner$0 {
120 fn f() {} 126 fn f() {}
121} 127}
122fn g() {} 128fn g() {}
@@ -130,4 +136,10 @@ fn f() {}
130"#, 136"#,
131 ); 137 );
132 } 138 }
139
140 #[test]
141 fn available_before_curly() {
142 mark::check!(available_before_curly);
143 check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#);
144 }
133} 145}
diff --git a/crates/assists/src/handlers/pull_assignment_up.rs b/crates/assists/src/handlers/pull_assignment_up.rs
new file mode 100644
index 000000000..13e1cb754
--- /dev/null
+++ b/crates/assists/src/handlers/pull_assignment_up.rs
@@ -0,0 +1,400 @@
1use syntax::{
2 ast::{self, edit::AstNodeEdit, make},
3 AstNode,
4};
5use test_utils::mark;
6
7use crate::{
8 assist_context::{AssistContext, Assists},
9 AssistId, AssistKind,
10};
11
12// Assist: pull_assignment_up
13//
14// Extracts variable assignment to outside an if or match statement.
15//
16// ```
17// fn main() {
18// let mut foo = 6;
19//
20// if true {
21// $0foo = 5;
22// } else {
23// foo = 4;
24// }
25// }
26// ```
27// ->
28// ```
29// fn main() {
30// let mut foo = 6;
31//
32// foo = if true {
33// 5
34// } else {
35// 4
36// };
37// }
38// ```
39pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
40 let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
41 let name_expr = if assign_expr.op_kind()? == ast::BinOp::Assignment {
42 assign_expr.lhs()?
43 } else {
44 return None;
45 };
46
47 let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() {
48 (
49 ast::Expr::cast(if_expr.syntax().to_owned())?,
50 exprify_if(&if_expr, &ctx.sema, &name_expr)?.indent(if_expr.indent_level()),
51 )
52 } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() {
53 (
54 ast::Expr::cast(match_expr.syntax().to_owned())?,
55 exprify_match(&match_expr, &ctx.sema, &name_expr)?,
56 )
57 } else {
58 return None;
59 };
60
61 let expr_stmt = make::expr_stmt(new_stmt);
62
63 acc.add(
64 AssistId("pull_assignment_up", AssistKind::RefactorExtract),
65 "Pull assignment up",
66 old_stmt.syntax().text_range(),
67 move |edit| {
68 edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name_expr, expr_stmt));
69 },
70 )
71}
72
73fn exprify_match(
74 match_expr: &ast::MatchExpr,
75 sema: &hir::Semantics<ide_db::RootDatabase>,
76 name: &ast::Expr,
77) -> Option<ast::Expr> {
78 let new_arm_list = match_expr
79 .match_arm_list()?
80 .arms()
81 .map(|arm| {
82 if let ast::Expr::BlockExpr(block) = arm.expr()? {
83 let new_block = exprify_block(&block, sema, name)?.indent(block.indent_level());
84 Some(arm.replace_descendant(block, new_block))
85 } else {
86 None
87 }
88 })
89 .collect::<Option<Vec<_>>>()?;
90 let new_arm_list = match_expr
91 .match_arm_list()?
92 .replace_descendants(match_expr.match_arm_list()?.arms().zip(new_arm_list));
93 Some(make::expr_match(match_expr.expr()?, new_arm_list))
94}
95
96fn exprify_if(
97 statement: &ast::IfExpr,
98 sema: &hir::Semantics<ide_db::RootDatabase>,
99 name: &ast::Expr,
100) -> Option<ast::Expr> {
101 let then_branch = exprify_block(&statement.then_branch()?, sema, name)?;
102 let else_branch = match statement.else_branch()? {
103 ast::ElseBranch::Block(ref block) => {
104 ast::ElseBranch::Block(exprify_block(block, sema, name)?)
105 }
106 ast::ElseBranch::IfExpr(expr) => {
107 mark::hit!(test_pull_assignment_up_chained_if);
108 ast::ElseBranch::IfExpr(ast::IfExpr::cast(
109 exprify_if(&expr, sema, name)?.syntax().to_owned(),
110 )?)
111 }
112 };
113 Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch)))
114}
115
116fn exprify_block(
117 block: &ast::BlockExpr,
118 sema: &hir::Semantics<ide_db::RootDatabase>,
119 name: &ast::Expr,
120) -> Option<ast::BlockExpr> {
121 if block.tail_expr().is_some() {
122 return None;
123 }
124
125 let mut stmts: Vec<_> = block.statements().collect();
126 let stmt = stmts.pop()?;
127
128 if let ast::Stmt::ExprStmt(stmt) = stmt {
129 if let ast::Expr::BinExpr(expr) = stmt.expr()? {
130 if expr.op_kind()? == ast::BinOp::Assignment && is_equivalent(sema, &expr.lhs()?, name)
131 {
132 // The last statement in the block is an assignment to the name we want
133 return Some(make::block_expr(stmts, Some(expr.rhs()?)));
134 }
135 }
136 }
137 None
138}
139
140fn is_equivalent(
141 sema: &hir::Semantics<ide_db::RootDatabase>,
142 expr0: &ast::Expr,
143 expr1: &ast::Expr,
144) -> bool {
145 match (expr0, expr1) {
146 (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => {
147 mark::hit!(test_pull_assignment_up_field_assignment);
148 sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1)
149 }
150 (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => {
151 let path0 = path0.path();
152 let path1 = path1.path();
153 if let (Some(path0), Some(path1)) = (path0, path1) {
154 sema.resolve_path(&path0) == sema.resolve_path(&path1)
155 } else {
156 false
157 }
158 }
159 _ => false,
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 use crate::tests::{check_assist, check_assist_not_applicable};
168
169 #[test]
170 fn test_pull_assignment_up_if() {
171 check_assist(
172 pull_assignment_up,
173 r#"
174fn foo() {
175 let mut a = 1;
176
177 if true {
178 $0a = 2;
179 } else {
180 a = 3;
181 }
182}"#,
183 r#"
184fn foo() {
185 let mut a = 1;
186
187 a = if true {
188 2
189 } else {
190 3
191 };
192}"#,
193 );
194 }
195
196 #[test]
197 fn test_pull_assignment_up_match() {
198 check_assist(
199 pull_assignment_up,
200 r#"
201fn foo() {
202 let mut a = 1;
203
204 match 1 {
205 1 => {
206 $0a = 2;
207 },
208 2 => {
209 a = 3;
210 },
211 3 => {
212 a = 4;
213 }
214 }
215}"#,
216 r#"
217fn foo() {
218 let mut a = 1;
219
220 a = match 1 {
221 1 => {
222 2
223 },
224 2 => {
225 3
226 },
227 3 => {
228 4
229 }
230 };
231}"#,
232 );
233 }
234
235 #[test]
236 fn test_pull_assignment_up_not_last_not_applicable() {
237 check_assist_not_applicable(
238 pull_assignment_up,
239 r#"
240fn foo() {
241 let mut a = 1;
242
243 if true {
244 $0a = 2;
245 b = a;
246 } else {
247 a = 3;
248 }
249}"#,
250 )
251 }
252
253 #[test]
254 fn test_pull_assignment_up_chained_if() {
255 mark::check!(test_pull_assignment_up_chained_if);
256 check_assist(
257 pull_assignment_up,
258 r#"
259fn foo() {
260 let mut a = 1;
261
262 if true {
263 $0a = 2;
264 } else if false {
265 a = 3;
266 } else {
267 a = 4;
268 }
269}"#,
270 r#"
271fn foo() {
272 let mut a = 1;
273
274 a = if true {
275 2
276 } else if false {
277 3
278 } else {
279 4
280 };
281}"#,
282 );
283 }
284
285 #[test]
286 fn test_pull_assignment_up_retains_stmts() {
287 check_assist(
288 pull_assignment_up,
289 r#"
290fn foo() {
291 let mut a = 1;
292
293 if true {
294 let b = 2;
295 $0a = 2;
296 } else {
297 let b = 3;
298 a = 3;
299 }
300}"#,
301 r#"
302fn foo() {
303 let mut a = 1;
304
305 a = if true {
306 let b = 2;
307 2
308 } else {
309 let b = 3;
310 3
311 };
312}"#,
313 )
314 }
315
316 #[test]
317 fn pull_assignment_up_let_stmt_not_applicable() {
318 check_assist_not_applicable(
319 pull_assignment_up,
320 r#"
321fn foo() {
322 let mut a = 1;
323
324 let b = if true {
325 $0a = 2
326 } else {
327 a = 3
328 };
329}"#,
330 )
331 }
332
333 #[test]
334 fn pull_assignment_up_if_missing_assigment_not_applicable() {
335 check_assist_not_applicable(
336 pull_assignment_up,
337 r#"
338fn foo() {
339 let mut a = 1;
340
341 if true {
342 $0a = 2;
343 } else {}
344}"#,
345 )
346 }
347
348 #[test]
349 fn pull_assignment_up_match_missing_assigment_not_applicable() {
350 check_assist_not_applicable(
351 pull_assignment_up,
352 r#"
353fn foo() {
354 let mut a = 1;
355
356 match 1 {
357 1 => {
358 $0a = 2;
359 },
360 2 => {
361 a = 3;
362 },
363 3 => {},
364 }
365}"#,
366 )
367 }
368
369 #[test]
370 fn test_pull_assignment_up_field_assignment() {
371 mark::check!(test_pull_assignment_up_field_assignment);
372 check_assist(
373 pull_assignment_up,
374 r#"
375struct A(usize);
376
377fn foo() {
378 let mut a = A(1);
379
380 if true {
381 $0a.0 = 2;
382 } else {
383 a.0 = 3;
384 }
385}"#,
386 r#"
387struct A(usize);
388
389fn foo() {
390 let mut a = A(1);
391
392 a.0 = if true {
393 2
394 } else {
395 3
396 };
397}"#,
398 )
399 }
400}
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs
index 98cb09214..f7fbf37f4 100644
--- a/crates/assists/src/handlers/qualify_path.rs
+++ b/crates/assists/src/handlers/qualify_path.rs
@@ -22,7 +22,7 @@ use crate::{
22// 22//
23// ``` 23// ```
24// fn main() { 24// fn main() {
25// let map = HashMap<|>::new(); 25// let map = HashMap$0::new();
26// } 26// }
27// # pub mod std { pub mod collections { pub struct HashMap { } } } 27// # pub mod std { pub mod collections { pub struct HashMap { } } }
28// ``` 28// ```
@@ -221,7 +221,7 @@ mod tests {
221 221
222 use std::fmt; 222 use std::fmt;
223 223
224 <|>Formatter 224 $0Formatter
225 ", 225 ",
226 r" 226 r"
227 mod std { 227 mod std {
@@ -242,7 +242,7 @@ mod tests {
242 check_assist( 242 check_assist(
243 qualify_path, 243 qualify_path,
244 r" 244 r"
245 <|>PubStruct 245 $0PubStruct
246 246
247 pub mod PubMod { 247 pub mod PubMod {
248 pub struct PubStruct; 248 pub struct PubStruct;
@@ -266,7 +266,7 @@ mod tests {
266 macro_rules! foo { 266 macro_rules! foo {
267 ($i:ident) => { fn foo(a: $i) {} } 267 ($i:ident) => { fn foo(a: $i) {} }
268 } 268 }
269 foo!(Pub<|>Struct); 269 foo!(Pub$0Struct);
270 270
271 pub mod PubMod { 271 pub mod PubMod {
272 pub struct PubStruct; 272 pub struct PubStruct;
@@ -290,7 +290,7 @@ mod tests {
290 check_assist( 290 check_assist(
291 qualify_path, 291 qualify_path,
292 r" 292 r"
293 PubSt<|>ruct 293 PubSt$0ruct
294 294
295 pub mod PubMod1 { 295 pub mod PubMod1 {
296 pub struct PubStruct; 296 pub struct PubStruct;
@@ -325,7 +325,7 @@ mod tests {
325 r" 325 r"
326 use PubMod::PubStruct; 326 use PubMod::PubStruct;
327 327
328 PubStruct<|> 328 PubStruct$0
329 329
330 pub mod PubMod { 330 pub mod PubMod {
331 pub struct PubStruct; 331 pub struct PubStruct;
@@ -339,7 +339,7 @@ mod tests {
339 check_assist_not_applicable( 339 check_assist_not_applicable(
340 qualify_path, 340 qualify_path,
341 r" 341 r"
342 PrivateStruct<|> 342 PrivateStruct$0
343 343
344 pub mod PubMod { 344 pub mod PubMod {
345 struct PrivateStruct; 345 struct PrivateStruct;
@@ -353,7 +353,7 @@ mod tests {
353 check_assist_not_applicable( 353 check_assist_not_applicable(
354 qualify_path, 354 qualify_path,
355 " 355 "
356 PubStruct<|>", 356 PubStruct$0",
357 ); 357 );
358 } 358 }
359 359
@@ -362,7 +362,7 @@ mod tests {
362 check_assist_not_applicable( 362 check_assist_not_applicable(
363 qualify_path, 363 qualify_path,
364 r" 364 r"
365 use PubStruct<|>; 365 use PubStruct$0;
366 366
367 pub mod PubMod { 367 pub mod PubMod {
368 pub struct PubStruct; 368 pub struct PubStruct;
@@ -375,7 +375,7 @@ mod tests {
375 check_assist( 375 check_assist(
376 qualify_path, 376 qualify_path,
377 r" 377 r"
378 test_function<|> 378 test_function$0
379 379
380 pub mod PubMod { 380 pub mod PubMod {
381 pub fn test_function() {}; 381 pub fn test_function() {};
@@ -404,7 +404,7 @@ macro_rules! foo {
404 404
405//- /main.rs crate:main deps:crate_with_macro 405//- /main.rs crate:main deps:crate_with_macro
406fn main() { 406fn main() {
407 foo<|> 407 foo$0
408} 408}
409", 409",
410 r" 410 r"
@@ -421,7 +421,7 @@ fn main() {
421 qualify_path, 421 qualify_path,
422 r" 422 r"
423 struct AssistInfo { 423 struct AssistInfo {
424 group_label: Option<<|>GroupLabel>, 424 group_label: Option<$0GroupLabel>,
425 } 425 }
426 426
427 mod m { pub struct GroupLabel; } 427 mod m { pub struct GroupLabel; }
@@ -445,7 +445,7 @@ fn main() {
445 445
446 use mod1::mod2; 446 use mod1::mod2;
447 fn main() { 447 fn main() {
448 mod2::mod3::TestStruct<|> 448 mod2::mod3::TestStruct$0
449 } 449 }
450 ", 450 ",
451 ); 451 );
@@ -462,7 +462,7 @@ fn main() {
462 462
463 use test_mod::test_function; 463 use test_mod::test_function;
464 fn main() { 464 fn main() {
465 test_function<|> 465 test_function$0
466 } 466 }
467 ", 467 ",
468 ); 468 );
@@ -481,7 +481,7 @@ fn main() {
481 } 481 }
482 482
483 fn main() { 483 fn main() {
484 TestStruct::test_function<|> 484 TestStruct::test_function$0
485 } 485 }
486 ", 486 ",
487 r" 487 r"
@@ -513,7 +513,7 @@ fn main() {
513 } 513 }
514 514
515 fn main() { 515 fn main() {
516 TestStruct::TEST_CONST<|> 516 TestStruct::TEST_CONST$0
517 } 517 }
518 ", 518 ",
519 r" 519 r"
@@ -547,7 +547,7 @@ fn main() {
547 } 547 }
548 548
549 fn main() { 549 fn main() {
550 test_mod::TestStruct::test_function<|> 550 test_mod::TestStruct::test_function$0
551 } 551 }
552 ", 552 ",
553 r" 553 r"
@@ -594,7 +594,7 @@ fn main() {
594 594
595 use test_mod::TestTrait2; 595 use test_mod::TestTrait2;
596 fn main() { 596 fn main() {
597 test_mod::TestEnum::test_function<|>; 597 test_mod::TestEnum::test_function$0;
598 } 598 }
599 ", 599 ",
600 ) 600 )
@@ -617,7 +617,7 @@ fn main() {
617 } 617 }
618 618
619 fn main() { 619 fn main() {
620 test_mod::TestStruct::TEST_CONST<|> 620 test_mod::TestStruct::TEST_CONST$0
621 } 621 }
622 ", 622 ",
623 r" 623 r"
@@ -664,7 +664,7 @@ fn main() {
664 664
665 use test_mod::TestTrait2; 665 use test_mod::TestTrait2;
666 fn main() { 666 fn main() {
667 test_mod::TestEnum::TEST_CONST<|>; 667 test_mod::TestEnum::TEST_CONST$0;
668 } 668 }
669 ", 669 ",
670 ) 670 )
@@ -688,7 +688,7 @@ fn main() {
688 688
689 fn main() { 689 fn main() {
690 let test_struct = test_mod::TestStruct {}; 690 let test_struct = test_mod::TestStruct {};
691 test_struct.test_meth<|>od() 691 test_struct.test_meth$0od()
692 } 692 }
693 ", 693 ",
694 r" 694 r"
@@ -727,7 +727,7 @@ fn main() {
727 727
728 fn main() { 728 fn main() {
729 let test_struct = test_mod::TestStruct {}; 729 let test_struct = test_mod::TestStruct {};
730 test_struct.test_meth<|>od(42) 730 test_struct.test_meth$0od(42)
731 } 731 }
732 ", 732 ",
733 r" 733 r"
@@ -766,7 +766,7 @@ fn main() {
766 766
767 fn main() { 767 fn main() {
768 let test_struct = test_mod::TestStruct {}; 768 let test_struct = test_mod::TestStruct {};
769 test_struct.test_meth<|>od() 769 test_struct.test_meth$0od()
770 } 770 }
771 ", 771 ",
772 r" 772 r"
@@ -796,7 +796,7 @@ fn main() {
796 //- /main.rs crate:main deps:dep 796 //- /main.rs crate:main deps:dep
797 fn main() { 797 fn main() {
798 let test_struct = dep::test_mod::TestStruct {}; 798 let test_struct = dep::test_mod::TestStruct {};
799 test_struct.test_meth<|>od() 799 test_struct.test_meth$0od()
800 } 800 }
801 //- /dep.rs crate:dep 801 //- /dep.rs crate:dep
802 pub mod test_mod { 802 pub mod test_mod {
@@ -825,7 +825,7 @@ fn main() {
825 r" 825 r"
826 //- /main.rs crate:main deps:dep 826 //- /main.rs crate:main deps:dep
827 fn main() { 827 fn main() {
828 dep::test_mod::TestStruct::test_func<|>tion 828 dep::test_mod::TestStruct::test_func$0tion
829 } 829 }
830 //- /dep.rs crate:dep 830 //- /dep.rs crate:dep
831 pub mod test_mod { 831 pub mod test_mod {
@@ -853,7 +853,7 @@ fn main() {
853 r" 853 r"
854 //- /main.rs crate:main deps:dep 854 //- /main.rs crate:main deps:dep
855 fn main() { 855 fn main() {
856 dep::test_mod::TestStruct::CONST<|> 856 dep::test_mod::TestStruct::CONST$0
857 } 857 }
858 //- /dep.rs crate:dep 858 //- /dep.rs crate:dep
859 pub mod test_mod { 859 pub mod test_mod {
@@ -882,7 +882,7 @@ fn main() {
882 //- /main.rs crate:main deps:dep 882 //- /main.rs crate:main deps:dep
883 fn main() { 883 fn main() {
884 let test_struct = dep::test_mod::TestStruct {}; 884 let test_struct = dep::test_mod::TestStruct {};
885 test_struct.test_func<|>tion() 885 test_struct.test_func$0tion()
886 } 886 }
887 //- /dep.rs crate:dep 887 //- /dep.rs crate:dep
888 pub mod test_mod { 888 pub mod test_mod {
@@ -906,7 +906,7 @@ fn main() {
906 //- /main.rs crate:main deps:dep 906 //- /main.rs crate:main deps:dep
907 fn main() { 907 fn main() {
908 let test_struct = dep::test_mod::TestStruct {}; 908 let test_struct = dep::test_mod::TestStruct {};
909 test_struct.test_meth<|>od() 909 test_struct.test_meth$0od()
910 } 910 }
911 //- /dep.rs crate:dep 911 //- /dep.rs crate:dep
912 pub mod test_mod { 912 pub mod test_mod {
@@ -949,7 +949,7 @@ fn main() {
949 use test_mod::TestTrait2; 949 use test_mod::TestTrait2;
950 fn main() { 950 fn main() {
951 let one = test_mod::TestEnum::One; 951 let one = test_mod::TestEnum::One;
952 one.test<|>_method(); 952 one.test$0_method();
953 } 953 }
954 ", 954 ",
955 ) 955 )
@@ -965,7 +965,7 @@ pub struct Struct;
965 965
966//- /main.rs crate:main deps:dep 966//- /main.rs crate:main deps:dep
967fn main() { 967fn main() {
968 Struct<|> 968 Struct$0
969} 969}
970", 970",
971 r" 971 r"
@@ -992,7 +992,7 @@ pub fn panic_fmt() {}
992//- /main.rs crate:main deps:dep 992//- /main.rs crate:main deps:dep
993struct S; 993struct S;
994 994
995impl f<|>mt::Display for S {} 995impl f$0mt::Display for S {}
996", 996",
997 r" 997 r"
998struct S; 998struct S;
@@ -1019,7 +1019,7 @@ mac!();
1019 1019
1020//- /main.rs crate:main deps:dep 1020//- /main.rs crate:main deps:dep
1021fn main() { 1021fn main() {
1022 Cheese<|>; 1022 Cheese$0;
1023} 1023}
1024", 1024",
1025 r" 1025 r"
@@ -1042,7 +1042,7 @@ pub struct fmt;
1042 1042
1043//- /main.rs crate:main deps:dep 1043//- /main.rs crate:main deps:dep
1044fn main() { 1044fn main() {
1045 FMT<|>; 1045 FMT$0;
1046} 1046}
1047", 1047",
1048 r" 1048 r"
@@ -1062,7 +1062,7 @@ fn main() {
1062pub mod generic { pub struct Thing<'a, T>(&'a T); } 1062pub mod generic { pub struct Thing<'a, T>(&'a T); }
1063 1063
1064//- /main.rs crate:main deps:dep 1064//- /main.rs crate:main deps:dep
1065fn foo() -> Thin<|>g<'static, ()> {} 1065fn foo() -> Thin$0g<'static, ()> {}
1066 1066
1067fn main() {} 1067fn main() {}
1068", 1068",
@@ -1083,7 +1083,7 @@ fn main() {}
1083pub mod generic { pub struct Thing<'a, T>(&'a T); } 1083pub mod generic { pub struct Thing<'a, T>(&'a T); }
1084 1084
1085//- /main.rs crate:main deps:dep 1085//- /main.rs crate:main deps:dep
1086fn foo() -> Thin<|>g::<'static, ()> {} 1086fn foo() -> Thin$0g::<'static, ()> {}
1087 1087
1088fn main() {} 1088fn main() {}
1089", 1089",
@@ -1108,7 +1108,7 @@ fn main() {}
1108 } 1108 }
1109 1109
1110 fn main() { 1110 fn main() {
1111 TestStruct::<()>::TEST_CONST<|> 1111 TestStruct::<()>::TEST_CONST$0
1112 } 1112 }
1113 ", 1113 ",
1114 r" 1114 r"
@@ -1142,7 +1142,7 @@ fn main() {}
1142 } 1142 }
1143 1143
1144 fn main() { 1144 fn main() {
1145 test_mod::TestStruct::<()>::TEST_CONST<|> 1145 test_mod::TestStruct::<()>::TEST_CONST$0
1146 } 1146 }
1147 ", 1147 ",
1148 r" 1148 r"
@@ -1180,7 +1180,7 @@ fn main() {}
1180 1180
1181 fn main() { 1181 fn main() {
1182 let test_struct = test_mod::TestStruct {}; 1182 let test_struct = test_mod::TestStruct {};
1183 test_struct.test_meth<|>od::<()>() 1183 test_struct.test_meth$0od::<()>()
1184 } 1184 }
1185 ", 1185 ",
1186 r" 1186 r"
diff --git a/crates/assists/src/handlers/raw_string.rs b/crates/assists/src/handlers/raw_string.rs
index 4c759cc25..be963f162 100644
--- a/crates/assists/src/handlers/raw_string.rs
+++ b/crates/assists/src/handlers/raw_string.rs
@@ -11,7 +11,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
11// 11//
12// ``` 12// ```
13// fn main() { 13// fn main() {
14// "Hello,<|> World!"; 14// "Hello,$0 World!";
15// } 15// }
16// ``` 16// ```
17// -> 17// ->
@@ -53,7 +53,7 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext) -> Option<
53// 53//
54// ``` 54// ```
55// fn main() { 55// fn main() {
56// r#"Hello,<|> "World!""#; 56// r#"Hello,$0 "World!""#;
57// } 57// }
58// ``` 58// ```
59// -> 59// ->
@@ -95,7 +95,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Optio
95// 95//
96// ``` 96// ```
97// fn main() { 97// fn main() {
98// r#"Hello,<|> World!"#; 98// r#"Hello,$0 World!"#;
99// } 99// }
100// ``` 100// ```
101// -> 101// ->
@@ -123,7 +123,7 @@ pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
123// 123//
124// ``` 124// ```
125// fn main() { 125// fn main() {
126// r#"Hello,<|> World!"#; 126// r#"Hello,$0 World!"#;
127// } 127// }
128// ``` 128// ```
129// -> 129// ->
@@ -194,7 +194,7 @@ mod tests {
194 make_raw_string, 194 make_raw_string,
195 r#" 195 r#"
196 fn f() { 196 fn f() {
197 let s = <|>"random\nstring"; 197 let s = $0"random\nstring";
198 } 198 }
199 "#, 199 "#,
200 r#""random\nstring""#, 200 r#""random\nstring""#,
@@ -207,7 +207,7 @@ mod tests {
207 make_raw_string, 207 make_raw_string,
208 r#" 208 r#"
209fn f() { 209fn f() {
210 let s = <|>"random\nstring"; 210 let s = $0"random\nstring";
211} 211}
212"#, 212"#,
213 r##" 213 r##"
@@ -225,7 +225,7 @@ string"#;
225 make_raw_string, 225 make_raw_string,
226 r#" 226 r#"
227 fn f() { 227 fn f() {
228 format!(<|>"x = {}", 92) 228 format!($0"x = {}", 92)
229 } 229 }
230 "#, 230 "#,
231 r##" 231 r##"
@@ -242,7 +242,7 @@ string"#;
242 make_raw_string, 242 make_raw_string,
243 r###" 243 r###"
244fn f() { 244fn f() {
245 let s = <|>"#random##\nstring"; 245 let s = $0"#random##\nstring";
246} 246}
247"###, 247"###,
248 r####" 248 r####"
@@ -260,7 +260,7 @@ string"#;
260 make_raw_string, 260 make_raw_string,
261 r###" 261 r###"
262fn f() { 262fn f() {
263 let s = <|>"#random\"##\nstring"; 263 let s = $0"#random\"##\nstring";
264} 264}
265"###, 265"###,
266 r####" 266 r####"
@@ -278,7 +278,7 @@ string"###;
278 make_raw_string, 278 make_raw_string,
279 r#" 279 r#"
280 fn f() { 280 fn f() {
281 let s = <|>"random string"; 281 let s = $0"random string";
282 } 282 }
283 "#, 283 "#,
284 r##" 284 r##"
@@ -295,7 +295,7 @@ string"###;
295 make_raw_string, 295 make_raw_string,
296 r#" 296 r#"
297 fn f() { 297 fn f() {
298 let s = "foo<|> 298 let s = "foo$0
299 } 299 }
300 "#, 300 "#,
301 ) 301 )
@@ -307,7 +307,7 @@ string"###;
307 make_usual_string, 307 make_usual_string,
308 r#" 308 r#"
309 fn main() { 309 fn main() {
310 let s = r#"bar<|> 310 let s = r#"bar$0
311 } 311 }
312 "#, 312 "#,
313 ) 313 )
@@ -319,7 +319,7 @@ string"###;
319 add_hash, 319 add_hash,
320 r#" 320 r#"
321 fn f() { 321 fn f() {
322 let s = <|>r"random string"; 322 let s = $0r"random string";
323 } 323 }
324 "#, 324 "#,
325 r#"r"random string""#, 325 r#"r"random string""#,
@@ -332,7 +332,7 @@ string"###;
332 add_hash, 332 add_hash,
333 r#" 333 r#"
334 fn f() { 334 fn f() {
335 let s = <|>r"random string"; 335 let s = $0r"random string";
336 } 336 }
337 "#, 337 "#,
338 r##" 338 r##"
@@ -349,7 +349,7 @@ string"###;
349 add_hash, 349 add_hash,
350 r##" 350 r##"
351 fn f() { 351 fn f() {
352 let s = <|>r#"random"string"#; 352 let s = $0r#"random"string"#;
353 } 353 }
354 "##, 354 "##,
355 r###" 355 r###"
@@ -366,7 +366,7 @@ string"###;
366 add_hash, 366 add_hash,
367 r#" 367 r#"
368 fn f() { 368 fn f() {
369 let s = <|>"random string"; 369 let s = $0"random string";
370 } 370 }
371 "#, 371 "#,
372 ); 372 );
@@ -378,7 +378,7 @@ string"###;
378 remove_hash, 378 remove_hash,
379 r##" 379 r##"
380 fn f() { 380 fn f() {
381 let s = <|>r#"random string"#; 381 let s = $0r#"random string"#;
382 } 382 }
383 "##, 383 "##,
384 r##"r#"random string"#"##, 384 r##"r#"random string"#"##,
@@ -389,7 +389,7 @@ string"###;
389 fn remove_hash_works() { 389 fn remove_hash_works() {
390 check_assist( 390 check_assist(
391 remove_hash, 391 remove_hash,
392 r##"fn f() { let s = <|>r#"random string"#; }"##, 392 r##"fn f() { let s = $0r#"random string"#; }"##,
393 r#"fn f() { let s = r"random string"; }"#, 393 r#"fn f() { let s = r"random string"; }"#,
394 ) 394 )
395 } 395 }
@@ -401,7 +401,7 @@ string"###;
401 remove_hash, 401 remove_hash,
402 r##" 402 r##"
403 fn f() { 403 fn f() {
404 let s = <|>r#"random"str"ing"#; 404 let s = $0r#"random"str"ing"#;
405 } 405 }
406 "##, 406 "##,
407 ) 407 )
@@ -413,7 +413,7 @@ string"###;
413 remove_hash, 413 remove_hash,
414 r###" 414 r###"
415 fn f() { 415 fn f() {
416 let s = <|>r##"random string"##; 416 let s = $0r##"random string"##;
417 } 417 }
418 "###, 418 "###,
419 r##" 419 r##"
@@ -426,12 +426,12 @@ string"###;
426 426
427 #[test] 427 #[test]
428 fn remove_hash_doesnt_work() { 428 fn remove_hash_doesnt_work() {
429 check_assist_not_applicable(remove_hash, r#"fn f() { let s = <|>"random string"; }"#); 429 check_assist_not_applicable(remove_hash, r#"fn f() { let s = $0"random string"; }"#);
430 } 430 }
431 431
432 #[test] 432 #[test]
433 fn remove_hash_no_hash_doesnt_work() { 433 fn remove_hash_no_hash_doesnt_work() {
434 check_assist_not_applicable(remove_hash, r#"fn f() { let s = <|>r"random string"; }"#); 434 check_assist_not_applicable(remove_hash, r#"fn f() { let s = $0r"random string"; }"#);
435 } 435 }
436 436
437 #[test] 437 #[test]
@@ -440,7 +440,7 @@ string"###;
440 make_usual_string, 440 make_usual_string,
441 r##" 441 r##"
442 fn f() { 442 fn f() {
443 let s = <|>r#"random string"#; 443 let s = $0r#"random string"#;
444 } 444 }
445 "##, 445 "##,
446 r##"r#"random string"#"##, 446 r##"r#"random string"#"##,
@@ -453,7 +453,7 @@ string"###;
453 make_usual_string, 453 make_usual_string,
454 r##" 454 r##"
455 fn f() { 455 fn f() {
456 let s = <|>r#"random string"#; 456 let s = $0r#"random string"#;
457 } 457 }
458 "##, 458 "##,
459 r#" 459 r#"
@@ -470,7 +470,7 @@ string"###;
470 make_usual_string, 470 make_usual_string,
471 r##" 471 r##"
472 fn f() { 472 fn f() {
473 let s = <|>r#"random"str"ing"#; 473 let s = $0r#"random"str"ing"#;
474 } 474 }
475 "##, 475 "##,
476 r#" 476 r#"
@@ -487,7 +487,7 @@ string"###;
487 make_usual_string, 487 make_usual_string,
488 r###" 488 r###"
489 fn f() { 489 fn f() {
490 let s = <|>r##"random string"##; 490 let s = $0r##"random string"##;
491 } 491 }
492 "###, 492 "###,
493 r##" 493 r##"
@@ -504,7 +504,7 @@ string"###;
504 make_usual_string, 504 make_usual_string,
505 r#" 505 r#"
506 fn f() { 506 fn f() {
507 let s = <|>"random string"; 507 let s = $0"random string";
508 } 508 }
509 "#, 509 "#,
510 ); 510 );
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs
index eae6367c1..6114091f2 100644
--- a/crates/assists/src/handlers/remove_dbg.rs
+++ b/crates/assists/src/handlers/remove_dbg.rs
@@ -1,6 +1,6 @@
1use syntax::{ 1use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 match_ast, SyntaxElement, SyntaxKind, TextRange, TextSize, T, 3 match_ast, SyntaxElement, TextRange, TextSize, T,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -11,7 +11,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
11// 11//
12// ``` 12// ```
13// fn main() { 13// fn main() {
14// <|>dbg!(92); 14// $0dbg!(92);
15// } 15// }
16// ``` 16// ```
17// -> 17// ->
@@ -136,14 +136,14 @@ fn needs_parentheses_around_macro_contents(macro_contents: Vec<SyntaxElement>) -
136 symbol_kind => { 136 symbol_kind => {
137 let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty(); 137 let symbol_not_in_bracket = unpaired_brackets_in_contents.is_empty();
138 if symbol_not_in_bracket 138 if symbol_not_in_bracket
139 && symbol_kind != SyntaxKind::COLON // paths 139 && symbol_kind != T![:] // paths
140 && (symbol_kind != SyntaxKind::DOT // field/method access 140 && (symbol_kind != T![.] // field/method access
141 || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations 141 || macro_contents // range expressions consist of two SyntaxKind::Dot in macro invocations
142 .peek() 142 .peek()
143 .map(|element| element.kind() == SyntaxKind::DOT) 143 .map(|element| element.kind() == T![.])
144 .unwrap_or(false)) 144 .unwrap_or(false))
145 && symbol_kind != SyntaxKind::QUESTION // try operator 145 && symbol_kind != T![?] // try operator
146 && (symbol_kind.is_punct() || symbol_kind == SyntaxKind::AS_KW) 146 && (symbol_kind.is_punct() || symbol_kind == T![as])
147 { 147 {
148 return true; 148 return true;
149 } 149 }
@@ -161,19 +161,19 @@ mod tests {
161 161
162 #[test] 162 #[test]
163 fn test_remove_dbg() { 163 fn test_remove_dbg() {
164 check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1"); 164 check_assist(remove_dbg, "$0dbg!(1 + 1)", "1 + 1");
165 165
166 check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)"); 166 check_assist(remove_dbg, "dbg!$0((1 + 1))", "(1 + 1)");
167 167
168 check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1"); 168 check_assist(remove_dbg, "dbg!(1 $0+ 1)", "1 + 1");
169 169
170 check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1"); 170 check_assist(remove_dbg, "let _ = $0dbg!(1 + 1)", "let _ = 1 + 1");
171 171
172 check_assist( 172 check_assist(
173 remove_dbg, 173 remove_dbg,
174 " 174 "
175fn foo(n: usize) { 175fn foo(n: usize) {
176 if let Some(_) = dbg!(n.<|>checked_sub(4)) { 176 if let Some(_) = dbg!(n.$0checked_sub(4)) {
177 // ... 177 // ...
178 } 178 }
179} 179}
@@ -187,20 +187,20 @@ fn foo(n: usize) {
187", 187",
188 ); 188 );
189 189
190 check_assist(remove_dbg, "<|>dbg!(Foo::foo_test()).bar()", "Foo::foo_test().bar()"); 190 check_assist(remove_dbg, "$0dbg!(Foo::foo_test()).bar()", "Foo::foo_test().bar()");
191 } 191 }
192 192
193 #[test] 193 #[test]
194 fn test_remove_dbg_with_brackets_and_braces() { 194 fn test_remove_dbg_with_brackets_and_braces() {
195 check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1"); 195 check_assist(remove_dbg, "dbg![$01 + 1]", "1 + 1");
196 check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1"); 196 check_assist(remove_dbg, "dbg!{$01 + 1}", "1 + 1");
197 } 197 }
198 198
199 #[test] 199 #[test]
200 fn test_remove_dbg_not_applicable() { 200 fn test_remove_dbg_not_applicable() {
201 check_assist_not_applicable(remove_dbg, "<|>vec![1, 2, 3]"); 201 check_assist_not_applicable(remove_dbg, "$0vec![1, 2, 3]");
202 check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)"); 202 check_assist_not_applicable(remove_dbg, "$0dbg(5, 6, 7)");
203 check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7"); 203 check_assist_not_applicable(remove_dbg, "$0dbg!(5, 6, 7");
204 } 204 }
205 205
206 #[test] 206 #[test]
@@ -209,7 +209,7 @@ fn foo(n: usize) {
209 remove_dbg, 209 remove_dbg,
210 " 210 "
211fn foo(n: usize) { 211fn foo(n: usize) {
212 if let Some(_) = dbg!(n.<|>checked_sub(4)) { 212 if let Some(_) = dbg!(n.$0checked_sub(4)) {
213 // ... 213 // ...
214 } 214 }
215} 215}
@@ -226,7 +226,7 @@ fn foo(n: usize) {
226 // the ast::MacroCall to include the semicolon at the end 226 // the ast::MacroCall to include the semicolon at the end
227 check_assist( 227 check_assist(
228 remove_dbg, 228 remove_dbg,
229 r#"let res = <|>dbg!(1 * 20); // needless comment"#, 229 r#"let res = $0dbg!(1 * 20); // needless comment"#,
230 r#"let res = 1 * 20; // needless comment"#, 230 r#"let res = 1 * 20; // needless comment"#,
231 ); 231 );
232 } 232 }
@@ -238,7 +238,7 @@ fn foo(n: usize) {
238 " 238 "
239fn main() { 239fn main() {
240 let mut a = 1; 240 let mut a = 1;
241 while dbg!<|>(a) < 10000 { 241 while dbg!$0(a) < 10000 {
242 a += 1; 242 a += 1;
243 } 243 }
244} 244}
@@ -258,31 +258,31 @@ fn main() {
258 fn test_remove_dbg_keep_expression() { 258 fn test_remove_dbg_keep_expression() {
259 check_assist( 259 check_assist(
260 remove_dbg, 260 remove_dbg,
261 r#"let res = <|>dbg!(a + b).foo();"#, 261 r#"let res = $0dbg!(a + b).foo();"#,
262 r#"let res = (a + b).foo();"#, 262 r#"let res = (a + b).foo();"#,
263 ); 263 );
264 264
265 check_assist(remove_dbg, r#"let res = <|>dbg!(2 + 2) * 5"#, r#"let res = (2 + 2) * 5"#); 265 check_assist(remove_dbg, r#"let res = $0dbg!(2 + 2) * 5"#, r#"let res = (2 + 2) * 5"#);
266 check_assist(remove_dbg, r#"let res = <|>dbg![2 + 2] * 5"#, r#"let res = (2 + 2) * 5"#); 266 check_assist(remove_dbg, r#"let res = $0dbg![2 + 2] * 5"#, r#"let res = (2 + 2) * 5"#);
267 } 267 }
268 268
269 #[test] 269 #[test]
270 fn test_remove_dbg_method_chaining() { 270 fn test_remove_dbg_method_chaining() {
271 check_assist( 271 check_assist(
272 remove_dbg, 272 remove_dbg,
273 r#"let res = <|>dbg!(foo().bar()).baz();"#, 273 r#"let res = $0dbg!(foo().bar()).baz();"#,
274 r#"let res = foo().bar().baz();"#, 274 r#"let res = foo().bar().baz();"#,
275 ); 275 );
276 check_assist( 276 check_assist(
277 remove_dbg, 277 remove_dbg,
278 r#"let res = <|>dbg!(foo.bar()).baz();"#, 278 r#"let res = $0dbg!(foo.bar()).baz();"#,
279 r#"let res = foo.bar().baz();"#, 279 r#"let res = foo.bar().baz();"#,
280 ); 280 );
281 } 281 }
282 282
283 #[test] 283 #[test]
284 fn test_remove_dbg_field_chaining() { 284 fn test_remove_dbg_field_chaining() {
285 check_assist(remove_dbg, r#"let res = <|>dbg!(foo.bar).baz;"#, r#"let res = foo.bar.baz;"#); 285 check_assist(remove_dbg, r#"let res = $0dbg!(foo.bar).baz;"#, r#"let res = foo.bar.baz;"#);
286 } 286 }
287 287
288 #[test] 288 #[test]
@@ -295,7 +295,7 @@ fn square(x: u32) -> u32 {
295} 295}
296 296
297fn main() { 297fn main() {
298 let x = square(dbg<|>!(5 + 10)); 298 let x = square(dbg$0!(5 + 10));
299 println!("{}", x); 299 println!("{}", x);
300}"#, 300}"#,
301 "dbg!(5 + 10)", 301 "dbg!(5 + 10)",
@@ -309,7 +309,7 @@ fn square(x: u32) -> u32 {
309} 309}
310 310
311fn main() { 311fn main() {
312 let x = square(dbg<|>!(5 + 10)); 312 let x = square(dbg$0!(5 + 10));
313 println!("{}", x); 313 println!("{}", x);
314}"#, 314}"#,
315 r#" 315 r#"
@@ -328,7 +328,7 @@ fn main() {
328 fn test_remove_dbg_try_expr() { 328 fn test_remove_dbg_try_expr() {
329 check_assist( 329 check_assist(
330 remove_dbg, 330 remove_dbg,
331 r#"let res = <|>dbg!(result?).foo();"#, 331 r#"let res = $0dbg!(result?).foo();"#,
332 r#"let res = result?.foo();"#, 332 r#"let res = result?.foo();"#,
333 ); 333 );
334 } 334 }
@@ -337,7 +337,7 @@ fn main() {
337 fn test_remove_dbg_await_expr() { 337 fn test_remove_dbg_await_expr() {
338 check_assist( 338 check_assist(
339 remove_dbg, 339 remove_dbg,
340 r#"let res = <|>dbg!(fut.await).foo();"#, 340 r#"let res = $0dbg!(fut.await).foo();"#,
341 r#"let res = fut.await.foo();"#, 341 r#"let res = fut.await.foo();"#,
342 ); 342 );
343 } 343 }
@@ -346,7 +346,7 @@ fn main() {
346 fn test_remove_dbg_as_cast() { 346 fn test_remove_dbg_as_cast() {
347 check_assist( 347 check_assist(
348 remove_dbg, 348 remove_dbg,
349 r#"let res = <|>dbg!(3 as usize).foo();"#, 349 r#"let res = $0dbg!(3 as usize).foo();"#,
350 r#"let res = (3 as usize).foo();"#, 350 r#"let res = (3 as usize).foo();"#,
351 ); 351 );
352 } 352 }
@@ -355,12 +355,12 @@ fn main() {
355 fn test_remove_dbg_index_expr() { 355 fn test_remove_dbg_index_expr() {
356 check_assist( 356 check_assist(
357 remove_dbg, 357 remove_dbg,
358 r#"let res = <|>dbg!(array[3]).foo();"#, 358 r#"let res = $0dbg!(array[3]).foo();"#,
359 r#"let res = array[3].foo();"#, 359 r#"let res = array[3].foo();"#,
360 ); 360 );
361 check_assist( 361 check_assist(
362 remove_dbg, 362 remove_dbg,
363 r#"let res = <|>dbg!(tuple.3).foo();"#, 363 r#"let res = $0dbg!(tuple.3).foo();"#,
364 r#"let res = tuple.3.foo();"#, 364 r#"let res = tuple.3.foo();"#,
365 ); 365 );
366 } 366 }
@@ -369,12 +369,12 @@ fn main() {
369 fn test_remove_dbg_range_expr() { 369 fn test_remove_dbg_range_expr() {
370 check_assist( 370 check_assist(
371 remove_dbg, 371 remove_dbg,
372 r#"let res = <|>dbg!(foo..bar).foo();"#, 372 r#"let res = $0dbg!(foo..bar).foo();"#,
373 r#"let res = (foo..bar).foo();"#, 373 r#"let res = (foo..bar).foo();"#,
374 ); 374 );
375 check_assist( 375 check_assist(
376 remove_dbg, 376 remove_dbg,
377 r#"let res = <|>dbg!(foo..=bar).foo();"#, 377 r#"let res = $0dbg!(foo..=bar).foo();"#,
378 r#"let res = (foo..=bar).foo();"#, 378 r#"let res = (foo..=bar).foo();"#,
379 ); 379 );
380 } 380 }
@@ -384,7 +384,7 @@ fn main() {
384 check_assist( 384 check_assist(
385 remove_dbg, 385 remove_dbg,
386 r#"fn foo() { 386 r#"fn foo() {
387 if <|>dbg!(x || y) {} 387 if $0dbg!(x || y) {}
388}"#, 388}"#,
389 r#"fn foo() { 389 r#"fn foo() {
390 if x || y {} 390 if x || y {}
@@ -393,7 +393,7 @@ fn main() {
393 check_assist( 393 check_assist(
394 remove_dbg, 394 remove_dbg,
395 r#"fn foo() { 395 r#"fn foo() {
396 while let foo = <|>dbg!(&x) {} 396 while let foo = $0dbg!(&x) {}
397}"#, 397}"#,
398 r#"fn foo() { 398 r#"fn foo() {
399 while let foo = &x {} 399 while let foo = &x {}
@@ -402,7 +402,7 @@ fn main() {
402 check_assist( 402 check_assist(
403 remove_dbg, 403 remove_dbg,
404 r#"fn foo() { 404 r#"fn foo() {
405 if let foo = <|>dbg!(&x) {} 405 if let foo = $0dbg!(&x) {}
406}"#, 406}"#,
407 r#"fn foo() { 407 r#"fn foo() {
408 if let foo = &x {} 408 if let foo = &x {}
@@ -411,7 +411,7 @@ fn main() {
411 check_assist( 411 check_assist(
412 remove_dbg, 412 remove_dbg,
413 r#"fn foo() { 413 r#"fn foo() {
414 match <|>dbg!(&x) {} 414 match $0dbg!(&x) {}
415}"#, 415}"#,
416 r#"fn foo() { 416 r#"fn foo() {
417 match &x {} 417 match &x {}
diff --git a/crates/assists/src/handlers/remove_mut.rs b/crates/assists/src/handlers/remove_mut.rs
index 575b271f7..30d36dacd 100644
--- a/crates/assists/src/handlers/remove_mut.rs
+++ b/crates/assists/src/handlers/remove_mut.rs
@@ -8,7 +8,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
8// 8//
9// ``` 9// ```
10// impl Walrus { 10// impl Walrus {
11// fn feed(&mut<|> self, amount: u32) {} 11// fn feed(&mut$0 self, amount: u32) {}
12// } 12// }
13// ``` 13// ```
14// -> 14// ->
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs
index f72dd49ed..c961680e2 100644
--- a/crates/assists/src/handlers/remove_unused_param.rs
+++ b/crates/assists/src/handlers/remove_unused_param.rs
@@ -1,8 +1,8 @@
1use ide_db::{defs::Definition, search::Reference}; 1use ide_db::{base_db::FileId, defs::Definition, search::FileReference};
2use syntax::{ 2use syntax::{
3 algo::find_node_at_range, 3 algo::find_node_at_range,
4 ast::{self, ArgListOwner}, 4 ast::{self, ArgListOwner},
5 AstNode, SyntaxKind, SyntaxNode, TextRange, T, 5 AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T,
6}; 6};
7use test_utils::mark; 7use test_utils::mark;
8use SyntaxKind::WHITESPACE; 8use SyntaxKind::WHITESPACE;
@@ -16,7 +16,7 @@ use crate::{
16// Removes unused function parameter. 16// Removes unused function parameter.
17// 17//
18// ``` 18// ```
19// fn frobnicate(x: i32<|>) {} 19// fn frobnicate(x: i32$0) {}
20// 20//
21// fn main() { 21// fn main() {
22// frobnicate(92); 22// frobnicate(92);
@@ -58,32 +58,41 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt
58 param.syntax().text_range(), 58 param.syntax().text_range(),
59 |builder| { 59 |builder| {
60 builder.delete(range_to_remove(param.syntax())); 60 builder.delete(range_to_remove(param.syntax()));
61 for usage in fn_def.usages(&ctx.sema).all() { 61 for (file_id, references) in fn_def.usages(&ctx.sema).all() {
62 process_usage(ctx, builder, usage, param_position); 62 process_usages(ctx, builder, file_id, references, param_position);
63 } 63 }
64 }, 64 },
65 ) 65 )
66} 66}
67 67
68fn process_usage( 68fn process_usages(
69 ctx: &AssistContext, 69 ctx: &AssistContext,
70 builder: &mut AssistBuilder, 70 builder: &mut AssistBuilder,
71 usage: Reference, 71 file_id: FileId,
72 references: Vec<FileReference>,
72 arg_to_remove: usize, 73 arg_to_remove: usize,
73) -> Option<()> { 74) {
74 let source_file = ctx.sema.parse(usage.file_range.file_id); 75 let source_file = ctx.sema.parse(file_id);
75 let call_expr: ast::CallExpr = 76 builder.edit_file(file_id);
76 find_node_at_range(source_file.syntax(), usage.file_range.range)?; 77 for usage in references {
78 if let Some(text_range) = process_usage(&source_file, usage, arg_to_remove) {
79 builder.delete(text_range);
80 }
81 }
82}
83
84fn process_usage(
85 source_file: &SourceFile,
86 FileReference { range, .. }: FileReference,
87 arg_to_remove: usize,
88) -> Option<TextRange> {
89 let call_expr: ast::CallExpr = find_node_at_range(source_file.syntax(), range)?;
77 let call_expr_range = call_expr.expr()?.syntax().text_range(); 90 let call_expr_range = call_expr.expr()?.syntax().text_range();
78 if !call_expr_range.contains_range(usage.file_range.range) { 91 if !call_expr_range.contains_range(range) {
79 return None; 92 return None;
80 } 93 }
81 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; 94 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
82 95 Some(range_to_remove(arg.syntax()))
83 builder.edit_file(usage.file_range.file_id);
84 builder.delete(range_to_remove(arg.syntax()));
85
86 Some(())
87} 96}
88 97
89fn range_to_remove(node: &SyntaxNode) -> TextRange { 98fn range_to_remove(node: &SyntaxNode) -> TextRange {
@@ -123,7 +132,7 @@ mod tests {
123 remove_unused_param, 132 remove_unused_param,
124 r#" 133 r#"
125fn a() { foo(9, 2) } 134fn a() { foo(9, 2) }
126fn foo(x: i32, <|>y: i32) { x; } 135fn foo(x: i32, $0y: i32) { x; }
127fn b() { foo(9, 2,) } 136fn b() { foo(9, 2,) }
128"#, 137"#,
129 r#" 138 r#"
@@ -139,7 +148,7 @@ fn b() { foo(9, ) }
139 check_assist( 148 check_assist(
140 remove_unused_param, 149 remove_unused_param,
141 r#" 150 r#"
142fn foo(<|>x: i32, y: i32) { y; } 151fn foo($0x: i32, y: i32) { y; }
143fn a() { foo(1, 2) } 152fn a() { foo(1, 2) }
144fn b() { foo(1, 2,) } 153fn b() { foo(1, 2,) }
145"#, 154"#,
@@ -156,7 +165,7 @@ fn b() { foo(2,) }
156 check_assist( 165 check_assist(
157 remove_unused_param, 166 remove_unused_param,
158 r#" 167 r#"
159fn foo(<|>x: i32) { 0; } 168fn foo($0x: i32) { 0; }
160fn a() { foo(1) } 169fn a() { foo(1) }
161fn b() { foo(1, ) } 170fn b() { foo(1, ) }
162"#, 171"#,
@@ -173,7 +182,7 @@ fn b() { foo( ) }
173 check_assist( 182 check_assist(
174 remove_unused_param, 183 remove_unused_param,
175 r#" 184 r#"
176fn foo(x: i32, <|>y: i32, z: i32) { x; } 185fn foo(x: i32, $0y: i32, z: i32) { x; }
177fn a() { foo(1, 2, 3) } 186fn a() { foo(1, 2, 3) }
178fn b() { foo(1, 2, 3,) } 187fn b() { foo(1, 2, 3,) }
179"#, 188"#,
@@ -190,7 +199,7 @@ fn b() { foo(1, 3,) }
190 check_assist( 199 check_assist(
191 remove_unused_param, 200 remove_unused_param,
192 r#" 201 r#"
193mod bar { pub fn foo(x: i32, <|>y: i32) { x; } } 202mod bar { pub fn foo(x: i32, $0y: i32) { x; } }
194fn b() { bar::foo(9, 2) } 203fn b() { bar::foo(9, 2) }
195"#, 204"#,
196 r#" 205 r#"
@@ -205,7 +214,7 @@ fn b() { bar::foo(9) }
205 check_assist( 214 check_assist(
206 remove_unused_param, 215 remove_unused_param,
207 r#" 216 r#"
208pub fn foo<T>(x: T, <|>y: i32) { x; } 217pub fn foo<T>(x: T, $0y: i32) { x; }
209fn b() { foo::<i32>(9, 2) } 218fn b() { foo::<i32>(9, 2) }
210"#, 219"#,
211 r#" 220 r#"
@@ -220,7 +229,7 @@ fn b() { foo::<i32>(9) }
220 check_assist( 229 check_assist(
221 remove_unused_param, 230 remove_unused_param,
222 r#" 231 r#"
223pub fn foo<T>(x: i32, <|>y: T) { x; } 232pub fn foo<T>(x: i32, $0y: T) { x; }
224fn b() { foo::<i32>(9, 2) } 233fn b() { foo::<i32>(9, 2) }
225fn b2() { foo(9, 2) } 234fn b2() { foo(9, 2) }
226"#, 235"#,
@@ -238,7 +247,7 @@ fn b2() { foo(9) }
238 check_assist_not_applicable( 247 check_assist_not_applicable(
239 remove_unused_param, 248 remove_unused_param,
240 r#" 249 r#"
241fn foo(x: i32, <|>y: i32) { y; } 250fn foo(x: i32, $0y: i32) { y; }
242fn main() { foo(9, 2) } 251fn main() { foo(9, 2) }
243"#, 252"#,
244 ); 253 );
@@ -250,7 +259,7 @@ fn main() { foo(9, 2) }
250 remove_unused_param, 259 remove_unused_param,
251 r#" 260 r#"
252//- /main.rs 261//- /main.rs
253fn foo(x: i32, <|>y: i32) { x; } 262fn foo(x: i32, $0y: i32) { x; }
254 263
255mod foo; 264mod foo;
256 265
diff --git a/crates/assists/src/handlers/reorder_fields.rs b/crates/assists/src/handlers/reorder_fields.rs
index fe5574242..fba7d6ddb 100644
--- a/crates/assists/src/handlers/reorder_fields.rs
+++ b/crates/assists/src/handlers/reorder_fields.rs
@@ -15,7 +15,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
15// 15//
16// ``` 16// ```
17// struct Foo {foo: i32, bar: i32}; 17// struct Foo {foo: i32, bar: i32};
18// const test: Foo = <|>Foo {bar: 0, foo: 1} 18// const test: Foo = $0Foo {bar: 0, foo: 1}
19// ``` 19// ```
20// -> 20// ->
21// ``` 21// ```
@@ -126,7 +126,7 @@ struct Foo {
126 bar: i32, 126 bar: i32,
127} 127}
128 128
129const test: Foo = <|>Foo { foo: 0, bar: 0 }; 129const test: Foo = $0Foo { foo: 0, bar: 0 };
130"#, 130"#,
131 ) 131 )
132 } 132 }
@@ -137,7 +137,7 @@ const test: Foo = <|>Foo { foo: 0, bar: 0 };
137 reorder_fields, 137 reorder_fields,
138 r#" 138 r#"
139struct Foo {}; 139struct Foo {};
140const test: Foo = <|>Foo {} 140const test: Foo = $0Foo {}
141"#, 141"#,
142 ) 142 )
143 } 143 }
@@ -148,7 +148,7 @@ const test: Foo = <|>Foo {}
148 reorder_fields, 148 reorder_fields,
149 r#" 149 r#"
150struct Foo {foo: i32, bar: i32}; 150struct Foo {foo: i32, bar: i32};
151const test: Foo = <|>Foo {bar: 0, foo: 1} 151const test: Foo = $0Foo {bar: 0, foo: 1}
152"#, 152"#,
153 r#" 153 r#"
154struct Foo {foo: i32, bar: i32}; 154struct Foo {foo: i32, bar: i32};
@@ -166,7 +166,7 @@ struct Foo { foo: i64, bar: i64, baz: i64 }
166 166
167fn f(f: Foo) -> { 167fn f(f: Foo) -> {
168 match f { 168 match f {
169 <|>Foo { baz: 0, ref mut bar, .. } => (), 169 $0Foo { baz: 0, ref mut bar, .. } => (),
170 _ => () 170 _ => ()
171 } 171 }
172} 172}
@@ -197,7 +197,7 @@ struct Foo {
197impl Foo { 197impl Foo {
198 fn new() -> Foo { 198 fn new() -> Foo {
199 let foo = String::new(); 199 let foo = String::new();
200 <|>Foo { 200 $0Foo {
201 bar: foo.clone(), 201 bar: foo.clone(),
202 extra: "Extra field", 202 extra: "Extra field",
203 foo, 203 foo,
diff --git a/crates/assists/src/handlers/reorder_impl.rs b/crates/assists/src/handlers/reorder_impl.rs
new file mode 100644
index 000000000..309f291c8
--- /dev/null
+++ b/crates/assists/src/handlers/reorder_impl.rs
@@ -0,0 +1,201 @@
1use itertools::Itertools;
2use rustc_hash::FxHashMap;
3
4use hir::{PathResolution, Semantics};
5use ide_db::RootDatabase;
6use syntax::{
7 algo,
8 ast::{self, NameOwner},
9 AstNode,
10};
11use test_utils::mark;
12
13use crate::{AssistContext, AssistId, AssistKind, Assists};
14
15// Assist: reorder_impl
16//
17// Reorder the methods of an `impl Trait`. The methods will be ordered
18// in the same order as in the trait definition.
19//
20// ```
21// trait Foo {
22// fn a() {}
23// fn b() {}
24// fn c() {}
25// }
26//
27// struct Bar;
28// $0impl Foo for Bar {
29// fn b() {}
30// fn c() {}
31// fn a() {}
32// }
33// ```
34// ->
35// ```
36// trait Foo {
37// fn a() {}
38// fn b() {}
39// fn c() {}
40// }
41//
42// struct Bar;
43// impl Foo for Bar {
44// fn a() {}
45// fn b() {}
46// fn c() {}
47// }
48// ```
49//
50pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
51 let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
52 let items = impl_ast.assoc_item_list()?;
53 let methods = get_methods(&items);
54
55 let path = impl_ast
56 .trait_()
57 .and_then(|t| match t {
58 ast::Type::PathType(path) => Some(path),
59 _ => None,
60 })?
61 .path()?;
62
63 let ranks = compute_method_ranks(&path, ctx)?;
64 let sorted: Vec<_> = methods
65 .iter()
66 .cloned()
67 .sorted_by_key(|f| {
68 f.name().and_then(|n| ranks.get(&n.to_string()).copied()).unwrap_or(usize::max_value())
69 })
70 .collect();
71
72 // Don't edit already sorted methods:
73 if methods == sorted {
74 mark::hit!(not_applicable_if_sorted);
75 return None;
76 }
77
78 let target = items.syntax().text_range();
79 acc.add(AssistId("reorder_impl", AssistKind::RefactorRewrite), "Sort methods", target, |edit| {
80 let mut rewriter = algo::SyntaxRewriter::default();
81 for (old, new) in methods.iter().zip(&sorted) {
82 rewriter.replace(old.syntax(), new.syntax());
83 }
84 edit.rewrite(rewriter);
85 })
86}
87
88fn compute_method_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
89 let td = trait_definition(path, &ctx.sema)?;
90
91 Some(
92 td.items(ctx.db())
93 .iter()
94 .flat_map(|i| match i {
95 hir::AssocItem::Function(f) => Some(f),
96 _ => None,
97 })
98 .enumerate()
99 .map(|(idx, func)| ((func.name(ctx.db()).to_string(), idx)))
100 .collect(),
101 )
102}
103
104fn trait_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<hir::Trait> {
105 match sema.resolve_path(path)? {
106 PathResolution::Def(hir::ModuleDef::Trait(trait_)) => Some(trait_),
107 _ => None,
108 }
109}
110
111fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
112 items
113 .assoc_items()
114 .flat_map(|i| match i {
115 ast::AssocItem::Fn(f) => Some(f),
116 _ => None,
117 })
118 .filter(|f| f.name().is_some())
119 .collect()
120}
121
122#[cfg(test)]
123mod tests {
124 use test_utils::mark;
125
126 use crate::tests::{check_assist, check_assist_not_applicable};
127
128 use super::*;
129
130 #[test]
131 fn not_applicable_if_sorted() {
132 mark::check!(not_applicable_if_sorted);
133 check_assist_not_applicable(
134 reorder_impl,
135 r#"
136trait Bar {
137 fn a() {}
138 fn z() {}
139 fn b() {}
140}
141struct Foo;
142$0impl Bar for Foo {
143 fn a() {}
144 fn z() {}
145 fn b() {}
146}
147 "#,
148 )
149 }
150
151 #[test]
152 fn not_applicable_if_empty() {
153 check_assist_not_applicable(
154 reorder_impl,
155 r#"
156trait Bar {};
157struct Foo;
158$0impl Bar for Foo {}
159 "#,
160 )
161 }
162
163 #[test]
164 fn reorder_impl_trait_methods() {
165 check_assist(
166 reorder_impl,
167 r#"
168trait Bar {
169 fn a() {}
170 fn c() {}
171 fn b() {}
172 fn d() {}
173}
174
175struct Foo;
176$0impl Bar for Foo {
177 fn d() {}
178 fn b() {}
179 fn c() {}
180 fn a() {}
181}
182 "#,
183 r#"
184trait Bar {
185 fn a() {}
186 fn c() {}
187 fn b() {}
188 fn d() {}
189}
190
191struct Foo;
192impl Bar for Foo {
193 fn a() {}
194 fn c() {}
195 fn b() {}
196 fn d() {}
197}
198 "#,
199 )
200 }
201}
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
index cb7a5c104..bd4c1c806 100644
--- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -22,7 +22,7 @@ use crate::{
22// 22//
23// ``` 23// ```
24// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; } 24// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
25// #[derive(Deb<|>ug, Display)] 25// #[derive(Deb$0ug, Display)]
26// struct S; 26// struct S;
27// ``` 27// ```
28// -> 28// ->
@@ -219,7 +219,7 @@ mod fmt {
219 } 219 }
220} 220}
221 221
222#[derive(Debu<|>g)] 222#[derive(Debu$0g)]
223struct Foo { 223struct Foo {
224 bar: String, 224 bar: String,
225} 225}
@@ -261,7 +261,7 @@ mod foo {
261 } 261 }
262} 262}
263 263
264#[derive(<|>Bar)] 264#[derive($0Bar)]
265struct Foo { 265struct Foo {
266 bar: String, 266 bar: String,
267} 267}
@@ -300,7 +300,7 @@ impl foo::Bar for Foo {
300 check_assist( 300 check_assist(
301 replace_derive_with_manual_impl, 301 replace_derive_with_manual_impl,
302 " 302 "
303#[derive(Debu<|>g)] 303#[derive(Debu$0g)]
304struct Foo { 304struct Foo {
305 bar: String, 305 bar: String,
306} 306}
@@ -322,7 +322,7 @@ impl Debug for Foo {
322 check_assist( 322 check_assist(
323 replace_derive_with_manual_impl, 323 replace_derive_with_manual_impl,
324 " 324 "
325#[derive(Debug<|>)] 325#[derive(Debug$0)]
326pub struct Foo { 326pub struct Foo {
327 bar: String, 327 bar: String,
328} 328}
@@ -344,7 +344,7 @@ impl Debug for Foo {
344 check_assist( 344 check_assist(
345 replace_derive_with_manual_impl, 345 replace_derive_with_manual_impl,
346 " 346 "
347#[derive(Display, Debug<|>, Serialize)] 347#[derive(Display, Debug$0, Serialize)]
348struct Foo {} 348struct Foo {}
349 ", 349 ",
350 " 350 "
@@ -363,7 +363,7 @@ impl Debug for Foo {
363 check_assist_not_applicable( 363 check_assist_not_applicable(
364 replace_derive_with_manual_impl, 364 replace_derive_with_manual_impl,
365 " 365 "
366#[derive(<|>)] 366#[derive($0)]
367struct Foo {} 367struct Foo {}
368 ", 368 ",
369 ) 369 )
@@ -374,7 +374,7 @@ struct Foo {}
374 check_assist_not_applicable( 374 check_assist_not_applicable(
375 replace_derive_with_manual_impl, 375 replace_derive_with_manual_impl,
376 " 376 "
377#[derive<|>(Debug)] 377#[derive$0(Debug)]
378struct Foo {} 378struct Foo {}
379 ", 379 ",
380 ); 380 );
@@ -382,7 +382,7 @@ struct Foo {}
382 check_assist_not_applicable( 382 check_assist_not_applicable(
383 replace_derive_with_manual_impl, 383 replace_derive_with_manual_impl,
384 " 384 "
385#[derive(Debug)<|>] 385#[derive(Debug)$0]
386struct Foo {} 386struct Foo {}
387 ", 387 ",
388 ) 388 )
@@ -393,7 +393,7 @@ struct Foo {}
393 check_assist_not_applicable( 393 check_assist_not_applicable(
394 replace_derive_with_manual_impl, 394 replace_derive_with_manual_impl,
395 " 395 "
396#[allow(non_camel_<|>case_types)] 396#[allow(non_camel_$0case_types)]
397struct Foo {} 397struct Foo {}
398 ", 398 ",
399 ) 399 )
diff --git a/crates/assists/src/handlers/replace_if_let_with_match.rs b/crates/assists/src/handlers/replace_if_let_with_match.rs
index 4a355c66f..aee3397ab 100644
--- a/crates/assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/assists/src/handlers/replace_if_let_with_match.rs
@@ -20,7 +20,7 @@ use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, As
20// enum Action { Move { distance: u32 }, Stop } 20// enum Action { Move { distance: u32 }, Stop }
21// 21//
22// fn handle(action: Action) { 22// fn handle(action: Action) {
23// <|>if let Action::Move { distance } = action { 23// $0if let Action::Move { distance } = action {
24// foo(distance) 24// foo(distance)
25// } else { 25// } else {
26// bar() 26// bar()
@@ -89,7 +89,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
89// enum Action { Move { distance: u32 }, Stop } 89// enum Action { Move { distance: u32 }, Stop }
90// 90//
91// fn handle(action: Action) { 91// fn handle(action: Action) {
92// <|>match action { 92// $0match action {
93// Action::Move { distance } => foo(distance), 93// Action::Move { distance } => foo(distance),
94// _ => bar(), 94// _ => bar(),
95// } 95// }
@@ -138,7 +138,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext)
138 }; 138 };
139 let else_expr = match else_expr { 139 let else_expr = match else_expr {
140 ast::Expr::BlockExpr(block) 140 ast::Expr::BlockExpr(block)
141 if block.statements().count() == 0 && block.expr().is_none() => 141 if block.statements().count() == 0 && block.tail_expr().is_none() =>
142 { 142 {
143 None 143 None
144 } 144 }
@@ -179,7 +179,7 @@ mod tests {
179 r#" 179 r#"
180impl VariantData { 180impl VariantData {
181 pub fn is_struct(&self) -> bool { 181 pub fn is_struct(&self) -> bool {
182 if <|>let VariantData::Struct(..) = *self { 182 if $0let VariantData::Struct(..) = *self {
183 true 183 true
184 } else { 184 } else {
185 false 185 false
@@ -204,7 +204,7 @@ impl VariantData {
204 replace_if_let_with_match, 204 replace_if_let_with_match,
205 r#" 205 r#"
206fn foo() { 206fn foo() {
207 if <|>let VariantData::Struct(..) = a { 207 if $0let VariantData::Struct(..) = a {
208 bar( 208 bar(
209 123 209 123
210 ) 210 )
@@ -233,7 +233,7 @@ fn foo() {
233 r#" 233 r#"
234impl VariantData { 234impl VariantData {
235 pub fn is_struct(&self) -> bool { 235 pub fn is_struct(&self) -> bool {
236 if <|>let VariantData::Struct(..) = *self { 236 if $0let VariantData::Struct(..) = *self {
237 true 237 true
238 } else { 238 } else {
239 false 239 false
@@ -257,7 +257,7 @@ enum Option<T> { Some(T), None }
257use Option::*; 257use Option::*;
258 258
259fn foo(x: Option<i32>) { 259fn foo(x: Option<i32>) {
260 <|>if let Some(x) = x { 260 $0if let Some(x) = x {
261 println!("{}", x) 261 println!("{}", x)
262 } else { 262 } else {
263 println!("none") 263 println!("none")
@@ -287,7 +287,7 @@ enum Result<T, E> { Ok(T), Err(E) }
287use Result::*; 287use Result::*;
288 288
289fn foo(x: Result<i32, ()>) { 289fn foo(x: Result<i32, ()>) {
290 <|>if let Ok(x) = x { 290 $0if let Ok(x) = x {
291 println!("{}", x) 291 println!("{}", x)
292 } else { 292 } else {
293 println!("none") 293 println!("none")
@@ -315,7 +315,7 @@ fn foo(x: Result<i32, ()>) {
315 r#" 315 r#"
316fn main() { 316fn main() {
317 if true { 317 if true {
318 <|>if let Ok(rel_path) = path.strip_prefix(root_path) { 318 $0if let Ok(rel_path) = path.strip_prefix(root_path) {
319 let rel_path = RelativePathBuf::from_path(rel_path).ok()?; 319 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
320 Some((*id, rel_path)) 320 Some((*id, rel_path))
321 } else { 321 } else {
@@ -347,7 +347,7 @@ fn main() {
347 r#" 347 r#"
348impl VariantData { 348impl VariantData {
349 pub fn is_struct(&self) -> bool { 349 pub fn is_struct(&self) -> bool {
350 <|>match *self { 350 $0match *self {
351 VariantData::Struct(..) => true, 351 VariantData::Struct(..) => true,
352 _ => false, 352 _ => false,
353 } 353 }
@@ -372,7 +372,7 @@ impl VariantData {
372 replace_match_with_if_let, 372 replace_match_with_if_let,
373 r#" 373 r#"
374fn foo() { 374fn foo() {
375 <|>match a { 375 $0match a {
376 VariantData::Struct(..) => { 376 VariantData::Struct(..) => {
377 bar( 377 bar(
378 123 378 123
@@ -401,7 +401,7 @@ fn foo() {
401 r#" 401 r#"
402impl VariantData { 402impl VariantData {
403 pub fn is_struct(&self) -> bool { 403 pub fn is_struct(&self) -> bool {
404 <|>match *self { 404 $0match *self {
405 VariantData::Struct(..) => true, 405 VariantData::Struct(..) => true,
406 _ => false, 406 _ => false,
407 } 407 }
@@ -423,7 +423,7 @@ enum Option<T> { Some(T), None }
423use Option::*; 423use Option::*;
424 424
425fn foo(x: Option<i32>) { 425fn foo(x: Option<i32>) {
426 <|>match x { 426 $0match x {
427 Some(x) => println!("{}", x), 427 Some(x) => println!("{}", x),
428 None => println!("none"), 428 None => println!("none"),
429 } 429 }
@@ -453,7 +453,7 @@ enum Result<T, E> { Ok(T), Err(E) }
453use Result::*; 453use Result::*;
454 454
455fn foo(x: Result<i32, ()>) { 455fn foo(x: Result<i32, ()>) {
456 <|>match x { 456 $0match x {
457 Ok(x) => println!("{}", x), 457 Ok(x) => println!("{}", x),
458 Err(_) => println!("none"), 458 Err(_) => println!("none"),
459 } 459 }
@@ -481,7 +481,7 @@ fn foo(x: Result<i32, ()>) {
481 r#" 481 r#"
482fn main() { 482fn main() {
483 if true { 483 if true {
484 <|>match path.strip_prefix(root_path) { 484 $0match path.strip_prefix(root_path) {
485 Ok(rel_path) => { 485 Ok(rel_path) => {
486 let rel_path = RelativePathBuf::from_path(rel_path).ok()?; 486 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
487 Some((*id, rel_path)) 487 Some((*id, rel_path))
@@ -512,7 +512,7 @@ fn main() {
512 replace_match_with_if_let, 512 replace_match_with_if_let,
513 r#" 513 r#"
514fn main() { 514fn main() {
515 <|>match path.strip_prefix(root_path) { 515 $0match path.strip_prefix(root_path) {
516 Ok(rel_path) => println!("{}", rel_path), 516 Ok(rel_path) => println!("{}", rel_path),
517 _ => (), 517 _ => (),
518 } 518 }
diff --git a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs
index 6738bc134..ff25b61ea 100644
--- a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs
+++ b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs
@@ -7,7 +7,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
7// Replaces `impl Trait` function argument with the named generic. 7// Replaces `impl Trait` function argument with the named generic.
8// 8//
9// ``` 9// ```
10// fn foo(bar: <|>impl Bar) {} 10// fn foo(bar: $0impl Bar) {}
11// ``` 11// ```
12// -> 12// ->
13// ``` 13// ```
@@ -56,7 +56,7 @@ mod tests {
56 check_assist( 56 check_assist(
57 replace_impl_trait_with_generic, 57 replace_impl_trait_with_generic,
58 r#" 58 r#"
59 fn foo<G>(bar: <|>impl Bar) {} 59 fn foo<G>(bar: $0impl Bar) {}
60 "#, 60 "#,
61 r#" 61 r#"
62 fn foo<G, B: Bar>(bar: B) {} 62 fn foo<G, B: Bar>(bar: B) {}
@@ -69,7 +69,7 @@ mod tests {
69 check_assist( 69 check_assist(
70 replace_impl_trait_with_generic, 70 replace_impl_trait_with_generic,
71 r#" 71 r#"
72 fn foo(bar: <|>impl Bar) {} 72 fn foo(bar: $0impl Bar) {}
73 "#, 73 "#,
74 r#" 74 r#"
75 fn foo<B: Bar>(bar: B) {} 75 fn foo<B: Bar>(bar: B) {}
@@ -82,7 +82,7 @@ mod tests {
82 check_assist( 82 check_assist(
83 replace_impl_trait_with_generic, 83 replace_impl_trait_with_generic,
84 r#" 84 r#"
85 fn foo<G>(foo: impl Foo, bar: <|>impl Bar) {} 85 fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}
86 "#, 86 "#,
87 r#" 87 r#"
88 fn foo<G, B: Bar>(foo: impl Foo, bar: B) {} 88 fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}
@@ -95,7 +95,7 @@ mod tests {
95 check_assist( 95 check_assist(
96 replace_impl_trait_with_generic, 96 replace_impl_trait_with_generic,
97 r#" 97 r#"
98 fn foo<>(bar: <|>impl Bar) {} 98 fn foo<>(bar: $0impl Bar) {}
99 "#, 99 "#,
100 r#" 100 r#"
101 fn foo<B: Bar>(bar: B) {} 101 fn foo<B: Bar>(bar: B) {}
@@ -109,7 +109,7 @@ mod tests {
109 replace_impl_trait_with_generic, 109 replace_impl_trait_with_generic,
110 r#" 110 r#"
111 fn foo< 111 fn foo<
112 >(bar: <|>impl Bar) {} 112 >(bar: $0impl Bar) {}
113 "#, 113 "#,
114 r#" 114 r#"
115 fn foo<B: Bar 115 fn foo<B: Bar
@@ -124,7 +124,7 @@ mod tests {
124 check_assist( 124 check_assist(
125 replace_impl_trait_with_generic, 125 replace_impl_trait_with_generic,
126 r#" 126 r#"
127 fn foo<B>(bar: <|>impl Bar) {} 127 fn foo<B>(bar: $0impl Bar) {}
128 "#, 128 "#,
129 r#" 129 r#"
130 fn foo<B, C: Bar>(bar: C) {} 130 fn foo<B, C: Bar>(bar: C) {}
@@ -141,7 +141,7 @@ mod tests {
141 G: Foo, 141 G: Foo,
142 F, 142 F,
143 H, 143 H,
144 >(bar: <|>impl Bar) {} 144 >(bar: $0impl Bar) {}
145 "#, 145 "#,
146 r#" 146 r#"
147 fn foo< 147 fn foo<
@@ -158,7 +158,7 @@ mod tests {
158 check_assist( 158 check_assist(
159 replace_impl_trait_with_generic, 159 replace_impl_trait_with_generic,
160 r#" 160 r#"
161 fn foo(bar: <|>impl Foo + Bar) {} 161 fn foo(bar: $0impl Foo + Bar) {}
162 "#, 162 "#,
163 r#" 163 r#"
164 fn foo<F: Foo + Bar>(bar: F) {} 164 fn foo<F: Foo + Bar>(bar: F) {}
diff --git a/crates/assists/src/handlers/replace_let_with_if_let.rs b/crates/assists/src/handlers/replace_let_with_if_let.rs
index 5970e283c..5a27ada6b 100644
--- a/crates/assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/assists/src/handlers/replace_let_with_if_let.rs
@@ -20,7 +20,7 @@ use ide_db::ty_filter::TryEnum;
20// # enum Option<T> { Some(T), None } 20// # enum Option<T> { Some(T), None }
21// 21//
22// fn main(action: Action) { 22// fn main(action: Action) {
23// <|>let x = compute(); 23// $0let x = compute();
24// } 24// }
25// 25//
26// fn compute() -> Option<i32> { None } 26// fn compute() -> Option<i32> { None }
@@ -85,7 +85,7 @@ mod tests {
85enum E<T> { X(T), Y(T) } 85enum E<T> { X(T), Y(T) }
86 86
87fn main() { 87fn main() {
88 <|>let x = E::X(92); 88 $0let x = E::X(92);
89} 89}
90 ", 90 ",
91 r" 91 r"
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
index 8193e45a8..f3bc6cf39 100644
--- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
@@ -9,7 +9,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
9// Adds a use statement for a given fully-qualified name. 9// Adds a use statement for a given fully-qualified name.
10// 10//
11// ``` 11// ```
12// fn process(map: std::collections::<|>HashMap<String, String>) {} 12// fn process(map: std::collections::$0HashMap<String, String>) {}
13// ``` 13// ```
14// -> 14// ->
15// ``` 15// ```
@@ -127,7 +127,7 @@ mod tests {
127 r"use std::fs; 127 r"use std::fs;
128 128
129fn main() { 129fn main() {
130 std::f<|>s::Path 130 std::f$0s::Path
131}", 131}",
132 r"use std::fs; 132 r"use std::fs;
133 133
@@ -142,7 +142,7 @@ fn main() {
142 check_assist( 142 check_assist(
143 replace_qualified_name_with_use, 143 replace_qualified_name_with_use,
144 r" 144 r"
145std::fmt::Debug<|> 145std::fmt::Debug$0
146 ", 146 ",
147 r" 147 r"
148use std::fmt::Debug; 148use std::fmt::Debug;
@@ -156,7 +156,7 @@ Debug
156 check_assist( 156 check_assist(
157 replace_qualified_name_with_use, 157 replace_qualified_name_with_use,
158 r" 158 r"
159std::fmt::Debug<|> 159std::fmt::Debug$0
160 160
161fn main() { 161fn main() {
162} 162}
@@ -180,7 +180,7 @@ fn main() {
180fn main() { 180fn main() {
181} 181}
182 182
183std::fmt::Debug<|> 183std::fmt::Debug$0
184 ", 184 ",
185 r" 185 r"
186use std::fmt::Debug; 186use std::fmt::Debug;
@@ -198,7 +198,7 @@ Debug
198 check_assist( 198 check_assist(
199 replace_qualified_name_with_use, 199 replace_qualified_name_with_use,
200 r" 200 r"
201std::fmt<|>::Debug 201std::fmt$0::Debug
202 ", 202 ",
203 r" 203 r"
204use std::fmt; 204use std::fmt;
@@ -215,7 +215,7 @@ fmt::Debug
215 r" 215 r"
216use stdx; 216use stdx;
217 217
218impl std::fmt::Debug<|> for Foo { 218impl std::fmt::Debug$0 for Foo {
219} 219}
220 ", 220 ",
221 r" 221 r"
@@ -234,7 +234,7 @@ impl Debug for Foo {
234 check_assist( 234 check_assist(
235 replace_qualified_name_with_use, 235 replace_qualified_name_with_use,
236 r" 236 r"
237impl std::fmt::Debug<|> for Foo { 237impl std::fmt::Debug$0 for Foo {
238} 238}
239 ", 239 ",
240 r" 240 r"
@@ -251,7 +251,7 @@ impl Debug for Foo {
251 check_assist( 251 check_assist(
252 replace_qualified_name_with_use, 252 replace_qualified_name_with_use,
253 r" 253 r"
254 impl std::fmt::Debug<|> for Foo { 254 impl std::fmt::Debug$0 for Foo {
255 } 255 }
256 ", 256 ",
257 r" 257 r"
@@ -270,7 +270,7 @@ impl Debug for Foo {
270 r" 270 r"
271use std::fmt; 271use std::fmt;
272 272
273impl std::io<|> for Foo { 273impl std::io$0 for Foo {
274} 274}
275 ", 275 ",
276 r" 276 r"
@@ -289,7 +289,7 @@ impl io for Foo {
289 r" 289 r"
290use std::fmt; 290use std::fmt;
291 291
292impl std::fmt::Debug<|> for Foo { 292impl std::fmt::Debug$0 for Foo {
293} 293}
294 ", 294 ",
295 r" 295 r"
@@ -308,7 +308,7 @@ impl Debug for Foo {
308 r" 308 r"
309use std::fmt::Debug; 309use std::fmt::Debug;
310 310
311impl std::fmt<|> for Foo { 311impl std::fmt$0 for Foo {
312} 312}
313 ", 313 ",
314 r" 314 r"
@@ -327,7 +327,7 @@ impl fmt for Foo {
327 r" 327 r"
328use std::fmt::{Debug, nested::{Display}}; 328use std::fmt::{Debug, nested::{Display}};
329 329
330impl std::fmt::nested<|> for Foo { 330impl std::fmt::nested$0 for Foo {
331} 331}
332", 332",
333 r" 333 r"
@@ -346,7 +346,7 @@ impl nested for Foo {
346 r" 346 r"
347use std::fmt::{Debug, nested::{self, Display}}; 347use std::fmt::{Debug, nested::{self, Display}};
348 348
349impl std::fmt::nested<|> for Foo { 349impl std::fmt::nested$0 for Foo {
350} 350}
351", 351",
352 r" 352 r"
@@ -365,7 +365,7 @@ impl nested for Foo {
365 r" 365 r"
366use std::fmt::{Debug, nested::{Display}}; 366use std::fmt::{Debug, nested::{Display}};
367 367
368impl std::fmt::nested::Debug<|> for Foo { 368impl std::fmt::nested::Debug$0 for Foo {
369} 369}
370", 370",
371 r" 371 r"
@@ -384,7 +384,7 @@ impl Debug for Foo {
384 r" 384 r"
385use std::fmt::Debug; 385use std::fmt::Debug;
386 386
387impl std::fmt::nested::Display<|> for Foo { 387impl std::fmt::nested::Display$0 for Foo {
388} 388}
389", 389",
390 r" 390 r"
@@ -403,7 +403,7 @@ impl Display for Foo {
403 r" 403 r"
404use std::fmt::nested::Debug; 404use std::fmt::nested::Debug;
405 405
406impl std::fmt::Display<|> for Foo { 406impl std::fmt::Display$0 for Foo {
407} 407}
408", 408",
409 r" 409 r"
@@ -425,7 +425,7 @@ use crate::{
425 AssocItem, 425 AssocItem,
426}; 426};
427 427
428fn foo() { crate::ty::lower<|>::trait_env() } 428fn foo() { crate::ty::lower$0::trait_env() }
429", 429",
430 r" 430 r"
431use crate::{AssocItem, ty::{Substs, Ty, lower}}; 431use crate::{AssocItem, ty::{Substs, Ty, lower}};
@@ -442,7 +442,7 @@ fn foo() { lower::trait_env() }
442 r" 442 r"
443use std::fmt as foo; 443use std::fmt as foo;
444 444
445impl foo::Debug<|> for Foo { 445impl foo::Debug$0 for Foo {
446} 446}
447", 447",
448 r" 448 r"
@@ -462,7 +462,7 @@ impl Debug for Foo {
462 check_assist_not_applicable( 462 check_assist_not_applicable(
463 replace_qualified_name_with_use, 463 replace_qualified_name_with_use,
464 r" 464 r"
465impl foo<|> for Foo { 465impl foo$0 for Foo {
466} 466}
467", 467",
468 ); 468 );
@@ -473,7 +473,7 @@ impl foo<|> for Foo {
473 check_assist_not_applicable( 473 check_assist_not_applicable(
474 replace_qualified_name_with_use, 474 replace_qualified_name_with_use,
475 r" 475 r"
476use std::fmt<|>; 476use std::fmt$0;
477", 477",
478 ); 478 );
479 } 479 }
@@ -485,7 +485,7 @@ use std::fmt<|>;
485 r" 485 r"
486mod foo { 486mod foo {
487 mod bar { 487 mod bar {
488 std::fmt::Debug<|> 488 std::fmt::Debug$0
489 } 489 }
490} 490}
491 ", 491 ",
@@ -509,7 +509,7 @@ mod foo {
509#![allow(dead_code)] 509#![allow(dead_code)]
510 510
511fn main() { 511fn main() {
512 std::fmt::Debug<|> 512 std::fmt::Debug$0
513} 513}
514 ", 514 ",
515 r" 515 r"
@@ -530,7 +530,7 @@ fn main() {
530 replace_qualified_name_with_use, 530 replace_qualified_name_with_use,
531 r" 531 r"
532fn main() { 532fn main() {
533 std::fmt::Debug<|>; 533 std::fmt::Debug$0;
534 let x: std::fmt::Debug = std::fmt::Debug; 534 let x: std::fmt::Debug = std::fmt::Debug;
535} 535}
536 ", 536 ",
@@ -552,7 +552,7 @@ fn main() {
552 r" 552 r"
553mod m { 553mod m {
554 fn f() { 554 fn f() {
555 std::fmt::Debug<|>; 555 std::fmt::Debug$0;
556 let x: std::fmt::Debug = std::fmt::Debug; 556 let x: std::fmt::Debug = std::fmt::Debug;
557 } 557 }
558 fn g() { 558 fn g() {
@@ -590,7 +590,7 @@ fn f() {
590 replace_qualified_name_with_use, 590 replace_qualified_name_with_use,
591 r" 591 r"
592fn main() { 592fn main() {
593 std::fmt::Debug<|>; 593 std::fmt::Debug$0;
594} 594}
595 595
596mod sub { 596mod sub {
@@ -623,7 +623,7 @@ mod sub {
623use std::fmt::Display; 623use std::fmt::Display;
624 624
625fn main() { 625fn main() {
626 std::fmt<|>; 626 std::fmt$0;
627} 627}
628 ", 628 ",
629 r" 629 r"
@@ -643,7 +643,7 @@ fn main() {
643 r" 643 r"
644pub use std::fmt; 644pub use std::fmt;
645 645
646impl std::io<|> for Foo { 646impl std::io$0 for Foo {
647} 647}
648 ", 648 ",
649 r" 649 r"
@@ -663,7 +663,7 @@ impl io for Foo {
663 r" 663 r"
664pub(crate) use std::fmt; 664pub(crate) use std::fmt;
665 665
666impl std::io<|> for Foo { 666impl std::io$0 for Foo {
667} 667}
668 ", 668 ",
669 r" 669 r"
diff --git a/crates/assists/src/handlers/replace_string_with_char.rs b/crates/assists/src/handlers/replace_string_with_char.rs
index b4b898846..317318c24 100644
--- a/crates/assists/src/handlers/replace_string_with_char.rs
+++ b/crates/assists/src/handlers/replace_string_with_char.rs
@@ -8,7 +8,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
8// 8//
9// ``` 9// ```
10// fn main() { 10// fn main() {
11// find("{<|>"); 11// find("{$0");
12// } 12// }
13// ``` 13// ```
14// -> 14// ->
@@ -48,7 +48,7 @@ mod tests {
48 replace_string_with_char, 48 replace_string_with_char,
49 r#" 49 r#"
50 fn f() { 50 fn f() {
51 let s = "<|>c"; 51 let s = "$0c";
52 } 52 }
53 "#, 53 "#,
54 r#""c""#, 54 r#""c""#,
@@ -61,7 +61,7 @@ mod tests {
61 replace_string_with_char, 61 replace_string_with_char,
62 r#" 62 r#"
63 fn f() { 63 fn f() {
64 let s = "<|>c"; 64 let s = "$0c";
65 } 65 }
66 "#, 66 "#,
67 r##" 67 r##"
@@ -78,7 +78,7 @@ mod tests {
78 replace_string_with_char, 78 replace_string_with_char,
79 r#" 79 r#"
80 fn f() { 80 fn f() {
81 let s = "<|>😀"; 81 let s = "$0😀";
82 } 82 }
83 "#, 83 "#,
84 r##" 84 r##"
@@ -95,7 +95,7 @@ mod tests {
95 replace_string_with_char, 95 replace_string_with_char,
96 r#" 96 r#"
97 fn f() { 97 fn f() {
98 let s = "<|>test"; 98 let s = "$0test";
99 } 99 }
100 "#, 100 "#,
101 ) 101 )
@@ -107,7 +107,7 @@ mod tests {
107 replace_string_with_char, 107 replace_string_with_char,
108 r#" 108 r#"
109 fn f() { 109 fn f() {
110 format!(<|>"x", 92) 110 format!($0"x", 92)
111 } 111 }
112 "#, 112 "#,
113 r##" 113 r##"
@@ -124,7 +124,7 @@ mod tests {
124 replace_string_with_char, 124 replace_string_with_char,
125 r#" 125 r#"
126 fn f() { 126 fn f() {
127 find(<|>"x"); 127 find($0"x");
128 } 128 }
129 "#, 129 "#,
130 r##" 130 r##"
diff --git a/crates/assists/src/handlers/replace_unwrap_with_match.rs b/crates/assists/src/handlers/replace_unwrap_with_match.rs
index f547066f0..a986a6ae8 100644
--- a/crates/assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/assists/src/handlers/replace_unwrap_with_match.rs
@@ -23,7 +23,7 @@ use ide_db::ty_filter::TryEnum;
23// enum Result<T, E> { Ok(T), Err(E) } 23// enum Result<T, E> { Ok(T), Err(E) }
24// fn main() { 24// fn main() {
25// let x: Result<i32, i32> = Result::Ok(92); 25// let x: Result<i32, i32> = Result::Ok(92);
26// let y = x.<|>unwrap(); 26// let y = x.$0unwrap();
27// } 27// }
28// ``` 28// ```
29// -> 29// ->
@@ -101,7 +101,7 @@ enum Result<T, E> { Ok(T), Err(E) }
101fn i<T>(a: T) -> T { a } 101fn i<T>(a: T) -> T { a }
102fn main() { 102fn main() {
103 let x: Result<i32, i32> = Result::Ok(92); 103 let x: Result<i32, i32> = Result::Ok(92);
104 let y = i(x).<|>unwrap(); 104 let y = i(x).$0unwrap();
105} 105}
106 ", 106 ",
107 r" 107 r"
@@ -127,7 +127,7 @@ enum Option<T> { Some(T), None }
127fn i<T>(a: T) -> T { a } 127fn i<T>(a: T) -> T { a }
128fn main() { 128fn main() {
129 let x = Option::Some(92); 129 let x = Option::Some(92);
130 let y = i(x).<|>unwrap(); 130 let y = i(x).$0unwrap();
131} 131}
132 ", 132 ",
133 r" 133 r"
@@ -153,7 +153,7 @@ enum Result<T, E> { Ok(T), Err(E) }
153fn i<T>(a: T) -> T { a } 153fn i<T>(a: T) -> T { a }
154fn main() { 154fn main() {
155 let x: Result<i32, i32> = Result::Ok(92); 155 let x: Result<i32, i32> = Result::Ok(92);
156 let y = i(x).<|>unwrap().count_zeroes(); 156 let y = i(x).$0unwrap().count_zeroes();
157} 157}
158 ", 158 ",
159 r" 159 r"
@@ -179,7 +179,7 @@ enum Option<T> { Some(T), None }
179fn i<T>(a: T) -> T { a } 179fn i<T>(a: T) -> T { a }
180fn main() { 180fn main() {
181 let x = Option::Some(92); 181 let x = Option::Some(92);
182 let y = i(x).<|>unwrap(); 182 let y = i(x).$0unwrap();
183} 183}
184 ", 184 ",
185 r"i(x).unwrap()", 185 r"i(x).unwrap()",
diff --git a/crates/assists/src/handlers/split_import.rs b/crates/assists/src/handlers/split_import.rs
index ef1f6b8a1..9319a4267 100644
--- a/crates/assists/src/handlers/split_import.rs
+++ b/crates/assists/src/handlers/split_import.rs
@@ -9,7 +9,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
9// Wraps the tail of import into braces. 9// Wraps the tail of import into braces.
10// 10//
11// ``` 11// ```
12// use std::<|>collections::HashMap; 12// use std::$0collections::HashMap;
13// ``` 13// ```
14// -> 14// ->
15// ``` 15// ```
@@ -43,7 +43,7 @@ mod tests {
43 fn test_split_import() { 43 fn test_split_import() {
44 check_assist( 44 check_assist(
45 split_import, 45 split_import,
46 "use crate::<|>db::RootDatabase;", 46 "use crate::$0db::RootDatabase;",
47 "use crate::{db::RootDatabase};", 47 "use crate::{db::RootDatabase};",
48 ) 48 )
49 } 49 }
@@ -52,19 +52,19 @@ mod tests {
52 fn split_import_works_with_trees() { 52 fn split_import_works_with_trees() {
53 check_assist( 53 check_assist(
54 split_import, 54 split_import,
55 "use crate:<|>:db::{RootDatabase, FileSymbol}", 55 "use crate:$0:db::{RootDatabase, FileSymbol}",
56 "use crate::{db::{RootDatabase, FileSymbol}}", 56 "use crate::{db::{RootDatabase, FileSymbol}}",
57 ) 57 )
58 } 58 }
59 59
60 #[test] 60 #[test]
61 fn split_import_target() { 61 fn split_import_target() {
62 check_assist_target(split_import, "use crate::<|>db::{RootDatabase, FileSymbol}", "::"); 62 check_assist_target(split_import, "use crate::$0db::{RootDatabase, FileSymbol}", "::");
63 } 63 }
64 64
65 #[test] 65 #[test]
66 fn issue4044() { 66 fn issue4044() {
67 check_assist_not_applicable(split_import, "use crate::<|>:::self;") 67 check_assist_not_applicable(split_import, "use crate::$0:::self;")
68 } 68 }
69 69
70 #[test] 70 #[test]
@@ -72,7 +72,7 @@ mod tests {
72 check_assist_not_applicable( 72 check_assist_not_applicable(
73 split_import, 73 split_import,
74 r" 74 r"
75use std::<|> 75use std::$0
76fn main() {}", 76fn main() {}",
77 ); 77 );
78 } 78 }
diff --git a/crates/assists/src/handlers/toggle_ignore.rs b/crates/assists/src/handlers/toggle_ignore.rs
index 14b420421..33e12a7d0 100644
--- a/crates/assists/src/handlers/toggle_ignore.rs
+++ b/crates/assists/src/handlers/toggle_ignore.rs
@@ -10,7 +10,7 @@ use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind,
10// Adds `#[ignore]` attribute to the test. 10// Adds `#[ignore]` attribute to the test.
11// 11//
12// ``` 12// ```
13// <|>#[test] 13// $0#[test]
14// fn arithmetics { 14// fn arithmetics {
15// assert_eq!(2 + 2, 5); 15// assert_eq!(2 + 2, 5);
16// } 16// }
@@ -69,7 +69,7 @@ mod tests {
69 check_assist( 69 check_assist(
70 toggle_ignore, 70 toggle_ignore,
71 r#" 71 r#"
72 #[test<|>] 72 #[test$0]
73 fn test() {} 73 fn test() {}
74 "#, 74 "#,
75 r#" 75 r#"
@@ -85,7 +85,7 @@ mod tests {
85 check_assist( 85 check_assist(
86 toggle_ignore, 86 toggle_ignore,
87 r#" 87 r#"
88 #[test<|>] 88 #[test$0]
89 #[ignore] 89 #[ignore]
90 fn test() {} 90 fn test() {}
91 "#, 91 "#,
diff --git a/crates/assists/src/handlers/unwrap_block.rs b/crates/assists/src/handlers/unwrap_block.rs
index 676db7137..ed6f6177d 100644
--- a/crates/assists/src/handlers/unwrap_block.rs
+++ b/crates/assists/src/handlers/unwrap_block.rs
@@ -14,7 +14,7 @@ use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, As
14// 14//
15// ``` 15// ```
16// fn foo() { 16// fn foo() {
17// if true {<|> 17// if true {$0
18// println!("foo"); 18// println!("foo");
19// } 19// }
20// } 20// }
@@ -124,7 +124,7 @@ mod tests {
124 unwrap_block, 124 unwrap_block,
125 r#" 125 r#"
126fn main() { 126fn main() {
127 <|>{ 127 $0{
128 92 128 92
129 } 129 }
130} 130}
@@ -143,7 +143,7 @@ fn main() {
143 unwrap_block, 143 unwrap_block,
144 r#" 144 r#"
145fn main() { 145fn main() {
146 <|>{ 146 $0{
147 92; 147 92;
148 } 148 }
149 () 149 ()
@@ -161,7 +161,7 @@ fn main() {
161 unwrap_block, 161 unwrap_block,
162 r#" 162 r#"
163fn main() { 163fn main() {
164 <|>{ 164 $0{
165 92 165 92
166 } 166 }
167 () 167 ()
@@ -183,7 +183,7 @@ fn main() {
183 r#" 183 r#"
184fn main() { 184fn main() {
185 bar(); 185 bar();
186 if true {<|> 186 if true {$0
187 foo(); 187 foo();
188 188
189 //comment 189 //comment
@@ -217,7 +217,7 @@ fn main() {
217 217
218 //comment 218 //comment
219 bar(); 219 bar();
220 } else {<|> 220 } else {$0
221 println!("bar"); 221 println!("bar");
222 } 222 }
223} 223}
@@ -249,7 +249,7 @@ fn main() {
249 249
250 //comment 250 //comment
251 //bar(); 251 //bar();
252 } else if false {<|> 252 } else if false {$0
253 println!("bar"); 253 println!("bar");
254 } else { 254 } else {
255 println!("foo"); 255 println!("foo");
@@ -285,7 +285,7 @@ fn main() {
285 //bar(); 285 //bar();
286 } else if false { 286 } else if false {
287 println!("bar"); 287 println!("bar");
288 } else if true {<|> 288 } else if true {$0
289 println!("foo"); 289 println!("foo");
290 } 290 }
291} 291}
@@ -323,7 +323,7 @@ fn main() {
323 println!("bar"); 323 println!("bar");
324 } else if true { 324 } else if true {
325 println!("foo"); 325 println!("foo");
326 } else {<|> 326 } else {$0
327 println!("else"); 327 println!("else");
328 } 328 }
329} 329}
@@ -361,7 +361,7 @@ fn main() {
361 //bar(); 361 //bar();
362 } else if false { 362 } else if false {
363 println!("bar"); 363 println!("bar");
364 } else if true {<|> 364 } else if true {$0
365 println!("foo"); 365 println!("foo");
366 } else { 366 } else {
367 println!("else"); 367 println!("else");
@@ -391,7 +391,7 @@ fn main() {
391 unwrap_block, 391 unwrap_block,
392 r#" 392 r#"
393fn main() { 393fn main() {
394 bar();<|> 394 bar();$0
395 if true { 395 if true {
396 foo(); 396 foo();
397 397
@@ -411,7 +411,7 @@ fn main() {
411 unwrap_block, 411 unwrap_block,
412 r#" 412 r#"
413fn main() { 413fn main() {
414 for i in 0..5 {<|> 414 for i in 0..5 {$0
415 if true { 415 if true {
416 foo(); 416 foo();
417 417
@@ -445,7 +445,7 @@ fn main() {
445 r#" 445 r#"
446fn main() { 446fn main() {
447 for i in 0..5 { 447 for i in 0..5 {
448 if true {<|> 448 if true {$0
449 foo(); 449 foo();
450 450
451 //comment 451 //comment
@@ -475,7 +475,7 @@ fn main() {
475 unwrap_block, 475 unwrap_block,
476 r#" 476 r#"
477fn main() { 477fn main() {
478 loop {<|> 478 loop {$0
479 if true { 479 if true {
480 foo(); 480 foo();
481 481
@@ -508,7 +508,7 @@ fn main() {
508 unwrap_block, 508 unwrap_block,
509 r#" 509 r#"
510fn main() { 510fn main() {
511 while true {<|> 511 while true {$0
512 if true { 512 if true {
513 foo(); 513 foo();
514 514
@@ -542,7 +542,7 @@ fn main() {
542 r#" 542 r#"
543fn main() { 543fn main() {
544 match rel_path { 544 match rel_path {
545 Ok(rel_path) => {<|> 545 Ok(rel_path) => {$0
546 let rel_path = RelativePathBuf::from_path(rel_path).ok()?; 546 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
547 Some((*id, rel_path)) 547 Some((*id, rel_path))
548 } 548 }
@@ -567,7 +567,7 @@ fn main() {
567fn main() { 567fn main() {
568 while true { 568 while true {
569 if true { 569 if true {
570 foo();<|> 570 foo();$0
571 571
572 //comment 572 //comment
573 bar(); 573 bar();
diff --git a/crates/assists/src/handlers/wrap_return_type_in_result.rs b/crates/assists/src/handlers/wrap_return_type_in_result.rs
index 59e5debb1..fec16fc49 100644
--- a/crates/assists/src/handlers/wrap_return_type_in_result.rs
+++ b/crates/assists/src/handlers/wrap_return_type_in_result.rs
@@ -13,7 +13,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
13// Wrap the function's return type into Result. 13// Wrap the function's return type into Result.
14// 14//
15// ``` 15// ```
16// fn foo() -> i32<|> { 42i32 } 16// fn foo() -> i32$0 { 42i32 }
17// ``` 17// ```
18// -> 18// ->
19// ``` 19// ```
@@ -98,7 +98,7 @@ impl TailReturnCollector {
98 } 98 }
99 99
100 // Browse tail expressions for each block 100 // Browse tail expressions for each block
101 if let Some(expr) = block_expr.expr() { 101 if let Some(expr) = block_expr.tail_expr() {
102 if let Some(last_exprs) = get_tail_expr_from_block(&expr) { 102 if let Some(last_exprs) = get_tail_expr_from_block(&expr) {
103 for last_expr in last_exprs { 103 for last_expr in last_exprs {
104 let last_expr = match last_expr { 104 let last_expr = match last_expr {
@@ -170,7 +170,7 @@ impl TailReturnCollector {
170 } 170 }
171 171
172 fn collect_tail_exprs(&mut self, block: &BlockExpr) { 172 fn collect_tail_exprs(&mut self, block: &BlockExpr) {
173 if let Some(expr) = block.expr() { 173 if let Some(expr) = block.tail_expr() {
174 self.handle_exprs(&expr, true); 174 self.handle_exprs(&expr, true);
175 self.fetch_tail_exprs(&expr); 175 self.fetch_tail_exprs(&expr);
176 } 176 }
@@ -206,7 +206,7 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
206 Expr::IfExpr(if_expr) => { 206 Expr::IfExpr(if_expr) => {
207 let mut nodes = vec![]; 207 let mut nodes = vec![];
208 for block in if_expr.blocks() { 208 for block in if_expr.blocks() {
209 if let Some(block_expr) = block.expr() { 209 if let Some(block_expr) = block.tail_expr() {
210 if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) { 210 if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) {
211 nodes.extend(tail_exprs); 211 nodes.extend(tail_exprs);
212 } 212 }
@@ -228,7 +228,7 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
228 while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) 228 while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
229 } 229 }
230 Expr::BlockExpr(block_expr) => { 230 Expr::BlockExpr(block_expr) => {
231 block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())]) 231 block_expr.tail_expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())])
232 } 232 }
233 Expr::MatchExpr(match_expr) => { 233 Expr::MatchExpr(match_expr) => {
234 let arm_list = match_expr.match_arm_list()?; 234 let arm_list = match_expr.match_arm_list()?;
@@ -282,7 +282,7 @@ mod tests {
282 check_assist( 282 check_assist(
283 wrap_return_type_in_result, 283 wrap_return_type_in_result,
284 r#" 284 r#"
285fn foo() -> i3<|>2 { 285fn foo() -> i3$02 {
286 let test = "test"; 286 let test = "test";
287 return 42i32; 287 return 42i32;
288} 288}
@@ -302,7 +302,7 @@ fn foo() -> Result<i32, ${0:_}> {
302 wrap_return_type_in_result, 302 wrap_return_type_in_result,
303 r#" 303 r#"
304fn foo() { 304fn foo() {
305 || -> i32<|> { 305 || -> i32$0 {
306 let test = "test"; 306 let test = "test";
307 return 42i32; 307 return 42i32;
308 }; 308 };
@@ -325,7 +325,7 @@ fn foo() {
325 wrap_return_type_in_result, 325 wrap_return_type_in_result,
326 r#" 326 r#"
327fn foo() -> i32 { 327fn foo() -> i32 {
328 let test = "test";<|> 328 let test = "test";$0
329 return 42i32; 329 return 42i32;
330} 330}
331"#, 331"#,
@@ -339,7 +339,7 @@ fn foo() -> i32 {
339 r#" 339 r#"
340fn foo() { 340fn foo() {
341 || -> i32 { 341 || -> i32 {
342 let test = "test";<|> 342 let test = "test";$0
343 return 42i32; 343 return 42i32;
344 }; 344 };
345} 345}
@@ -349,7 +349,7 @@ fn foo() {
349 349
350 #[test] 350 #[test]
351 fn wrap_return_type_in_result_closure_non_block() { 351 fn wrap_return_type_in_result_closure_non_block() {
352 check_assist_not_applicable(wrap_return_type_in_result, r#"fn foo() { || -> i<|>32 3; }"#); 352 check_assist_not_applicable(wrap_return_type_in_result, r#"fn foo() { || -> i$032 3; }"#);
353 } 353 }
354 354
355 #[test] 355 #[test]
@@ -357,7 +357,7 @@ fn foo() {
357 check_assist_not_applicable( 357 check_assist_not_applicable(
358 wrap_return_type_in_result, 358 wrap_return_type_in_result,
359 r#" 359 r#"
360fn foo() -> std::result::Result<i32<|>, String> { 360fn foo() -> std::result::Result<i32$0, String> {
361 let test = "test"; 361 let test = "test";
362 return 42i32; 362 return 42i32;
363} 363}
@@ -371,7 +371,7 @@ fn foo() -> std::result::Result<i32<|>, String> {
371 check_assist_not_applicable( 371 check_assist_not_applicable(
372 wrap_return_type_in_result, 372 wrap_return_type_in_result,
373 r#" 373 r#"
374fn foo() -> Result<i32<|>, String> { 374fn foo() -> Result<i32$0, String> {
375 let test = "test"; 375 let test = "test";
376 return 42i32; 376 return 42i32;
377} 377}
@@ -385,7 +385,7 @@ fn foo() -> Result<i32<|>, String> {
385 wrap_return_type_in_result, 385 wrap_return_type_in_result,
386 r#" 386 r#"
387fn foo() { 387fn foo() {
388 || -> Result<i32<|>, String> { 388 || -> Result<i32$0, String> {
389 let test = "test"; 389 let test = "test";
390 return 42i32; 390 return 42i32;
391 }; 391 };
@@ -399,7 +399,7 @@ fn foo() {
399 check_assist( 399 check_assist(
400 wrap_return_type_in_result, 400 wrap_return_type_in_result,
401 r#" 401 r#"
402fn foo() -> <|>i32 { 402fn foo() -> $0i32 {
403 let test = "test"; 403 let test = "test";
404 return 42i32; 404 return 42i32;
405} 405}
@@ -418,7 +418,7 @@ fn foo() -> Result<i32, ${0:_}> {
418 check_assist( 418 check_assist(
419 wrap_return_type_in_result, 419 wrap_return_type_in_result,
420 r#" 420 r#"
421fn foo() -><|> i32 { 421fn foo() ->$0 i32 {
422 let test = "test"; 422 let test = "test";
423 42i32 423 42i32
424} 424}
@@ -438,7 +438,7 @@ fn foo() -> Result<i32, ${0:_}> {
438 wrap_return_type_in_result, 438 wrap_return_type_in_result,
439 r#" 439 r#"
440fn foo() { 440fn foo() {
441 || -><|> i32 { 441 || ->$0 i32 {
442 let test = "test"; 442 let test = "test";
443 42i32 443 42i32
444 }; 444 };
@@ -459,7 +459,7 @@ fn foo() {
459 fn wrap_return_type_in_result_simple_with_tail_only() { 459 fn wrap_return_type_in_result_simple_with_tail_only() {
460 check_assist( 460 check_assist(
461 wrap_return_type_in_result, 461 wrap_return_type_in_result,
462 r#"fn foo() -> i32<|> { 42i32 }"#, 462 r#"fn foo() -> i32$0 { 42i32 }"#,
463 r#"fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }"#, 463 r#"fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }"#,
464 ); 464 );
465 } 465 }
@@ -469,7 +469,7 @@ fn foo() {
469 check_assist( 469 check_assist(
470 wrap_return_type_in_result, 470 wrap_return_type_in_result,
471 r#" 471 r#"
472fn foo() -> i32<|> { 472fn foo() -> i32$0 {
473 if true { 473 if true {
474 42i32 474 42i32
475 } else { 475 } else {
@@ -495,7 +495,7 @@ fn foo() -> Result<i32, ${0:_}> {
495 wrap_return_type_in_result, 495 wrap_return_type_in_result,
496 r#" 496 r#"
497fn foo() { 497fn foo() {
498 || -> i32<|> { 498 || -> i32$0 {
499 if true { 499 if true {
500 42i32 500 42i32
501 } else { 501 } else {
@@ -523,7 +523,7 @@ fn foo() {
523 check_assist( 523 check_assist(
524 wrap_return_type_in_result, 524 wrap_return_type_in_result,
525 r#" 525 r#"
526fn foo() -> i32<|> { 526fn foo() -> i32$0 {
527 if true { 527 if true {
528 if false { 528 if false {
529 1 529 1
@@ -556,7 +556,7 @@ fn foo() -> Result<i32, ${0:_}> {
556 check_assist( 556 check_assist(
557 wrap_return_type_in_result, 557 wrap_return_type_in_result,
558 r#" 558 r#"
559async fn foo() -> i<|>32 { 559async fn foo() -> i$032 {
560 if true { 560 if true {
561 if false { 561 if false {
562 1.await 562 1.await
@@ -588,7 +588,7 @@ async fn foo() -> Result<i32, ${0:_}> {
588 fn wrap_return_type_in_result_simple_with_array() { 588 fn wrap_return_type_in_result_simple_with_array() {
589 check_assist( 589 check_assist(
590 wrap_return_type_in_result, 590 wrap_return_type_in_result,
591 r#"fn foo() -> [i32;<|> 3] { [1, 2, 3] }"#, 591 r#"fn foo() -> [i32;$0 3] { [1, 2, 3] }"#,
592 r#"fn foo() -> Result<[i32; 3], ${0:_}> { Ok([1, 2, 3]) }"#, 592 r#"fn foo() -> Result<[i32; 3], ${0:_}> { Ok([1, 2, 3]) }"#,
593 ); 593 );
594 } 594 }
@@ -598,7 +598,7 @@ async fn foo() -> Result<i32, ${0:_}> {
598 check_assist( 598 check_assist(
599 wrap_return_type_in_result, 599 wrap_return_type_in_result,
600 r#" 600 r#"
601fn foo() -<|>> i32 { 601fn foo() -$0> i32 {
602 if true { 602 if true {
603 if false { 603 if false {
604 1 as i32 604 1 as i32
@@ -631,7 +631,7 @@ fn foo() -> Result<i32, ${0:_}> {
631 check_assist( 631 check_assist(
632 wrap_return_type_in_result, 632 wrap_return_type_in_result,
633 r#" 633 r#"
634fn foo() -> i32<|> { 634fn foo() -> i32$0 {
635 let my_var = 5; 635 let my_var = 5;
636 match my_var { 636 match my_var {
637 5 => 42i32, 637 5 => 42i32,
@@ -656,7 +656,7 @@ fn foo() -> Result<i32, ${0:_}> {
656 check_assist( 656 check_assist(
657 wrap_return_type_in_result, 657 wrap_return_type_in_result,
658 r#" 658 r#"
659fn foo() -> i32<|> { 659fn foo() -> i32$0 {
660 let my_var = 5; 660 let my_var = 5;
661 loop { 661 loop {
662 println!("test"); 662 println!("test");
@@ -683,7 +683,7 @@ fn foo() -> Result<i32, ${0:_}> {
683 check_assist( 683 check_assist(
684 wrap_return_type_in_result, 684 wrap_return_type_in_result,
685 r#" 685 r#"
686fn foo() -> i32<|> { 686fn foo() -> i32$0 {
687 let my_var = let x = loop { 687 let my_var = let x = loop {
688 break 1; 688 break 1;
689 }; 689 };
@@ -706,7 +706,7 @@ fn foo() -> Result<i32, ${0:_}> {
706 check_assist( 706 check_assist(
707 wrap_return_type_in_result, 707 wrap_return_type_in_result,
708 r#" 708 r#"
709fn foo() -> i32<|> { 709fn foo() -> i32$0 {
710 let my_var = 5; 710 let my_var = 5;
711 let res = match my_var { 711 let res = match my_var {
712 5 => 42i32, 712 5 => 42i32,
@@ -730,7 +730,7 @@ fn foo() -> Result<i32, ${0:_}> {
730 check_assist( 730 check_assist(
731 wrap_return_type_in_result, 731 wrap_return_type_in_result,
732 r#" 732 r#"
733fn foo() -> i32<|> { 733fn foo() -> i32$0 {
734 let my_var = 5; 734 let my_var = 5;
735 let res = if my_var == 5 { 735 let res = if my_var == 5 {
736 42i32 736 42i32
@@ -759,7 +759,7 @@ fn foo() -> Result<i32, ${0:_}> {
759 check_assist( 759 check_assist(
760 wrap_return_type_in_result, 760 wrap_return_type_in_result,
761 r#" 761 r#"
762fn foo() -> i32<|> { 762fn foo() -> i32$0 {
763 let my_var = 5; 763 let my_var = 5;
764 match my_var { 764 match my_var {
765 5 => { 765 5 => {
@@ -808,7 +808,7 @@ fn foo() -> Result<i32, ${0:_}> {
808 check_assist( 808 check_assist(
809 wrap_return_type_in_result, 809 wrap_return_type_in_result,
810 r#" 810 r#"
811fn foo() -> i<|>32 { 811fn foo() -> i$032 {
812 let test = "test"; 812 let test = "test";
813 if test == "test" { 813 if test == "test" {
814 return 24i32; 814 return 24i32;
@@ -833,7 +833,7 @@ fn foo() -> Result<i32, ${0:_}> {
833 check_assist( 833 check_assist(
834 wrap_return_type_in_result, 834 wrap_return_type_in_result,
835 r#" 835 r#"
836fn foo(the_field: u32) -><|> u32 { 836fn foo(the_field: u32) ->$0 u32 {
837 let true_closure = || { return true; }; 837 let true_closure = || { return true; };
838 if the_field < 5 { 838 if the_field < 5 {
839 let mut i = 0; 839 let mut i = 0;
@@ -865,7 +865,7 @@ fn foo(the_field: u32) -> Result<u32, ${0:_}> {
865 check_assist( 865 check_assist(
866 wrap_return_type_in_result, 866 wrap_return_type_in_result,
867 r#" 867 r#"
868 fn foo(the_field: u32) -> u32<|> { 868 fn foo(the_field: u32) -> u32$0 {
869 let true_closure = || { 869 let true_closure = || {
870 return true; 870 return true;
871 }; 871 };
@@ -912,7 +912,7 @@ fn foo(the_field: u32) -> Result<u32, ${0:_}> {
912 check_assist( 912 check_assist(
913 wrap_return_type_in_result, 913 wrap_return_type_in_result,
914 r#" 914 r#"
915fn foo() -> i32<|> { 915fn foo() -> i32$0 {
916 let test = "test"; 916 let test = "test";
917 if test == "test" { 917 if test == "test" {
918 return 24i32; 918 return 24i32;
@@ -946,7 +946,7 @@ fn foo() -> Result<i32, ${0:_}> {
946 check_assist( 946 check_assist(
947 wrap_return_type_in_result, 947 wrap_return_type_in_result,
948 r#" 948 r#"
949fn foo() -> i32<|> { 949fn foo() -> i32$0 {
950 let test = "test"; 950 let test = "test";
951 if test == "test" { 951 if test == "test" {
952 return 24i32; 952 return 24i32;
@@ -984,7 +984,7 @@ fn foo() -> Result<i32, ${0:_}> {
984 check_assist( 984 check_assist(
985 wrap_return_type_in_result, 985 wrap_return_type_in_result,
986 r#" 986 r#"
987fn foo() -> i3<|>2 { 987fn foo() -> i3$02 {
988 let test = "test"; 988 let test = "test";
989 let other = 5; 989 let other = 5;
990 if test == "test" { 990 if test == "test" {
@@ -1030,7 +1030,7 @@ fn foo() -> Result<i32, ${0:_}> {
1030 check_assist( 1030 check_assist(
1031 wrap_return_type_in_result, 1031 wrap_return_type_in_result,
1032 r#" 1032 r#"
1033fn foo(the_field: u32) -> u32<|> { 1033fn foo(the_field: u32) -> u32$0 {
1034 if the_field < 5 { 1034 if the_field < 5 {
1035 let mut i = 0; 1035 let mut i = 0;
1036 loop { 1036 loop {
@@ -1070,7 +1070,7 @@ fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1070 check_assist( 1070 check_assist(
1071 wrap_return_type_in_result, 1071 wrap_return_type_in_result,
1072 r#" 1072 r#"
1073fn foo(the_field: u32) -> u3<|>2 { 1073fn foo(the_field: u32) -> u3$02 {
1074 if the_field < 5 { 1074 if the_field < 5 {
1075 let mut i = 0; 1075 let mut i = 0;
1076 match i { 1076 match i {
@@ -1098,7 +1098,7 @@ fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1098 check_assist( 1098 check_assist(
1099 wrap_return_type_in_result, 1099 wrap_return_type_in_result,
1100 r#" 1100 r#"
1101fn foo(the_field: u32) -> u32<|> { 1101fn foo(the_field: u32) -> u32$0 {
1102 if the_field < 5 { 1102 if the_field < 5 {
1103 let mut i = 0; 1103 let mut i = 0;
1104 if i == 5 { 1104 if i == 5 {
@@ -1128,7 +1128,7 @@ fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1128 check_assist( 1128 check_assist(
1129 wrap_return_type_in_result, 1129 wrap_return_type_in_result,
1130 r#" 1130 r#"
1131fn foo(the_field: u32) -> <|>u32 { 1131fn foo(the_field: u32) -> $0u32 {
1132 if the_field < 5 { 1132 if the_field < 5 {
1133 let mut i = 0; 1133 let mut i = 0;
1134 if i == 5 { 1134 if i == 5 {
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index fdec886e9..1080294ab 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -24,7 +24,7 @@ use syntax::TextRange;
24 24
25pub(crate) use crate::assist_context::{AssistContext, Assists}; 25pub(crate) use crate::assist_context::{AssistContext, Assists};
26 26
27pub use assist_config::AssistConfig; 27pub use assist_config::{AssistConfig, InsertUseConfig};
28 28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)] 29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum AssistKind { 30pub enum AssistKind {
@@ -116,7 +116,6 @@ mod handlers {
116 mod convert_integer_literal; 116 mod convert_integer_literal;
117 mod early_return; 117 mod early_return;
118 mod expand_glob_import; 118 mod expand_glob_import;
119 mod extract_module_to_file;
120 mod extract_struct_from_enum_variant; 119 mod extract_struct_from_enum_variant;
121 mod extract_variable; 120 mod extract_variable;
122 mod fill_match_arms; 121 mod fill_match_arms;
@@ -124,13 +123,14 @@ mod handlers {
124 mod flip_binexpr; 123 mod flip_binexpr;
125 mod flip_comma; 124 mod flip_comma;
126 mod flip_trait_bound; 125 mod flip_trait_bound;
127 mod generate_derive;
128 mod generate_default_from_enum_variant; 126 mod generate_default_from_enum_variant;
127 mod generate_derive;
129 mod generate_from_impl_for_enum; 128 mod generate_from_impl_for_enum;
130 mod generate_function; 129 mod generate_function;
131 mod generate_impl; 130 mod generate_impl;
132 mod generate_new; 131 mod generate_new;
133 mod infer_function_return_type; 132 mod infer_function_return_type;
133 mod inline_function;
134 mod inline_local_variable; 134 mod inline_local_variable;
135 mod introduce_named_lifetime; 135 mod introduce_named_lifetime;
136 mod invert_if; 136 mod invert_if;
@@ -138,12 +138,15 @@ mod handlers {
138 mod merge_match_arms; 138 mod merge_match_arms;
139 mod move_bounds; 139 mod move_bounds;
140 mod move_guard; 140 mod move_guard;
141 mod move_module_to_file;
142 mod pull_assignment_up;
141 mod qualify_path; 143 mod qualify_path;
142 mod raw_string; 144 mod raw_string;
143 mod remove_dbg; 145 mod remove_dbg;
144 mod remove_mut; 146 mod remove_mut;
145 mod remove_unused_param; 147 mod remove_unused_param;
146 mod reorder_fields; 148 mod reorder_fields;
149 mod reorder_impl;
147 mod replace_derive_with_manual_impl; 150 mod replace_derive_with_manual_impl;
148 mod replace_if_let_with_match; 151 mod replace_if_let_with_match;
149 mod replace_impl_trait_with_generic; 152 mod replace_impl_trait_with_generic;
@@ -167,7 +170,7 @@ mod handlers {
167 convert_integer_literal::convert_integer_literal, 170 convert_integer_literal::convert_integer_literal,
168 early_return::convert_to_guarded_return, 171 early_return::convert_to_guarded_return,
169 expand_glob_import::expand_glob_import, 172 expand_glob_import::expand_glob_import,
170 extract_module_to_file::extract_module_to_file, 173 move_module_to_file::move_module_to_file,
171 extract_struct_from_enum_variant::extract_struct_from_enum_variant, 174 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
172 extract_variable::extract_variable, 175 extract_variable::extract_variable,
173 fill_match_arms::fill_match_arms, 176 fill_match_arms::fill_match_arms,
@@ -175,13 +178,14 @@ mod handlers {
175 flip_binexpr::flip_binexpr, 178 flip_binexpr::flip_binexpr,
176 flip_comma::flip_comma, 179 flip_comma::flip_comma,
177 flip_trait_bound::flip_trait_bound, 180 flip_trait_bound::flip_trait_bound,
178 generate_derive::generate_derive,
179 generate_default_from_enum_variant::generate_default_from_enum_variant, 181 generate_default_from_enum_variant::generate_default_from_enum_variant,
182 generate_derive::generate_derive,
180 generate_from_impl_for_enum::generate_from_impl_for_enum, 183 generate_from_impl_for_enum::generate_from_impl_for_enum,
181 generate_function::generate_function, 184 generate_function::generate_function,
182 generate_impl::generate_impl, 185 generate_impl::generate_impl,
183 generate_new::generate_new, 186 generate_new::generate_new,
184 infer_function_return_type::infer_function_return_type, 187 infer_function_return_type::infer_function_return_type,
188 inline_function::inline_function,
185 inline_local_variable::inline_local_variable, 189 inline_local_variable::inline_local_variable,
186 introduce_named_lifetime::introduce_named_lifetime, 190 introduce_named_lifetime::introduce_named_lifetime,
187 invert_if::invert_if, 191 invert_if::invert_if,
@@ -190,6 +194,7 @@ mod handlers {
190 move_bounds::move_bounds_to_where_clause, 194 move_bounds::move_bounds_to_where_clause,
191 move_guard::move_arm_cond_to_match_guard, 195 move_guard::move_arm_cond_to_match_guard,
192 move_guard::move_guard_to_arm_body, 196 move_guard::move_guard_to_arm_body,
197 pull_assignment_up::pull_assignment_up,
193 qualify_path::qualify_path, 198 qualify_path::qualify_path,
194 raw_string::add_hash, 199 raw_string::add_hash,
195 raw_string::make_usual_string, 200 raw_string::make_usual_string,
@@ -198,6 +203,7 @@ mod handlers {
198 remove_mut::remove_mut, 203 remove_mut::remove_mut,
199 remove_unused_param::remove_unused_param, 204 remove_unused_param::remove_unused_param,
200 reorder_fields::reorder_fields, 205 reorder_fields::reorder_fields,
206 reorder_impl::reorder_impl,
201 replace_derive_with_manual_impl::replace_derive_with_manual_impl, 207 replace_derive_with_manual_impl::replace_derive_with_manual_impl,
202 replace_if_let_with_match::replace_if_let_with_match, 208 replace_if_let_with_match::replace_if_let_with_match,
203 replace_if_let_with_match::replace_match_with_if_let, 209 replace_if_let_with_match::replace_match_with_if_let,
diff --git a/crates/assists/src/tests.rs b/crates/assists/src/tests.rs
index 21e448fb8..fef29a0b8 100644
--- a/crates/assists/src/tests.rs
+++ b/crates/assists/src/tests.rs
@@ -1,15 +1,29 @@
1mod generated; 1mod generated;
2 2
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; 4use ide_db::{
5use ide_db::source_change::FileSystemEdit; 5 base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt},
6use ide_db::RootDatabase; 6 helpers::{insert_use::MergeBehavior, SnippetCap},
7 source_change::FileSystemEdit,
8 RootDatabase,
9};
7use syntax::TextRange; 10use syntax::TextRange;
8use test_utils::{assert_eq_text, extract_offset, extract_range}; 11use test_utils::{assert_eq_text, extract_offset, extract_range};
9 12
10use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; 13use crate::{
14 handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists, InsertUseConfig,
15};
11use stdx::{format_to, trim_indent}; 16use stdx::{format_to, trim_indent};
12 17
18pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
19 snippet_cap: SnippetCap::new(true),
20 allowed: None,
21 insert_use: InsertUseConfig {
22 merge: Some(MergeBehavior::Full),
23 prefix_kind: hir::PrefixKind::Plain,
24 },
25};
26
13pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { 27pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
14 RootDatabase::with_single_file(text) 28 RootDatabase::with_single_file(text)
15} 29}
@@ -48,14 +62,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
48 let before = db.file_text(file_id).to_string(); 62 let before = db.file_text(file_id).to_string();
49 let frange = FileRange { file_id, range: selection.into() }; 63 let frange = FileRange { file_id, range: selection.into() };
50 64
51 let assist = Assist::get(&db, &AssistConfig::default(), true, frange) 65 let assist = Assist::get(&db, &TEST_CONFIG, true, frange)
52 .into_iter() 66 .into_iter()
53 .find(|assist| assist.id.0 == assist_id) 67 .find(|assist| assist.id.0 == assist_id)
54 .unwrap_or_else(|| { 68 .unwrap_or_else(|| {
55 panic!( 69 panic!(
56 "\n\nAssist is not applicable: {}\nAvailable assists: {}", 70 "\n\nAssist is not applicable: {}\nAvailable assists: {}",
57 assist_id, 71 assist_id,
58 Assist::get(&db, &AssistConfig::default(), false, frange) 72 Assist::get(&db, &TEST_CONFIG, false, frange)
59 .into_iter() 73 .into_iter()
60 .map(|assist| assist.id.0) 74 .map(|assist| assist.id.0)
61 .collect::<Vec<_>>() 75 .collect::<Vec<_>>()
@@ -89,7 +103,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
89 let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() }; 103 let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
90 104
91 let sema = Semantics::new(&db); 105 let sema = Semantics::new(&db);
92 let config = AssistConfig::default(); 106 let config = TEST_CONFIG;
93 let ctx = AssistContext::new(sema, &config, frange); 107 let ctx = AssistContext::new(sema, &config, frange);
94 let mut acc = Assists::new(&ctx, true); 108 let mut acc = Assists::new(&ctx, true);
95 handler(&mut acc, &ctx); 109 handler(&mut acc, &ctx);
@@ -152,11 +166,11 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
152 166
153#[test] 167#[test]
154fn assist_order_field_struct() { 168fn assist_order_field_struct() {
155 let before = "struct Foo { <|>bar: u32 }"; 169 let before = "struct Foo { $0bar: u32 }";
156 let (before_cursor_pos, before) = extract_offset(before); 170 let (before_cursor_pos, before) = extract_offset(before);
157 let (db, file_id) = with_single_file(&before); 171 let (db, file_id) = with_single_file(&before);
158 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; 172 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
159 let assists = Assist::get(&db, &AssistConfig::default(), false, frange); 173 let assists = Assist::get(&db, &TEST_CONFIG, false, frange);
160 let mut assists = assists.iter(); 174 let mut assists = assists.iter();
161 175
162 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); 176 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
@@ -167,7 +181,7 @@ fn assist_order_field_struct() {
167fn assist_order_if_expr() { 181fn assist_order_if_expr() {
168 let before = " 182 let before = "
169 pub fn test_some_range(a: int) -> bool { 183 pub fn test_some_range(a: int) -> bool {
170 if let 2..6 = <|>5<|> { 184 if let 2..6 = $05$0 {
171 true 185 true
172 } else { 186 } else {
173 false 187 false
@@ -176,7 +190,7 @@ fn assist_order_if_expr() {
176 let (range, before) = extract_range(before); 190 let (range, before) = extract_range(before);
177 let (db, file_id) = with_single_file(&before); 191 let (db, file_id) = with_single_file(&before);
178 let frange = FileRange { file_id, range }; 192 let frange = FileRange { file_id, range };
179 let assists = Assist::get(&db, &AssistConfig::default(), false, frange); 193 let assists = Assist::get(&db, &TEST_CONFIG, false, frange);
180 let mut assists = assists.iter(); 194 let mut assists = assists.iter();
181 195
182 assert_eq!(assists.next().expect("expected assist").label, "Extract into variable"); 196 assert_eq!(assists.next().expect("expected assist").label, "Extract into variable");
@@ -187,7 +201,7 @@ fn assist_order_if_expr() {
187fn assist_filter_works() { 201fn assist_filter_works() {
188 let before = " 202 let before = "
189 pub fn test_some_range(a: int) -> bool { 203 pub fn test_some_range(a: int) -> bool {
190 if let 2..6 = <|>5<|> { 204 if let 2..6 = $05$0 {
191 true 205 true
192 } else { 206 } else {
193 false 207 false
@@ -198,7 +212,7 @@ fn assist_filter_works() {
198 let frange = FileRange { file_id, range }; 212 let frange = FileRange { file_id, range };
199 213
200 { 214 {
201 let mut cfg = AssistConfig::default(); 215 let mut cfg = TEST_CONFIG;
202 cfg.allowed = Some(vec![AssistKind::Refactor]); 216 cfg.allowed = Some(vec![AssistKind::Refactor]);
203 217
204 let assists = Assist::get(&db, &cfg, false, frange); 218 let assists = Assist::get(&db, &cfg, false, frange);
@@ -209,7 +223,7 @@ fn assist_filter_works() {
209 } 223 }
210 224
211 { 225 {
212 let mut cfg = AssistConfig::default(); 226 let mut cfg = TEST_CONFIG;
213 cfg.allowed = Some(vec![AssistKind::RefactorExtract]); 227 cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
214 let assists = Assist::get(&db, &cfg, false, frange); 228 let assists = Assist::get(&db, &cfg, false, frange);
215 assert_eq!(assists.len(), 1); 229 assert_eq!(assists.len(), 1);
@@ -219,7 +233,7 @@ fn assist_filter_works() {
219 } 233 }
220 234
221 { 235 {
222 let mut cfg = AssistConfig::default(); 236 let mut cfg = TEST_CONFIG;
223 cfg.allowed = Some(vec![AssistKind::QuickFix]); 237 cfg.allowed = Some(vec![AssistKind::QuickFix]);
224 let assists = Assist::get(&db, &cfg, false, frange); 238 let assists = Assist::get(&db, &cfg, false, frange);
225 assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out"); 239 assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out");
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index d3dfe24e7..217f577eb 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -8,7 +8,7 @@ fn doctest_add_explicit_type() {
8 "add_explicit_type", 8 "add_explicit_type",
9 r#####" 9 r#####"
10fn main() { 10fn main() {
11 let x<|> = 92; 11 let x$0 = 92;
12} 12}
13"#####, 13"#####,
14 r#####" 14 r#####"
@@ -25,7 +25,7 @@ fn doctest_add_hash() {
25 "add_hash", 25 "add_hash",
26 r#####" 26 r#####"
27fn main() { 27fn main() {
28 r#"Hello,<|> World!"#; 28 r#"Hello,$0 World!"#;
29} 29}
30"#####, 30"#####,
31 r#####" 31 r#####"
@@ -49,7 +49,7 @@ trait Trait {
49 49
50impl Trait for () { 50impl Trait for () {
51 type X = (); 51 type X = ();
52 fn foo(&self) {}<|> 52 fn foo(&self) {}$0
53 53
54} 54}
55"#####, 55"#####,
@@ -81,7 +81,7 @@ trait Trait<T> {
81 fn bar(&self) {} 81 fn bar(&self) {}
82} 82}
83 83
84impl Trait<u32> for () {<|> 84impl Trait<u32> for () {$0
85 85
86} 86}
87"#####, 87"#####,
@@ -110,7 +110,7 @@ fn doctest_add_turbo_fish() {
110 r#####" 110 r#####"
111fn make<T>() -> T { todo!() } 111fn make<T>() -> T { todo!() }
112fn main() { 112fn main() {
113 let x = make<|>(); 113 let x = make$0();
114} 114}
115"#####, 115"#####,
116 r#####" 116 r#####"
@@ -128,7 +128,7 @@ fn doctest_apply_demorgan() {
128 "apply_demorgan", 128 "apply_demorgan",
129 r#####" 129 r#####"
130fn main() { 130fn main() {
131 if x != 4 ||<|> !y {} 131 if x != 4 ||$0 !y {}
132} 132}
133"#####, 133"#####,
134 r#####" 134 r#####"
@@ -145,7 +145,7 @@ fn doctest_auto_import() {
145 "auto_import", 145 "auto_import",
146 r#####" 146 r#####"
147fn main() { 147fn main() {
148 let map = HashMap<|>::new(); 148 let map = HashMap$0::new();
149} 149}
150pub mod std { pub mod collections { pub struct HashMap { } } } 150pub mod std { pub mod collections { pub struct HashMap { } } }
151"#####, 151"#####,
@@ -165,7 +165,7 @@ fn doctest_change_visibility() {
165 check_doc_test( 165 check_doc_test(
166 "change_visibility", 166 "change_visibility",
167 r#####" 167 r#####"
168<|>fn frobnicate() {} 168$0fn frobnicate() {}
169"#####, 169"#####,
170 r#####" 170 r#####"
171pub(crate) fn frobnicate() {} 171pub(crate) fn frobnicate() {}
@@ -178,7 +178,7 @@ fn doctest_convert_integer_literal() {
178 check_doc_test( 178 check_doc_test(
179 "convert_integer_literal", 179 "convert_integer_literal",
180 r#####" 180 r#####"
181const _: i32 = 10<|>; 181const _: i32 = 10$0;
182"#####, 182"#####,
183 r#####" 183 r#####"
184const _: i32 = 0b1010; 184const _: i32 = 0b1010;
@@ -192,7 +192,7 @@ fn doctest_convert_to_guarded_return() {
192 "convert_to_guarded_return", 192 "convert_to_guarded_return",
193 r#####" 193 r#####"
194fn main() { 194fn main() {
195 <|>if cond { 195 $0if cond {
196 foo(); 196 foo();
197 bar(); 197 bar();
198 } 198 }
@@ -220,7 +220,7 @@ mod foo {
220 pub struct Baz; 220 pub struct Baz;
221} 221}
222 222
223use foo::*<|>; 223use foo::*$0;
224 224
225fn qux(bar: Bar, baz: Baz) {} 225fn qux(bar: Bar, baz: Baz) {}
226"#####, 226"#####,
@@ -238,26 +238,11 @@ fn qux(bar: Bar, baz: Baz) {}
238} 238}
239 239
240#[test] 240#[test]
241fn doctest_extract_module_to_file() {
242 check_doc_test(
243 "extract_module_to_file",
244 r#####"
245mod foo {<|>
246 fn t() {}
247}
248"#####,
249 r#####"
250mod foo;
251"#####,
252 )
253}
254
255#[test]
256fn doctest_extract_struct_from_enum_variant() { 241fn doctest_extract_struct_from_enum_variant() {
257 check_doc_test( 242 check_doc_test(
258 "extract_struct_from_enum_variant", 243 "extract_struct_from_enum_variant",
259 r#####" 244 r#####"
260enum A { <|>One(u32, u32) } 245enum A { $0One(u32, u32) }
261"#####, 246"#####,
262 r#####" 247 r#####"
263struct One(pub u32, pub u32); 248struct One(pub u32, pub u32);
@@ -273,7 +258,7 @@ fn doctest_extract_variable() {
273 "extract_variable", 258 "extract_variable",
274 r#####" 259 r#####"
275fn main() { 260fn main() {
276 <|>(1 + 2)<|> * 4; 261 $0(1 + 2)$0 * 4;
277} 262}
278"#####, 263"#####,
279 r#####" 264 r#####"
@@ -294,7 +279,7 @@ enum Action { Move { distance: u32 }, Stop }
294 279
295fn handle(action: Action) { 280fn handle(action: Action) {
296 match action { 281 match action {
297 <|> 282 $0
298 } 283 }
299} 284}
300"#####, 285"#####,
@@ -320,7 +305,7 @@ mod m {
320 fn frobnicate() {} 305 fn frobnicate() {}
321} 306}
322fn main() { 307fn main() {
323 m::frobnicate<|>() {} 308 m::frobnicate$0() {}
324} 309}
325"#####, 310"#####,
326 r#####" 311 r#####"
@@ -340,7 +325,7 @@ fn doctest_flip_binexpr() {
340 "flip_binexpr", 325 "flip_binexpr",
341 r#####" 326 r#####"
342fn main() { 327fn main() {
343 let _ = 90 +<|> 2; 328 let _ = 90 +$0 2;
344} 329}
345"#####, 330"#####,
346 r#####" 331 r#####"
@@ -357,7 +342,7 @@ fn doctest_flip_comma() {
357 "flip_comma", 342 "flip_comma",
358 r#####" 343 r#####"
359fn main() { 344fn main() {
360 ((1, 2),<|> (3, 4)); 345 ((1, 2),$0 (3, 4));
361} 346}
362"#####, 347"#####,
363 r#####" 348 r#####"
@@ -373,7 +358,7 @@ fn doctest_flip_trait_bound() {
373 check_doc_test( 358 check_doc_test(
374 "flip_trait_bound", 359 "flip_trait_bound",
375 r#####" 360 r#####"
376fn foo<T: Clone +<|> Copy>() { } 361fn foo<T: Clone +$0 Copy>() { }
377"#####, 362"#####,
378 r#####" 363 r#####"
379fn foo<T: Copy + Clone>() { } 364fn foo<T: Copy + Clone>() { }
@@ -388,7 +373,7 @@ fn doctest_generate_default_from_enum_variant() {
388 r#####" 373 r#####"
389enum Version { 374enum Version {
390 Undefined, 375 Undefined,
391 Minor<|>, 376 Minor$0,
392 Major, 377 Major,
393} 378}
394"#####, 379"#####,
@@ -415,7 +400,7 @@ fn doctest_generate_derive() {
415 r#####" 400 r#####"
416struct Point { 401struct Point {
417 x: u32, 402 x: u32,
418 y: u32,<|> 403 y: u32,$0
419} 404}
420"#####, 405"#####,
421 r#####" 406 r#####"
@@ -433,7 +418,7 @@ fn doctest_generate_from_impl_for_enum() {
433 check_doc_test( 418 check_doc_test(
434 "generate_from_impl_for_enum", 419 "generate_from_impl_for_enum",
435 r#####" 420 r#####"
436enum A { <|>One(u32) } 421enum A { $0One(u32) }
437"#####, 422"#####,
438 r#####" 423 r#####"
439enum A { One(u32) } 424enum A { One(u32) }
@@ -455,7 +440,7 @@ fn doctest_generate_function() {
455struct Baz; 440struct Baz;
456fn baz() -> Baz { Baz } 441fn baz() -> Baz { Baz }
457fn foo() { 442fn foo() {
458 bar<|>("", baz()); 443 bar$0("", baz());
459} 444}
460 445
461"#####, 446"#####,
@@ -480,7 +465,7 @@ fn doctest_generate_impl() {
480 "generate_impl", 465 "generate_impl",
481 r#####" 466 r#####"
482struct Ctx<T: Clone> { 467struct Ctx<T: Clone> {
483 data: T,<|> 468 data: T,$0
484} 469}
485"#####, 470"#####,
486 r#####" 471 r#####"
@@ -501,7 +486,7 @@ fn doctest_generate_new() {
501 "generate_new", 486 "generate_new",
502 r#####" 487 r#####"
503struct Ctx<T: Clone> { 488struct Ctx<T: Clone> {
504 data: T,<|> 489 data: T,$0
505} 490}
506"#####, 491"#####,
507 r#####" 492 r#####"
@@ -522,7 +507,7 @@ fn doctest_infer_function_return_type() {
522 check_doc_test( 507 check_doc_test(
523 "infer_function_return_type", 508 "infer_function_return_type",
524 r#####" 509 r#####"
525fn foo() { 4<|>2i32 } 510fn foo() { 4$02i32 }
526"#####, 511"#####,
527 r#####" 512 r#####"
528fn foo() -> i32 { 42i32 } 513fn foo() -> i32 { 42i32 }
@@ -531,12 +516,35 @@ fn foo() -> i32 { 42i32 }
531} 516}
532 517
533#[test] 518#[test]
519fn doctest_inline_function() {
520 check_doc_test(
521 "inline_function",
522 r#####"
523fn add(a: u32, b: u32) -> u32 { a + b }
524fn main() {
525 let x = add$0(1, 2);
526}
527"#####,
528 r#####"
529fn add(a: u32, b: u32) -> u32 { a + b }
530fn main() {
531 let x = {
532 let a = 1;
533 let b = 2;
534 a + b
535 };
536}
537"#####,
538 )
539}
540
541#[test]
534fn doctest_inline_local_variable() { 542fn doctest_inline_local_variable() {
535 check_doc_test( 543 check_doc_test(
536 "inline_local_variable", 544 "inline_local_variable",
537 r#####" 545 r#####"
538fn main() { 546fn main() {
539 let x<|> = 1 + 2; 547 let x$0 = 1 + 2;
540 x * 4; 548 x * 4;
541} 549}
542"#####, 550"#####,
@@ -553,7 +561,7 @@ fn doctest_introduce_named_lifetime() {
553 check_doc_test( 561 check_doc_test(
554 "introduce_named_lifetime", 562 "introduce_named_lifetime",
555 r#####" 563 r#####"
556impl Cursor<'_<|>> { 564impl Cursor<'_$0> {
557 fn node(self) -> &SyntaxNode { 565 fn node(self) -> &SyntaxNode {
558 match self { 566 match self {
559 Cursor::Replace(node) | Cursor::Before(node) => node, 567 Cursor::Replace(node) | Cursor::Before(node) => node,
@@ -579,7 +587,7 @@ fn doctest_invert_if() {
579 "invert_if", 587 "invert_if",
580 r#####" 588 r#####"
581fn main() { 589fn main() {
582 if<|> !y { A } else { B } 590 if$0 !y { A } else { B }
583} 591}
584"#####, 592"#####,
585 r#####" 593 r#####"
@@ -596,7 +604,7 @@ fn doctest_make_raw_string() {
596 "make_raw_string", 604 "make_raw_string",
597 r#####" 605 r#####"
598fn main() { 606fn main() {
599 "Hello,<|> World!"; 607 "Hello,$0 World!";
600} 608}
601"#####, 609"#####,
602 r#####" 610 r#####"
@@ -613,7 +621,7 @@ fn doctest_make_usual_string() {
613 "make_usual_string", 621 "make_usual_string",
614 r#####" 622 r#####"
615fn main() { 623fn main() {
616 r#"Hello,<|> "World!""#; 624 r#"Hello,$0 "World!""#;
617} 625}
618"#####, 626"#####,
619 r#####" 627 r#####"
@@ -629,7 +637,7 @@ fn doctest_merge_imports() {
629 check_doc_test( 637 check_doc_test(
630 "merge_imports", 638 "merge_imports",
631 r#####" 639 r#####"
632use std::<|>fmt::Formatter; 640use std::$0fmt::Formatter;
633use std::io; 641use std::io;
634"#####, 642"#####,
635 r#####" 643 r#####"
@@ -647,7 +655,7 @@ enum Action { Move { distance: u32 }, Stop }
647 655
648fn handle(action: Action) { 656fn handle(action: Action) {
649 match action { 657 match action {
650 <|>Action::Move(..) => foo(), 658 $0Action::Move(..) => foo(),
651 Action::Stop => foo(), 659 Action::Stop => foo(),
652 } 660 }
653} 661}
@@ -673,7 +681,7 @@ enum Action { Move { distance: u32 }, Stop }
673 681
674fn handle(action: Action) { 682fn handle(action: Action) {
675 match action { 683 match action {
676 Action::Move { distance } => <|>if distance > 10 { foo() }, 684 Action::Move { distance } => $0if distance > 10 { foo() },
677 _ => (), 685 _ => (),
678 } 686 }
679} 687}
@@ -696,7 +704,7 @@ fn doctest_move_bounds_to_where_clause() {
696 check_doc_test( 704 check_doc_test(
697 "move_bounds_to_where_clause", 705 "move_bounds_to_where_clause",
698 r#####" 706 r#####"
699fn apply<T, U, <|>F: FnOnce(T) -> U>(f: F, x: T) -> U { 707fn apply<T, U, $0F: FnOnce(T) -> U>(f: F, x: T) -> U {
700 f(x) 708 f(x)
701} 709}
702"#####, 710"#####,
@@ -717,7 +725,7 @@ enum Action { Move { distance: u32 }, Stop }
717 725
718fn handle(action: Action) { 726fn handle(action: Action) {
719 match action { 727 match action {
720 Action::Move { distance } <|>if distance > 10 => foo(), 728 Action::Move { distance } $0if distance > 10 => foo(),
721 _ => (), 729 _ => (),
722 } 730 }
723} 731}
@@ -738,12 +746,56 @@ fn handle(action: Action) {
738} 746}
739 747
740#[test] 748#[test]
749fn doctest_move_module_to_file() {
750 check_doc_test(
751 "move_module_to_file",
752 r#####"
753mod $0foo {
754 fn t() {}
755}
756"#####,
757 r#####"
758mod foo;
759"#####,
760 )
761}
762
763#[test]
764fn doctest_pull_assignment_up() {
765 check_doc_test(
766 "pull_assignment_up",
767 r#####"
768fn main() {
769 let mut foo = 6;
770
771 if true {
772 $0foo = 5;
773 } else {
774 foo = 4;
775 }
776}
777"#####,
778 r#####"
779fn main() {
780 let mut foo = 6;
781
782 foo = if true {
783 5
784 } else {
785 4
786 };
787}
788"#####,
789 )
790}
791
792#[test]
741fn doctest_qualify_path() { 793fn doctest_qualify_path() {
742 check_doc_test( 794 check_doc_test(
743 "qualify_path", 795 "qualify_path",
744 r#####" 796 r#####"
745fn main() { 797fn main() {
746 let map = HashMap<|>::new(); 798 let map = HashMap$0::new();
747} 799}
748pub mod std { pub mod collections { pub struct HashMap { } } } 800pub mod std { pub mod collections { pub struct HashMap { } } }
749"#####, 801"#####,
@@ -762,7 +814,7 @@ fn doctest_remove_dbg() {
762 "remove_dbg", 814 "remove_dbg",
763 r#####" 815 r#####"
764fn main() { 816fn main() {
765 <|>dbg!(92); 817 $0dbg!(92);
766} 818}
767"#####, 819"#####,
768 r#####" 820 r#####"
@@ -779,7 +831,7 @@ fn doctest_remove_hash() {
779 "remove_hash", 831 "remove_hash",
780 r#####" 832 r#####"
781fn main() { 833fn main() {
782 r#"Hello,<|> World!"#; 834 r#"Hello,$0 World!"#;
783} 835}
784"#####, 836"#####,
785 r#####" 837 r#####"
@@ -796,7 +848,7 @@ fn doctest_remove_mut() {
796 "remove_mut", 848 "remove_mut",
797 r#####" 849 r#####"
798impl Walrus { 850impl Walrus {
799 fn feed(&mut<|> self, amount: u32) {} 851 fn feed(&mut$0 self, amount: u32) {}
800} 852}
801"#####, 853"#####,
802 r#####" 854 r#####"
@@ -812,7 +864,7 @@ fn doctest_remove_unused_param() {
812 check_doc_test( 864 check_doc_test(
813 "remove_unused_param", 865 "remove_unused_param",
814 r#####" 866 r#####"
815fn frobnicate(x: i32<|>) {} 867fn frobnicate(x: i32$0) {}
816 868
817fn main() { 869fn main() {
818 frobnicate(92); 870 frobnicate(92);
@@ -834,7 +886,7 @@ fn doctest_reorder_fields() {
834 "reorder_fields", 886 "reorder_fields",
835 r#####" 887 r#####"
836struct Foo {foo: i32, bar: i32}; 888struct Foo {foo: i32, bar: i32};
837const test: Foo = <|>Foo {bar: 0, foo: 1} 889const test: Foo = $0Foo {bar: 0, foo: 1}
838"#####, 890"#####,
839 r#####" 891 r#####"
840struct Foo {foo: i32, bar: i32}; 892struct Foo {foo: i32, bar: i32};
@@ -844,12 +896,47 @@ const test: Foo = Foo {foo: 1, bar: 0}
844} 896}
845 897
846#[test] 898#[test]
899fn doctest_reorder_impl() {
900 check_doc_test(
901 "reorder_impl",
902 r#####"
903trait Foo {
904 fn a() {}
905 fn b() {}
906 fn c() {}
907}
908
909struct Bar;
910$0impl Foo for Bar {
911 fn b() {}
912 fn c() {}
913 fn a() {}
914}
915"#####,
916 r#####"
917trait Foo {
918 fn a() {}
919 fn b() {}
920 fn c() {}
921}
922
923struct Bar;
924impl Foo for Bar {
925 fn a() {}
926 fn b() {}
927 fn c() {}
928}
929"#####,
930 )
931}
932
933#[test]
847fn doctest_replace_derive_with_manual_impl() { 934fn doctest_replace_derive_with_manual_impl() {
848 check_doc_test( 935 check_doc_test(
849 "replace_derive_with_manual_impl", 936 "replace_derive_with_manual_impl",
850 r#####" 937 r#####"
851trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; } 938trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
852#[derive(Deb<|>ug, Display)] 939#[derive(Deb$0ug, Display)]
853struct S; 940struct S;
854"#####, 941"#####,
855 r#####" 942 r#####"
@@ -874,7 +961,7 @@ fn doctest_replace_if_let_with_match() {
874enum Action { Move { distance: u32 }, Stop } 961enum Action { Move { distance: u32 }, Stop }
875 962
876fn handle(action: Action) { 963fn handle(action: Action) {
877 <|>if let Action::Move { distance } = action { 964 $0if let Action::Move { distance } = action {
878 foo(distance) 965 foo(distance)
879 } else { 966 } else {
880 bar() 967 bar()
@@ -899,7 +986,7 @@ fn doctest_replace_impl_trait_with_generic() {
899 check_doc_test( 986 check_doc_test(
900 "replace_impl_trait_with_generic", 987 "replace_impl_trait_with_generic",
901 r#####" 988 r#####"
902fn foo(bar: <|>impl Bar) {} 989fn foo(bar: $0impl Bar) {}
903"#####, 990"#####,
904 r#####" 991 r#####"
905fn foo<B: Bar>(bar: B) {} 992fn foo<B: Bar>(bar: B) {}
@@ -915,7 +1002,7 @@ fn doctest_replace_let_with_if_let() {
915enum Option<T> { Some(T), None } 1002enum Option<T> { Some(T), None }
916 1003
917fn main(action: Action) { 1004fn main(action: Action) {
918 <|>let x = compute(); 1005 $0let x = compute();
919} 1006}
920 1007
921fn compute() -> Option<i32> { None } 1008fn compute() -> Option<i32> { None }
@@ -941,7 +1028,7 @@ fn doctest_replace_match_with_if_let() {
941enum Action { Move { distance: u32 }, Stop } 1028enum Action { Move { distance: u32 }, Stop }
942 1029
943fn handle(action: Action) { 1030fn handle(action: Action) {
944 <|>match action { 1031 $0match action {
945 Action::Move { distance } => foo(distance), 1032 Action::Move { distance } => foo(distance),
946 _ => bar(), 1033 _ => bar(),
947 } 1034 }
@@ -966,7 +1053,7 @@ fn doctest_replace_qualified_name_with_use() {
966 check_doc_test( 1053 check_doc_test(
967 "replace_qualified_name_with_use", 1054 "replace_qualified_name_with_use",
968 r#####" 1055 r#####"
969fn process(map: std::collections::<|>HashMap<String, String>) {} 1056fn process(map: std::collections::$0HashMap<String, String>) {}
970"#####, 1057"#####,
971 r#####" 1058 r#####"
972use std::collections::HashMap; 1059use std::collections::HashMap;
@@ -982,7 +1069,7 @@ fn doctest_replace_string_with_char() {
982 "replace_string_with_char", 1069 "replace_string_with_char",
983 r#####" 1070 r#####"
984fn main() { 1071fn main() {
985 find("{<|>"); 1072 find("{$0");
986} 1073}
987"#####, 1074"#####,
988 r#####" 1075 r#####"
@@ -1001,7 +1088,7 @@ fn doctest_replace_unwrap_with_match() {
1001enum Result<T, E> { Ok(T), Err(E) } 1088enum Result<T, E> { Ok(T), Err(E) }
1002fn main() { 1089fn main() {
1003 let x: Result<i32, i32> = Result::Ok(92); 1090 let x: Result<i32, i32> = Result::Ok(92);
1004 let y = x.<|>unwrap(); 1091 let y = x.$0unwrap();
1005} 1092}
1006"#####, 1093"#####,
1007 r#####" 1094 r#####"
@@ -1022,7 +1109,7 @@ fn doctest_split_import() {
1022 check_doc_test( 1109 check_doc_test(
1023 "split_import", 1110 "split_import",
1024 r#####" 1111 r#####"
1025use std::<|>collections::HashMap; 1112use std::$0collections::HashMap;
1026"#####, 1113"#####,
1027 r#####" 1114 r#####"
1028use std::{collections::HashMap}; 1115use std::{collections::HashMap};
@@ -1035,7 +1122,7 @@ fn doctest_toggle_ignore() {
1035 check_doc_test( 1122 check_doc_test(
1036 "toggle_ignore", 1123 "toggle_ignore",
1037 r#####" 1124 r#####"
1038<|>#[test] 1125$0#[test]
1039fn arithmetics { 1126fn arithmetics {
1040 assert_eq!(2 + 2, 5); 1127 assert_eq!(2 + 2, 5);
1041} 1128}
@@ -1056,7 +1143,7 @@ fn doctest_unwrap_block() {
1056 "unwrap_block", 1143 "unwrap_block",
1057 r#####" 1144 r#####"
1058fn foo() { 1145fn foo() {
1059 if true {<|> 1146 if true {$0
1060 println!("foo"); 1147 println!("foo");
1061 } 1148 }
1062} 1149}
@@ -1074,7 +1161,7 @@ fn doctest_wrap_return_type_in_result() {
1074 check_doc_test( 1161 check_doc_test(
1075 "wrap_return_type_in_result", 1162 "wrap_return_type_in_result",
1076 r#####" 1163 r#####"
1077fn foo() -> i32<|> { 42i32 } 1164fn foo() -> i32$0 { 42i32 }
1078"#####, 1165"#####,
1079 r#####" 1166 r#####"
1080fn foo() -> Result<i32, ${0:_}> { Ok(42i32) } 1167fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index d41084b59..9ea96eb73 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -4,7 +4,7 @@ pub(crate) mod import_assets;
4use std::ops; 4use std::ops;
5 5
6use hir::HasSource; 6use hir::HasSource;
7use ide_db::RootDatabase; 7use ide_db::{helpers::SnippetCap, RootDatabase};
8use itertools::Itertools; 8use itertools::Itertools;
9use syntax::{ 9use syntax::{
10 ast::edit::AstNodeEdit, 10 ast::edit::AstNodeEdit,
@@ -16,10 +16,7 @@ use syntax::{
16 SyntaxNode, TextSize, T, 16 SyntaxNode, TextSize, T,
17}; 17};
18 18
19use crate::{ 19use crate::ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams};
20 assist_config::SnippetCap,
21 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
22};
23 20
24pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { 21pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
25 extract_trivial_expression(&block) 22 extract_trivial_expression(&block)
@@ -37,7 +34,7 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> {
37 non_trivial_children.next().is_some() 34 non_trivial_children.next().is_some()
38 }; 35 };
39 36
40 if let Some(expr) = block.expr() { 37 if let Some(expr) = block.tail_expr() {
41 if has_anything_else(expr.syntax()) { 38 if has_anything_else(expr.syntax()) {
42 return None; 39 return None;
43 } 40 }
@@ -94,14 +91,18 @@ pub fn filter_assoc_items(
94 ast::AssocItem::MacroCall(_) => None, 91 ast::AssocItem::MacroCall(_) => None,
95 } 92 }
96 .is_some() 93 .is_some()
97 }; 94 }
98 95
99 items 96 items
100 .iter() 97 .iter()
101 .map(|i| match i { 98 // Note: This throws away items with no source.
102 hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db).value), 99 .filter_map(|i| {
103 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db).value), 100 let item = match i {
104 hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db).value), 101 hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db)?.value),
102 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db)?.value),
103 hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db)?.value),
104 };
105 Some(item)
105 }) 106 })
106 .filter(has_def_name) 107 .filter(has_def_name)
107 .filter(|it| match it { 108 .filter(|it| match it {
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 66e6443cb..98acd61b1 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -61,7 +61,9 @@ use std::{str::FromStr, sync::Arc};
61 61
62use cfg::CfgOptions; 62use cfg::CfgOptions;
63use rustc_hash::FxHashMap; 63use rustc_hash::FxHashMap;
64use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER}; 64use test_utils::{
65 extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER,
66};
65use vfs::{file_set::FileSet, VfsPath}; 67use vfs::{file_set::FileSet, VfsPath};
66 68
67use crate::{ 69use crate::{
@@ -142,10 +144,14 @@ impl ChangeFixture {
142 144
143 for entry in fixture { 145 for entry in fixture {
144 let text = if entry.text.contains(CURSOR_MARKER) { 146 let text = if entry.text.contains(CURSOR_MARKER) {
145 let (range_or_offset, text) = extract_range_or_offset(&entry.text); 147 if entry.text.contains(ESCAPED_CURSOR_MARKER) {
146 assert!(file_position.is_none()); 148 entry.text.replace(ESCAPED_CURSOR_MARKER, CURSOR_MARKER)
147 file_position = Some((file_id, range_or_offset)); 149 } else {
148 text.to_string() 150 let (range_or_offset, text) = extract_range_or_offset(&entry.text);
151 assert!(file_position.is_none());
152 file_position = Some((file_id, range_or_offset));
153 text.to_string()
154 }
149 } else { 155 } else {
150 entry.text.clone() 156 entry.text.clone()
151 }; 157 };
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 9567bcc42..2dd8fbe67 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -190,10 +190,11 @@ pub struct CrateData {
190 pub proc_macro: Vec<ProcMacro>, 190 pub proc_macro: Vec<ProcMacro>,
191} 191}
192 192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 193#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
194pub enum Edition { 194pub enum Edition {
195 Edition2018,
196 Edition2015, 195 Edition2015,
196 Edition2018,
197 Edition2021,
197} 198}
198 199
199#[derive(Default, Debug, Clone, PartialEq, Eq)] 200#[derive(Default, Debug, Clone, PartialEq, Eq)]
@@ -393,6 +394,7 @@ impl FromStr for Edition {
393 let res = match s { 394 let res = match s {
394 "2015" => Edition::Edition2015, 395 "2015" => Edition::Edition2015,
395 "2018" => Edition::Edition2018, 396 "2018" => Edition::Edition2018,
397 "2021" => Edition::Edition2021,
396 _ => return Err(ParseEditionError { invalid_input: s.to_string() }), 398 _ => return Err(ParseEditionError { invalid_input: s.to_string() }),
397 }; 399 };
398 Ok(res) 400 Ok(res)
@@ -404,6 +406,7 @@ impl fmt::Display for Edition {
404 f.write_str(match self { 406 f.write_str(match self {
405 Edition::Edition2015 => "2015", 407 Edition::Edition2015 => "2015",
406 Edition::Edition2018 => "2018", 408 Edition::Edition2018 => "2018",
409 Edition::Edition2021 => "2021",
407 }) 410 })
408 } 411 }
409} 412}
diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml
index c68e391c1..73247d130 100644
--- a/crates/cfg/Cargo.toml
+++ b/crates/cfg/Cargo.toml
@@ -17,4 +17,4 @@ tt = { path = "../tt", version = "0.0.0" }
17[dev-dependencies] 17[dev-dependencies]
18mbe = { path = "../mbe" } 18mbe = { path = "../mbe" }
19syntax = { path = "../syntax" } 19syntax = { path = "../syntax" }
20expect-test = "1.0" 20expect-test = "1.1"
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml
index 35e169a28..99a1bdd54 100644
--- a/crates/completion/Cargo.toml
+++ b/crates/completion/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13itertools = "0.9.0" 13itertools = "0.10.0"
14log = "0.4.8" 14log = "0.4.8"
15rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
16either = "1.6.1" 16either = "1.6.1"
@@ -28,4 +28,4 @@ test_utils = { path = "../test_utils", version = "0.0.0" }
28hir = { path = "../hir", version = "0.0.0" } 28hir = { path = "../hir", version = "0.0.0" }
29 29
30[dev-dependencies] 30[dev-dependencies]
31expect-test = "1.0" 31expect-test = "1.1"
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs
index d9fe13485..00c9e76f0 100644
--- a/crates/completion/src/completions.rs
+++ b/crates/completion/src/completions.rs
@@ -106,8 +106,9 @@ impl Completions {
106 func: hir::Function, 106 func: hir::Function,
107 local_name: Option<String>, 107 local_name: Option<String>,
108 ) { 108 ) {
109 let item = render_fn(RenderContext::new(ctx), None, local_name, func); 109 if let Some(item) = render_fn(RenderContext::new(ctx), None, local_name, func) {
110 self.add(item) 110 self.add(item)
111 }
111 } 112 }
112 113
113 pub(crate) fn add_variant_pat( 114 pub(crate) fn add_variant_pat(
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs
index 19ce2482f..e5522980d 100644
--- a/crates/completion/src/completions/attribute.rs
+++ b/crates/completion/src/completions/attribute.rs
@@ -5,7 +5,7 @@
5 5
6use itertools::Itertools; 6use itertools::Itertools;
7use rustc_hash::FxHashSet; 7use rustc_hash::FxHashSet;
8use syntax::{ast, AstNode, SyntaxKind}; 8use syntax::{ast, AstNode, T};
9 9
10use crate::{ 10use crate::{
11 context::CompletionContext, 11 context::CompletionContext,
@@ -21,19 +21,16 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
21 21
22 let attribute = ctx.attribute_under_caret.as_ref()?; 22 let attribute = ctx.attribute_under_caret.as_ref()?;
23 match (attribute.path(), attribute.token_tree()) { 23 match (attribute.path(), attribute.token_tree()) {
24 (Some(path), Some(token_tree)) if path.to_string() == "derive" => { 24 (Some(path), Some(token_tree)) => {
25 complete_derive(acc, ctx, token_tree) 25 let path = path.syntax().text();
26 } 26 if path == "derive" {
27 (Some(path), Some(token_tree)) if path.to_string() == "feature" => { 27 complete_derive(acc, ctx, token_tree)
28 complete_lint(acc, ctx, token_tree, FEATURES); 28 } else if path == "feature" {
29 } 29 complete_lint(acc, ctx, token_tree, FEATURES)
30 (Some(path), Some(token_tree)) 30 } else if path == "allow" || path == "warn" || path == "deny" || path == "forbid" {
31 if ["allow", "warn", "deny", "forbid"] 31 complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS);
32 .iter() 32 complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
33 .any(|lint_level| lint_level == &path.to_string()) => 33 }
34 {
35 complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS);
36 complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
37 } 34 }
38 (_, Some(_token_tree)) => {} 35 (_, Some(_token_tree)) => {}
39 _ => complete_attribute_start(acc, ctx, attribute), 36 _ => complete_attribute_start(acc, ctx, attribute),
@@ -54,11 +51,8 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
54 item = item.lookup_by(lookup); 51 item = item.lookup_by(lookup);
55 } 52 }
56 53
57 match (attr_completion.snippet, ctx.config.snippet_cap) { 54 if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) {
58 (Some(snippet), Some(cap)) => { 55 item = item.insert_snippet(cap, snippet);
59 item = item.insert_snippet(cap, snippet);
60 }
61 _ => {}
62 } 56 }
63 57
64 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { 58 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
@@ -98,7 +92,7 @@ const ATTRIBUTES: &[AttrCompletion] = &[
98 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#)) 92 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#))
99 .prefer_inner(), 93 .prefer_inner(),
100 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")), 94 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
101 attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)), 95 attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)),
102 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)), 96 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
103 attr( 97 attr(
104 r#"export_name = "…""#, 98 r#"export_name = "…""#,
@@ -121,7 +115,7 @@ const ATTRIBUTES: &[AttrCompletion] = &[
121 ), 115 ),
122 attr("macro_export", None, None), 116 attr("macro_export", None, None),
123 attr("macro_use", None, None), 117 attr("macro_use", None, None),
124 attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)), 118 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)),
125 attr("no_link", None, None).prefer_inner(), 119 attr("no_link", None, None).prefer_inner(),
126 attr("no_implicit_prelude", None, None).prefer_inner(), 120 attr("no_implicit_prelude", None, None).prefer_inner(),
127 attr("no_main", None, None).prefer_inner(), 121 attr("no_main", None, None).prefer_inner(),
@@ -136,11 +130,7 @@ const ATTRIBUTES: &[AttrCompletion] = &[
136 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}")) 130 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
137 .prefer_inner(), 131 .prefer_inner(),
138 attr("repr(…)", Some("repr"), Some("repr(${0:C})")), 132 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
139 attr( 133 attr("should_panic", Some("should_panic"), Some(r#"should_panic"#)),
140 "should_panic(…)",
141 Some("should_panic"),
142 Some(r#"should_panic(expected = "${0:reason}")"#),
143 ),
144 attr( 134 attr(
145 r#"target_feature = "…""#, 135 r#"target_feature = "…""#,
146 Some("target_feature"), 136 Some("target_feature"),
@@ -215,8 +205,7 @@ fn complete_lint(
215fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> { 205fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> {
216 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) { 206 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
217 (Some(left_paren), Some(right_paren)) 207 (Some(left_paren), Some(right_paren))
218 if left_paren.kind() == SyntaxKind::L_PAREN 208 if left_paren.kind() == T!['('] && right_paren.kind() == T![')'] =>
219 && right_paren.kind() == SyntaxKind::R_PAREN =>
220 { 209 {
221 let mut input_derives = FxHashSet::default(); 210 let mut input_derives = FxHashSet::default();
222 let mut current_derive = String::new(); 211 let mut current_derive = String::new();
@@ -228,13 +217,13 @@ fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<Strin
228 .skip(1) 217 .skip(1)
229 .take_while(|token| token != &right_paren) 218 .take_while(|token| token != &right_paren)
230 { 219 {
231 if SyntaxKind::COMMA == token.kind() { 220 if T![,] == token.kind() {
232 if !current_derive.is_empty() { 221 if !current_derive.is_empty() {
233 input_derives.insert(current_derive); 222 input_derives.insert(current_derive);
234 current_derive = String::new(); 223 current_derive = String::new();
235 } 224 }
236 } else { 225 } else {
237 current_derive.push_str(token.to_string().trim()); 226 current_derive.push_str(token.text().trim());
238 } 227 }
239 } 228 }
240 229
@@ -417,7 +406,7 @@ mod tests {
417 fn empty_derive_completion() { 406 fn empty_derive_completion() {
418 check( 407 check(
419 r#" 408 r#"
420#[derive(<|>)] 409#[derive($0)]
421struct Test {} 410struct Test {}
422 "#, 411 "#,
423 expect![[r#" 412 expect![[r#"
@@ -438,7 +427,7 @@ struct Test {}
438 fn no_completion_for_incorrect_derive() { 427 fn no_completion_for_incorrect_derive() {
439 check( 428 check(
440 r#" 429 r#"
441#[derive{<|>)] 430#[derive{$0)]
442struct Test {} 431struct Test {}
443"#, 432"#,
444 expect![[r#""#]], 433 expect![[r#""#]],
@@ -449,7 +438,7 @@ struct Test {}
449 fn derive_with_input_completion() { 438 fn derive_with_input_completion() {
450 check( 439 check(
451 r#" 440 r#"
452#[derive(serde::Serialize, PartialEq, <|>)] 441#[derive(serde::Serialize, PartialEq, $0)]
453struct Test {} 442struct Test {}
454"#, 443"#,
455 expect![[r#" 444 expect![[r#"
@@ -468,7 +457,7 @@ struct Test {}
468 #[test] 457 #[test]
469 fn test_attribute_completion() { 458 fn test_attribute_completion() {
470 check( 459 check(
471 r#"#[<|>]"#, 460 r#"#[$0]"#,
472 expect![[r#" 461 expect![[r#"
473 at allow(…) 462 at allow(…)
474 at automatically_derived 463 at automatically_derived
@@ -476,7 +465,7 @@ struct Test {}
476 at cfg(…) 465 at cfg(…)
477 at cold 466 at cold
478 at deny(…) 467 at deny(…)
479 at deprecated = "…" 468 at deprecated
480 at derive(…) 469 at derive(…)
481 at export_name = "…" 470 at export_name = "…"
482 at doc = "…" 471 at doc = "…"
@@ -488,7 +477,7 @@ struct Test {}
488 at link_section = "…" 477 at link_section = "…"
489 at macro_export 478 at macro_export
490 at macro_use 479 at macro_use
491 at must_use = "…" 480 at must_use
492 at no_mangle 481 at no_mangle
493 at non_exhaustive 482 at non_exhaustive
494 at path = "…" 483 at path = "…"
@@ -496,7 +485,7 @@ struct Test {}
496 at proc_macro_attribute 485 at proc_macro_attribute
497 at proc_macro_derive(…) 486 at proc_macro_derive(…)
498 at repr(…) 487 at repr(…)
499 at should_panic(…) 488 at should_panic
500 at target_feature = "…" 489 at target_feature = "…"
501 at test 490 at test
502 at track_caller 491 at track_caller
@@ -508,13 +497,13 @@ struct Test {}
508 497
509 #[test] 498 #[test]
510 fn test_attribute_completion_inside_nested_attr() { 499 fn test_attribute_completion_inside_nested_attr() {
511 check(r#"#[cfg(<|>)]"#, expect![[]]) 500 check(r#"#[cfg($0)]"#, expect![[]])
512 } 501 }
513 502
514 #[test] 503 #[test]
515 fn test_inner_attribute_completion() { 504 fn test_inner_attribute_completion() {
516 check( 505 check(
517 r"#![<|>]", 506 r"#![$0]",
518 expect![[r#" 507 expect![[r#"
519 at allow(…) 508 at allow(…)
520 at automatically_derived 509 at automatically_derived
@@ -523,7 +512,7 @@ struct Test {}
523 at cold 512 at cold
524 at crate_name = "" 513 at crate_name = ""
525 at deny(…) 514 at deny(…)
526 at deprecated = "…" 515 at deprecated
527 at derive(…) 516 at derive(…)
528 at export_name = "…" 517 at export_name = "…"
529 at doc = "…" 518 at doc = "…"
@@ -537,7 +526,7 @@ struct Test {}
537 at link_section = "…" 526 at link_section = "…"
538 at macro_export 527 at macro_export
539 at macro_use 528 at macro_use
540 at must_use = "…" 529 at must_use
541 at no_link 530 at no_link
542 at no_implicit_prelude 531 at no_implicit_prelude
543 at no_main 532 at no_main
@@ -551,7 +540,7 @@ struct Test {}
551 at proc_macro_derive(…) 540 at proc_macro_derive(…)
552 at recursion_limit = … 541 at recursion_limit = …
553 at repr(…) 542 at repr(…)
554 at should_panic(…) 543 at should_panic
555 at target_feature = "…" 544 at target_feature = "…"
556 at test 545 at test
557 at track_caller 546 at track_caller
diff --git a/crates/completion/src/completions/dot.rs b/crates/completion/src/completions/dot.rs
index 551ef1771..d04eef65a 100644
--- a/crates/completion/src/completions/dot.rs
+++ b/crates/completion/src/completions/dot.rs
@@ -79,7 +79,7 @@ struct S { foo: u32 }
79impl S { 79impl S {
80 fn bar(&self) {} 80 fn bar(&self) {}
81} 81}
82fn foo(s: S) { s.<|> } 82fn foo(s: S) { s.$0 }
83"#, 83"#,
84 expect![[r#" 84 expect![[r#"
85 fd foo u32 85 fd foo u32
@@ -94,7 +94,7 @@ fn foo(s: S) { s.<|> }
94 r#" 94 r#"
95struct S { the_field: (u32,) } 95struct S { the_field: (u32,) }
96impl S { 96impl S {
97 fn foo(self) { self.<|> } 97 fn foo(self) { self.$0 }
98} 98}
99"#, 99"#,
100 expect![[r#" 100 expect![[r#"
@@ -110,7 +110,7 @@ impl S {
110 r#" 110 r#"
111struct A { the_field: (u32, i32) } 111struct A { the_field: (u32, i32) }
112impl A { 112impl A {
113 fn foo(&self) { self.<|> } 113 fn foo(&self) { self.$0 }
114} 114}
115"#, 115"#,
116 expect![[r#" 116 expect![[r#"
@@ -126,7 +126,7 @@ impl A {
126 check( 126 check(
127 r#" 127 r#"
128struct A { the_field: u32 } 128struct A { the_field: u32 }
129fn foo(a: A) { a.<|>() } 129fn foo(a: A) { a.$0() }
130"#, 130"#,
131 expect![[""]], 131 expect![[""]],
132 ); 132 );
@@ -144,7 +144,7 @@ mod inner {
144 pub(crate) super_field: u32, 144 pub(crate) super_field: u32,
145 } 145 }
146} 146}
147fn foo(a: inner::A) { a.<|> } 147fn foo(a: inner::A) { a.$0 }
148"#, 148"#,
149 expect![[r#" 149 expect![[r#"
150 fd pub_field u32 150 fd pub_field u32
@@ -162,7 +162,7 @@ mod m {
162 pub(crate) fn the_method(&self) {} 162 pub(crate) fn the_method(&self) {}
163 } 163 }
164} 164}
165fn foo(a: A) { a.<|> } 165fn foo(a: A) { a.$0 }
166"#, 166"#,
167 expect![[r#" 167 expect![[r#"
168 me the_method() pub(crate) fn the_method(&self) 168 me the_method() pub(crate) fn the_method(&self)
@@ -175,7 +175,7 @@ fn foo(a: A) { a.<|> }
175 check( 175 check(
176 r#" 176 r#"
177union U { field: u8, other: u16 } 177union U { field: u8, other: u16 }
178fn foo(u: U) { u.<|> } 178fn foo(u: U) { u.$0 }
179"#, 179"#,
180 expect![[r#" 180 expect![[r#"
181 fd field u8 181 fd field u8
@@ -195,7 +195,7 @@ impl A<u32> {
195impl A<i32> { 195impl A<i32> {
196 fn the_other_method(&self) {} 196 fn the_other_method(&self) {}
197} 197}
198fn foo(a: A<u32>) { a.<|> } 198fn foo(a: A<u32>) { a.$0 }
199"#, 199"#,
200 expect![[r#" 200 expect![[r#"
201 me the_method() fn the_method(&self) 201 me the_method() fn the_method(&self)
@@ -210,7 +210,7 @@ fn foo(a: A<u32>) { a.<|> }
210struct A {} 210struct A {}
211trait Trait { fn the_method(&self); } 211trait Trait { fn the_method(&self); }
212impl Trait for A {} 212impl Trait for A {}
213fn foo(a: A) { a.<|> } 213fn foo(a: A) { a.$0 }
214"#, 214"#,
215 expect![[r#" 215 expect![[r#"
216 me the_method() fn the_method(&self) 216 me the_method() fn the_method(&self)
@@ -225,7 +225,7 @@ fn foo(a: A) { a.<|> }
225struct A {} 225struct A {}
226trait Trait { fn the_method(&self); } 226trait Trait { fn the_method(&self); }
227impl<T> Trait for T {} 227impl<T> Trait for T {}
228fn foo(a: &A) { a.<|> } 228fn foo(a: &A) { a.$0 }
229", 229",
230 expect![[r#" 230 expect![[r#"
231 me the_method() fn the_method(&self) 231 me the_method() fn the_method(&self)
@@ -243,7 +243,7 @@ mod m {
243} 243}
244use m::Trait; 244use m::Trait;
245impl Trait for A {} 245impl Trait for A {}
246fn foo(a: A) { a.<|> } 246fn foo(a: A) { a.$0 }
247", 247",
248 expect![[r#" 248 expect![[r#"
249 me the_method() fn the_method(&self) 249 me the_method() fn the_method(&self)
@@ -260,7 +260,7 @@ impl A {
260 fn the_method() {} 260 fn the_method() {}
261} 261}
262fn foo(a: A) { 262fn foo(a: A) {
263 a.<|> 263 a.$0
264} 264}
265"#, 265"#,
266 expect![[""]], 266 expect![[""]],
@@ -273,7 +273,7 @@ fn foo(a: A) {
273 r#" 273 r#"
274fn foo() { 274fn foo() {
275 let b = (0, 3.14); 275 let b = (0, 3.14);
276 b.<|> 276 b.$0
277} 277}
278"#, 278"#,
279 expect![[r#" 279 expect![[r#"
@@ -295,7 +295,7 @@ struct T(S);
295impl T { 295impl T {
296 fn foo(&self) { 296 fn foo(&self) {
297 // FIXME: This doesn't work without the trailing `a` as `0.` is a float 297 // FIXME: This doesn't work without the trailing `a` as `0.` is a float
298 self.0.a<|> 298 self.0.a$0
299 } 299 }
300} 300}
301"#, 301"#,
@@ -311,7 +311,7 @@ impl T {
311 r#" 311 r#"
312struct A { the_field: u32 } 312struct A { the_field: u32 }
313const X: u32 = { 313const X: u32 = {
314 A { the_field: 92 }.<|> 314 A { the_field: 92 }.$0
315}; 315};
316"#, 316"#,
317 expect![[r#" 317 expect![[r#"
@@ -327,7 +327,7 @@ const X: u32 = {
327macro_rules! m { ($e:expr) => { $e } } 327macro_rules! m { ($e:expr) => { $e } }
328struct A { the_field: u32 } 328struct A { the_field: u32 }
329fn foo(a: A) { 329fn foo(a: A) {
330 m!(a.x<|>) 330 m!(a.x$0)
331} 331}
332"#, 332"#,
333 expect![[r#" 333 expect![[r#"
@@ -344,7 +344,7 @@ fn foo(a: A) {
344macro_rules! m { ($e:expr) => { $e } } 344macro_rules! m { ($e:expr) => { $e } }
345struct A { the_field: u32 } 345struct A { the_field: u32 }
346fn foo(a: A) { 346fn foo(a: A) {
347 m!(a.<|>) 347 m!(a.$0)
348} 348}
349"#, 349"#,
350 expect![[r#" 350 expect![[r#"
@@ -360,7 +360,7 @@ fn foo(a: A) {
360macro_rules! m { ($e:expr) => { $e } } 360macro_rules! m { ($e:expr) => { $e } }
361struct A { the_field: u32 } 361struct A { the_field: u32 }
362fn foo(a: A) { 362fn foo(a: A) {
363 m!(m!(m!(a.x<|>))) 363 m!(m!(m!(a.x$0)))
364} 364}
365"#, 365"#,
366 expect![[r#" 366 expect![[r#"
@@ -373,20 +373,20 @@ fn foo(a: A) {
373 fn macro_expansion_resilient() { 373 fn macro_expansion_resilient() {
374 check( 374 check(
375 r#" 375 r#"
376macro_rules! dbg { 376macro_rules! d {
377 () => {}; 377 () => {};
378 ($val:expr) => { 378 ($val:expr) => {
379 match $val { tmp => { tmp } } 379 match $val { tmp => { tmp } }
380 }; 380 };
381 // Trailing comma with single argument is ignored 381 // Trailing comma with single argument is ignored
382 ($val:expr,) => { $crate::dbg!($val) }; 382 ($val:expr,) => { $crate::d!($val) };
383 ($($val:expr),+ $(,)?) => { 383 ($($val:expr),+ $(,)?) => {
384 ($($crate::dbg!($val)),+,) 384 ($($crate::d!($val)),+,)
385 }; 385 };
386} 386}
387struct A { the_field: u32 } 387struct A { the_field: u32 }
388fn foo(a: A) { 388fn foo(a: A) {
389 dbg!(a.<|>) 389 d!(a.$0)
390} 390}
391"#, 391"#,
392 expect![[r#" 392 expect![[r#"
@@ -405,7 +405,7 @@ impl<T> HashSet<T> {
405} 405}
406fn foo() { 406fn foo() {
407 let s: HashSet<_>; 407 let s: HashSet<_>;
408 s.<|> 408 s.$0
409} 409}
410"#, 410"#,
411 expect![[r#" 411 expect![[r#"
@@ -421,7 +421,7 @@ fn foo() {
421struct S; 421struct S;
422impl S { fn foo(&self) {} } 422impl S { fn foo(&self) {} }
423macro_rules! make_s { () => { S }; } 423macro_rules! make_s { () => { S }; }
424fn main() { make_s!().f<|>; } 424fn main() { make_s!().f$0; }
425"#, 425"#,
426 expect![[r#" 426 expect![[r#"
427 me foo() fn foo(&self) 427 me foo() fn foo(&self)
diff --git a/crates/completion/src/completions/fn_param.rs b/crates/completion/src/completions/fn_param.rs
index e777a53c1..5505c3559 100644
--- a/crates/completion/src/completions/fn_param.rs
+++ b/crates/completion/src/completions/fn_param.rs
@@ -81,7 +81,7 @@ mod tests {
81 r#" 81 r#"
82fn foo(file_id: FileId) {} 82fn foo(file_id: FileId) {}
83fn bar(file_id: FileId) {} 83fn bar(file_id: FileId) {}
84fn baz(file<|>) {} 84fn baz(file$0) {}
85"#, 85"#,
86 expect![[r#" 86 expect![[r#"
87 bn file_id: FileId 87 bn file_id: FileId
@@ -94,7 +94,7 @@ fn baz(file<|>) {}
94 check( 94 check(
95 r#" 95 r#"
96fn foo(file_id: FileId) {} 96fn foo(file_id: FileId) {}
97fn baz(file<|>, x: i32) {} 97fn baz(file$0, x: i32) {}
98"#, 98"#,
99 expect![[r#" 99 expect![[r#"
100 bn file_id: FileId 100 bn file_id: FileId
@@ -110,7 +110,7 @@ pub(crate) trait SourceRoot {
110 pub fn contains(&self, file_id: FileId) -> bool; 110 pub fn contains(&self, file_id: FileId) -> bool;
111 pub fn module_map(&self) -> &ModuleMap; 111 pub fn module_map(&self) -> &ModuleMap;
112 pub fn lines(&self, file_id: FileId) -> &LineIndex; 112 pub fn lines(&self, file_id: FileId) -> &LineIndex;
113 pub fn syntax(&self, file<|>) 113 pub fn syntax(&self, file$0)
114} 114}
115"#, 115"#,
116 expect![[r#" 116 expect![[r#"
@@ -124,7 +124,7 @@ pub(crate) trait SourceRoot {
124 check( 124 check(
125 r#" 125 r#"
126fn outer(text: String) { 126fn outer(text: String) {
127 fn inner(<|>) 127 fn inner($0)
128} 128}
129"#, 129"#,
130 expect![[r#" 130 expect![[r#"
diff --git a/crates/completion/src/completions/keyword.rs b/crates/completion/src/completions/keyword.rs
index 1859dec70..425a688ff 100644
--- a/crates/completion/src/completions/keyword.rs
+++ b/crates/completion/src/completions/keyword.rs
@@ -193,7 +193,7 @@ mod tests {
193 #[test] 193 #[test]
194 fn test_keywords_in_use_stmt() { 194 fn test_keywords_in_use_stmt() {
195 check( 195 check(
196 r"use <|>", 196 r"use $0",
197 expect![[r#" 197 expect![[r#"
198 kw crate:: 198 kw crate::
199 kw self 199 kw self
@@ -202,7 +202,7 @@ mod tests {
202 ); 202 );
203 203
204 check( 204 check(
205 r"use a::<|>", 205 r"use a::$0",
206 expect![[r#" 206 expect![[r#"
207 kw self 207 kw self
208 kw super:: 208 kw super::
@@ -210,7 +210,7 @@ mod tests {
210 ); 210 );
211 211
212 check( 212 check(
213 r"use a::{b, <|>}", 213 r"use a::{b, $0}",
214 expect![[r#" 214 expect![[r#"
215 kw self 215 kw self
216 kw super:: 216 kw super::
@@ -221,7 +221,7 @@ mod tests {
221 #[test] 221 #[test]
222 fn test_keywords_at_source_file_level() { 222 fn test_keywords_at_source_file_level() {
223 check( 223 check(
224 r"m<|>", 224 r"m$0",
225 expect![[r#" 225 expect![[r#"
226 kw fn 226 kw fn
227 kw use 227 kw use
@@ -245,7 +245,7 @@ mod tests {
245 #[test] 245 #[test]
246 fn test_keywords_in_function() { 246 fn test_keywords_in_function() {
247 check( 247 check(
248 r"fn quux() { <|> }", 248 r"fn quux() { $0 }",
249 expect![[r#" 249 expect![[r#"
250 kw fn 250 kw fn
251 kw use 251 kw use
@@ -271,7 +271,7 @@ mod tests {
271 #[test] 271 #[test]
272 fn test_keywords_inside_block() { 272 fn test_keywords_inside_block() {
273 check( 273 check(
274 r"fn quux() { if true { <|> } }", 274 r"fn quux() { if true { $0 } }",
275 expect![[r#" 275 expect![[r#"
276 kw fn 276 kw fn
277 kw use 277 kw use
@@ -297,7 +297,7 @@ mod tests {
297 #[test] 297 #[test]
298 fn test_keywords_after_if() { 298 fn test_keywords_after_if() {
299 check( 299 check(
300 r#"fn quux() { if true { () } <|> }"#, 300 r#"fn quux() { if true { () } $0 }"#,
301 expect![[r#" 301 expect![[r#"
302 kw fn 302 kw fn
303 kw use 303 kw use
@@ -322,7 +322,7 @@ mod tests {
322 ); 322 );
323 check_edit( 323 check_edit(
324 "else", 324 "else",
325 r#"fn quux() { if true { () } <|> }"#, 325 r#"fn quux() { if true { () } $0 }"#,
326 r#"fn quux() { if true { () } else {$0} }"#, 326 r#"fn quux() { if true { () } else {$0} }"#,
327 ); 327 );
328 } 328 }
@@ -332,7 +332,7 @@ mod tests {
332 check( 332 check(
333 r#" 333 r#"
334fn quux() -> i32 { 334fn quux() -> i32 {
335 match () { () => <|> } 335 match () { () => $0 }
336} 336}
337"#, 337"#,
338 expect![[r#" 338 expect![[r#"
@@ -350,7 +350,7 @@ fn quux() -> i32 {
350 #[test] 350 #[test]
351 fn test_keywords_in_trait_def() { 351 fn test_keywords_in_trait_def() {
352 check( 352 check(
353 r"trait My { <|> }", 353 r"trait My { $0 }",
354 expect![[r#" 354 expect![[r#"
355 kw fn 355 kw fn
356 kw const 356 kw const
@@ -363,7 +363,7 @@ fn quux() -> i32 {
363 #[test] 363 #[test]
364 fn test_keywords_in_impl_def() { 364 fn test_keywords_in_impl_def() {
365 check( 365 check(
366 r"impl My { <|> }", 366 r"impl My { $0 }",
367 expect![[r#" 367 expect![[r#"
368 kw fn 368 kw fn
369 kw const 369 kw const
@@ -378,7 +378,7 @@ fn quux() -> i32 {
378 #[test] 378 #[test]
379 fn test_keywords_in_loop() { 379 fn test_keywords_in_loop() {
380 check( 380 check(
381 r"fn my() { loop { <|> } }", 381 r"fn my() { loop { $0 } }",
382 expect![[r#" 382 expect![[r#"
383 kw fn 383 kw fn
384 kw use 384 kw use
@@ -406,7 +406,7 @@ fn quux() -> i32 {
406 #[test] 406 #[test]
407 fn test_keywords_after_unsafe_in_item_list() { 407 fn test_keywords_after_unsafe_in_item_list() {
408 check( 408 check(
409 r"unsafe <|>", 409 r"unsafe $0",
410 expect![[r#" 410 expect![[r#"
411 kw fn 411 kw fn
412 kw trait 412 kw trait
@@ -418,7 +418,7 @@ fn quux() -> i32 {
418 #[test] 418 #[test]
419 fn test_keywords_after_unsafe_in_block_expr() { 419 fn test_keywords_after_unsafe_in_block_expr() {
420 check( 420 check(
421 r"fn my_fn() { unsafe <|> }", 421 r"fn my_fn() { unsafe $0 }",
422 expect![[r#" 422 expect![[r#"
423 kw fn 423 kw fn
424 kw trait 424 kw trait
@@ -430,19 +430,19 @@ fn quux() -> i32 {
430 #[test] 430 #[test]
431 fn test_mut_in_ref_and_in_fn_parameters_list() { 431 fn test_mut_in_ref_and_in_fn_parameters_list() {
432 check( 432 check(
433 r"fn my_fn(&<|>) {}", 433 r"fn my_fn(&$0) {}",
434 expect![[r#" 434 expect![[r#"
435 kw mut 435 kw mut
436 "#]], 436 "#]],
437 ); 437 );
438 check( 438 check(
439 r"fn my_fn(<|>) {}", 439 r"fn my_fn($0) {}",
440 expect![[r#" 440 expect![[r#"
441 kw mut 441 kw mut
442 "#]], 442 "#]],
443 ); 443 );
444 check( 444 check(
445 r"fn my_fn() { let &<|> }", 445 r"fn my_fn() { let &$0 }",
446 expect![[r#" 446 expect![[r#"
447 kw mut 447 kw mut
448 "#]], 448 "#]],
@@ -452,13 +452,13 @@ fn quux() -> i32 {
452 #[test] 452 #[test]
453 fn test_where_keyword() { 453 fn test_where_keyword() {
454 check( 454 check(
455 r"trait A <|>", 455 r"trait A $0",
456 expect![[r#" 456 expect![[r#"
457 kw where 457 kw where
458 "#]], 458 "#]],
459 ); 459 );
460 check( 460 check(
461 r"impl A <|>", 461 r"impl A $0",
462 expect![[r#" 462 expect![[r#"
463 kw where 463 kw where
464 "#]], 464 "#]],
@@ -471,7 +471,7 @@ fn quux() -> i32 {
471 check( 471 check(
472 r#" 472 r#"
473fn test() { 473fn test() {
474 let x = 2; // A comment<|> 474 let x = 2; // A comment$0
475} 475}
476"#, 476"#,
477 expect![[""]], 477 expect![[""]],
@@ -479,7 +479,7 @@ fn test() {
479 check( 479 check(
480 r#" 480 r#"
481/* 481/*
482Some multi-line comment<|> 482Some multi-line comment$0
483*/ 483*/
484"#, 484"#,
485 expect![[""]], 485 expect![[""]],
@@ -487,7 +487,7 @@ Some multi-line comment<|>
487 check( 487 check(
488 r#" 488 r#"
489/// Some doc comment 489/// Some doc comment
490/// let test<|> = 1 490/// let test$0 = 1
491"#, 491"#,
492 expect![[""]], 492 expect![[""]],
493 ); 493 );
@@ -501,7 +501,7 @@ Some multi-line comment<|>
501use std::future::*; 501use std::future::*;
502struct A {} 502struct A {}
503impl Future for A {} 503impl Future for A {}
504fn foo(a: A) { a.<|> } 504fn foo(a: A) { a.$0 }
505 505
506//- /std/lib.rs crate:std 506//- /std/lib.rs crate:std
507pub mod future { 507pub mod future {
@@ -520,7 +520,7 @@ pub mod future {
520use std::future::*; 520use std::future::*;
521fn foo() { 521fn foo() {
522 let a = async {}; 522 let a = async {};
523 a.<|> 523 a.$0
524} 524}
525 525
526//- /std/lib.rs crate:std 526//- /std/lib.rs crate:std
@@ -540,7 +540,7 @@ pub mod future {
540 #[test] 540 #[test]
541 fn after_let() { 541 fn after_let() {
542 check( 542 check(
543 r#"fn main() { let _ = <|> }"#, 543 r#"fn main() { let _ = $0 }"#,
544 expect![[r#" 544 expect![[r#"
545 kw match 545 kw match
546 kw while 546 kw while
@@ -557,7 +557,7 @@ pub mod future {
557 check( 557 check(
558 r#" 558 r#"
559struct Foo { 559struct Foo {
560 <|> 560 $0
561 pub f: i32, 561 pub f: i32,
562} 562}
563"#, 563"#,
@@ -578,7 +578,7 @@ struct Foo {
578} 578}
579fn foo() { 579fn foo() {
580 Foo { 580 Foo {
581 <|> 581 $0
582 } 582 }
583} 583}
584"#, 584"#,
@@ -595,7 +595,7 @@ struct Foo {
595} 595}
596fn foo() { 596fn foo() {
597 Foo { 597 Foo {
598 f: <|> 598 f: $0
599 } 599 }
600} 600}
601"#, 601"#,
diff --git a/crates/completion/src/completions/macro_in_item_position.rs b/crates/completion/src/completions/macro_in_item_position.rs
index 82884a181..2be299ac2 100644
--- a/crates/completion/src/completions/macro_in_item_position.rs
+++ b/crates/completion/src/completions/macro_in_item_position.rs
@@ -31,7 +31,7 @@ mod tests {
31macro_rules! foo { () => {} } 31macro_rules! foo { () => {} }
32fn foo() {} 32fn foo() {}
33 33
34<|> 34$0
35"#, 35"#,
36 expect![[r#" 36 expect![[r#"
37 ma foo!(…) macro_rules! foo 37 ma foo!(…) macro_rules! foo
diff --git a/crates/completion/src/completions/mod_.rs b/crates/completion/src/completions/mod_.rs
index f77864b77..00e951ca9 100644
--- a/crates/completion/src/completions/mod_.rs
+++ b/crates/completion/src/completions/mod_.rs
@@ -1,5 +1,7 @@
1//! Completes mod declarations. 1//! Completes mod declarations.
2 2
3use std::iter;
4
3use hir::{Module, ModuleSource}; 5use hir::{Module, ModuleSource};
4use ide_db::base_db::{SourceDatabaseExt, VfsPath}; 6use ide_db::base_db::{SourceDatabaseExt, VfsPath};
5use ide_db::RootDatabase; 7use ide_db::RootDatabase;
@@ -9,12 +11,11 @@ use crate::{CompletionItem, CompletionItemKind};
9 11
10use crate::{context::CompletionContext, item::CompletionKind, Completions}; 12use crate::{context::CompletionContext, item::CompletionKind, Completions};
11 13
12/// Complete mod declaration, i.e. `mod <|> ;` 14/// Complete mod declaration, i.e. `mod $0 ;`
13pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 15pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
14 let mod_under_caret = match &ctx.mod_declaration_under_caret { 16 let mod_under_caret = match &ctx.mod_declaration_under_caret {
15 Some(mod_under_caret) if mod_under_caret.item_list().is_some() => return None, 17 Some(mod_under_caret) if mod_under_caret.item_list().is_none() => mod_under_caret,
16 Some(mod_under_caret) => mod_under_caret, 18 _ => return None,
17 None => return None,
18 }; 19 };
19 20
20 let _p = profile::span("completion::complete_mod"); 21 let _p = profile::span("completion::complete_mod");
@@ -49,9 +50,13 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op
49 .filter_map(|submodule_file| { 50 .filter_map(|submodule_file| {
50 let submodule_path = source_root.path_for_file(&submodule_file)?; 51 let submodule_path = source_root.path_for_file(&submodule_file)?;
51 let directory_with_submodule = submodule_path.parent()?; 52 let directory_with_submodule = submodule_path.parent()?;
52 match submodule_path.name_and_extension()? { 53 let (name, ext) = submodule_path.name_and_extension()?;
53 ("lib", Some("rs")) | ("main", Some("rs")) => None, 54 if ext != Some("rs") {
54 ("mod", Some("rs")) => { 55 return None;
56 }
57 match name {
58 "lib" | "main" => None,
59 "mod" => {
55 if directory_with_submodule.parent()? == directory_to_look_for_submodules { 60 if directory_with_submodule.parent()? == directory_to_look_for_submodules {
56 match directory_with_submodule.name_and_extension()? { 61 match directory_with_submodule.name_and_extension()? {
57 (directory_name, None) => Some(directory_name.to_owned()), 62 (directory_name, None) => Some(directory_name.to_owned()),
@@ -61,9 +66,7 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op
61 None 66 None
62 } 67 }
63 } 68 }
64 (file_name, Some("rs")) 69 file_name if directory_with_submodule == directory_to_look_for_submodules => {
65 if directory_with_submodule == directory_to_look_for_submodules =>
66 {
67 Some(file_name.to_owned()) 70 Some(file_name.to_owned())
68 } 71 }
69 _ => None, 72 _ => None,
@@ -73,7 +76,7 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op
73 .for_each(|submodule_name| { 76 .for_each(|submodule_name| {
74 let mut label = submodule_name; 77 let mut label = submodule_name;
75 if mod_under_caret.semicolon_token().is_none() { 78 if mod_under_caret.semicolon_token().is_none() {
76 label.push(';') 79 label.push(';');
77 } 80 }
78 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label) 81 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label)
79 .kind(CompletionItemKind::Module) 82 .kind(CompletionItemKind::Module)
@@ -89,11 +92,13 @@ fn directory_to_look_for_submodules(
89 module_file_path: &VfsPath, 92 module_file_path: &VfsPath,
90) -> Option<VfsPath> { 93) -> Option<VfsPath> {
91 let directory_with_module_path = module_file_path.parent()?; 94 let directory_with_module_path = module_file_path.parent()?;
92 let base_directory = match module_file_path.name_and_extension()? { 95 let (name, ext) = module_file_path.name_and_extension()?;
93 ("mod", Some("rs")) | ("lib", Some("rs")) | ("main", Some("rs")) => { 96 if ext != Some("rs") {
94 Some(directory_with_module_path) 97 return None;
95 } 98 }
96 (regular_rust_file_name, Some("rs")) => { 99 let base_directory = match name {
100 "mod" | "lib" | "main" => Some(directory_with_module_path),
101 regular_rust_file_name => {
97 if matches!( 102 if matches!(
98 ( 103 (
99 directory_with_module_path 104 directory_with_module_path
@@ -110,37 +115,25 @@ fn directory_to_look_for_submodules(
110 directory_with_module_path.join(regular_rust_file_name) 115 directory_with_module_path.join(regular_rust_file_name)
111 } 116 }
112 } 117 }
113 _ => None,
114 }?; 118 }?;
115 119
116 let mut resulting_path = base_directory; 120 module_chain_to_containing_module_file(module, db)
117 for module in module_chain_to_containing_module_file(module, db) { 121 .into_iter()
118 if let Some(name) = module.name(db) { 122 .filter_map(|module| module.name(db))
119 resulting_path = resulting_path.join(&name.to_string())?; 123 .try_fold(base_directory, |path, name| path.join(&name.to_string()))
120 }
121 }
122
123 Some(resulting_path)
124} 124}
125 125
126fn module_chain_to_containing_module_file( 126fn module_chain_to_containing_module_file(
127 current_module: Module, 127 current_module: Module,
128 db: &RootDatabase, 128 db: &RootDatabase,
129) -> Vec<Module> { 129) -> Vec<Module> {
130 let mut path = Vec::new(); 130 let mut path =
131 131 iter::successors(Some(current_module), |current_module| current_module.parent(db))
132 let mut current_module = Some(current_module); 132 .take_while(|current_module| {
133 while let Some(ModuleSource::Module(_)) = 133 matches!(current_module.definition_source(db).value, ModuleSource::Module(_))
134 current_module.map(|module| module.definition_source(db).value) 134 })
135 { 135 .collect::<Vec<_>>();
136 if let Some(module) = current_module { 136 path.reverse();
137 path.insert(0, module);
138 current_module = module.parent(db);
139 } else {
140 current_module = None;
141 }
142 }
143
144 path 137 path
145} 138}
146 139
@@ -159,7 +152,7 @@ mod tests {
159 check( 152 check(
160 r#" 153 r#"
161 //- /lib.rs 154 //- /lib.rs
162 mod <|> 155 mod $0
163 //- /foo.rs 156 //- /foo.rs
164 fn foo() {} 157 fn foo() {}
165 //- /foo/ignored_foo.rs 158 //- /foo/ignored_foo.rs
@@ -181,7 +174,7 @@ mod tests {
181 check( 174 check(
182 r#" 175 r#"
183 //- /lib.rs 176 //- /lib.rs
184 mod <|> { 177 mod $0 {
185 178
186 } 179 }
187 //- /foo.rs 180 //- /foo.rs
@@ -196,7 +189,7 @@ mod tests {
196 check( 189 check(
197 r#" 190 r#"
198 //- /main.rs 191 //- /main.rs
199 mod <|> 192 mod $0
200 //- /foo.rs 193 //- /foo.rs
201 fn foo() {} 194 fn foo() {}
202 //- /foo/ignored_foo.rs 195 //- /foo/ignored_foo.rs
@@ -219,7 +212,7 @@ mod tests {
219 r#" 212 r#"
220 //- /main.rs 213 //- /main.rs
221 mod tests { 214 mod tests {
222 mod <|>; 215 mod $0;
223 } 216 }
224 //- /tests/foo.rs 217 //- /tests/foo.rs
225 fn foo() {} 218 fn foo() {}
@@ -237,7 +230,7 @@ mod tests {
237 //- /lib.rs 230 //- /lib.rs
238 mod foo; 231 mod foo;
239 //- /foo.rs 232 //- /foo.rs
240 mod <|>; 233 mod $0;
241 //- /foo/bar.rs 234 //- /foo/bar.rs
242 fn bar() {} 235 fn bar() {}
243 //- /foo/bar/ignored_bar.rs 236 //- /foo/bar/ignored_bar.rs
@@ -262,7 +255,7 @@ mod tests {
262 mod foo; 255 mod foo;
263 //- /foo.rs 256 //- /foo.rs
264 mod bar { 257 mod bar {
265 mod <|> 258 mod $0
266 } 259 }
267 //- /foo/bar/baz.rs 260 //- /foo/bar/baz.rs
268 fn baz() {} 261 fn baz() {}
@@ -288,7 +281,7 @@ mod tests {
288 // //- /src/bin.rs 281 // //- /src/bin.rs
289 // fn main() {} 282 // fn main() {}
290 // //- /src/bin/foo.rs 283 // //- /src/bin/foo.rs
291 // mod <|> 284 // mod $0
292 // //- /src/bin/bar.rs 285 // //- /src/bin/bar.rs
293 // fn bar() {} 286 // fn bar() {}
294 // //- /src/bin/bar/bar_ignored.rs 287 // //- /src/bin/bar/bar_ignored.rs
@@ -307,7 +300,7 @@ mod tests {
307 //- /src/bin.rs crate:main 300 //- /src/bin.rs crate:main
308 fn main() {} 301 fn main() {}
309 //- /src/bin/foo.rs 302 //- /src/bin/foo.rs
310 mod <|> 303 mod $0
311 //- /src/bin/bar.rs 304 //- /src/bin/bar.rs
312 mod foo; 305 mod foo;
313 fn bar() {} 306 fn bar() {}
diff --git a/crates/completion/src/completions/pattern.rs b/crates/completion/src/completions/pattern.rs
index eee31098d..595160ff5 100644
--- a/crates/completion/src/completions/pattern.rs
+++ b/crates/completion/src/completions/pattern.rs
@@ -71,7 +71,7 @@ static FOO: E = E::X;
71struct Bar { f: u32 } 71struct Bar { f: u32 }
72 72
73fn foo() { 73fn foo() {
74 match E::X { <|> } 74 match E::X { $0 }
75} 75}
76"#, 76"#,
77 expect![[r#" 77 expect![[r#"
@@ -92,7 +92,7 @@ macro_rules! m { ($e:expr) => { $e } }
92enum E { X } 92enum E { X }
93 93
94fn foo() { 94fn foo() {
95 m!(match E::X { <|> }) 95 m!(match E::X { $0 })
96} 96}
97"#, 97"#,
98 expect![[r#" 98 expect![[r#"
@@ -115,7 +115,7 @@ static FOO: E = E::X;
115struct Bar { f: u32 } 115struct Bar { f: u32 }
116 116
117fn foo() { 117fn foo() {
118 let <|> 118 let $0
119} 119}
120"#, 120"#,
121 expect![[r#" 121 expect![[r#"
@@ -133,7 +133,7 @@ enum E { X }
133static FOO: E = E::X; 133static FOO: E = E::X;
134struct Bar { f: u32 } 134struct Bar { f: u32 }
135 135
136fn foo(<|>) { 136fn foo($0) {
137} 137}
138"#, 138"#,
139 expect![[r#" 139 expect![[r#"
@@ -149,7 +149,7 @@ fn foo(<|>) {
149struct Bar { f: u32 } 149struct Bar { f: u32 }
150 150
151fn foo() { 151fn foo() {
152 let <|> 152 let $0
153} 153}
154"#, 154"#,
155 expect![[r#" 155 expect![[r#"
@@ -165,7 +165,7 @@ fn foo() {
165struct Foo { bar: String, baz: String } 165struct Foo { bar: String, baz: String }
166struct Bar(String, String); 166struct Bar(String, String);
167struct Baz; 167struct Baz;
168fn outer(<|>) {} 168fn outer($0) {}
169"#, 169"#,
170 expect![[r#" 170 expect![[r#"
171 bn Foo Foo { bar$1, baz$2 }: Foo$0 171 bn Foo Foo { bar$1, baz$2 }: Foo$0
@@ -182,7 +182,7 @@ struct Foo { bar: String, baz: String }
182struct Bar(String, String); 182struct Bar(String, String);
183struct Baz; 183struct Baz;
184fn outer() { 184fn outer() {
185 let <|> 185 let $0
186} 186}
187"#, 187"#,
188 expect![[r#" 188 expect![[r#"
@@ -201,7 +201,7 @@ struct Bar(String, String);
201struct Baz; 201struct Baz;
202fn outer() { 202fn outer() {
203 match () { 203 match () {
204 <|> 204 $0
205 } 205 }
206} 206}
207"#, 207"#,
@@ -225,7 +225,7 @@ use foo::*;
225 225
226fn outer() { 226fn outer() {
227 match () { 227 match () {
228 <|> 228 $0
229 } 229 }
230} 230}
231"#, 231"#,
@@ -244,7 +244,7 @@ fn outer() {
244struct Foo(i32); 244struct Foo(i32);
245fn main() { 245fn main() {
246 match Foo(92) { 246 match Foo(92) {
247 <|>(92) => (), 247 $0(92) => (),
248 } 248 }
249} 249}
250"#, 250"#,
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs
index 3883d6d21..9c34ed0b6 100644
--- a/crates/completion/src/completions/postfix.rs
+++ b/crates/completion/src/completions/postfix.rs
@@ -1,8 +1,8 @@
1//! Postfix completions, like `Ok(10).ifl<|>` => `if let Ok() = Ok(10) { <|> }`. 1//! Postfix completions, like `Ok(10).ifl$0` => `if let Ok() = Ok(10) { $0 }`.
2 2
3mod format_like; 3mod format_like;
4 4
5use ide_db::ty_filter::TryEnum; 5use ide_db::{helpers::SnippetCap, ty_filter::TryEnum};
6use syntax::{ 6use syntax::{
7 ast::{self, AstNode, AstToken}, 7 ast::{self, AstNode, AstToken},
8 SyntaxKind::{BLOCK_EXPR, EXPR_STMT}, 8 SyntaxKind::{BLOCK_EXPR, EXPR_STMT},
@@ -10,9 +10,8 @@ use syntax::{
10}; 10};
11use text_edit::TextEdit; 11use text_edit::TextEdit;
12 12
13use self::format_like::add_format_like_completions;
14use crate::{ 13use crate::{
15 config::SnippetCap, 14 completions::postfix::format_like::add_format_like_completions,
16 context::CompletionContext, 15 context::CompletionContext,
17 item::{Builder, CompletionKind}, 16 item::{Builder, CompletionKind},
18 CompletionItem, CompletionItemKind, Completions, 17 CompletionItem, CompletionItemKind, Completions,
@@ -36,11 +35,14 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
36 None => return, 35 None => return,
37 }; 36 };
38 37
38 let ref_removed_ty =
39 std::iter::successors(Some(receiver_ty.clone()), |ty| ty.remove_ref()).last().unwrap();
40
39 let cap = match ctx.config.snippet_cap { 41 let cap = match ctx.config.snippet_cap {
40 Some(it) => it, 42 Some(it) => it,
41 None => return, 43 None => return,
42 }; 44 };
43 let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty); 45 let try_enum = TryEnum::from_ty(&ctx.sema, &ref_removed_ty);
44 if let Some(try_enum) = &try_enum { 46 if let Some(try_enum) = &try_enum {
45 match try_enum { 47 match try_enum {
46 TryEnum::Result => { 48 TryEnum::Result => {
@@ -311,7 +313,7 @@ mod tests {
311 r#" 313 r#"
312fn main() { 314fn main() {
313 let bar = true; 315 let bar = true;
314 bar.<|> 316 bar.$0
315} 317}
316"#, 318"#,
317 expect![[r#" 319 expect![[r#"
@@ -343,7 +345,7 @@ fn foo(elt: bool) -> bool {
343 345
344fn main() { 346fn main() {
345 let bar = true; 347 let bar = true;
346 foo(bar.<|>) 348 foo(bar.$0)
347} 349}
348"#, 350"#,
349 expect![[r#" 351 expect![[r#"
@@ -369,7 +371,7 @@ fn main() {
369 r#" 371 r#"
370fn main() { 372fn main() {
371 let bar: u8 = 12; 373 let bar: u8 = 12;
372 bar.<|> 374 bar.$0
373} 375}
374"#, 376"#,
375 expect![[r#" 377 expect![[r#"
@@ -393,7 +395,7 @@ fn main() {
393 check( 395 check(
394 r#" 396 r#"
395fn main() { 397fn main() {
396 baz.l<|> 398 baz.l$0
397 res 399 res
398} 400}
399"#, 401"#,
@@ -425,7 +427,7 @@ enum Option<T> { Some(T), None }
425 427
426fn main() { 428fn main() {
427 let bar = Option::Some(true); 429 let bar = Option::Some(true);
428 bar.<|> 430 bar.$0
429} 431}
430"#, 432"#,
431 r#" 433 r#"
@@ -450,7 +452,7 @@ enum Result<T, E> { Ok(T), Err(E) }
450 452
451fn main() { 453fn main() {
452 let bar = Result::Ok(true); 454 let bar = Result::Ok(true);
453 bar.<|> 455 bar.$0
454} 456}
455"#, 457"#,
456 r#" 458 r#"
@@ -469,7 +471,7 @@ fn main() {
469 471
470 #[test] 472 #[test]
471 fn postfix_completion_works_for_ambiguous_float_literal() { 473 fn postfix_completion_works_for_ambiguous_float_literal() {
472 check_edit("refm", r#"fn main() { 42.<|> }"#, r#"fn main() { &mut 42 }"#) 474 check_edit("refm", r#"fn main() { 42.$0 }"#, r#"fn main() { &mut 42 }"#)
473 } 475 }
474 476
475 #[test] 477 #[test]
@@ -480,7 +482,7 @@ fn main() {
480macro_rules! m { ($e:expr) => { $e } } 482macro_rules! m { ($e:expr) => { $e } }
481fn main() { 483fn main() {
482 let bar: u8 = 12; 484 let bar: u8 = 12;
483 m!(bar.d<|>) 485 m!(bar.d$0)
484} 486}
485"#, 487"#,
486 r#" 488 r#"
@@ -495,55 +497,68 @@ fn main() {
495 497
496 #[test] 498 #[test]
497 fn postfix_completion_for_references() { 499 fn postfix_completion_for_references() {
498 check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#); 500 check_edit("dbg", r#"fn main() { &&42.$0 }"#, r#"fn main() { dbg!(&&42) }"#);
499 check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#); 501 check_edit("refm", r#"fn main() { &&42.$0 }"#, r#"fn main() { &&&mut 42 }"#);
502 check_edit(
503 "ifl",
504 r#"
505enum Option<T> { Some(T), None }
506
507fn main() {
508 let bar = &Option::Some(true);
509 bar.$0
510}
511"#,
512 r#"
513enum Option<T> { Some(T), None }
514
515fn main() {
516 let bar = &Option::Some(true);
517 if let Some($1) = bar {
518 $0
519}
520}
521"#,
522 )
500 } 523 }
501 524
502 #[test] 525 #[test]
503 fn postfix_completion_for_format_like_strings() { 526 fn postfix_completion_for_format_like_strings() {
504 check_edit( 527 check_edit(
505 "format", 528 "format",
506 r#"fn main() { "{some_var:?}".<|> }"#, 529 r#"fn main() { "{some_var:?}".$0 }"#,
507 r#"fn main() { format!("{:?}", some_var) }"#, 530 r#"fn main() { format!("{:?}", some_var) }"#,
508 ); 531 );
509 check_edit( 532 check_edit(
510 "panic", 533 "panic",
511 r#"fn main() { "Panic with {a}".<|> }"#, 534 r#"fn main() { "Panic with {a}".$0 }"#,
512 r#"fn main() { panic!("Panic with {}", a) }"#, 535 r#"fn main() { panic!("Panic with {}", a) }"#,
513 ); 536 );
514 check_edit( 537 check_edit(
515 "println", 538 "println",
516 r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".<|> }"#, 539 r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".$0 }"#,
517 r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }) }"#, 540 r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }) }"#,
518 ); 541 );
519 check_edit( 542 check_edit(
520 "loge", 543 "loge",
521 r#"fn main() { "{2+2}".<|> }"#, 544 r#"fn main() { "{2+2}".$0 }"#,
522 r#"fn main() { log::error!("{}", 2+2) }"#, 545 r#"fn main() { log::error!("{}", 2+2) }"#,
523 ); 546 );
524 check_edit( 547 check_edit(
525 "logt", 548 "logt",
526 r#"fn main() { "{2+2}".<|> }"#, 549 r#"fn main() { "{2+2}".$0 }"#,
527 r#"fn main() { log::trace!("{}", 2+2) }"#, 550 r#"fn main() { log::trace!("{}", 2+2) }"#,
528 ); 551 );
529 check_edit( 552 check_edit(
530 "logd", 553 "logd",
531 r#"fn main() { "{2+2}".<|> }"#, 554 r#"fn main() { "{2+2}".$0 }"#,
532 r#"fn main() { log::debug!("{}", 2+2) }"#, 555 r#"fn main() { log::debug!("{}", 2+2) }"#,
533 ); 556 );
534 check_edit( 557 check_edit("logi", r#"fn main() { "{2+2}".$0 }"#, r#"fn main() { log::info!("{}", 2+2) }"#);
535 "logi", 558 check_edit("logw", r#"fn main() { "{2+2}".$0 }"#, r#"fn main() { log::warn!("{}", 2+2) }"#);
536 r#"fn main() { "{2+2}".<|> }"#,
537 r#"fn main() { log::info!("{}", 2+2) }"#,
538 );
539 check_edit(
540 "logw",
541 r#"fn main() { "{2+2}".<|> }"#,
542 r#"fn main() { log::warn!("{}", 2+2) }"#,
543 );
544 check_edit( 559 check_edit(
545 "loge", 560 "loge",
546 r#"fn main() { "{2+2}".<|> }"#, 561 r#"fn main() { "{2+2}".$0 }"#,
547 r#"fn main() { log::error!("{}", 2+2) }"#, 562 r#"fn main() { log::error!("{}", 2+2) }"#,
548 ); 563 );
549 } 564 }
diff --git a/crates/completion/src/completions/postfix/format_like.rs b/crates/completion/src/completions/postfix/format_like.rs
index def4b13fb..3afc63021 100644
--- a/crates/completion/src/completions/postfix/format_like.rs
+++ b/crates/completion/src/completions/postfix/format_like.rs
@@ -14,12 +14,11 @@
14// + `logw` -> `log::warn!(...)` 14// + `logw` -> `log::warn!(...)`
15// + `loge` -> `log::error!(...)` 15// + `loge` -> `log::error!(...)`
16 16
17use crate::{ 17use ide_db::helpers::SnippetCap;
18 completions::postfix::postfix_snippet, config::SnippetCap, context::CompletionContext,
19 Completions,
20};
21use syntax::ast::{self, AstToken}; 18use syntax::ast::{self, AstToken};
22 19
20use crate::{completions::postfix::postfix_snippet, context::CompletionContext, Completions};
21
23/// Mapping ("postfix completion item" => "macro to use") 22/// Mapping ("postfix completion item" => "macro to use")
24static KINDS: &[(&str, &str)] = &[ 23static KINDS: &[(&str, &str)] = &[
25 ("format", "format!"), 24 ("format", "format!"),
diff --git a/crates/completion/src/completions/qualified_path.rs b/crates/completion/src/completions/qualified_path.rs
index 882c4dcbc..fa9e6e810 100644
--- a/crates/completion/src/completions/qualified_path.rs
+++ b/crates/completion/src/completions/qualified_path.rs
@@ -1,4 +1,4 @@
1//! Completion of paths, i.e. `some::prefix::<|>`. 1//! Completion of paths, i.e. `some::prefix::$0`.
2 2
3use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; 3use hir::{Adt, HasVisibility, PathResolution, ScopeDef};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
@@ -38,7 +38,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
38 if let ScopeDef::Unknown = def { 38 if let ScopeDef::Unknown = def {
39 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { 39 if let Some(name_ref) = ctx.name_ref_syntax.as_ref() {
40 if name_ref.syntax().text() == name.to_string().as_str() { 40 if name_ref.syntax().text() == name.to_string().as_str() {
41 // for `use self::foo<|>`, don't suggest `foo` as a completion 41 // for `use self::foo$0`, don't suggest `foo` as a completion
42 mark::hit!(dont_complete_current_use); 42 mark::hit!(dont_complete_current_use);
43 continue; 43 continue;
44 } 44 }
@@ -173,7 +173,7 @@ mod tests {
173 #[test] 173 #[test]
174 fn dont_complete_current_use() { 174 fn dont_complete_current_use() {
175 mark::check!(dont_complete_current_use); 175 mark::check!(dont_complete_current_use);
176 check(r#"use self::foo<|>;"#, expect![[""]]); 176 check(r#"use self::foo$0;"#, expect![[""]]);
177 } 177 }
178 178
179 #[test] 179 #[test]
@@ -181,7 +181,7 @@ mod tests {
181 check( 181 check(
182 r#" 182 r#"
183mod foo { pub struct S; } 183mod foo { pub struct S; }
184use self::{foo::*, bar<|>}; 184use self::{foo::*, bar$0};
185"#, 185"#,
186 expect![[r#" 186 expect![[r#"
187 st S 187 st S
@@ -192,18 +192,18 @@ use self::{foo::*, bar<|>};
192 192
193 #[test] 193 #[test]
194 fn dont_complete_primitive_in_use() { 194 fn dont_complete_primitive_in_use() {
195 check_builtin(r#"use self::<|>;"#, expect![[""]]); 195 check_builtin(r#"use self::$0;"#, expect![[""]]);
196 } 196 }
197 197
198 #[test] 198 #[test]
199 fn dont_complete_primitive_in_module_scope() { 199 fn dont_complete_primitive_in_module_scope() {
200 check_builtin(r#"fn foo() { self::<|> }"#, expect![[""]]); 200 check_builtin(r#"fn foo() { self::$0 }"#, expect![[""]]);
201 } 201 }
202 202
203 #[test] 203 #[test]
204 fn completes_primitives() { 204 fn completes_primitives() {
205 check_builtin( 205 check_builtin(
206 r#"fn main() { let _: <|> = 92; }"#, 206 r#"fn main() { let _: $0 = 92; }"#,
207 expect![[r#" 207 expect![[r#"
208 bt u32 208 bt u32
209 bt bool 209 bt bool
@@ -230,7 +230,7 @@ use self::{foo::*, bar<|>};
230 fn completes_mod_with_same_name_as_function() { 230 fn completes_mod_with_same_name_as_function() {
231 check( 231 check(
232 r#" 232 r#"
233use self::my::<|>; 233use self::my::$0;
234 234
235mod my { pub struct Bar; } 235mod my { pub struct Bar; }
236fn my() {} 236fn my() {}
@@ -245,7 +245,7 @@ fn my() {}
245 fn filters_visibility() { 245 fn filters_visibility() {
246 check( 246 check(
247 r#" 247 r#"
248use self::my::<|>; 248use self::my::$0;
249 249
250mod my { 250mod my {
251 struct Bar; 251 struct Bar;
@@ -264,7 +264,7 @@ mod my {
264 fn completes_use_item_starting_with_self() { 264 fn completes_use_item_starting_with_self() {
265 check( 265 check(
266 r#" 266 r#"
267use self::m::<|>; 267use self::m::$0;
268 268
269mod m { pub struct Bar; } 269mod m { pub struct Bar; }
270"#, 270"#,
@@ -282,7 +282,7 @@ mod m { pub struct Bar; }
282mod foo; 282mod foo;
283struct Spam; 283struct Spam;
284//- /foo.rs 284//- /foo.rs
285use crate::Sp<|> 285use crate::Sp$0
286"#, 286"#,
287 expect![[r#" 287 expect![[r#"
288 md foo 288 md foo
@@ -299,7 +299,7 @@ use crate::Sp<|>
299mod foo; 299mod foo;
300struct Spam; 300struct Spam;
301//- /foo.rs 301//- /foo.rs
302use crate::{Sp<|>}; 302use crate::{Sp$0};
303"#, 303"#,
304 expect![[r#" 304 expect![[r#"
305 md foo 305 md foo
@@ -320,7 +320,7 @@ pub mod bar {
320 } 320 }
321} 321}
322//- /foo.rs 322//- /foo.rs
323use crate::{bar::{baz::Sp<|>}}; 323use crate::{bar::{baz::Sp$0}};
324"#, 324"#,
325 expect![[r#" 325 expect![[r#"
326 st Spam 326 st Spam
@@ -333,7 +333,7 @@ use crate::{bar::{baz::Sp<|>}};
333 check( 333 check(
334 r#" 334 r#"
335enum E { Foo, Bar(i32) } 335enum E { Foo, Bar(i32) }
336fn foo() { let _ = E::<|> } 336fn foo() { let _ = E::$0 }
337"#, 337"#,
338 expect![[r#" 338 expect![[r#"
339 ev Foo () 339 ev Foo ()
@@ -356,7 +356,7 @@ impl S {
356 type T = i32; 356 type T = i32;
357} 357}
358 358
359fn foo() { let _ = S::<|> } 359fn foo() { let _ = S::$0 }
360"#, 360"#,
361 expect![[r#" 361 expect![[r#"
362 fn a() fn a() 362 fn a() fn a()
@@ -384,7 +384,7 @@ mod m {
384 } 384 }
385} 385}
386 386
387fn foo() { let _ = S::<|> } 387fn foo() { let _ = S::$0 }
388"#, 388"#,
389 expect![[r#" 389 expect![[r#"
390 fn public_method() pub(crate) fn public_method() 390 fn public_method() pub(crate) fn public_method()
@@ -401,7 +401,7 @@ fn foo() { let _ = S::<|> }
401enum E {}; 401enum E {};
402impl E { fn m() { } } 402impl E { fn m() { } }
403 403
404fn foo() { let _ = E::<|> } 404fn foo() { let _ = E::$0 }
405 "#, 405 "#,
406 expect![[r#" 406 expect![[r#"
407 fn m() fn m() 407 fn m() fn m()
@@ -416,7 +416,7 @@ fn foo() { let _ = E::<|> }
416union U {}; 416union U {};
417impl U { fn m() { } } 417impl U { fn m() { } }
418 418
419fn foo() { let _ = U::<|> } 419fn foo() { let _ = U::$0 }
420"#, 420"#,
421 expect![[r#" 421 expect![[r#"
422 fn m() fn m() 422 fn m() fn m()
@@ -429,7 +429,7 @@ fn foo() { let _ = U::<|> }
429 check( 429 check(
430 r#" 430 r#"
431//- /main.rs crate:main deps:foo 431//- /main.rs crate:main deps:foo
432use foo::<|>; 432use foo::$0;
433 433
434//- /foo/lib.rs crate:foo 434//- /foo/lib.rs crate:foo
435pub mod bar { pub struct S; } 435pub mod bar { pub struct S; }
@@ -446,7 +446,7 @@ pub mod bar { pub struct S; }
446 r#" 446 r#"
447trait Trait { fn m(); } 447trait Trait { fn m(); }
448 448
449fn foo() { let _ = Trait::<|> } 449fn foo() { let _ = Trait::$0 }
450"#, 450"#,
451 expect![[r#" 451 expect![[r#"
452 fn m() fn m() 452 fn m() fn m()
@@ -463,7 +463,7 @@ trait Trait { fn m(); }
463struct S; 463struct S;
464impl Trait for S {} 464impl Trait for S {}
465 465
466fn foo() { let _ = S::<|> } 466fn foo() { let _ = S::$0 }
467"#, 467"#,
468 expect![[r#" 468 expect![[r#"
469 fn m() fn m() 469 fn m() fn m()
@@ -480,7 +480,7 @@ trait Trait { fn m(); }
480struct S; 480struct S;
481impl Trait for S {} 481impl Trait for S {}
482 482
483fn foo() { let _ = <S as Trait>::<|> } 483fn foo() { let _ = <S as Trait>::$0 }
484"#, 484"#,
485 expect![[r#" 485 expect![[r#"
486 fn m() fn m() 486 fn m() fn m()
@@ -506,7 +506,7 @@ trait Sub: Super {
506 fn submethod(&self) {} 506 fn submethod(&self) {}
507} 507}
508 508
509fn foo<T: Sub>() { T::<|> } 509fn foo<T: Sub>() { T::$0 }
510"#, 510"#,
511 expect![[r#" 511 expect![[r#"
512 ta SubTy type SubTy; 512 ta SubTy type SubTy;
@@ -544,7 +544,7 @@ impl<T> Super for Wrap<T> {}
544impl<T> Sub for Wrap<T> { 544impl<T> Sub for Wrap<T> {
545 fn subfunc() { 545 fn subfunc() {
546 // Should be able to assume `Self: Sub + Super` 546 // Should be able to assume `Self: Sub + Super`
547 Self::<|> 547 Self::$0
548 } 548 }
549} 549}
550"#, 550"#,
@@ -570,7 +570,7 @@ impl S { fn foo() {} }
570type T = S; 570type T = S;
571impl T { fn bar() {} } 571impl T { fn bar() {} }
572 572
573fn main() { T::<|>; } 573fn main() { T::$0; }
574"#, 574"#,
575 expect![[r#" 575 expect![[r#"
576 fn foo() fn foo() 576 fn foo() fn foo()
@@ -586,7 +586,7 @@ fn main() { T::<|>; }
586#[macro_export] 586#[macro_export]
587macro_rules! foo { () => {} } 587macro_rules! foo { () => {} }
588 588
589fn main() { let _ = crate::<|> } 589fn main() { let _ = crate::$0 }
590 "#, 590 "#,
591 expect![[r##" 591 expect![[r##"
592 fn main() fn main() 592 fn main() fn main()
@@ -604,7 +604,7 @@ mod a {
604 const A: usize = 0; 604 const A: usize = 0;
605 mod b { 605 mod b {
606 const B: usize = 0; 606 const B: usize = 0;
607 mod c { use super::super::<|> } 607 mod c { use super::super::$0 }
608 } 608 }
609} 609}
610"#, 610"#,
@@ -619,7 +619,7 @@ mod a {
619 fn completes_reexported_items_under_correct_name() { 619 fn completes_reexported_items_under_correct_name() {
620 check( 620 check(
621 r#" 621 r#"
622fn foo() { self::m::<|> } 622fn foo() { self::m::$0 }
623 623
624mod m { 624mod m {
625 pub use super::p::wrong_fn as right_fn; 625 pub use super::p::wrong_fn as right_fn;
@@ -642,7 +642,7 @@ mod p {
642 check_edit( 642 check_edit(
643 "RightType", 643 "RightType",
644 r#" 644 r#"
645fn foo() { self::m::<|> } 645fn foo() { self::m::$0 }
646 646
647mod m { 647mod m {
648 pub use super::p::wrong_fn as right_fn; 648 pub use super::p::wrong_fn as right_fn;
@@ -677,7 +677,7 @@ mod p {
677 check( 677 check(
678 r#" 678 r#"
679macro_rules! m { ($e:expr) => { $e } } 679macro_rules! m { ($e:expr) => { $e } }
680fn main() { m!(self::f<|>); } 680fn main() { m!(self::f$0); }
681fn foo() {} 681fn foo() {}
682"#, 682"#,
683 expect![[r#" 683 expect![[r#"
@@ -691,7 +691,7 @@ fn foo() {}
691 fn function_mod_share_name() { 691 fn function_mod_share_name() {
692 check( 692 check(
693 r#" 693 r#"
694fn foo() { self::m::<|> } 694fn foo() { self::m::$0 }
695 695
696mod m { 696mod m {
697 pub mod z {} 697 pub mod z {}
@@ -716,7 +716,7 @@ impl<K, V> HashMap<K, V, RandomState> {
716 pub fn new() -> HashMap<K, V, RandomState> { } 716 pub fn new() -> HashMap<K, V, RandomState> { }
717} 717}
718fn foo() { 718fn foo() {
719 HashMap::<|> 719 HashMap::$0
720} 720}
721"#, 721"#,
722 expect![[r#" 722 expect![[r#"
@@ -730,7 +730,7 @@ fn foo() {
730 check( 730 check(
731 r#" 731 r#"
732mod foo { pub struct Foo; } 732mod foo { pub struct Foo; }
733#[foo::<|>] 733#[foo::$0]
734fn f() {} 734fn f() {}
735"#, 735"#,
736 expect![[""]], 736 expect![[""]],
@@ -749,7 +749,7 @@ fn foo(
749} 749}
750 750
751fn main() { 751fn main() {
752 fo<|> 752 fo$0
753} 753}
754"#, 754"#,
755 expect![[r#" 755 expect![[r#"
@@ -770,7 +770,7 @@ enum Foo {
770 770
771impl Foo { 771impl Foo {
772 fn foo(self) { 772 fn foo(self) {
773 Self::<|> 773 Self::$0
774 } 774 }
775} 775}
776"#, 776"#,
diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs
index 91bf4a8ad..bb6354ded 100644
--- a/crates/completion/src/completions/record.rs
+++ b/crates/completion/src/completions/record.rs
@@ -20,13 +20,17 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
20 20
21 let missing_fields = ctx.sema.record_literal_missing_fields(record_lit); 21 let missing_fields = ctx.sema.record_literal_missing_fields(record_lit);
22 if impl_default_trait && !missing_fields.is_empty() { 22 if impl_default_trait && !missing_fields.is_empty() {
23 let completion_text = "..Default::default()";
24 let completion_text = completion_text
25 .strip_prefix(ctx.token.to_string().as_str())
26 .unwrap_or(completion_text);
23 acc.add( 27 acc.add(
24 CompletionItem::new( 28 CompletionItem::new(
25 CompletionKind::Snippet, 29 CompletionKind::Snippet,
26 ctx.source_range(), 30 ctx.source_range(),
27 "..Default::default()", 31 "..Default::default()",
28 ) 32 )
29 .insert_text("..Default::default()") 33 .insert_text(completion_text)
30 .kind(CompletionItemKind::Field) 34 .kind(CompletionItemKind::Field)
31 .build(), 35 .build(),
32 ); 36 );
@@ -48,7 +52,10 @@ mod tests {
48 use expect_test::{expect, Expect}; 52 use expect_test::{expect, Expect};
49 use ide_db::helpers::FamousDefs; 53 use ide_db::helpers::FamousDefs;
50 54
51 use crate::{test_utils::completion_list, CompletionKind}; 55 use crate::{
56 test_utils::{self, completion_list},
57 CompletionKind,
58 };
52 59
53 fn check(ra_fixture: &str, expect: Expect) { 60 fn check(ra_fixture: &str, expect: Expect) {
54 let actual = completion_list(ra_fixture, CompletionKind::Reference); 61 let actual = completion_list(ra_fixture, CompletionKind::Reference);
@@ -63,6 +70,18 @@ mod tests {
63 expect.assert_eq(&actual); 70 expect.assert_eq(&actual);
64 } 71 }
65 72
73 fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
74 test_utils::check_edit(
75 what,
76 &format!(
77 "//- /main.rs crate:main deps:core{}\n{}",
78 ra_fixture_before,
79 FamousDefs::FIXTURE,
80 ),
81 &(ra_fixture_after.to_owned() + "\n"),
82 );
83 }
84
66 #[test] 85 #[test]
67 fn test_record_literal_field_default() { 86 fn test_record_literal_field_default() {
68 let test_code = r#" 87 let test_code = r#"
@@ -80,7 +99,7 @@ impl core::default::Default for S {
80fn process(f: S) { 99fn process(f: S) {
81 let other = S { 100 let other = S {
82 foo: 5, 101 foo: 5,
83 .<|> 102 .$0
84 }; 103 };
85} 104}
86"#; 105"#;
@@ -102,6 +121,51 @@ fn process(f: S) {
102 } 121 }
103 122
104 #[test] 123 #[test]
124 fn test_record_literal_field_default_completion() {
125 check_edit(
126 "..Default::default()",
127 r#"
128struct S { foo: u32, bar: usize }
129
130impl core::default::Default for S {
131 fn default() -> Self {
132 S {
133 foo: 0,
134 bar: 0,
135 }
136 }
137}
138
139fn process(f: S) {
140 let other = S {
141 foo: 5,
142 .$0
143 };
144}
145"#,
146 r#"
147struct S { foo: u32, bar: usize }
148
149impl core::default::Default for S {
150 fn default() -> Self {
151 S {
152 foo: 0,
153 bar: 0,
154 }
155 }
156}
157
158fn process(f: S) {
159 let other = S {
160 foo: 5,
161 ..Default::default()
162 };
163}
164"#,
165 );
166 }
167
168 #[test]
105 fn test_record_literal_field_without_default() { 169 fn test_record_literal_field_without_default() {
106 let test_code = r#" 170 let test_code = r#"
107struct S { foo: u32, bar: usize } 171struct S { foo: u32, bar: usize }
@@ -109,7 +173,7 @@ struct S { foo: u32, bar: usize }
109fn process(f: S) { 173fn process(f: S) {
110 let other = S { 174 let other = S {
111 foo: 5, 175 foo: 5,
112 .<|> 176 .$0
113 }; 177 };
114} 178}
115"#; 179"#;
@@ -137,7 +201,7 @@ struct S { foo: u32 }
137 201
138fn process(f: S) { 202fn process(f: S) {
139 match f { 203 match f {
140 S { f<|>: 92 } => (), 204 S { f$0: 92 } => (),
141 } 205 }
142} 206}
143"#, 207"#,
@@ -155,7 +219,7 @@ enum E { S { foo: u32, bar: () } }
155 219
156fn process(e: E) { 220fn process(e: E) {
157 match e { 221 match e {
158 E::S { <|> } => (), 222 E::S { $0 } => (),
159 } 223 }
160} 224}
161"#, 225"#,
@@ -175,7 +239,7 @@ struct S { foo: u32 }
175 239
176fn process(f: S) { 240fn process(f: S) {
177 m!(match f { 241 m!(match f {
178 S { f<|>: 92 } => (), 242 S { f$0: 92 } => (),
179 }) 243 })
180} 244}
181", 245",
@@ -199,7 +263,7 @@ fn main() {
199 foo1: 1, foo2: 2, 263 foo1: 1, foo2: 2,
200 bar: 3, baz: 4, 264 bar: 3, baz: 4,
201 }; 265 };
202 if let S { foo1, foo2: a, <|> } = s {} 266 if let S { foo1, foo2: a, $0 } = s {}
203} 267}
204"#, 268"#,
205 expect![[r#" 269 expect![[r#"
@@ -215,7 +279,7 @@ fn main() {
215 r#" 279 r#"
216struct A { the_field: u32 } 280struct A { the_field: u32 }
217fn foo() { 281fn foo() {
218 A { the<|> } 282 A { the$0 }
219} 283}
220"#, 284"#,
221 expect![[r#" 285 expect![[r#"
@@ -230,7 +294,7 @@ fn foo() {
230 r#" 294 r#"
231enum E { A { a: u32 } } 295enum E { A { a: u32 } }
232fn foo() { 296fn foo() {
233 let _ = E::A { <|> } 297 let _ = E::A { $0 }
234} 298}
235"#, 299"#,
236 expect![[r#" 300 expect![[r#"
@@ -247,7 +311,7 @@ struct A { a: u32 }
247struct B { b: u32 } 311struct B { b: u32 }
248 312
249fn foo() { 313fn foo() {
250 let _: A = B { <|> } 314 let _: A = B { $0 }
251} 315}
252"#, 316"#,
253 expect![[r#" 317 expect![[r#"
@@ -263,7 +327,7 @@ fn foo() {
263struct A<T> { a: T } 327struct A<T> { a: T }
264 328
265fn foo() { 329fn foo() {
266 let _: A<u32> = A { <|> } 330 let _: A<u32> = A { $0 }
267} 331}
268"#, 332"#,
269 expect![[r#" 333 expect![[r#"
@@ -279,7 +343,7 @@ fn foo() {
279macro_rules! m { ($e:expr) => { $e } } 343macro_rules! m { ($e:expr) => { $e } }
280struct A { the_field: u32 } 344struct A { the_field: u32 }
281fn foo() { 345fn foo() {
282 m!(A { the<|> }) 346 m!(A { the$0 })
283} 347}
284"#, 348"#,
285 expect![[r#" 349 expect![[r#"
@@ -299,7 +363,7 @@ struct S {
299 363
300fn main() { 364fn main() {
301 let foo1 = 1; 365 let foo1 = 1;
302 let s = S { foo1, foo2: 5, <|> } 366 let s = S { foo1, foo2: 5, $0 }
303} 367}
304"#, 368"#,
305 expect![[r#" 369 expect![[r#"
@@ -317,7 +381,7 @@ struct S { foo1: u32, foo2: u32 }
317 381
318fn main() { 382fn main() {
319 let foo1 = 1; 383 let foo1 = 1;
320 let s = S { foo1, <|> .. loop {} } 384 let s = S { foo1, $0 .. loop {} }
321} 385}
322"#, 386"#,
323 expect![[r#" 387 expect![[r#"
diff --git a/crates/completion/src/completions/snippet.rs b/crates/completion/src/completions/snippet.rs
index 842590130..df17a15c5 100644
--- a/crates/completion/src/completions/snippet.rs
+++ b/crates/completion/src/completions/snippet.rs
@@ -1,8 +1,10 @@
1//! This file provides snippet completions, like `pd` => `eprintln!(...)`. 1//! This file provides snippet completions, like `pd` => `eprintln!(...)`.
2 2
3use ide_db::helpers::SnippetCap;
4
3use crate::{ 5use crate::{
4 config::SnippetCap, item::Builder, CompletionContext, CompletionItem, CompletionItemKind, 6 item::Builder, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
5 CompletionKind, Completions, 7 Completions,
6}; 8};
7 9
8fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { 10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
@@ -81,7 +83,7 @@ mod tests {
81 #[test] 83 #[test]
82 fn completes_snippets_in_expressions() { 84 fn completes_snippets_in_expressions() {
83 check( 85 check(
84 r#"fn foo(x: i32) { <|> }"#, 86 r#"fn foo(x: i32) { $0 }"#,
85 expect![[r#" 87 expect![[r#"
86 sn pd 88 sn pd
87 sn ppd 89 sn ppd
@@ -91,8 +93,8 @@ mod tests {
91 93
92 #[test] 94 #[test]
93 fn should_not_complete_snippets_in_path() { 95 fn should_not_complete_snippets_in_path() {
94 check(r#"fn foo(x: i32) { ::foo<|> }"#, expect![[""]]); 96 check(r#"fn foo(x: i32) { ::foo$0 }"#, expect![[""]]);
95 check(r#"fn foo(x: i32) { ::<|> }"#, expect![[""]]); 97 check(r#"fn foo(x: i32) { ::$0 }"#, expect![[""]]);
96 } 98 }
97 99
98 #[test] 100 #[test]
@@ -101,7 +103,7 @@ mod tests {
101 r#" 103 r#"
102#[cfg(test)] 104#[cfg(test)]
103mod tests { 105mod tests {
104 <|> 106 $0
105} 107}
106"#, 108"#,
107 expect![[r#" 109 expect![[r#"
diff --git a/crates/completion/src/completions/trait_impl.rs b/crates/completion/src/completions/trait_impl.rs
index c4e0d0669..135ae49dc 100644
--- a/crates/completion/src/completions/trait_impl.rs
+++ b/crates/completion/src/completions/trait_impl.rs
@@ -15,7 +15,7 @@
15//! } 15//! }
16//! 16//!
17//! impl SomeTrait for () { 17//! impl SomeTrait for () {
18//! fn f<|> 18//! fn f$0
19//! } 19//! }
20//! ``` 20//! ```
21//! 21//!
@@ -27,7 +27,7 @@
27//! # } 27//! # }
28//! 28//!
29//! impl SomeTrait for () { 29//! impl SomeTrait for () {
30//! fn foo() {}<|> 30//! fn foo() {}$0
31//! } 31//! }
32//! ``` 32//! ```
33 33
@@ -82,7 +82,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
82 82
83fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> { 83fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> {
84 let mut token = ctx.token.clone(); 84 let mut token = ctx.token.clone();
85 // For keywork without name like `impl .. { fn <|> }`, the current position is inside 85 // For keywork without name like `impl .. { fn $0 }`, the current position is inside
86 // the whitespace token, which is outside `FN` syntax node. 86 // the whitespace token, which is outside `FN` syntax node.
87 // We need to follow the previous token in this case. 87 // We need to follow the previous token in this case.
88 if token.kind() == SyntaxKind::WHITESPACE { 88 if token.kind() == SyntaxKind::WHITESPACE {
@@ -90,20 +90,20 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Synt
90 } 90 }
91 91
92 let impl_item_offset = match token.kind() { 92 let impl_item_offset = match token.kind() {
93 // `impl .. { const <|> }` 93 // `impl .. { const $0 }`
94 // ERROR 0 94 // ERROR 0
95 // CONST_KW <- * 95 // CONST_KW <- *
96 SyntaxKind::CONST_KW => 0, 96 T![const] => 0,
97 // `impl .. { fn/type <|> }` 97 // `impl .. { fn/type $0 }`
98 // FN/TYPE_ALIAS 0 98 // FN/TYPE_ALIAS 0
99 // FN_KW <- * 99 // FN_KW <- *
100 SyntaxKind::FN_KW | SyntaxKind::TYPE_KW => 0, 100 T![fn] | T![type] => 0,
101 // `impl .. { fn/type/const foo<|> }` 101 // `impl .. { fn/type/const foo$0 }`
102 // FN/TYPE_ALIAS/CONST 1 102 // FN/TYPE_ALIAS/CONST 1
103 // NAME 0 103 // NAME 0
104 // IDENT <- * 104 // IDENT <- *
105 SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME => 1, 105 SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME => 1,
106 // `impl .. { foo<|> }` 106 // `impl .. { foo$0 }`
107 // MACRO_CALL 3 107 // MACRO_CALL 3
108 // PATH 2 108 // PATH 2
109 // PATH_SEGMENT 1 109 // PATH_SEGMENT 1
@@ -120,8 +120,8 @@ fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, Synt
120 // <item> 120 // <item>
121 let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?; 121 let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?;
122 let kind = match impl_item.kind() { 122 let kind = match impl_item.kind() {
123 // `impl ... { const <|> fn/type/const }` 123 // `impl ... { const $0 fn/type/const }`
124 _ if token.kind() == SyntaxKind::CONST_KW => ImplCompletionKind::Const, 124 _ if token.kind() == T![const] => ImplCompletionKind::Const,
125 SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const, 125 SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const,
126 SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias, 126 SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias,
127 SyntaxKind::FN => ImplCompletionKind::Fn, 127 SyntaxKind::FN => ImplCompletionKind::Fn,
@@ -156,19 +156,21 @@ fn add_function_impl(
156 }; 156 };
157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end()); 157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
158 158
159 let function_decl = function_declaration(&func.source(ctx.db).value); 159 if let Some(src) = func.source(ctx.db) {
160 match ctx.config.snippet_cap { 160 let function_decl = function_declaration(&src.value);
161 Some(cap) => { 161 match ctx.config.snippet_cap {
162 let snippet = format!("{} {{\n $0\n}}", function_decl); 162 Some(cap) => {
163 builder.snippet_edit(cap, TextEdit::replace(range, snippet)) 163 let snippet = format!("{} {{\n $0\n}}", function_decl);
164 } 164 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
165 None => { 165 }
166 let header = format!("{} {{", function_decl); 166 None => {
167 builder.text_edit(TextEdit::replace(range, header)) 167 let header = format!("{} {{", function_decl);
168 builder.text_edit(TextEdit::replace(range, header))
169 }
168 } 170 }
171 .kind(completion_kind)
172 .add_to(acc);
169 } 173 }
170 .kind(completion_kind)
171 .add_to(acc);
172} 174}
173 175
174fn add_type_alias_impl( 176fn add_type_alias_impl(
@@ -200,16 +202,19 @@ fn add_const_impl(
200 let const_name = const_.name(ctx.db).map(|n| n.to_string()); 202 let const_name = const_.name(ctx.db).map(|n| n.to_string());
201 203
202 if let Some(const_name) = const_name { 204 if let Some(const_name) = const_name {
203 let snippet = make_const_compl_syntax(&const_.source(ctx.db).value); 205 if let Some(source) = const_.source(ctx.db) {
204 206 let snippet = make_const_compl_syntax(&source.value);
205 let range = TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); 207
206 208 let range =
207 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 209 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
208 .text_edit(TextEdit::replace(range, snippet)) 210
209 .lookup_by(const_name) 211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
210 .kind(CompletionItemKind::Const) 212 .text_edit(TextEdit::replace(range, snippet))
211 .set_documentation(const_.docs(ctx.db)) 213 .lookup_by(const_name)
212 .add_to(acc); 214 .kind(CompletionItemKind::Const)
215 .set_documentation(const_.docs(ctx.db))
216 .add_to(acc);
217 }
213 } 218 }
214} 219}
215 220
@@ -262,7 +267,7 @@ trait Test {
262struct T; 267struct T;
263 268
264impl Test for T { 269impl Test for T {
265 t<|> 270 t$0
266} 271}
267"#, 272"#,
268 expect![[" 273 expect![["
@@ -282,7 +287,7 @@ struct T;
282 287
283impl Test for T { 288impl Test for T {
284 fn test() { 289 fn test() {
285 t<|> 290 t$0
286 } 291 }
287} 292}
288", 293",
@@ -296,7 +301,7 @@ struct T;
296 301
297impl Test for T { 302impl Test for T {
298 fn test() { 303 fn test() {
299 fn t<|> 304 fn t$0
300 } 305 }
301} 306}
302", 307",
@@ -310,7 +315,7 @@ struct T;
310 315
311impl Test for T { 316impl Test for T {
312 fn test() { 317 fn test() {
313 fn <|> 318 fn $0
314 } 319 }
315} 320}
316", 321",
@@ -325,7 +330,7 @@ struct T;
325 330
326impl Test for T { 331impl Test for T {
327 fn test() { 332 fn test() {
328 foo.<|> 333 foo.$0
329 } 334 }
330} 335}
331", 336",
@@ -338,7 +343,7 @@ trait Test { fn test(_: i32); fn test2(); }
338struct T; 343struct T;
339 344
340impl Test for T { 345impl Test for T {
341 fn test(t<|>) 346 fn test(t$0)
342} 347}
343", 348",
344 expect![[""]], 349 expect![[""]],
@@ -350,7 +355,7 @@ trait Test { fn test(_: fn()); fn test2(); }
350struct T; 355struct T;
351 356
352impl Test for T { 357impl Test for T {
353 fn test(f: fn <|>) 358 fn test(f: fn $0)
354} 359}
355", 360",
356 expect![[""]], 361 expect![[""]],
@@ -365,7 +370,7 @@ trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
365struct T; 370struct T;
366 371
367impl Test for T { 372impl Test for T {
368 const TEST: fn <|> 373 const TEST: fn $0
369} 374}
370", 375",
371 expect![[""]], 376 expect![[""]],
@@ -377,7 +382,7 @@ trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
377struct T; 382struct T;
378 383
379impl Test for T { 384impl Test for T {
380 const TEST: T<|> 385 const TEST: T$0
381} 386}
382", 387",
383 expect![[""]], 388 expect![[""]],
@@ -389,7 +394,7 @@ trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
389struct T; 394struct T;
390 395
391impl Test for T { 396impl Test for T {
392 const TEST: u32 = f<|> 397 const TEST: u32 = f$0
393} 398}
394", 399",
395 expect![[""]], 400 expect![[""]],
@@ -402,7 +407,7 @@ struct T;
402 407
403impl Test for T { 408impl Test for T {
404 const TEST: u32 = { 409 const TEST: u32 = {
405 t<|> 410 t$0
406 }; 411 };
407} 412}
408", 413",
@@ -416,7 +421,7 @@ struct T;
416 421
417impl Test for T { 422impl Test for T {
418 const TEST: u32 = { 423 const TEST: u32 = {
419 fn <|> 424 fn $0
420 }; 425 };
421} 426}
422", 427",
@@ -430,7 +435,7 @@ struct T;
430 435
431impl Test for T { 436impl Test for T {
432 const TEST: u32 = { 437 const TEST: u32 = {
433 fn t<|> 438 fn t$0
434 }; 439 };
435} 440}
436", 441",
@@ -446,7 +451,7 @@ trait Test { type Test; type Test2; fn test(); }
446struct T; 451struct T;
447 452
448impl Test for T { 453impl Test for T {
449 type Test = T<|>; 454 type Test = T$0;
450} 455}
451", 456",
452 expect![[""]], 457 expect![[""]],
@@ -458,7 +463,7 @@ trait Test { type Test; type Test2; fn test(); }
458struct T; 463struct T;
459 464
460impl Test for T { 465impl Test for T {
461 type Test = fn <|>; 466 type Test = fn $0;
462} 467}
463", 468",
464 expect![[""]], 469 expect![[""]],
@@ -476,7 +481,7 @@ trait Test {
476struct T; 481struct T;
477 482
478impl Test for T { 483impl Test for T {
479 t<|> 484 t$0
480} 485}
481"#, 486"#,
482 r#" 487 r#"
@@ -505,7 +510,7 @@ trait Test {
505struct T; 510struct T;
506 511
507impl Test for T { 512impl Test for T {
508 fn t<|> 513 fn t$0
509} 514}
510"#, 515"#,
511 r#" 516 r#"
@@ -535,7 +540,7 @@ struct T;
535 540
536impl Test for T { 541impl Test for T {
537 fn foo() {} 542 fn foo() {}
538 fn f<|> 543 fn f$0
539} 544}
540"#, 545"#,
541 expect![[r#" 546 expect![[r#"
@@ -555,7 +560,7 @@ trait Test {
555struct T; 560struct T;
556 561
557impl Test for T { 562impl Test for T {
558 fn f<|> 563 fn f$0
559} 564}
560"#, 565"#,
561 r#" 566 r#"
@@ -580,7 +585,7 @@ trait Test {
580struct T; 585struct T;
581 586
582impl Test for T { 587impl Test for T {
583 fn f<|> 588 fn f$0
584} 589}
585"#, 590"#,
586 r#" 591 r#"
@@ -609,7 +614,7 @@ trait Test {
609} 614}
610 615
611impl Test for () { 616impl Test for () {
612 type S<|> 617 type S$0
613} 618}
614"#, 619"#,
615 " 620 "
@@ -634,7 +639,7 @@ trait Test {
634} 639}
635 640
636impl Test for () { 641impl Test for () {
637 const S<|> 642 const S$0
638} 643}
639"#, 644"#,
640 " 645 "
@@ -656,7 +661,7 @@ trait Test {
656} 661}
657 662
658impl Test for () { 663impl Test for () {
659 const S<|> 664 const S$0
660} 665}
661"#, 666"#,
662 " 667 "
@@ -719,7 +724,7 @@ impl Test for T {{
719 // Enumerate some possible next siblings. 724 // Enumerate some possible next siblings.
720 for next_sibling in &[ 725 for next_sibling in &[
721 "", 726 "",
722 "fn other_fn() {}", // `const <|> fn` -> `const fn` 727 "fn other_fn() {}", // `const $0 fn` -> `const fn`
723 "type OtherType = i32;", 728 "type OtherType = i32;",
724 "const OTHER_CONST: i32 = 0;", 729 "const OTHER_CONST: i32 = 0;",
725 "async fn other_fn() {}", 730 "async fn other_fn() {}",
@@ -728,9 +733,9 @@ impl Test for T {{
728 "default type OtherType = i32;", 733 "default type OtherType = i32;",
729 "default const OTHER_CONST: i32 = 0;", 734 "default const OTHER_CONST: i32 = 0;",
730 ] { 735 ] {
731 test("bar", "fn <|>", "fn bar() {\n $0\n}", next_sibling); 736 test("bar", "fn $0", "fn bar() {\n $0\n}", next_sibling);
732 test("Foo", "type <|>", "type Foo = ", next_sibling); 737 test("Foo", "type $0", "type Foo = ", next_sibling);
733 test("CONST", "const <|>", "const CONST: u16 = ", next_sibling); 738 test("CONST", "const $0", "const CONST: u16 = ", next_sibling);
734 } 739 }
735 } 740 }
736} 741}
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 81a6d00e2..12cdb869d 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -46,7 +46,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
46 acc.add_resolution(ctx, name.to_string(), &res) 46 acc.add_resolution(ctx, name.to_string(), &res)
47 }); 47 });
48 48
49 if ctx.config.enable_autoimport_completions && ctx.config.resolve_additional_edits_lazily() { 49 if ctx.config.enable_autoimport_completions {
50 fuzzy_completion(acc, ctx); 50 fuzzy_completion(acc, ctx);
51 } 51 }
52} 52}
@@ -85,7 +85,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
85// 85//
86// ``` 86// ```
87// fn main() { 87// fn main() {
88// pda<|> 88// pda$0
89// } 89// }
90// # pub mod std { pub mod marker { pub struct PhantomData { } } } 90// # pub mod std { pub mod marker { pub struct PhantomData { } } }
91// ``` 91// ```
@@ -124,8 +124,8 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
124// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 124// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
125// capability enabled. 125// capability enabled.
126fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 126fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
127 let _p = profile::span("fuzzy_completion");
128 let potential_import_name = ctx.token.to_string(); 127 let potential_import_name = ctx.token.to_string();
128 let _p = profile::span("fuzzy_completion").detail(|| potential_import_name.clone());
129 129
130 if potential_import_name.len() < 2 { 130 if potential_import_name.len() < 2 {
131 return None; 131 return None;
@@ -142,6 +142,7 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()
142 Some(40), 142 Some(40),
143 potential_import_name, 143 potential_import_name,
144 true, 144 true,
145 true,
145 ) 146 )
146 .filter_map(|import_candidate| { 147 .filter_map(|import_candidate| {
147 Some(match import_candidate { 148 Some(match import_candidate {
@@ -191,12 +192,14 @@ mod tests {
191 use test_utils::mark; 192 use test_utils::mark;
192 193
193 use crate::{ 194 use crate::{
194 test_utils::{check_edit, check_edit_with_config, completion_list_with_config}, 195 test_utils::{
196 check_edit, check_edit_with_config, completion_list_with_config, TEST_CONFIG,
197 },
195 CompletionConfig, CompletionKind, 198 CompletionConfig, CompletionKind,
196 }; 199 };
197 200
198 fn check(ra_fixture: &str, expect: Expect) { 201 fn check(ra_fixture: &str, expect: Expect) {
199 check_with_config(CompletionConfig::default(), ra_fixture, expect); 202 check_with_config(TEST_CONFIG, ra_fixture, expect);
200 } 203 }
201 204
202 fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) { 205 fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) {
@@ -204,20 +207,12 @@ mod tests {
204 expect.assert_eq(&actual) 207 expect.assert_eq(&actual)
205 } 208 }
206 209
207 fn fuzzy_completion_config() -> CompletionConfig {
208 let mut completion_config = CompletionConfig::default();
209 completion_config
210 .active_resolve_capabilities
211 .insert(crate::CompletionResolveCapability::AdditionalTextEdits);
212 completion_config
213 }
214
215 #[test] 210 #[test]
216 fn self_fulfilling_completion() { 211 fn self_fulfilling_completion() {
217 mark::check!(self_fulfilling_completion); 212 mark::check!(self_fulfilling_completion);
218 check( 213 check(
219 r#" 214 r#"
220use foo<|> 215use foo$0
221use std::collections; 216use std::collections;
222"#, 217"#,
223 expect![[r#" 218 expect![[r#"
@@ -234,7 +229,7 @@ enum Enum { A, B }
234fn quux(x: Option<Enum>) { 229fn quux(x: Option<Enum>) {
235 match x { 230 match x {
236 None => (), 231 None => (),
237 Some(en<|> @ Enum::A) => (), 232 Some(en$0 @ Enum::A) => (),
238 } 233 }
239} 234}
240"#, 235"#,
@@ -250,7 +245,7 @@ enum Enum { A, B }
250fn quux(x: Option<Enum>) { 245fn quux(x: Option<Enum>) {
251 match x { 246 match x {
252 None => (), 247 None => (),
253 Some(ref en<|>) => (), 248 Some(ref en$0) => (),
254 } 249 }
255} 250}
256"#, 251"#,
@@ -266,7 +261,7 @@ enum Enum { A, B }
266fn quux(x: Option<Enum>) { 261fn quux(x: Option<Enum>) {
267 match x { 262 match x {
268 None => (), 263 None => (),
269 Some(En<|>) => (), 264 Some(En$0) => (),
270 } 265 }
271} 266}
272"#, 267"#,
@@ -282,7 +277,7 @@ fn quux(x: Option<Enum>) {
282 r#" 277 r#"
283fn quux(x: i32) { 278fn quux(x: i32) {
284 let y = 92; 279 let y = 92;
285 1 + <|>; 280 1 + $0;
286 let z = (); 281 let z = ();
287} 282}
288"#, 283"#,
@@ -304,7 +299,7 @@ fn quux() {
304 }; 299 };
305 if let Some(a) = bar() { 300 if let Some(a) = bar() {
306 let b = 62; 301 let b = 62;
307 1 + <|> 302 1 + $0
308 } 303 }
309} 304}
310"#, 305"#,
@@ -321,7 +316,7 @@ fn quux() {
321 check( 316 check(
322 r#" 317 r#"
323fn quux() { 318fn quux() {
324 for x in &[1, 2, 3] { <|> } 319 for x in &[1, 2, 3] { $0 }
325} 320}
326"#, 321"#,
327 expect![[r#" 322 expect![[r#"
@@ -339,7 +334,7 @@ fn quux() {
339 r#" 334 r#"
340fn main() { 335fn main() {
341 let wherewolf = 92; 336 let wherewolf = 92;
342 drop(where<|>) 337 drop(where$0)
343} 338}
344"#, 339"#,
345 r#" 340 r#"
@@ -354,7 +349,7 @@ fn main() {
354 #[test] 349 #[test]
355 fn completes_generic_params() { 350 fn completes_generic_params() {
356 check( 351 check(
357 r#"fn quux<T>() { <|> }"#, 352 r#"fn quux<T>() { $0 }"#,
358 expect![[r#" 353 expect![[r#"
359 tp T 354 tp T
360 fn quux() fn quux<T>() 355 fn quux() fn quux<T>()
@@ -365,7 +360,7 @@ fn main() {
365 #[test] 360 #[test]
366 fn completes_generic_params_in_struct() { 361 fn completes_generic_params_in_struct() {
367 check( 362 check(
368 r#"struct S<T> { x: <|>}"#, 363 r#"struct S<T> { x: $0}"#,
369 expect![[r#" 364 expect![[r#"
370 tp Self 365 tp Self
371 tp T 366 tp T
@@ -377,7 +372,7 @@ fn main() {
377 #[test] 372 #[test]
378 fn completes_self_in_enum() { 373 fn completes_self_in_enum() {
379 check( 374 check(
380 r#"enum X { Y(<|>) }"#, 375 r#"enum X { Y($0) }"#,
381 expect![[r#" 376 expect![[r#"
382 tp Self 377 tp Self
383 en X 378 en X
@@ -391,7 +386,7 @@ fn main() {
391 r#" 386 r#"
392struct S; 387struct S;
393enum E {} 388enum E {}
394fn quux() { <|> } 389fn quux() { $0 }
395"#, 390"#,
396 expect![[r#" 391 expect![[r#"
397 st S 392 st S
@@ -408,7 +403,7 @@ fn quux() { <|> }
408 "_alpha", 403 "_alpha",
409 r#" 404 r#"
410fn main() { 405fn main() {
411 _<|> 406 _$0
412} 407}
413fn _alpha() {} 408fn _alpha() {}
414"#, 409"#,
@@ -426,7 +421,7 @@ fn _alpha() {}
426 check( 421 check(
427 r#" 422 r#"
428//- /lib.rs crate:main deps:other_crate 423//- /lib.rs crate:main deps:other_crate
429use <|>; 424use $0;
430 425
431//- /other_crate/lib.rs crate:other_crate 426//- /other_crate/lib.rs crate:other_crate
432// nothing here 427// nothing here
@@ -444,7 +439,7 @@ use <|>;
444struct Foo; 439struct Foo;
445mod m { 440mod m {
446 struct Bar; 441 struct Bar;
447 fn quux() { <|> } 442 fn quux() { $0 }
448} 443}
449"#, 444"#,
450 expect![[r#" 445 expect![[r#"
@@ -459,7 +454,7 @@ mod m {
459 check( 454 check(
460 r#" 455 r#"
461struct Foo; 456struct Foo;
462fn x() -> <|> 457fn x() -> $0
463"#, 458"#,
464 expect![[r#" 459 expect![[r#"
465 st Foo 460 st Foo
@@ -476,7 +471,7 @@ fn foo() {
476 let bar = 92; 471 let bar = 92;
477 { 472 {
478 let bar = 62; 473 let bar = 62;
479 drop(<|>) 474 drop($0)
480 } 475 }
481} 476}
482"#, 477"#,
@@ -492,7 +487,7 @@ fn foo() {
492 #[test] 487 #[test]
493 fn completes_self_in_methods() { 488 fn completes_self_in_methods() {
494 check( 489 check(
495 r#"impl S { fn foo(&self) { <|> } }"#, 490 r#"impl S { fn foo(&self) { $0 } }"#,
496 expect![[r#" 491 expect![[r#"
497 bn self &{unknown} 492 bn self &{unknown}
498 tp Self 493 tp Self
@@ -505,7 +500,7 @@ fn foo() {
505 check( 500 check(
506 r#" 501 r#"
507//- /main.rs crate:main deps:std 502//- /main.rs crate:main deps:std
508fn foo() { let x: <|> } 503fn foo() { let x: $0 }
509 504
510//- /std/lib.rs crate:std 505//- /std/lib.rs crate:std
511#[prelude_import] 506#[prelude_import]
@@ -526,7 +521,7 @@ mod prelude { struct Option; }
526 check( 521 check(
527 r#" 522 r#"
528//- /main.rs crate:main deps:core,std 523//- /main.rs crate:main deps:core,std
529fn foo() { let x: <|> } 524fn foo() { let x: $0 }
530 525
531//- /core/lib.rs crate:core 526//- /core/lib.rs crate:core
532#[prelude_import] 527#[prelude_import]
@@ -567,7 +562,7 @@ mod m2 {
567 macro_rules! baz { () => {} } 562 macro_rules! baz { () => {} }
568} 563}
569 564
570fn main() { let v = <|> } 565fn main() { let v = $0 }
571"#, 566"#,
572 expect![[r##" 567 expect![[r##"
573 md m1 568 md m1
@@ -586,7 +581,7 @@ fn main() { let v = <|> }
586 check( 581 check(
587 r#" 582 r#"
588macro_rules! foo { () => {} } 583macro_rules! foo { () => {} }
589fn foo() { <|> } 584fn foo() { $0 }
590"#, 585"#,
591 expect![[r#" 586 expect![[r#"
592 fn foo() fn foo() 587 fn foo() fn foo()
@@ -600,7 +595,7 @@ fn foo() { <|> }
600 check( 595 check(
601 r#" 596 r#"
602macro_rules! foo { () => {} } 597macro_rules! foo { () => {} }
603fn main() { let x: <|> } 598fn main() { let x: $0 }
604"#, 599"#,
605 expect![[r#" 600 expect![[r#"
606 fn main() fn main() 601 fn main() fn main()
@@ -614,7 +609,7 @@ fn main() { let x: <|> }
614 check( 609 check(
615 r#" 610 r#"
616macro_rules! foo { () => {} } 611macro_rules! foo { () => {} }
617fn main() { <|> } 612fn main() { $0 }
618"#, 613"#,
619 expect![[r#" 614 expect![[r#"
620 fn main() fn main() 615 fn main() fn main()
@@ -628,7 +623,7 @@ fn main() { <|> }
628 check( 623 check(
629 r#" 624 r#"
630fn main() { 625fn main() {
631 return f<|>; 626 return f$0;
632 fn frobnicate() {} 627 fn frobnicate() {}
633} 628}
634"#, 629"#,
@@ -646,7 +641,7 @@ fn main() {
646macro_rules! m { ($e:expr) => { $e } } 641macro_rules! m { ($e:expr) => { $e } }
647fn quux(x: i32) { 642fn quux(x: i32) {
648 let y = 92; 643 let y = 92;
649 m!(<|>); 644 m!($0);
650} 645}
651"#, 646"#,
652 expect![[r#" 647 expect![[r#"
@@ -665,7 +660,7 @@ fn quux(x: i32) {
665macro_rules! m { ($e:expr) => { $e } } 660macro_rules! m { ($e:expr) => { $e } }
666fn quux(x: i32) { 661fn quux(x: i32) {
667 let y = 92; 662 let y = 92;
668 m!(x<|>); 663 m!(x$0);
669} 664}
670", 665",
671 expect![[r#" 666 expect![[r#"
@@ -684,7 +679,7 @@ fn quux(x: i32) {
684macro_rules! m { ($e:expr) => { $e } } 679macro_rules! m { ($e:expr) => { $e } }
685fn quux(x: i32) { 680fn quux(x: i32) {
686 let y = 92; 681 let y = 92;
687 m!(x<|> 682 m!(x$0
688} 683}
689"#, 684"#,
690 expect![[r#" 685 expect![[r#"
@@ -702,7 +697,7 @@ fn quux(x: i32) {
702 r#" 697 r#"
703use spam::Quux; 698use spam::Quux;
704 699
705fn main() { <|> } 700fn main() { $0 }
706"#, 701"#,
707 expect![[r#" 702 expect![[r#"
708 fn main() fn main() 703 fn main() fn main()
@@ -719,7 +714,7 @@ enum Foo { Bar, Baz, Quux }
719 714
720fn main() { 715fn main() {
721 let foo = Foo::Quux; 716 let foo = Foo::Quux;
722 match foo { Qu<|> } 717 match foo { Qu$0 }
723} 718}
724"#, 719"#,
725 expect![[r#" 720 expect![[r#"
@@ -739,7 +734,7 @@ enum Foo { Bar, Baz, Quux }
739 734
740fn main() { 735fn main() {
741 let foo = Foo::Quux; 736 let foo = Foo::Quux;
742 match &foo { Qu<|> } 737 match &foo { Qu$0 }
743} 738}
744"#, 739"#,
745 expect![[r#" 740 expect![[r#"
@@ -759,7 +754,7 @@ enum Foo { Bar, Baz, Quux }
759 754
760fn main() { 755fn main() {
761 let foo = Foo::Quux; 756 let foo = Foo::Quux;
762 if let Qu<|> = foo { } 757 if let Qu$0 = foo { }
763} 758}
764"#, 759"#,
765 expect![[r#" 760 expect![[r#"
@@ -776,7 +771,7 @@ fn main() {
776 check( 771 check(
777 r#" 772 r#"
778enum Foo { Bar, Baz, Quux } 773enum Foo { Bar, Baz, Quux }
779fn main() { let foo: Foo = Q<|> } 774fn main() { let foo: Foo = Q$0 }
780"#, 775"#,
781 expect![[r#" 776 expect![[r#"
782 ev Foo::Bar () 777 ev Foo::Bar ()
@@ -793,7 +788,7 @@ fn main() { let foo: Foo = Q<|> }
793 check( 788 check(
794 r#" 789 r#"
795mod m { pub enum E { V } } 790mod m { pub enum E { V } }
796fn f() -> m::E { V<|> } 791fn f() -> m::E { V$0 }
797"#, 792"#,
798 expect![[r#" 793 expect![[r#"
799 ev m::E::V () 794 ev m::E::V ()
@@ -808,7 +803,7 @@ fn f() -> m::E { V<|> }
808 check( 803 check(
809 r#" 804 r#"
810struct Foo; 805struct Foo;
811#[<|>] 806#[$0]
812fn f() {} 807fn f() {}
813"#, 808"#,
814 expect![[""]], 809 expect![[""]],
@@ -822,7 +817,7 @@ fn f() {}
822trait MyTrait {} 817trait MyTrait {}
823struct MyStruct {} 818struct MyStruct {}
824 819
825impl My<|> 820impl My$0
826"#, 821"#,
827 expect![[r#" 822 expect![[r#"
828 tp Self 823 tp Self
@@ -835,7 +830,7 @@ impl My<|>
835 #[test] 830 #[test]
836 fn function_fuzzy_completion() { 831 fn function_fuzzy_completion() {
837 check_edit_with_config( 832 check_edit_with_config(
838 fuzzy_completion_config(), 833 TEST_CONFIG,
839 "stdin", 834 "stdin",
840 r#" 835 r#"
841//- /lib.rs crate:dep 836//- /lib.rs crate:dep
@@ -845,7 +840,7 @@ pub mod io {
845 840
846//- /main.rs crate:main deps:dep 841//- /main.rs crate:main deps:dep
847fn main() { 842fn main() {
848 stdi<|> 843 stdi$0
849} 844}
850"#, 845"#,
851 r#" 846 r#"
@@ -861,7 +856,7 @@ fn main() {
861 #[test] 856 #[test]
862 fn macro_fuzzy_completion() { 857 fn macro_fuzzy_completion() {
863 check_edit_with_config( 858 check_edit_with_config(
864 fuzzy_completion_config(), 859 TEST_CONFIG,
865 "macro_with_curlies!", 860 "macro_with_curlies!",
866 r#" 861 r#"
867//- /lib.rs crate:dep 862//- /lib.rs crate:dep
@@ -873,7 +868,7 @@ macro_rules! macro_with_curlies {
873 868
874//- /main.rs crate:main deps:dep 869//- /main.rs crate:main deps:dep
875fn main() { 870fn main() {
876 curli<|> 871 curli$0
877} 872}
878"#, 873"#,
879 r#" 874 r#"
@@ -889,7 +884,7 @@ fn main() {
889 #[test] 884 #[test]
890 fn struct_fuzzy_completion() { 885 fn struct_fuzzy_completion() {
891 check_edit_with_config( 886 check_edit_with_config(
892 fuzzy_completion_config(), 887 TEST_CONFIG,
893 "ThirdStruct", 888 "ThirdStruct",
894 r#" 889 r#"
895//- /lib.rs crate:dep 890//- /lib.rs crate:dep
@@ -903,7 +898,7 @@ pub mod some_module {
903use dep::{FirstStruct, some_module::SecondStruct}; 898use dep::{FirstStruct, some_module::SecondStruct};
904 899
905fn main() { 900fn main() {
906 this<|> 901 this$0
907} 902}
908"#, 903"#,
909 r#" 904 r#"
@@ -920,7 +915,7 @@ fn main() {
920 fn fuzzy_completions_come_in_specific_order() { 915 fn fuzzy_completions_come_in_specific_order() {
921 mark::check!(certain_fuzzy_order_test); 916 mark::check!(certain_fuzzy_order_test);
922 check_with_config( 917 check_with_config(
923 fuzzy_completion_config(), 918 TEST_CONFIG,
924 r#" 919 r#"
925//- /lib.rs crate:dep 920//- /lib.rs crate:dep
926pub struct FirstStruct; 921pub struct FirstStruct;
@@ -941,7 +936,7 @@ pub mod some_module {
941use dep::{FirstStruct, some_module::SecondStruct}; 936use dep::{FirstStruct, some_module::SecondStruct};
942 937
943fn main() { 938fn main() {
944 hir<|> 939 hir$0
945} 940}
946"#, 941"#,
947 expect![[r#" 942 expect![[r#"
diff --git a/crates/completion/src/config.rs b/crates/completion/src/config.rs
index 30577dc11..b4439b7d1 100644
--- a/crates/completion/src/config.rs
+++ b/crates/completion/src/config.rs
@@ -4,8 +4,7 @@
4//! module, and we use to statically check that we only produce snippet 4//! module, and we use to statically check that we only produce snippet
5//! completions if we are allowed to. 5//! completions if we are allowed to.
6 6
7use ide_db::helpers::insert_use::MergeBehavior; 7use ide_db::helpers::{insert_use::MergeBehavior, SnippetCap};
8use rustc_hash::FxHashSet;
9 8
10#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct CompletionConfig { 10pub struct CompletionConfig {
@@ -15,49 +14,4 @@ pub struct CompletionConfig {
15 pub add_call_argument_snippets: bool, 14 pub add_call_argument_snippets: bool,
16 pub snippet_cap: Option<SnippetCap>, 15 pub snippet_cap: Option<SnippetCap>,
17 pub merge: Option<MergeBehavior>, 16 pub merge: Option<MergeBehavior>,
18 /// A set of capabilities, enabled on the client and supported on the server.
19 pub active_resolve_capabilities: FxHashSet<CompletionResolveCapability>,
20}
21
22/// A resolve capability, supported on the server.
23/// If the client registers any completion resolve capabilities,
24/// the server is able to render completion items' corresponding fields later,
25/// not during an initial completion item request.
26/// See https://github.com/rust-analyzer/rust-analyzer/issues/6366 for more details.
27#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
28pub enum CompletionResolveCapability {
29 Documentation,
30 Detail,
31 AdditionalTextEdits,
32}
33
34impl CompletionConfig {
35 pub fn allow_snippets(&mut self, yes: bool) {
36 self.snippet_cap = if yes { Some(SnippetCap { _private: () }) } else { None }
37 }
38
39 /// Whether the completions' additional edits are calculated when sending an initional completions list
40 /// or later, in a separate resolve request.
41 pub fn resolve_additional_edits_lazily(&self) -> bool {
42 self.active_resolve_capabilities.contains(&CompletionResolveCapability::AdditionalTextEdits)
43 }
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub struct SnippetCap {
48 _private: (),
49}
50
51impl Default for CompletionConfig {
52 fn default() -> Self {
53 CompletionConfig {
54 enable_postfix_completions: true,
55 enable_autoimport_completions: true,
56 add_call_parenthesis: true,
57 add_call_argument_snippets: true,
58 snippet_cap: Some(SnippetCap { _private: () }),
59 merge: Some(MergeBehavior::Full),
60 active_resolve_capabilities: FxHashSet::default(),
61 }
62 }
63} 17}
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs
index 41de324d8..ebf28e887 100644
--- a/crates/completion/src/context.rs
+++ b/crates/completion/src/context.rs
@@ -63,7 +63,7 @@ pub(crate) struct CompletionContext<'a> {
63 pub(super) is_expr: bool, 63 pub(super) is_expr: bool,
64 /// Something is typed at the "top" level, in module or impl/trait. 64 /// Something is typed at the "top" level, in module or impl/trait.
65 pub(super) is_new_item: bool, 65 pub(super) is_new_item: bool,
66 /// The receiver if this is a field or method access, i.e. writing something.<|> 66 /// The receiver if this is a field or method access, i.e. writing something.$0
67 pub(super) dot_receiver: Option<ast::Expr>, 67 pub(super) dot_receiver: Option<ast::Expr>,
68 pub(super) dot_receiver_is_ambiguous_float_literal: bool, 68 pub(super) dot_receiver_is_ambiguous_float_literal: bool,
69 /// If this is a call (method or function) in particular, i.e. the () are already there. 69 /// If this is a call (method or function) in particular, i.e. the () are already there.
@@ -228,9 +228,9 @@ impl<'a> CompletionContext<'a> {
228 228
229 /// Checks whether completions in that particular case don't make much sense. 229 /// Checks whether completions in that particular case don't make much sense.
230 /// Examples: 230 /// Examples:
231 /// - `fn <|>` -- we expect function name, it's unlikely that "hint" will be helpful. 231 /// - `fn $0` -- we expect function name, it's unlikely that "hint" will be helpful.
232 /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names. 232 /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names.
233 /// - `for _ i<|>` -- obviously, it'll be "in" keyword. 233 /// - `for _ i$0` -- obviously, it'll be "in" keyword.
234 pub(crate) fn no_completion_required(&self) -> bool { 234 pub(crate) fn no_completion_required(&self) -> bool {
235 (self.fn_is_prev && !self.inside_impl_trait_block) || self.for_is_prev2 235 (self.fn_is_prev && !self.inside_impl_trait_block) || self.for_is_prev2
236 } 236 }
@@ -279,7 +279,7 @@ impl<'a> CompletionContext<'a> {
279 offset: TextSize, 279 offset: TextSize,
280 ) { 280 ) {
281 // FIXME: this is wrong in at least two cases: 281 // FIXME: this is wrong in at least two cases:
282 // * when there's no token `foo(<|>)` 282 // * when there's no token `foo($0)`
283 // * when there is a token, but it happens to have type of it's own 283 // * when there is a token, but it happens to have type of it's own
284 self.expected_type = self 284 self.expected_type = self
285 .token 285 .token
@@ -458,7 +458,7 @@ impl<'a> CompletionContext<'a> {
458 } 458 }
459 if let Some(block) = ast::BlockExpr::cast(node) { 459 if let Some(block) = ast::BlockExpr::cast(node) {
460 return Some( 460 return Some(
461 block.expr().map(|e| e.syntax().text_range()) 461 block.tail_expr().map(|e| e.syntax().text_range())
462 == Some(name_ref.syntax().text_range()), 462 == Some(name_ref.syntax().text_range()),
463 ); 463 );
464 } 464 }
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index 65f8353e7..35af354b0 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -5,13 +5,11 @@ use std::fmt;
5use hir::{Documentation, ModPath, Mutability}; 5use hir::{Documentation, ModPath, Mutability};
6use ide_db::helpers::{ 6use ide_db::helpers::{
7 insert_use::{self, ImportScope, MergeBehavior}, 7 insert_use::{self, ImportScope, MergeBehavior},
8 mod_path_to_ast, 8 mod_path_to_ast, SnippetCap,
9}; 9};
10use syntax::{algo, TextRange}; 10use syntax::{algo, TextRange};
11use text_edit::TextEdit; 11use text_edit::TextEdit;
12 12
13use crate::config::SnippetCap;
14
15/// `CompletionItem` describes a single completion variant in the editor pop-up. 13/// `CompletionItem` describes a single completion variant in the editor pop-up.
16/// It is basically a POD with various properties. To construct a 14/// It is basically a POD with various properties. To construct a
17/// `CompletionItem`, use `new` method and the `Builder` struct. 15/// `CompletionItem`, use `new` method and the `Builder` struct.
@@ -45,7 +43,7 @@ pub struct CompletionItem {
45 /// Lookup is used to check if completion item indeed can complete current 43 /// Lookup is used to check if completion item indeed can complete current
46 /// ident. 44 /// ident.
47 /// 45 ///
48 /// That is, in `foo.bar<|>` lookup of `abracadabra` will be accepted (it 46 /// That is, in `foo.bar$0` lookup of `abracadabra` will be accepted (it
49 /// contains `bar` sub sequence), and `quux` will rejected. 47 /// contains `bar` sub sequence), and `quux` will rejected.
50 lookup: Option<String>, 48 lookup: Option<String>,
51 49
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index c57d05bbe..6cba88a6b 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -20,7 +20,7 @@ use text_edit::TextEdit;
20use crate::{completions::Completions, context::CompletionContext, item::CompletionKind}; 20use crate::{completions::Completions, context::CompletionContext, item::CompletionKind};
21 21
22pub use crate::{ 22pub use crate::{
23 config::{CompletionConfig, CompletionResolveCapability}, 23 config::CompletionConfig,
24 item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat}, 24 item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat},
25}; 25};
26 26
@@ -47,8 +47,8 @@ pub use crate::{
47// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result` 47// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
48// - `expr.ref` -> `&expr` 48// - `expr.ref` -> `&expr`
49// - `expr.refm` -> `&mut expr` 49// - `expr.refm` -> `&mut expr`
50// - `expr.let` -> `let <|> = expr;` 50// - `expr.let` -> `let $0 = expr;`
51// - `expr.letm` -> `let mut <|> = expr;` 51// - `expr.letm` -> `let mut $0 = expr;`
52// - `expr.not` -> `!expr` 52// - `expr.not` -> `!expr`
53// - `expr.dbg` -> `dbg!(expr)` 53// - `expr.dbg` -> `dbg!(expr)`
54// - `expr.dbgr` -> `dbg!(&expr)` 54// - `expr.dbgr` -> `dbg!(&expr)`
@@ -92,7 +92,7 @@ pub use crate::{
92/// ```no_run 92/// ```no_run
93/// fn f() { 93/// fn f() {
94/// let foo = 92; 94/// let foo = 92;
95/// let _ = bar<|> 95/// let _ = bar$0
96/// } 96/// }
97/// ``` 97/// ```
98/// 98///
@@ -158,8 +158,7 @@ pub fn resolve_completion_edits(
158 158
159#[cfg(test)] 159#[cfg(test)]
160mod tests { 160mod tests {
161 use crate::config::CompletionConfig; 161 use crate::test_utils::{self, TEST_CONFIG};
162 use crate::test_utils;
163 162
164 struct DetailAndDocumentation<'a> { 163 struct DetailAndDocumentation<'a> {
165 detail: &'a str, 164 detail: &'a str,
@@ -168,7 +167,7 @@ mod tests {
168 167
169 fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) { 168 fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
170 let (db, position) = test_utils::position(ra_fixture); 169 let (db, position) = test_utils::position(ra_fixture);
171 let config = CompletionConfig::default(); 170 let config = TEST_CONFIG;
172 let completions: Vec<_> = crate::completions(&db, &config, position).unwrap().into(); 171 let completions: Vec<_> = crate::completions(&db, &config, position).unwrap().into();
173 for item in completions { 172 for item in completions {
174 if item.detail() == Some(expected.detail) { 173 if item.detail() == Some(expected.detail) {
@@ -183,7 +182,7 @@ mod tests {
183 182
184 fn check_no_completion(ra_fixture: &str) { 183 fn check_no_completion(ra_fixture: &str) {
185 let (db, position) = test_utils::position(ra_fixture); 184 let (db, position) = test_utils::position(ra_fixture);
186 let config = CompletionConfig::default(); 185 let config = TEST_CONFIG;
187 186
188 let completions: Option<Vec<String>> = crate::completions(&db, &config, position) 187 let completions: Option<Vec<String>> = crate::completions(&db, &config, position)
189 .and_then(|completions| { 188 .and_then(|completions| {
@@ -221,7 +220,7 @@ mod tests {
221 220
222 fn foo() { 221 fn foo() {
223 let bar = Bar; 222 let bar = Bar;
224 bar.fo<|>; 223 bar.fo$0;
225 } 224 }
226 "#, 225 "#,
227 DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" }, 226 DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
@@ -247,7 +246,7 @@ mod tests {
247 246
248 fn foo() { 247 fn foo() {
249 let bar = Bar; 248 let bar = Bar;
250 bar.fo<|>; 249 bar.fo$0;
251 } 250 }
252 "#, 251 "#,
253 DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" }, 252 DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
@@ -260,7 +259,7 @@ mod tests {
260 check_no_completion( 259 check_no_completion(
261 r#" 260 r#"
262 fn foo() { 261 fn foo() {
263 for i i<|> 262 for i i$0
264 } 263 }
265 "#, 264 "#,
266 ); 265 );
@@ -271,7 +270,7 @@ mod tests {
271 fn foo() -> &'static str { "foo" } 270 fn foo() -> &'static str { "foo" }
272 271
273 fn bar() { 272 fn bar() {
274 for c in fo<|> 273 for c in fo$0
275 } 274 }
276 "#, 275 "#,
277 DetailAndDocumentation { 276 DetailAndDocumentation {
diff --git a/crates/completion/src/patterns.rs b/crates/completion/src/patterns.rs
index b0f35f9bf..f3ce91dd1 100644
--- a/crates/completion/src/patterns.rs
+++ b/crates/completion/src/patterns.rs
@@ -5,7 +5,7 @@ use syntax::{
5 ast::{self, LoopBodyOwner}, 5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, 6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*, 7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken, 8 SyntaxNode, SyntaxToken, T,
9}; 9};
10 10
11#[cfg(test)] 11#[cfg(test)]
@@ -20,7 +20,7 @@ pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool {
20} 20}
21#[test] 21#[test]
22fn test_has_trait_parent() { 22fn test_has_trait_parent() {
23 check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent); 23 check_pattern_is_applicable(r"trait A { f$0 }", has_trait_parent);
24} 24}
25 25
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { 26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool {
@@ -32,7 +32,7 @@ pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool {
32} 32}
33#[test] 33#[test]
34fn test_has_impl_parent() { 34fn test_has_impl_parent() {
35 check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent); 35 check_pattern_is_applicable(r"impl A { f$0 }", has_impl_parent);
36} 36}
37 37
38pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { 38pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
@@ -47,10 +47,10 @@ pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
47} 47}
48#[test] 48#[test]
49fn test_inside_impl_trait_block() { 49fn test_inside_impl_trait_block() {
50 check_pattern_is_applicable(r"impl Foo for Bar { f<|> }", inside_impl_trait_block); 50 check_pattern_is_applicable(r"impl Foo for Bar { f$0 }", inside_impl_trait_block);
51 check_pattern_is_applicable(r"impl Foo for Bar { fn f<|> }", inside_impl_trait_block); 51 check_pattern_is_applicable(r"impl Foo for Bar { fn f$0 }", inside_impl_trait_block);
52 check_pattern_is_not_applicable(r"impl A { f<|> }", inside_impl_trait_block); 52 check_pattern_is_not_applicable(r"impl A { f$0 }", inside_impl_trait_block);
53 check_pattern_is_not_applicable(r"impl A { fn f<|> }", inside_impl_trait_block); 53 check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block);
54} 54}
55 55
56pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool { 56pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool {
@@ -58,8 +58,8 @@ pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool {
58} 58}
59#[test] 59#[test]
60fn test_has_field_list_parent() { 60fn test_has_field_list_parent() {
61 check_pattern_is_applicable(r"struct Foo { f<|> }", has_field_list_parent); 61 check_pattern_is_applicable(r"struct Foo { f$0 }", has_field_list_parent);
62 check_pattern_is_applicable(r"struct Foo { f<|> pub f: i32}", has_field_list_parent); 62 check_pattern_is_applicable(r"struct Foo { f$0 pub f: i32}", has_field_list_parent);
63} 63}
64 64
65pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { 65pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
@@ -67,7 +67,7 @@ pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
67} 67}
68#[test] 68#[test]
69fn test_has_block_expr_parent() { 69fn test_has_block_expr_parent() {
70 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f<|> }", has_block_expr_parent); 70 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f$0 }", has_block_expr_parent);
71} 71}
72 72
73pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { 73pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
@@ -75,8 +75,8 @@ pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
75} 75}
76#[test] 76#[test]
77fn test_has_bind_pat_parent() { 77fn test_has_bind_pat_parent() {
78 check_pattern_is_applicable(r"fn my_fn(m<|>) {}", has_bind_pat_parent); 78 check_pattern_is_applicable(r"fn my_fn(m$0) {}", has_bind_pat_parent);
79 check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent); 79 check_pattern_is_applicable(r"fn my_fn() { let m$0 }", has_bind_pat_parent);
80} 80}
81 81
82pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool { 82pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
@@ -86,8 +86,8 @@ pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
86} 86}
87#[test] 87#[test]
88fn test_has_ref_parent() { 88fn test_has_ref_parent() {
89 check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent); 89 check_pattern_is_applicable(r"fn my_fn(&m$0) {}", has_ref_parent);
90 check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent); 90 check_pattern_is_applicable(r"fn my() { let &m$0 }", has_ref_parent);
91} 91}
92 92
93pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool { 93pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
@@ -99,8 +99,8 @@ pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> boo
99} 99}
100#[test] 100#[test]
101fn test_has_item_list_or_source_file_parent() { 101fn test_has_item_list_or_source_file_parent() {
102 check_pattern_is_applicable(r"i<|>", has_item_list_or_source_file_parent); 102 check_pattern_is_applicable(r"i$0", has_item_list_or_source_file_parent);
103 check_pattern_is_applicable(r"mod foo { f<|> }", has_item_list_or_source_file_parent); 103 check_pattern_is_applicable(r"mod foo { f$0 }", has_item_list_or_source_file_parent);
104} 104}
105 105
106pub(crate) fn is_match_arm(element: SyntaxElement) -> bool { 106pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
@@ -112,26 +112,26 @@ pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
112} 112}
113#[test] 113#[test]
114fn test_is_match_arm() { 114fn test_is_match_arm() {
115 check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm); 115 check_pattern_is_applicable(r"fn my_fn() { match () { () => m$0 } }", is_match_arm);
116} 116}
117 117
118pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool { 118pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
119 element 119 element
120 .into_token() 120 .into_token()
121 .and_then(|it| previous_non_trivia_token(it)) 121 .and_then(|it| previous_non_trivia_token(it))
122 .filter(|it| it.kind() == UNSAFE_KW) 122 .filter(|it| it.kind() == T![unsafe])
123 .is_some() 123 .is_some()
124} 124}
125#[test] 125#[test]
126fn test_unsafe_is_prev() { 126fn test_unsafe_is_prev() {
127 check_pattern_is_applicable(r"unsafe i<|>", unsafe_is_prev); 127 check_pattern_is_applicable(r"unsafe i$0", unsafe_is_prev);
128} 128}
129 129
130pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { 130pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
131 element 131 element
132 .into_token() 132 .into_token()
133 .and_then(|it| previous_non_trivia_token(it)) 133 .and_then(|it| previous_non_trivia_token(it))
134 .filter(|it| it.kind() == IF_KW) 134 .filter(|it| it.kind() == T![if])
135 .is_some() 135 .is_some()
136} 136}
137 137
@@ -139,32 +139,32 @@ pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool {
139 element 139 element
140 .into_token() 140 .into_token()
141 .and_then(|it| previous_non_trivia_token(it)) 141 .and_then(|it| previous_non_trivia_token(it))
142 .filter(|it| it.kind() == FN_KW) 142 .filter(|it| it.kind() == T![fn])
143 .is_some() 143 .is_some()
144} 144}
145#[test] 145#[test]
146fn test_fn_is_prev() { 146fn test_fn_is_prev() {
147 check_pattern_is_applicable(r"fn l<|>", fn_is_prev); 147 check_pattern_is_applicable(r"fn l$0", fn_is_prev);
148} 148}
149 149
150/// Check if the token previous to the previous one is `for`. 150/// Check if the token previous to the previous one is `for`.
151/// For example, `for _ i<|>` => true. 151/// For example, `for _ i$0` => true.
152pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool { 152pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool {
153 element 153 element
154 .into_token() 154 .into_token()
155 .and_then(|it| previous_non_trivia_token(it)) 155 .and_then(|it| previous_non_trivia_token(it))
156 .and_then(|it| previous_non_trivia_token(it)) 156 .and_then(|it| previous_non_trivia_token(it))
157 .filter(|it| it.kind() == FOR_KW) 157 .filter(|it| it.kind() == T![for])
158 .is_some() 158 .is_some()
159} 159}
160#[test] 160#[test]
161fn test_for_is_prev2() { 161fn test_for_is_prev2() {
162 check_pattern_is_applicable(r"for i i<|>", for_is_prev2); 162 check_pattern_is_applicable(r"for i i$0", for_is_prev2);
163} 163}
164 164
165#[test] 165#[test]
166fn test_if_is_prev() { 166fn test_if_is_prev() {
167 check_pattern_is_applicable(r"if l<|>", if_is_prev); 167 check_pattern_is_applicable(r"if l$0", if_is_prev);
168} 168}
169 169
170pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool { 170pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
@@ -172,7 +172,7 @@ pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
172} 172}
173#[test] 173#[test]
174fn test_has_trait_as_prev_sibling() { 174fn test_has_trait_as_prev_sibling() {
175 check_pattern_is_applicable(r"trait A w<|> {}", has_trait_as_prev_sibling); 175 check_pattern_is_applicable(r"trait A w$0 {}", has_trait_as_prev_sibling);
176} 176}
177 177
178pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool { 178pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
@@ -180,7 +180,7 @@ pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
180} 180}
181#[test] 181#[test]
182fn test_has_impl_as_prev_sibling() { 182fn test_has_impl_as_prev_sibling() {
183 check_pattern_is_applicable(r"impl A w<|> {}", has_impl_as_prev_sibling); 183 check_pattern_is_applicable(r"impl A w$0 {}", has_impl_as_prev_sibling);
184} 184}
185 185
186pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { 186pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index 1ba7201a1..e93c59f71 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -11,13 +11,13 @@ pub(crate) mod type_alias;
11mod builder_ext; 11mod builder_ext;
12 12
13use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type}; 13use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type};
14use ide_db::RootDatabase; 14use ide_db::{helpers::SnippetCap, RootDatabase};
15use syntax::TextRange; 15use syntax::TextRange;
16use test_utils::mark; 16use test_utils::mark;
17 17
18use crate::{ 18use crate::{
19 config::SnippetCap, item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, 19 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
20 CompletionKind, CompletionScore, 20 CompletionScore,
21}; 21};
22 22
23use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; 23use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
@@ -157,8 +157,7 @@ impl<'a> Render<'a> {
157 157
158 let kind = match resolution { 158 let kind = match resolution {
159 ScopeDef::ModuleDef(Function(func)) => { 159 ScopeDef::ModuleDef(Function(func)) => {
160 let item = render_fn(self.ctx, import_to_add, Some(local_name), *func); 160 return render_fn(self.ctx, import_to_add, Some(local_name), *func);
161 return Some(item);
162 } 161 }
163 ScopeDef::ModuleDef(Variant(_)) 162 ScopeDef::ModuleDef(Variant(_))
164 if self.ctx.completion.is_pat_binding_or_const 163 if self.ctx.completion.is_pat_binding_or_const
@@ -321,8 +320,8 @@ mod tests {
321 use test_utils::mark; 320 use test_utils::mark;
322 321
323 use crate::{ 322 use crate::{
324 test_utils::{check_edit, do_completion, get_all_items}, 323 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
325 CompletionConfig, CompletionKind, CompletionScore, 324 CompletionKind, CompletionScore,
326 }; 325 };
327 326
328 fn check(ra_fixture: &str, expect: Expect) { 327 fn check(ra_fixture: &str, expect: Expect) {
@@ -339,7 +338,7 @@ mod tests {
339 } 338 }
340 } 339 }
341 340
342 let mut completions = get_all_items(CompletionConfig::default(), ra_fixture); 341 let mut completions = get_all_items(TEST_CONFIG, ra_fixture);
343 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); 342 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
344 let actual = completions 343 let actual = completions
345 .into_iter() 344 .into_iter()
@@ -359,7 +358,7 @@ mod tests {
359 r#" 358 r#"
360enum Foo { Foo { x: i32, y: i32 } } 359enum Foo { Foo { x: i32, y: i32 } }
361 360
362fn main() { Foo::Fo<|> } 361fn main() { Foo::Fo$0 }
363"#, 362"#,
364 expect![[r#" 363 expect![[r#"
365 [ 364 [
@@ -382,7 +381,7 @@ fn main() { Foo::Fo<|> }
382 r#" 381 r#"
383enum Foo { Foo (i32, i32) } 382enum Foo { Foo (i32, i32) }
384 383
385fn main() { Foo::Fo<|> } 384fn main() { Foo::Fo$0 }
386"#, 385"#,
387 expect![[r#" 386 expect![[r#"
388 [ 387 [
@@ -407,7 +406,7 @@ fn main() { Foo::Fo<|> }
407 r#" 406 r#"
408enum Foo { Foo } 407enum Foo { Foo }
409 408
410fn main() { Foo::Fo<|> } 409fn main() { Foo::Fo$0 }
411"#, 410"#,
412 expect![[r#" 411 expect![[r#"
413 [ 412 [
@@ -431,7 +430,7 @@ fn main() { Foo::Fo<|> }
431mod m { 430mod m {
432 pub enum Spam { Foo, Bar(i32) } 431 pub enum Spam { Foo, Bar(i32) }
433} 432}
434fn main() { let _: m::Spam = S<|> } 433fn main() { let _: m::Spam = S$0 }
435"#, 434"#,
436 expect![[r#" 435 expect![[r#"
437 [ 436 [
@@ -484,7 +483,7 @@ fn something_deprecated() {}
484#[deprecated(since = "1.0.0")] 483#[deprecated(since = "1.0.0")]
485fn something_else_deprecated() {} 484fn something_else_deprecated() {}
486 485
487fn main() { som<|> } 486fn main() { som$0 }
488"#, 487"#,
489 expect![[r#" 488 expect![[r#"
490 [ 489 [
@@ -524,7 +523,7 @@ fn main() { som<|> }
524 check( 523 check(
525 r#" 524 r#"
526struct A { #[deprecated] the_field: u32 } 525struct A { #[deprecated] the_field: u32 }
527fn foo() { A { the<|> } } 526fn foo() { A { the$0 } }
528"#, 527"#,
529 expect![[r#" 528 expect![[r#"
530 [ 529 [
@@ -552,7 +551,7 @@ struct S {
552} 551}
553impl S { 552impl S {
554 /// Method docs 553 /// Method docs
555 fn bar(self) { self.<|> } 554 fn bar(self) { self.$0 }
556}"#, 555}"#,
557 expect![[r#" 556 expect![[r#"
558 [ 557 [
@@ -585,7 +584,7 @@ impl S {
585 584
586 check( 585 check(
587 r#" 586 r#"
588use self::my<|>; 587use self::my$0;
589 588
590/// mod docs 589/// mod docs
591mod my { } 590mod my { }
@@ -644,7 +643,7 @@ impl S {
644 #[inline] 643 #[inline]
645 fn the_method(&self) { } 644 fn the_method(&self) { }
646} 645}
647fn foo(s: S) { s.<|> } 646fn foo(s: S) { s.$0 }
648"#, 647"#,
649 expect![[r#" 648 expect![[r#"
650 [ 649 [
@@ -672,7 +671,7 @@ fn foo(foo: u8, bar: u8) {}
672struct ManualVtable { f: fn(u8, u8) } 671struct ManualVtable { f: fn(u8, u8) }
673 672
674fn main() -> ManualVtable { 673fn main() -> ManualVtable {
675 ManualVtable { f: f<|> } 674 ManualVtable { f: f$0 }
676} 675}
677"#, 676"#,
678 r#" 677 r#"
@@ -693,7 +692,7 @@ fn main() -> ManualVtable {
693 "foo", 692 "foo",
694 r#" 693 r#"
695mod m { pub fn foo() {} } 694mod m { pub fn foo() {} }
696use crate::m::f<|>; 695use crate::m::f$0;
697"#, 696"#,
698 r#" 697 r#"
699mod m { pub fn foo() {} } 698mod m { pub fn foo() {} }
@@ -708,7 +707,7 @@ use crate::m::foo;
708 "foo", 707 "foo",
709 r#" 708 r#"
710fn foo(x: i32) {} 709fn foo(x: i32) {}
711fn main() { f<|>(); } 710fn main() { f$0(); }
712"#, 711"#,
713 r#" 712 r#"
714fn foo(x: i32) {} 713fn foo(x: i32) {}
@@ -720,7 +719,7 @@ fn main() { foo(); }
720 r#" 719 r#"
721struct Foo; 720struct Foo;
722impl Foo { fn foo(&self){} } 721impl Foo { fn foo(&self){} }
723fn f(foo: &Foo) { foo.f<|>(); } 722fn f(foo: &Foo) { foo.f$0(); }
724"#, 723"#,
725 r#" 724 r#"
726struct Foo; 725struct Foo;
@@ -737,7 +736,7 @@ fn f(foo: &Foo) { foo.foo(); }
737 "Vec", 736 "Vec",
738 r#" 737 r#"
739struct Vec<T> {} 738struct Vec<T> {}
740fn foo(xs: Ve<|>) 739fn foo(xs: Ve$0)
741"#, 740"#,
742 r#" 741 r#"
743struct Vec<T> {} 742struct Vec<T> {}
@@ -748,7 +747,7 @@ fn foo(xs: Vec<$0>)
748 "Vec", 747 "Vec",
749 r#" 748 r#"
750type Vec<T> = (T,); 749type Vec<T> = (T,);
751fn foo(xs: Ve<|>) 750fn foo(xs: Ve$0)
752"#, 751"#,
753 r#" 752 r#"
754type Vec<T> = (T,); 753type Vec<T> = (T,);
@@ -759,7 +758,7 @@ fn foo(xs: Vec<$0>)
759 "Vec", 758 "Vec",
760 r#" 759 r#"
761struct Vec<T = i128> {} 760struct Vec<T = i128> {}
762fn foo(xs: Ve<|>) 761fn foo(xs: Ve$0)
763"#, 762"#,
764 r#" 763 r#"
765struct Vec<T = i128> {} 764struct Vec<T = i128> {}
@@ -770,7 +769,7 @@ fn foo(xs: Vec)
770 "Vec", 769 "Vec",
771 r#" 770 r#"
772struct Vec<T> {} 771struct Vec<T> {}
773fn foo(xs: Ve<|><i128>) 772fn foo(xs: Ve$0<i128>)
774"#, 773"#,
775 r#" 774 r#"
776struct Vec<T> {} 775struct Vec<T> {}
@@ -786,7 +785,7 @@ fn foo(xs: Vec<i128>)
786 r#" 785 r#"
787struct S { foo: i64, bar: u32, baz: u32 } 786struct S { foo: i64, bar: u32, baz: u32 }
788fn test(bar: u32) { } 787fn test(bar: u32) { }
789fn foo(s: S) { test(s.<|>) } 788fn foo(s: S) { test(s.$0) }
790"#, 789"#,
791 expect![[r#" 790 expect![[r#"
792 fd bar [type+name] 791 fd bar [type+name]
@@ -803,7 +802,7 @@ fn foo(s: S) { test(s.<|>) }
803 r#" 802 r#"
804struct A { foo: i64, bar: u32, baz: u32 } 803struct A { foo: i64, bar: u32, baz: u32 }
805struct B { x: (), y: f32, bar: u32 } 804struct B { x: (), y: f32, bar: u32 }
806fn foo(a: A) { B { bar: a.<|> }; } 805fn foo(a: A) { B { bar: a.$0 }; }
807"#, 806"#,
808 expect![[r#" 807 expect![[r#"
809 fd bar [type+name] 808 fd bar [type+name]
@@ -820,7 +819,7 @@ fn foo(a: A) { B { bar: a.<|> }; }
820struct A { foo: i64, bar: u32, baz: u32 } 819struct A { foo: i64, bar: u32, baz: u32 }
821struct B { x: (), y: f32, bar: u32 } 820struct B { x: (), y: f32, bar: u32 }
822fn f(foo: i64) { } 821fn f(foo: i64) { }
823fn foo(a: A) { B { bar: f(a.<|>) }; } 822fn foo(a: A) { B { bar: f(a.$0) }; }
824"#, 823"#,
825 expect![[r#" 824 expect![[r#"
826 fd foo [type+name] 825 fd foo [type+name]
@@ -833,7 +832,7 @@ fn foo(a: A) { B { bar: f(a.<|>) }; }
833struct A { foo: i64, bar: u32, baz: u32 } 832struct A { foo: i64, bar: u32, baz: u32 }
834struct B { x: (), y: f32, bar: u32 } 833struct B { x: (), y: f32, bar: u32 }
835fn f(foo: i64) { } 834fn f(foo: i64) { }
836fn foo(a: A) { f(B { bar: a.<|> }); } 835fn foo(a: A) { f(B { bar: a.$0 }); }
837"#, 836"#,
838 expect![[r#" 837 expect![[r#"
839 fd bar [type+name] 838 fd bar [type+name]
@@ -848,7 +847,7 @@ fn foo(a: A) { f(B { bar: a.<|> }); }
848 check_scores( 847 check_scores(
849 r#" 848 r#"
850struct WorldSnapshot { _f: () }; 849struct WorldSnapshot { _f: () };
851fn go(world: &WorldSnapshot) { go(w<|>) } 850fn go(world: &WorldSnapshot) { go(w$0) }
852"#, 851"#,
853 expect![[r#" 852 expect![[r#"
854 bn world [type+name] 853 bn world [type+name]
@@ -863,7 +862,7 @@ fn go(world: &WorldSnapshot) { go(w<|>) }
863 check_scores( 862 check_scores(
864 r#" 863 r#"
865struct Foo; 864struct Foo;
866fn f(foo: &Foo) { f(foo, w<|>) } 865fn f(foo: &Foo) { f(foo, w$0) }
867"#, 866"#,
868 expect![[r#" 867 expect![[r#"
869 st Foo [] 868 st Foo []
diff --git a/crates/completion/src/render/const_.rs b/crates/completion/src/render/const_.rs
index 039bdabc0..ce924f309 100644
--- a/crates/completion/src/render/const_.rs
+++ b/crates/completion/src/render/const_.rs
@@ -15,7 +15,7 @@ pub(crate) fn render_const<'a>(
15 ctx: RenderContext<'a>, 15 ctx: RenderContext<'a>,
16 const_: hir::Const, 16 const_: hir::Const,
17) -> Option<CompletionItem> { 17) -> Option<CompletionItem> {
18 ConstRender::new(ctx, const_).render() 18 ConstRender::new(ctx, const_)?.render()
19} 19}
20 20
21#[derive(Debug)] 21#[derive(Debug)]
@@ -26,9 +26,9 @@ struct ConstRender<'a> {
26} 26}
27 27
28impl<'a> ConstRender<'a> { 28impl<'a> ConstRender<'a> {
29 fn new(ctx: RenderContext<'a>, const_: hir::Const) -> ConstRender<'a> { 29 fn new(ctx: RenderContext<'a>, const_: hir::Const) -> Option<ConstRender<'a>> {
30 let ast_node = const_.source(ctx.db()).value; 30 let ast_node = const_.source(ctx.db())?.value;
31 ConstRender { ctx, const_, ast_node } 31 Some(ConstRender { ctx, const_, ast_node })
32 } 32 }
33 33
34 fn render(self) -> Option<CompletionItem> { 34 fn render(self) -> Option<CompletionItem> {
diff --git a/crates/completion/src/render/enum_variant.rs b/crates/completion/src/render/enum_variant.rs
index 732e139ec..89fb49773 100644
--- a/crates/completion/src/render/enum_variant.rs
+++ b/crates/completion/src/render/enum_variant.rs
@@ -115,7 +115,7 @@ mod tests {
115enum Option<T> { Some(T), None } 115enum Option<T> { Some(T), None }
116use Option::*; 116use Option::*;
117fn main() -> Option<i32> { 117fn main() -> Option<i32> {
118 Som<|> 118 Som$0
119} 119}
120"#, 120"#,
121 r#" 121 r#"
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index 316e05b52..f5b0ce3e3 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -14,9 +14,9 @@ pub(crate) fn render_fn<'a>(
14 import_to_add: Option<ImportEdit>, 14 import_to_add: Option<ImportEdit>,
15 local_name: Option<String>, 15 local_name: Option<String>,
16 fn_: hir::Function, 16 fn_: hir::Function,
17) -> CompletionItem { 17) -> Option<CompletionItem> {
18 let _p = profile::span("render_fn"); 18 let _p = profile::span("render_fn");
19 FunctionRender::new(ctx, local_name, fn_).render(import_to_add) 19 Some(FunctionRender::new(ctx, local_name, fn_)?.render(import_to_add))
20} 20}
21 21
22#[derive(Debug)] 22#[derive(Debug)]
@@ -32,11 +32,11 @@ impl<'a> FunctionRender<'a> {
32 ctx: RenderContext<'a>, 32 ctx: RenderContext<'a>,
33 local_name: Option<String>, 33 local_name: Option<String>,
34 fn_: hir::Function, 34 fn_: hir::Function,
35 ) -> FunctionRender<'a> { 35 ) -> Option<FunctionRender<'a>> {
36 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string()); 36 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string());
37 let ast_node = fn_.source(ctx.db()).value; 37 let ast_node = fn_.source(ctx.db())?.value;
38 38
39 FunctionRender { ctx, name, func: fn_, ast_node } 39 Some(FunctionRender { ctx, name, func: fn_, ast_node })
40 } 40 }
41 41
42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
@@ -113,7 +113,7 @@ mod tests {
113 use test_utils::mark; 113 use test_utils::mark;
114 114
115 use crate::{ 115 use crate::{
116 test_utils::{check_edit, check_edit_with_config}, 116 test_utils::{check_edit, check_edit_with_config, TEST_CONFIG},
117 CompletionConfig, 117 CompletionConfig,
118 }; 118 };
119 119
@@ -124,7 +124,7 @@ mod tests {
124 "no_args", 124 "no_args",
125 r#" 125 r#"
126fn no_args() {} 126fn no_args() {}
127fn main() { no_<|> } 127fn main() { no_$0 }
128"#, 128"#,
129 r#" 129 r#"
130fn no_args() {} 130fn no_args() {}
@@ -136,7 +136,7 @@ fn main() { no_args()$0 }
136 "with_args", 136 "with_args",
137 r#" 137 r#"
138fn with_args(x: i32, y: String) {} 138fn with_args(x: i32, y: String) {}
139fn main() { with_<|> } 139fn main() { with_$0 }
140"#, 140"#,
141 r#" 141 r#"
142fn with_args(x: i32, y: String) {} 142fn with_args(x: i32, y: String) {}
@@ -151,7 +151,7 @@ struct S;
151impl S { 151impl S {
152 fn foo(&self) {} 152 fn foo(&self) {}
153} 153}
154fn bar(s: &S) { s.f<|> } 154fn bar(s: &S) { s.f$0 }
155"#, 155"#,
156 r#" 156 r#"
157struct S; 157struct S;
@@ -170,7 +170,7 @@ impl S {
170 fn foo(&self, x: i32) {} 170 fn foo(&self, x: i32) {}
171} 171}
172fn bar(s: &S) { 172fn bar(s: &S) {
173 s.f<|> 173 s.f$0
174} 174}
175"#, 175"#,
176 r#" 176 r#"
@@ -195,7 +195,7 @@ struct S;
195impl S { 195impl S {
196 fn foo(&self) {} 196 fn foo(&self) {}
197} 197}
198fn main() { S::f<|> } 198fn main() { S::f$0 }
199"#, 199"#,
200 r#" 200 r#"
201struct S; 201struct S;
@@ -211,11 +211,11 @@ fn main() { S::foo(${1:&self})$0 }
211 fn suppress_arg_snippets() { 211 fn suppress_arg_snippets() {
212 mark::check!(suppress_arg_snippets); 212 mark::check!(suppress_arg_snippets);
213 check_edit_with_config( 213 check_edit_with_config(
214 CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() }, 214 CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG },
215 "with_args", 215 "with_args",
216 r#" 216 r#"
217fn with_args(x: i32, y: String) {} 217fn with_args(x: i32, y: String) {}
218fn main() { with_<|> } 218fn main() { with_$0 }
219"#, 219"#,
220 r#" 220 r#"
221fn with_args(x: i32, y: String) {} 221fn with_args(x: i32, y: String) {}
@@ -230,7 +230,7 @@ fn main() { with_args($0) }
230 "foo", 230 "foo",
231 r#" 231 r#"
232fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {} 232fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
233fn main() { f<|> } 233fn main() { f$0 }
234"#, 234"#,
235 r#" 235 r#"
236fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {} 236fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
@@ -248,7 +248,7 @@ struct Foo {}
248fn ref_arg(x: &Foo) {} 248fn ref_arg(x: &Foo) {}
249fn main() { 249fn main() {
250 let x = Foo {}; 250 let x = Foo {};
251 ref_ar<|> 251 ref_ar$0
252} 252}
253"#, 253"#,
254 r#" 254 r#"
@@ -271,7 +271,7 @@ struct Foo {}
271fn ref_arg(x: &mut Foo) {} 271fn ref_arg(x: &mut Foo) {}
272fn main() { 272fn main() {
273 let x = Foo {}; 273 let x = Foo {};
274 ref_ar<|> 274 ref_ar$0
275} 275}
276"#, 276"#,
277 r#" 277 r#"
@@ -299,7 +299,7 @@ impl Bar {
299fn main() { 299fn main() {
300 let x = Foo {}; 300 let x = Foo {};
301 let y = Bar {}; 301 let y = Bar {};
302 y.<|> 302 y.$0
303} 303}
304"#, 304"#,
305 r#" 305 r#"
@@ -326,7 +326,7 @@ fn main() {
326fn take_mutably(mut x: &i32) {} 326fn take_mutably(mut x: &i32) {}
327 327
328fn main() { 328fn main() {
329 take_m<|> 329 take_m$0
330} 330}
331"#, 331"#,
332 r#" 332 r#"
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
index dac79592f..f893e420a 100644
--- a/crates/completion/src/render/macro_.rs
+++ b/crates/completion/src/render/macro_.rs
@@ -39,20 +39,13 @@ impl<'a> MacroRender<'a> {
39 } 39 }
40 40
41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> { 41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
42 // FIXME: Currently proc-macro do not have ast-node,
43 // such that it does not have source
44 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
45 if self.macro_.is_proc_macro() {
46 return None;
47 }
48
49 let mut builder = 42 let mut builder =
50 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label()) 43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label())
51 .kind(CompletionItemKind::Macro) 44 .kind(CompletionItemKind::Macro)
52 .set_documentation(self.docs.clone()) 45 .set_documentation(self.docs.clone())
53 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 46 .set_deprecated(self.ctx.is_deprecated(self.macro_))
54 .add_import(import_to_add) 47 .add_import(import_to_add)
55 .detail(self.detail()); 48 .set_detail(self.detail());
56 49
57 let needs_bang = self.needs_bang(); 50 let needs_bang = self.needs_bang();
58 builder = match self.ctx.snippet_cap() { 51 builder = match self.ctx.snippet_cap() {
@@ -95,9 +88,9 @@ impl<'a> MacroRender<'a> {
95 format!("{}!", self.name) 88 format!("{}!", self.name)
96 } 89 }
97 90
98 fn detail(&self) -> String { 91 fn detail(&self) -> Option<String> {
99 let ast_node = self.macro_.source(self.ctx.db()).value; 92 let ast_node = self.macro_.source(self.ctx.db())?.value;
100 macro_label(&ast_node) 93 Some(macro_label(&ast_node))
101 } 94 }
102} 95}
103 96
@@ -142,7 +135,7 @@ mod tests {
142 "frobnicate!", 135 "frobnicate!",
143 r#" 136 r#"
144//- /main.rs crate:main deps:foo 137//- /main.rs crate:main deps:foo
145use foo::<|>; 138use foo::$0;
146//- /foo/lib.rs crate:foo 139//- /foo/lib.rs crate:foo
147#[macro_export] 140#[macro_export]
148macro_rules! frobnicate { () => () } 141macro_rules! frobnicate { () => () }
@@ -156,7 +149,7 @@ use foo::frobnicate;
156 "frobnicate!", 149 "frobnicate!",
157 r#" 150 r#"
158macro_rules! frobnicate { () => () } 151macro_rules! frobnicate { () => () }
159fn main() { frob<|>!(); } 152fn main() { frob$0!(); }
160"#, 153"#,
161 r#" 154 r#"
162macro_rules! frobnicate { () => () } 155macro_rules! frobnicate { () => () }
@@ -180,7 +173,7 @@ fn main() { frobnicate!(); }
180/// ``` 173/// ```
181macro_rules! vec { () => {} } 174macro_rules! vec { () => {} }
182 175
183fn fn main() { v<|> } 176fn fn main() { v$0 }
184"#, 177"#,
185 r#" 178 r#"
186/// Creates a [`Vec`] containing the arguments. 179/// Creates a [`Vec`] containing the arguments.
@@ -205,7 +198,7 @@ fn fn main() { vec![$0] }
205/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`, 198/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
206/// call as `let _=foo! { hello world };` 199/// call as `let _=foo! { hello world };`
207macro_rules! foo { () => {} } 200macro_rules! foo { () => {} }
208fn main() { <|> } 201fn main() { $0 }
209"#, 202"#,
210 r#" 203 r#"
211/// Foo 204/// Foo
diff --git a/crates/completion/src/render/pattern.rs b/crates/completion/src/render/pattern.rs
index a3b6a3cac..61d8a17e5 100644
--- a/crates/completion/src/render/pattern.rs
+++ b/crates/completion/src/render/pattern.rs
@@ -1,12 +1,10 @@
1//! Renderer for patterns. 1//! Renderer for patterns.
2 2
3use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind}; 3use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
4use ide_db::helpers::SnippetCap;
4use itertools::Itertools; 5use itertools::Itertools;
5 6
6use crate::{ 7use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind};
7 config::SnippetCap, item::CompletionKind, render::RenderContext, CompletionItem,
8 CompletionItemKind,
9};
10 8
11fn visible_fields( 9fn visible_fields(
12 ctx: &RenderContext<'_>, 10 ctx: &RenderContext<'_>,
diff --git a/crates/completion/src/render/type_alias.rs b/crates/completion/src/render/type_alias.rs
index 9605c7fa9..69b445b9c 100644
--- a/crates/completion/src/render/type_alias.rs
+++ b/crates/completion/src/render/type_alias.rs
@@ -15,7 +15,7 @@ pub(crate) fn render_type_alias<'a>(
15 ctx: RenderContext<'a>, 15 ctx: RenderContext<'a>,
16 type_alias: hir::TypeAlias, 16 type_alias: hir::TypeAlias,
17) -> Option<CompletionItem> { 17) -> Option<CompletionItem> {
18 TypeAliasRender::new(ctx, type_alias).render() 18 TypeAliasRender::new(ctx, type_alias)?.render()
19} 19}
20 20
21#[derive(Debug)] 21#[derive(Debug)]
@@ -26,9 +26,9 @@ struct TypeAliasRender<'a> {
26} 26}
27 27
28impl<'a> TypeAliasRender<'a> { 28impl<'a> TypeAliasRender<'a> {
29 fn new(ctx: RenderContext<'a>, type_alias: hir::TypeAlias) -> TypeAliasRender<'a> { 29 fn new(ctx: RenderContext<'a>, type_alias: hir::TypeAlias) -> Option<TypeAliasRender<'a>> {
30 let ast_node = type_alias.source(ctx.db()).value; 30 let ast_node = type_alias.source(ctx.db())?.value;
31 TypeAliasRender { ctx, type_alias, ast_node } 31 Some(TypeAliasRender { ctx, type_alias, ast_node })
32 } 32 }
33 33
34 fn render(self) -> Option<CompletionItem> { 34 fn render(self) -> Option<CompletionItem> {
diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs
index eb0c16f52..3f4b9d4ac 100644
--- a/crates/completion/src/test_utils.rs
+++ b/crates/completion/src/test_utils.rs
@@ -1,8 +1,11 @@
1//! Runs completion for testing purposes. 1//! Runs completion for testing purposes.
2 2
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::base_db::{fixture::ChangeFixture, FileLoader, FilePosition}; 4use ide_db::{
5use ide_db::RootDatabase; 5 base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
6 helpers::{insert_use::MergeBehavior, SnippetCap},
7 RootDatabase,
8};
6use itertools::Itertools; 9use itertools::Itertools;
7use stdx::{format_to, trim_indent}; 10use stdx::{format_to, trim_indent};
8use syntax::{AstNode, NodeOrToken, SyntaxElement}; 11use syntax::{AstNode, NodeOrToken, SyntaxElement};
@@ -10,12 +13,21 @@ use test_utils::{assert_eq_text, RangeOrOffset};
10 13
11use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; 14use crate::{item::CompletionKind, CompletionConfig, CompletionItem};
12 15
13/// Creates analysis from a multi-file fixture, returns positions marked with <|>. 16pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
17 enable_postfix_completions: true,
18 enable_autoimport_completions: true,
19 add_call_parenthesis: true,
20 add_call_argument_snippets: true,
21 snippet_cap: SnippetCap::new(true),
22 merge: Some(MergeBehavior::Full),
23};
24
25/// Creates analysis from a multi-file fixture, returns positions marked with $0.
14pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { 26pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
15 let change_fixture = ChangeFixture::parse(ra_fixture); 27 let change_fixture = ChangeFixture::parse(ra_fixture);
16 let mut database = RootDatabase::default(); 28 let mut database = RootDatabase::default();
17 database.apply_change(change_fixture.change); 29 database.apply_change(change_fixture.change);
18 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); 30 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
19 let offset = match range_or_offset { 31 let offset = match range_or_offset {
20 RangeOrOffset::Range(_) => panic!(), 32 RangeOrOffset::Range(_) => panic!(),
21 RangeOrOffset::Offset(it) => it, 33 RangeOrOffset::Offset(it) => it,
@@ -24,7 +36,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
24} 36}
25 37
26pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 38pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
27 do_completion_with_config(CompletionConfig::default(), code, kind) 39 do_completion_with_config(TEST_CONFIG, code, kind)
28} 40}
29 41
30pub(crate) fn do_completion_with_config( 42pub(crate) fn do_completion_with_config(
@@ -39,7 +51,7 @@ pub(crate) fn do_completion_with_config(
39} 51}
40 52
41pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { 53pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
42 completion_list_with_config(CompletionConfig::default(), code, kind) 54 completion_list_with_config(TEST_CONFIG, code, kind)
43} 55}
44 56
45pub(crate) fn completion_list_with_config( 57pub(crate) fn completion_list_with_config(
@@ -76,7 +88,7 @@ fn monospace_width(s: &str) -> usize {
76} 88}
77 89
78pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 90pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
79 check_edit_with_config(CompletionConfig::default(), what, ra_fixture_before, ra_fixture_after) 91 check_edit_with_config(TEST_CONFIG, what, ra_fixture_before, ra_fixture_after)
80} 92}
81 93
82pub(crate) fn check_edit_with_config( 94pub(crate) fn check_edit_with_config(
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml
index 3d9436d69..1d19c7886 100644
--- a/crates/flycheck/Cargo.toml
+++ b/crates/flycheck/Cargo.toml
@@ -12,8 +12,9 @@ doctest = false
12[dependencies] 12[dependencies]
13crossbeam-channel = "0.5.0" 13crossbeam-channel = "0.5.0"
14log = "0.4.8" 14log = "0.4.8"
15cargo_metadata = "=0.12.0" 15cargo_metadata = "0.12.2"
16serde_json = "1.0.48" 16serde_json = "1.0.48"
17jod-thread = "0.1.1" 17jod-thread = "0.1.1"
18 18
19toolchain = { path = "../toolchain", version = "0.0.0" } 19toolchain = { path = "../toolchain", version = "0.0.0" }
20stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index d982c5f29..4388e8c67 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -5,13 +5,13 @@
5use std::{ 5use std::{
6 fmt, 6 fmt,
7 io::{self, BufReader}, 7 io::{self, BufReader},
8 ops,
9 path::PathBuf, 8 path::PathBuf,
10 process::{self, Command, Stdio}, 9 process::{self, Command, Stdio},
11 time::Duration, 10 time::Duration,
12}; 11};
13 12
14use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; 13use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
14use stdx::JodChild;
15 15
16pub use cargo_metadata::diagnostic::{ 16pub use cargo_metadata::diagnostic::{
17 Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan, 17 Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
@@ -323,24 +323,3 @@ impl CargoActor {
323 Ok(read_at_least_one_message) 323 Ok(read_at_least_one_message)
324 } 324 }
325} 325}
326
327struct JodChild(process::Child);
328
329impl ops::Deref for JodChild {
330 type Target = process::Child;
331 fn deref(&self) -> &process::Child {
332 &self.0
333 }
334}
335
336impl ops::DerefMut for JodChild {
337 fn deref_mut(&mut self) -> &mut process::Child {
338 &mut self.0
339 }
340}
341
342impl Drop for JodChild {
343 fn drop(&mut self) {
344 let _ = self.0.kill();
345 }
346}
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index 6dc5ad63b..d4ea7327e 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -14,7 +14,7 @@ log = "0.4.8"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15either = "1.5.3" 15either = "1.5.3"
16arrayvec = "0.5.1" 16arrayvec = "0.5.1"
17itertools = "0.9.0" 17itertools = "0.10.0"
18 18
19stdx = { path = "../stdx", version = "0.0.0" } 19stdx = { path = "../stdx", version = "0.0.0" }
20syntax = { path = "../syntax", version = "0.0.0" } 20syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index d32ce37ed..99fb65bac 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -3,15 +3,15 @@ use hir_def::{
3 attr::{Attrs, Documentation}, 3 attr::{Attrs, Documentation},
4 path::ModPath, 4 path::ModPath,
5 resolver::HasResolver, 5 resolver::HasResolver,
6 AttrDefId, ModuleDefId, 6 AttrDefId, GenericParamId, ModuleDefId,
7}; 7};
8use hir_expand::hygiene::Hygiene; 8use hir_expand::hygiene::Hygiene;
9use hir_ty::db::HirDatabase; 9use hir_ty::db::HirDatabase;
10use syntax::ast; 10use syntax::ast;
11 11
12use crate::{ 12use crate::{
13 Adt, Const, Enum, Field, Function, MacroDef, Module, ModuleDef, Static, Struct, Trait, 13 Adt, Const, ConstParam, Enum, Field, Function, GenericParam, LifetimeParam, MacroDef, Module,
14 TypeAlias, Union, Variant, 14 ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
15}; 15};
16 16
17pub trait HasAttrs { 17pub trait HasAttrs {
@@ -62,25 +62,27 @@ impl_has_attrs![
62 (Function, FunctionId), 62 (Function, FunctionId),
63 (Adt, AdtId), 63 (Adt, AdtId),
64 (Module, ModuleId), 64 (Module, ModuleId),
65 (GenericParam, GenericParamId),
65]; 66];
66 67
67macro_rules! impl_has_attrs_adt { 68macro_rules! impl_has_attrs_enum {
68 ($($adt:ident),*) => {$( 69 ($($variant:ident),* for $enum:ident) => {$(
69 impl HasAttrs for $adt { 70 impl HasAttrs for $variant {
70 fn attrs(self, db: &dyn HirDatabase) -> Attrs { 71 fn attrs(self, db: &dyn HirDatabase) -> Attrs {
71 Adt::$adt(self).attrs(db) 72 $enum::$variant(self).attrs(db)
72 } 73 }
73 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { 74 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
74 Adt::$adt(self).docs(db) 75 $enum::$variant(self).docs(db)
75 } 76 }
76 fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> { 77 fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
77 Adt::$adt(self).resolve_doc_path(db, link, ns) 78 $enum::$variant(self).resolve_doc_path(db, link, ns)
78 } 79 }
79 } 80 }
80 )*}; 81 )*};
81} 82}
82 83
83impl_has_attrs_adt![Struct, Union, Enum]; 84impl_has_attrs_enum![Struct, Union, Enum for Adt];
85impl_has_attrs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam];
84 86
85fn resolve_doc_path( 87fn resolve_doc_path(
86 db: &dyn HirDatabase, 88 db: &dyn HirDatabase,
@@ -99,6 +101,12 @@ fn resolve_doc_path(
99 AttrDefId::TraitId(it) => it.resolver(db.upcast()), 101 AttrDefId::TraitId(it) => it.resolver(db.upcast()),
100 AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()), 102 AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()),
101 AttrDefId::ImplId(it) => it.resolver(db.upcast()), 103 AttrDefId::ImplId(it) => it.resolver(db.upcast()),
104 AttrDefId::GenericParamId(it) => match it {
105 GenericParamId::TypeParamId(it) => it.parent,
106 GenericParamId::LifetimeParamId(it) => it.parent,
107 GenericParamId::ConstParamId(it) => it.parent,
108 }
109 .resolver(db.upcast()),
102 AttrDefId::MacroDefId(_) => return None, 110 AttrDefId::MacroDefId(_) => return None,
103 }; 111 };
104 let path = ast::Path::parse(link).ok()?; 112 let path = ast::Path::parse(link).ok()?;
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index b7ded3478..6cbf5cecf 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -5,9 +5,7 @@ use arrayvec::ArrayVec;
5use base_db::{CrateDisplayName, CrateId, Edition, FileId}; 5use base_db::{CrateDisplayName, CrateId, Edition, FileId};
6use either::Either; 6use either::Either;
7use hir_def::{ 7use hir_def::{
8 adt::ReprKind, 8 adt::{ReprKind, StructKind, VariantData},
9 adt::StructKind,
10 adt::VariantData,
11 builtin_type::BuiltinType, 9 builtin_type::BuiltinType,
12 expr::{BindingAnnotation, LabelId, Pat, PatId}, 10 expr::{BindingAnnotation, LabelId, Pat, PatId},
13 import_map, 11 import_map,
@@ -18,10 +16,10 @@ use hir_def::{
18 resolver::{HasResolver, Resolver}, 16 resolver::{HasResolver, Resolver},
19 src::HasSource as _, 17 src::HasSource as _,
20 type_ref::{Mutability, TypeRef}, 18 type_ref::{Mutability, TypeRef},
21 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, DefWithBodyId, EnumId, 19 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
22 FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, 20 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
23 LocalModuleId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, 21 LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StaticId, StructId, TraitId,
24 UnionId, 22 TypeAliasId, TypeParamId, UnionId,
25}; 23};
26use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility}; 24use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility};
27use hir_expand::{ 25use hir_expand::{
@@ -31,7 +29,7 @@ use hir_expand::{
31}; 29};
32use hir_ty::{ 30use hir_ty::{
33 autoderef, 31 autoderef,
34 display::{HirDisplayError, HirFormatter}, 32 display::{write_bounds_like_dyn_trait, HirDisplayError, HirFormatter},
35 method_resolution, 33 method_resolution,
36 traits::{FnTrait, Solution, SolutionVariables}, 34 traits::{FnTrait, Solution, SolutionVariables},
37 ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate, 35 ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate,
@@ -39,7 +37,7 @@ use hir_ty::{
39 TyDefId, TyKind, TypeCtor, 37 TyDefId, TyKind, TypeCtor,
40}; 38};
41use rustc_hash::FxHashSet; 39use rustc_hash::FxHashSet;
42use stdx::impl_from; 40use stdx::{format_to, impl_from};
43use syntax::{ 41use syntax::{
44 ast::{self, AttrsOwner, NameOwner}, 42 ast::{self, AttrsOwner, NameOwner},
45 AstNode, SmolStr, 43 AstNode, SmolStr,
@@ -745,6 +743,18 @@ impl Function {
745 db.function_data(self.id).name.clone() 743 db.function_data(self.id).name.clone()
746 } 744 }
747 745
746 /// Get this function's return type
747 pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
748 let resolver = self.id.resolver(db.upcast());
749 let ret_type = &db.function_data(self.id).ret_type;
750 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
751 let environment = TraitEnvironment::lower(db, &resolver);
752 Type {
753 krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate,
754 ty: InEnvironment { value: Ty::from_hir_ext(&ctx, ret_type).0, environment },
755 }
756 }
757
748 pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> { 758 pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
749 if !db.function_data(self.id).has_self_param { 759 if !db.function_data(self.id).has_self_param {
750 return None; 760 return None;
@@ -797,6 +807,19 @@ impl Function {
797 pub fn has_body(self, db: &dyn HirDatabase) -> bool { 807 pub fn has_body(self, db: &dyn HirDatabase) -> bool {
798 db.function_data(self.id).has_body 808 db.function_data(self.id).has_body
799 } 809 }
810
811 /// A textual representation of the HIR of this function for debugging purposes.
812 pub fn debug_hir(self, db: &dyn HirDatabase) -> String {
813 let body = db.body(self.id.into());
814
815 let mut result = String::new();
816 format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db));
817 for (id, expr) in body.exprs.iter() {
818 format_to!(result, "{:?}: {:?}\n", id, expr);
819 }
820
821 result
822 }
800} 823}
801 824
802// Note: logically, this belongs to `hir_ty`, but we are not using it there yet. 825// Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
@@ -983,13 +1006,7 @@ impl MacroDef {
983 1006
984 /// XXX: this parses the file 1007 /// XXX: this parses the file
985 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { 1008 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
986 // FIXME: Currently proc-macro do not have ast-node, 1009 self.source(db)?.value.name().map(|it| it.as_name())
987 // such that it does not have source
988 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
989 if self.is_proc_macro() {
990 return None;
991 }
992 self.source(db).value.name().map(|it| it.as_name())
993 } 1010 }
994 1011
995 /// Indicate it is a proc-macro 1012 /// Indicate it is a proc-macro
@@ -1125,7 +1142,12 @@ impl GenericDef {
1125 id: LifetimeParamId { parent: self.into(), local_id }, 1142 id: LifetimeParamId { parent: self.into(), local_id },
1126 }) 1143 })
1127 .map(GenericParam::LifetimeParam); 1144 .map(GenericParam::LifetimeParam);
1128 ty_params.chain(lt_params).collect() 1145 let const_params = generics
1146 .consts
1147 .iter()
1148 .map(|(local_id, _)| ConstParam { id: ConstParamId { parent: self.into(), local_id } })
1149 .map(GenericParam::ConstParam);
1150 ty_params.chain(lt_params).chain(const_params).collect()
1129 } 1151 }
1130 1152
1131 pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> { 1153 pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> {
@@ -1237,8 +1259,27 @@ impl Label {
1237pub enum GenericParam { 1259pub enum GenericParam {
1238 TypeParam(TypeParam), 1260 TypeParam(TypeParam),
1239 LifetimeParam(LifetimeParam), 1261 LifetimeParam(LifetimeParam),
1262 ConstParam(ConstParam),
1263}
1264impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam);
1265
1266impl GenericParam {
1267 pub fn module(self, db: &dyn HirDatabase) -> Module {
1268 match self {
1269 GenericParam::TypeParam(it) => it.module(db),
1270 GenericParam::LifetimeParam(it) => it.module(db),
1271 GenericParam::ConstParam(it) => it.module(db),
1272 }
1273 }
1274
1275 pub fn name(self, db: &dyn HirDatabase) -> Name {
1276 match self {
1277 GenericParam::TypeParam(it) => it.name(db),
1278 GenericParam::LifetimeParam(it) => it.name(db),
1279 GenericParam::ConstParam(it) => it.name(db),
1280 }
1281 }
1240} 1282}
1241impl_from!(TypeParam, LifetimeParam for GenericParam);
1242 1283
1243#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 1284#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1244pub struct TypeParam { 1285pub struct TypeParam {
@@ -1265,6 +1306,18 @@ impl TypeParam {
1265 } 1306 }
1266 } 1307 }
1267 1308
1309 pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
1310 db.generic_predicates_for_param(self.id)
1311 .into_iter()
1312 .filter_map(|pred| match &pred.value {
1313 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1314 Some(Trait::from(trait_ref.trait_))
1315 }
1316 _ => None,
1317 })
1318 .collect()
1319 }
1320
1268 pub fn default(self, db: &dyn HirDatabase) -> Option<Type> { 1321 pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
1269 let params = db.generic_defaults(self.id.parent); 1322 let params = db.generic_defaults(self.id.parent);
1270 let local_idx = hir_ty::param_idx(db, self.id)?; 1323 let local_idx = hir_ty::param_idx(db, self.id)?;
@@ -1280,6 +1333,20 @@ impl TypeParam {
1280 } 1333 }
1281} 1334}
1282 1335
1336impl HirDisplay for TypeParam {
1337 fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
1338 write!(f, "{}", self.name(f.db))?;
1339 let bounds = f.db.generic_predicates_for_param(self.id);
1340 let substs = Substs::type_params(f.db, self.id.parent);
1341 let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>();
1342 if !(predicates.is_empty() || f.omit_verbose_types()) {
1343 write!(f, ": ")?;
1344 write_bounds_like_dyn_trait(&predicates, f)?;
1345 }
1346 Ok(())
1347 }
1348}
1349
1283#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 1350#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1284pub struct LifetimeParam { 1351pub struct LifetimeParam {
1285 pub(crate) id: LifetimeParamId, 1352 pub(crate) id: LifetimeParamId,
@@ -1300,6 +1367,32 @@ impl LifetimeParam {
1300 } 1367 }
1301} 1368}
1302 1369
1370#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1371pub struct ConstParam {
1372 pub(crate) id: ConstParamId,
1373}
1374
1375impl ConstParam {
1376 pub fn name(self, db: &dyn HirDatabase) -> Name {
1377 let params = db.generic_params(self.id.parent);
1378 params.consts[self.id.local_id].name.clone()
1379 }
1380
1381 pub fn module(self, db: &dyn HirDatabase) -> Module {
1382 self.id.parent.module(db.upcast()).into()
1383 }
1384
1385 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1386 self.id.parent.into()
1387 }
1388
1389 pub fn ty(self, db: &dyn HirDatabase) -> Type {
1390 let def = self.id.parent;
1391 let krate = def.module(db.upcast()).krate;
1392 Type::new(db, krate, def, db.const_param_ty(self.id))
1393 }
1394}
1395
1303#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 1396#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1304pub struct Impl { 1397pub struct Impl {
1305 pub(crate) id: ImplId, 1398 pub(crate) id: ImplId,
@@ -1352,7 +1445,7 @@ impl Impl {
1352 } 1445 }
1353 1446
1354 pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> { 1447 pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> {
1355 let src = self.source(db); 1448 let src = self.source(db)?;
1356 let item = src.file_id.is_builtin_derive(db.upcast())?; 1449 let item = src.file_id.is_builtin_derive(db.upcast())?;
1357 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id); 1450 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id);
1358 1451
@@ -1567,9 +1660,10 @@ impl Type {
1567 } 1660 }
1568 1661
1569 pub fn is_fn(&self) -> bool { 1662 pub fn is_fn(&self) -> bool {
1570 matches!(&self.ty.value, 1663 matches!(
1571 Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(..), .. }) | 1664 &self.ty.value,
1572 Ty::Apply(ApplicationTy { ctor: TypeCtor::FnPtr { .. }, .. }) 1665 Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(..), .. })
1666 | Ty::Apply(ApplicationTy { ctor: TypeCtor::FnPtr { .. }, .. })
1573 ) 1667 )
1574 } 1668 }
1575 1669
diff --git a/crates/hir/src/db.rs b/crates/hir/src/db.rs
index d01e1b33d..d5d4cf5b6 100644
--- a/crates/hir/src/db.rs
+++ b/crates/hir/src/db.rs
@@ -10,8 +10,8 @@ pub use hir_def::db::{
10 TypeAliasDataQuery, UnionDataQuery, 10 TypeAliasDataQuery, UnionDataQuery,
11}; 11};
12pub use hir_expand::db::{ 12pub use hir_expand::db::{
13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, 13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, HygieneFrameQuery, InternEagerExpansionQuery,
14 MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery, 14 InternMacroQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandQuery, ParseMacroExpansionQuery,
15}; 15};
16pub use hir_ty::db::*; 16pub use hir_ty::db::*;
17 17
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index b1c924167..447faa04f 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -4,6 +4,6 @@ pub use hir_expand::diagnostics::{
4 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder, 4 Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
5}; 5};
6pub use hir_ty::diagnostics::{ 6pub use hir_ty::diagnostics::{
7 IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, 7 IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
8 NoSuchField, RemoveThisSemicolon, 8 NoSuchField, RemoveThisSemicolon,
9}; 9};
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index a0792b9a6..c8c5fecd7 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -1,18 +1,18 @@
1//! Utility module for converting between hir_def ids and code_model wrappers. 1//! Utility module for converting between hir_def ids and code_model wrappers.
2//! 2//!
3//! It's unclear if we need this long-term, but it's definitelly useful while we 3//! It's unclear if we need this long-term, but it's definitely useful while we
4//! are splitting the hir. 4//! are splitting the hir.
5 5
6use hir_def::{ 6use hir_def::{
7 expr::{LabelId, PatId}, 7 expr::{LabelId, PatId},
8 item_scope::ItemInNs, 8 item_scope::ItemInNs,
9 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, ModuleDefId, 9 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
10 VariantId, 10 ModuleDefId, VariantId,
11}; 11};
12 12
13use crate::{ 13use crate::{
14 Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local, MacroDef, ModuleDef, Variant, 14 code_model::GenericParam, Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local,
15 VariantDef, 15 MacroDef, ModuleDef, Variant, VariantDef,
16}; 16};
17 17
18macro_rules! from_id { 18macro_rules! from_id {
@@ -44,6 +44,7 @@ from_id![
44 (hir_def::ImplId, crate::Impl), 44 (hir_def::ImplId, crate::Impl),
45 (hir_def::TypeParamId, crate::TypeParam), 45 (hir_def::TypeParamId, crate::TypeParam),
46 (hir_def::LifetimeParamId, crate::LifetimeParam), 46 (hir_def::LifetimeParamId, crate::LifetimeParam),
47 (hir_def::ConstParamId, crate::ConstParam),
47 (hir_expand::MacroDefId, crate::MacroDef) 48 (hir_expand::MacroDefId, crate::MacroDef)
48]; 49];
49 50
@@ -67,6 +68,26 @@ impl From<Adt> for AdtId {
67 } 68 }
68} 69}
69 70
71impl From<GenericParamId> for GenericParam {
72 fn from(id: GenericParamId) -> Self {
73 match id {
74 GenericParamId::TypeParamId(it) => GenericParam::TypeParam(it.into()),
75 GenericParamId::LifetimeParamId(it) => GenericParam::LifetimeParam(it.into()),
76 GenericParamId::ConstParamId(it) => GenericParam::ConstParam(it.into()),
77 }
78 }
79}
80
81impl From<GenericParam> for GenericParamId {
82 fn from(id: GenericParam) -> Self {
83 match id {
84 GenericParam::TypeParam(it) => GenericParamId::TypeParamId(it.id),
85 GenericParam::LifetimeParam(it) => GenericParamId::LifetimeParamId(it.id),
86 GenericParam::ConstParam(it) => GenericParamId::ConstParamId(it.id),
87 }
88 }
89}
90
70impl From<EnumVariantId> for Variant { 91impl From<EnumVariantId> for Variant {
71 fn from(id: EnumVariantId) -> Self { 92 fn from(id: EnumVariantId) -> Self {
72 Variant { parent: id.parent.into(), id: id.local_id } 93 Variant { parent: id.parent.into(), id: id.local_id }
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index 0dc07c33e..7c57d8378 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -10,13 +10,13 @@ use hir_expand::InFile;
10use syntax::ast; 10use syntax::ast;
11 11
12use crate::{ 12use crate::{
13 db::HirDatabase, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam, MacroDef, 13 db::HirDatabase, Const, ConstParam, Enum, Field, FieldSource, Function, Impl, LifetimeParam,
14 Module, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant, 14 MacroDef, Module, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
15}; 15};
16 16
17pub trait HasSource { 17pub trait HasSource {
18 type Ast; 18 type Ast;
19 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast>; 19 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>>;
20} 20}
21 21
22/// NB: Module is !HasSource, because it has two source nodes at the same time: 22/// NB: Module is !HasSource, because it has two source nodes at the same time:
@@ -46,97 +46,104 @@ impl Module {
46 46
47impl HasSource for Field { 47impl HasSource for Field {
48 type Ast = FieldSource; 48 type Ast = FieldSource;
49 fn source(self, db: &dyn HirDatabase) -> InFile<FieldSource> { 49 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
50 let var = VariantId::from(self.parent); 50 let var = VariantId::from(self.parent);
51 let src = var.child_source(db.upcast()); 51 let src = var.child_source(db.upcast());
52 src.map(|it| match it[self.id].clone() { 52 let field_source = src.map(|it| match it[self.id].clone() {
53 Either::Left(it) => FieldSource::Pos(it), 53 Either::Left(it) => FieldSource::Pos(it),
54 Either::Right(it) => FieldSource::Named(it), 54 Either::Right(it) => FieldSource::Named(it),
55 }) 55 });
56 Some(field_source)
56 } 57 }
57} 58}
58impl HasSource for Struct { 59impl HasSource for Struct {
59 type Ast = ast::Struct; 60 type Ast = ast::Struct;
60 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Struct> { 61 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
61 self.id.lookup(db.upcast()).source(db.upcast()) 62 Some(self.id.lookup(db.upcast()).source(db.upcast()))
62 } 63 }
63} 64}
64impl HasSource for Union { 65impl HasSource for Union {
65 type Ast = ast::Union; 66 type Ast = ast::Union;
66 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Union> { 67 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
67 self.id.lookup(db.upcast()).source(db.upcast()) 68 Some(self.id.lookup(db.upcast()).source(db.upcast()))
68 } 69 }
69} 70}
70impl HasSource for Enum { 71impl HasSource for Enum {
71 type Ast = ast::Enum; 72 type Ast = ast::Enum;
72 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Enum> { 73 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
73 self.id.lookup(db.upcast()).source(db.upcast()) 74 Some(self.id.lookup(db.upcast()).source(db.upcast()))
74 } 75 }
75} 76}
76impl HasSource for Variant { 77impl HasSource for Variant {
77 type Ast = ast::Variant; 78 type Ast = ast::Variant;
78 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Variant> { 79 fn source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Variant>> {
79 self.parent.id.child_source(db.upcast()).map(|map| map[self.id].clone()) 80 Some(self.parent.id.child_source(db.upcast()).map(|map| map[self.id].clone()))
80 } 81 }
81} 82}
82impl HasSource for Function { 83impl HasSource for Function {
83 type Ast = ast::Fn; 84 type Ast = ast::Fn;
84 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Fn> { 85 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
85 self.id.lookup(db.upcast()).source(db.upcast()) 86 Some(self.id.lookup(db.upcast()).source(db.upcast()))
86 } 87 }
87} 88}
88impl HasSource for Const { 89impl HasSource for Const {
89 type Ast = ast::Const; 90 type Ast = ast::Const;
90 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Const> { 91 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
91 self.id.lookup(db.upcast()).source(db.upcast()) 92 Some(self.id.lookup(db.upcast()).source(db.upcast()))
92 } 93 }
93} 94}
94impl HasSource for Static { 95impl HasSource for Static {
95 type Ast = ast::Static; 96 type Ast = ast::Static;
96 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Static> { 97 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
97 self.id.lookup(db.upcast()).source(db.upcast()) 98 Some(self.id.lookup(db.upcast()).source(db.upcast()))
98 } 99 }
99} 100}
100impl HasSource for Trait { 101impl HasSource for Trait {
101 type Ast = ast::Trait; 102 type Ast = ast::Trait;
102 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Trait> { 103 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
103 self.id.lookup(db.upcast()).source(db.upcast()) 104 Some(self.id.lookup(db.upcast()).source(db.upcast()))
104 } 105 }
105} 106}
106impl HasSource for TypeAlias { 107impl HasSource for TypeAlias {
107 type Ast = ast::TypeAlias; 108 type Ast = ast::TypeAlias;
108 fn source(self, db: &dyn HirDatabase) -> InFile<ast::TypeAlias> { 109 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
109 self.id.lookup(db.upcast()).source(db.upcast()) 110 Some(self.id.lookup(db.upcast()).source(db.upcast()))
110 } 111 }
111} 112}
112impl HasSource for MacroDef { 113impl HasSource for MacroDef {
113 type Ast = ast::Macro; 114 type Ast = ast::Macro;
114 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Macro> { 115 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
115 InFile { 116 let ast_id = self.id.ast_id?;
116 file_id: self.id.ast_id.expect("MacroDef without ast_id").file_id, 117 Some(InFile { file_id: ast_id.file_id, value: ast_id.to_node(db.upcast()) })
117 value: self.id.ast_id.expect("MacroDef without ast_id").to_node(db.upcast()),
118 }
119 } 118 }
120} 119}
121impl HasSource for Impl { 120impl HasSource for Impl {
122 type Ast = ast::Impl; 121 type Ast = ast::Impl;
123 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Impl> { 122 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
124 self.id.lookup(db.upcast()).source(db.upcast()) 123 Some(self.id.lookup(db.upcast()).source(db.upcast()))
125 } 124 }
126} 125}
127 126
128impl HasSource for TypeParam { 127impl HasSource for TypeParam {
129 type Ast = Either<ast::Trait, ast::TypeParam>; 128 type Ast = Either<ast::Trait, ast::TypeParam>;
130 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> { 129 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
131 let child_source = self.id.parent.child_source(db.upcast()); 130 let child_source = self.id.parent.child_source(db.upcast());
132 child_source.map(|it| it[self.id.local_id].clone()) 131 Some(child_source.map(|it| it[self.id.local_id].clone()))
133 } 132 }
134} 133}
135 134
136impl HasSource for LifetimeParam { 135impl HasSource for LifetimeParam {
137 type Ast = ast::LifetimeParam; 136 type Ast = ast::LifetimeParam;
138 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> { 137 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
138 let child_source = self.id.parent.child_source(db.upcast());
139 Some(child_source.map(|it| it[self.id.local_id].clone()))
140 }
141}
142
143impl HasSource for ConstParam {
144 type Ast = ast::ConstParam;
145 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
139 let child_source = self.id.parent.child_source(db.upcast()); 146 let child_source = self.id.parent.child_source(db.upcast());
140 child_source.map(|it| it[self.id.local_id].clone()) 147 Some(child_source.map(|it| it[self.id.local_id].clone()))
141 } 148 }
142} 149}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 7ac9fd507..769945c47 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -34,9 +34,10 @@ pub use crate::{
34 attrs::{HasAttrs, Namespace}, 34 attrs::{HasAttrs, Namespace},
35 code_model::{ 35 code_model::{
36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const, 36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const,
37 Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function, GenericDef, 37 ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function,
38 HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, ScopeDef, 38 GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef,
39 Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, 39 Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union,
40 Variant, VariantDef,
40 }, 41 },
41 has_source::HasSource, 42 has_source::HasSource,
42 semantics::{PathResolution, Semantics, SemanticsScope}, 43 semantics::{PathResolution, Semantics, SemanticsScope},
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 67cd16e31..cd689c869 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -25,9 +25,9 @@ use crate::{
25 diagnostics::Diagnostic, 25 diagnostics::Diagnostic,
26 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, 26 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
27 source_analyzer::{resolve_hir_path, SourceAnalyzer}, 27 source_analyzer::{resolve_hir_path, SourceAnalyzer},
28 AssocItem, Callable, Crate, Field, Function, HirFileId, Impl, InFile, Label, LifetimeParam, 28 AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile, Label,
29 Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, 29 LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type,
30 VariantDef, 30 TypeAlias, TypeParam, VariantDef,
31}; 31};
32 32
33#[derive(Debug, Clone, PartialEq, Eq)] 33#[derive(Debug, Clone, PartialEq, Eq)]
@@ -38,6 +38,7 @@ pub enum PathResolution {
38 Local(Local), 38 Local(Local),
39 /// A generic parameter 39 /// A generic parameter
40 TypeParam(TypeParam), 40 TypeParam(TypeParam),
41 ConstParam(ConstParam),
41 SelfType(Impl), 42 SelfType(Impl),
42 Macro(MacroDef), 43 Macro(MacroDef),
43 AssocItem(AssocItem), 44 AssocItem(AssocItem),
@@ -59,7 +60,9 @@ impl PathResolution {
59 PathResolution::Def(ModuleDef::TypeAlias(alias)) => { 60 PathResolution::Def(ModuleDef::TypeAlias(alias)) => {
60 Some(TypeNs::TypeAliasId((*alias).into())) 61 Some(TypeNs::TypeAliasId((*alias).into()))
61 } 62 }
62 PathResolution::Local(_) | PathResolution::Macro(_) => None, 63 PathResolution::Local(_) | PathResolution::Macro(_) | PathResolution::ConstParam(_) => {
64 None
65 }
63 PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())), 66 PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
64 PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())), 67 PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
65 PathResolution::AssocItem(AssocItem::Const(_)) 68 PathResolution::AssocItem(AssocItem::Const(_))
@@ -744,6 +747,7 @@ to_def_impls![
744 (crate::Variant, ast::Variant, enum_variant_to_def), 747 (crate::Variant, ast::Variant, enum_variant_to_def),
745 (crate::TypeParam, ast::TypeParam, type_param_to_def), 748 (crate::TypeParam, ast::TypeParam, type_param_to_def),
746 (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def), 749 (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def),
750 (crate::ConstParam, ast::ConstParam, const_param_to_def),
747 (crate::MacroDef, ast::MacroRules, macro_rules_to_def), 751 (crate::MacroDef, ast::MacroRules, macro_rules_to_def),
748 (crate::Local, ast::IdentPat, bind_pat_to_def), 752 (crate::Local, ast::IdentPat, bind_pat_to_def),
749 (crate::Label, ast::Label, label_to_def), 753 (crate::Label, ast::Label, label_to_def),
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 424e6e8a9..4b9ebff72 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -6,9 +6,9 @@ use hir_def::{
6 dyn_map::DynMap, 6 dyn_map::DynMap,
7 expr::{LabelId, PatId}, 7 expr::{LabelId, PatId},
8 keys::{self, Key}, 8 keys::{self, Key},
9 ConstId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, ImplId, 9 ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId,
10 LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, 10 ImplId, LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
11 VariantId, 11 UnionId, VariantId,
12}; 12};
13use hir_expand::{name::AsName, AstId, MacroDefKind}; 13use hir_expand::{name::AsName, AstId, MacroDefKind};
14use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
@@ -157,6 +157,18 @@ impl SourceToDefCtx<'_, '_> {
157 dyn_map[keys::LIFETIME_PARAM].get(&src).copied() 157 dyn_map[keys::LIFETIME_PARAM].get(&src).copied()
158 } 158 }
159 159
160 pub(super) fn const_param_to_def(
161 &mut self,
162 src: InFile<ast::ConstParam>,
163 ) -> Option<ConstParamId> {
164 let container: ChildContainer =
165 self.find_generic_param_container(src.as_ref().map(|it| it.syntax()))?.into();
166 let db = self.db;
167 let dyn_map =
168 &*self.cache.entry(container).or_insert_with(|| container.child_by_source(db));
169 dyn_map[keys::CONST_PARAM].get(&src).copied()
170 }
171
160 // FIXME: use DynMap as well? 172 // FIXME: use DynMap as well?
161 pub(super) fn macro_rules_to_def( 173 pub(super) fn macro_rules_to_def(
162 &mut self, 174 &mut self,
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index bddc49c05..30a8e513d 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -479,6 +479,7 @@ pub(crate) fn resolve_hir_path(
479 ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), 479 ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()),
480 ValueNs::EnumVariantId(it) => PathResolution::Def(Variant::from(it).into()), 480 ValueNs::EnumVariantId(it) => PathResolution::Def(Variant::from(it).into()),
481 ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()), 481 ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()),
482 ValueNs::GenericParam(it) => PathResolution::ConstParam(it.into()),
482 }; 483 };
483 Some(res) 484 Some(res)
484 }); 485 });
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml
index a88b5f57e..7ef966cd2 100644
--- a/crates/hir_def/Cargo.toml
+++ b/crates/hir_def/Cargo.toml
@@ -17,7 +17,7 @@ either = "1.5.3"
17anymap = "0.12.1" 17anymap = "0.12.1"
18drop_bomb = "0.1.4" 18drop_bomb = "0.1.4"
19fst = { version = "0.4", default-features = false } 19fst = { version = "0.4", default-features = false }
20itertools = "0.9.0" 20itertools = "0.10.0"
21indexmap = "1.4.0" 21indexmap = "1.4.0"
22smallvec = "1.4.0" 22smallvec = "1.4.0"
23 23
@@ -33,4 +33,4 @@ cfg = { path = "../cfg", version = "0.0.0" }
33tt = { path = "../tt", version = "0.0.0" } 33tt = { path = "../tt", version = "0.0.0" }
34 34
35[dev-dependencies] 35[dev-dependencies]
36expect-test = "1.0" 36expect-test = "1.1"
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 042e119b1..9e6426b31 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -2,6 +2,7 @@
2 2
3use std::{ops, sync::Arc}; 3use std::{ops, sync::Arc};
4 4
5use arena::map::ArenaMap;
5use base_db::CrateId; 6use base_db::CrateId;
6use cfg::{CfgExpr, CfgOptions}; 7use cfg::{CfgExpr, CfgOptions};
7use either::Either; 8use either::Either;
@@ -21,7 +22,8 @@ use crate::{
21 nameres::ModuleSource, 22 nameres::ModuleSource,
22 path::{ModPath, PathKind}, 23 path::{ModPath, PathKind},
23 src::HasChildSource, 24 src::HasChildSource,
24 AdtId, AttrDefId, Lookup, 25 AdtId, AttrDefId, EnumId, GenericParamId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup,
26 VariantId,
25}; 27};
26 28
27/// Holds documentation 29/// Holds documentation
@@ -210,16 +212,10 @@ impl Attrs {
210 } 212 }
211 } 213 }
212 AttrDefId::FieldId(it) => { 214 AttrDefId::FieldId(it) => {
213 let src = it.parent.child_source(db); 215 return db.fields_attrs(it.parent)[it.local_id].clone();
214 match &src.value[it.local_id] {
215 Either::Left(_tuple) => RawAttrs::default(),
216 Either::Right(record) => RawAttrs::from_attrs_owner(db, src.with_value(record)),
217 }
218 } 216 }
219 AttrDefId::EnumVariantId(var_id) => { 217 AttrDefId::EnumVariantId(it) => {
220 let src = var_id.parent.child_source(db); 218 return db.variants_attrs(it.parent)[it.local_id].clone();
221 let src = src.as_ref().map(|it| &it[var_id.local_id]);
222 RawAttrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
223 } 219 }
224 AttrDefId::AdtId(it) => match it { 220 AttrDefId::AdtId(it) => match it {
225 AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), 221 AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
@@ -235,11 +231,70 @@ impl Attrs {
235 AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), 231 AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
236 AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), 232 AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
237 AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), 233 AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
234 AttrDefId::GenericParamId(it) => match it {
235 GenericParamId::TypeParamId(it) => {
236 let src = it.parent.child_source(db);
237 RawAttrs::from_attrs_owner(
238 db,
239 src.with_value(
240 src.value[it.local_id].as_ref().either(|it| it as _, |it| it as _),
241 ),
242 )
243 }
244 GenericParamId::LifetimeParamId(it) => {
245 let src = it.parent.child_source(db);
246 RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id]))
247 }
248 GenericParamId::ConstParamId(it) => {
249 let src = it.parent.child_source(db);
250 RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id]))
251 }
252 },
238 }; 253 };
239 254
240 raw_attrs.filter(db, def.krate(db)) 255 raw_attrs.filter(db, def.krate(db))
241 } 256 }
242 257
258 pub(crate) fn variants_attrs_query(
259 db: &dyn DefDatabase,
260 e: EnumId,
261 ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> {
262 let krate = e.lookup(db).container.module(db).krate;
263 let src = e.child_source(db);
264 let mut res = ArenaMap::default();
265
266 for (id, var) in src.value.iter() {
267 let attrs = RawAttrs::from_attrs_owner(db, src.with_value(var as &dyn AttrsOwner))
268 .filter(db, krate);
269
270 res.insert(id, attrs)
271 }
272
273 Arc::new(res)
274 }
275
276 pub(crate) fn fields_attrs_query(
277 db: &dyn DefDatabase,
278 v: VariantId,
279 ) -> Arc<ArenaMap<LocalFieldId, Attrs>> {
280 let krate = v.module(db).krate;
281 let src = v.child_source(db);
282 let mut res = ArenaMap::default();
283
284 for (id, fld) in src.value.iter() {
285 let attrs = match fld {
286 Either::Left(_tuple) => Attrs::default(),
287 Either::Right(record) => {
288 RawAttrs::from_attrs_owner(db, src.with_value(record)).filter(db, krate)
289 }
290 };
291
292 res.insert(id, attrs);
293 }
294
295 Arc::new(res)
296 }
297
243 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { 298 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
244 AttrQuery { attrs: self, key } 299 AttrQuery { attrs: self, key }
245 } 300 }
@@ -260,14 +315,13 @@ impl Attrs {
260 } 315 }
261 316
262 pub fn docs(&self) -> Option<Documentation> { 317 pub fn docs(&self) -> Option<Documentation> {
263 let docs = self 318 let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_ref()? {
264 .by_key("doc") 319 AttrInput::Literal(s) => Some(s),
265 .attrs() 320 AttrInput::TokenTree(_) => None,
266 .flat_map(|attr| match attr.input.as_ref()? { 321 });
267 AttrInput::Literal(s) => Some(s), 322 // FIXME: Replace `Itertools::intersperse` with `Iterator::intersperse[_with]` until the
268 AttrInput::TokenTree(_) => None, 323 // libstd api gets stabilized (https://github.com/rust-lang/rust/issues/79524).
269 }) 324 let docs = Itertools::intersperse(docs, &SmolStr::new_inline("\n"))
270 .intersperse(&SmolStr::new_inline("\n"))
271 .map(|it| it.as_str()) 325 .map(|it| it.as_str())
272 .collect::<String>(); 326 .collect::<String>();
273 if docs.is_empty() { 327 if docs.is_empty() {
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 17c72779b..3dc33f248 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -581,7 +581,7 @@ impl ExprCollector<'_> {
581 match res.value { 581 match res.value {
582 Some((mark, expansion)) => { 582 Some((mark, expansion)) => {
583 // FIXME: Statements are too complicated to recover from error for now. 583 // FIXME: Statements are too complicated to recover from error for now.
584 // It is because we don't have any hygenine for local variable expansion right now. 584 // It is because we don't have any hygiene for local variable expansion right now.
585 if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() { 585 if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() {
586 self.expander.exit(self.db, mark); 586 self.expander.exit(self.db, mark);
587 collector(self, None); 587 collector(self, None);
@@ -695,7 +695,7 @@ impl ExprCollector<'_> {
695 self.collect_stmts_items(block.statements()); 695 self.collect_stmts_items(block.statements());
696 let statements = 696 let statements =
697 block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); 697 block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect();
698 let tail = block.expr().map(|e| self.collect_expr(e)); 698 let tail = block.tail_expr().map(|e| self.collect_expr(e));
699 self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr) 699 self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr)
700 } 700 }
701 701
@@ -959,7 +959,7 @@ impl ExprCollector<'_> {
959 959
960 fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) { 960 fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) {
961 // Find the location of the `..`, if there is one. Note that we do not 961 // Find the location of the `..`, if there is one. Note that we do not
962 // consider the possiblity of there being multiple `..` here. 962 // consider the possibility of there being multiple `..` here.
963 let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_))); 963 let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
964 // We want to skip the `..` pattern here, since we account for it above. 964 // We want to skip the `..` pattern here, since we account for it above.
965 let args = args 965 let args = args
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
index 9142bc05b..065785da7 100644
--- a/crates/hir_def/src/body/scope.rs
+++ b/crates/hir_def/src/body/scope.rs
@@ -194,7 +194,7 @@ mod tests {
194 let mut buf = String::new(); 194 let mut buf = String::new();
195 let off: usize = offset.into(); 195 let off: usize = offset.into();
196 buf.push_str(&code[..off]); 196 buf.push_str(&code[..off]);
197 buf.push_str("<|>marker"); 197 buf.push_str("$0marker");
198 buf.push_str(&code[off..]); 198 buf.push_str(&code[off..]);
199 buf 199 buf
200 }; 200 };
@@ -231,7 +231,7 @@ mod tests {
231 r" 231 r"
232 fn quux(foo: i32) { 232 fn quux(foo: i32) {
233 let f = |bar, baz: i32| { 233 let f = |bar, baz: i32| {
234 <|> 234 $0
235 }; 235 };
236 }", 236 }",
237 &["bar", "baz", "foo"], 237 &["bar", "baz", "foo"],
@@ -243,7 +243,7 @@ mod tests {
243 do_check( 243 do_check(
244 r" 244 r"
245 fn quux() { 245 fn quux() {
246 f(|x| <|> ); 246 f(|x| $0 );
247 }", 247 }",
248 &["x"], 248 &["x"],
249 ); 249 );
@@ -254,7 +254,7 @@ mod tests {
254 do_check( 254 do_check(
255 r" 255 r"
256 fn quux() { 256 fn quux() {
257 z.f(|x| <|> ); 257 z.f(|x| $0 );
258 }", 258 }",
259 &["x"], 259 &["x"],
260 ); 260 );
@@ -267,7 +267,7 @@ mod tests {
267 fn quux() { 267 fn quux() {
268 loop { 268 loop {
269 let x = (); 269 let x = ();
270 <|> 270 $0
271 }; 271 };
272 }", 272 }",
273 &["x"], 273 &["x"],
@@ -281,7 +281,7 @@ mod tests {
281 fn quux() { 281 fn quux() {
282 match () { 282 match () {
283 Some(x) => { 283 Some(x) => {
284 <|> 284 $0
285 } 285 }
286 }; 286 };
287 }", 287 }",
@@ -294,7 +294,7 @@ mod tests {
294 do_check( 294 do_check(
295 r" 295 r"
296 fn foo(x: String) { 296 fn foo(x: String) {
297 let x : &str = &x<|>; 297 let x : &str = &x$0;
298 }", 298 }",
299 &["x"], 299 &["x"],
300 ); 300 );
@@ -307,7 +307,7 @@ mod tests {
307fn foo() { 307fn foo() {
308 match Some(()) { 308 match Some(()) {
309 opt @ Some(unit) => { 309 opt @ Some(unit) => {
310 <|> 310 $0
311 } 311 }
312 _ => {} 312 _ => {}
313 } 313 }
@@ -330,7 +330,7 @@ fn foo() {
330 330
331 fn foo() { 331 fn foo() {
332 mac!(); 332 mac!();
333 <|> 333 $0
334 } 334 }
335 ", 335 ",
336 &[], 336 &[],
@@ -343,7 +343,7 @@ fn foo() {
343 r" 343 r"
344 fn foo() { 344 fn foo() {
345 trait {} 345 trait {}
346 <|> 346 $0
347 } 347 }
348 ", 348 ",
349 &[], 349 &[],
@@ -391,7 +391,7 @@ fn foo(x: i32, y: u32) {
391 let z = x * 2; 391 let z = x * 2;
392 } 392 }
393 { 393 {
394 let t = x<|> * 3; 394 let t = x$0 * 3;
395 } 395 }
396} 396}
397"#, 397"#,
@@ -404,7 +404,7 @@ fn foo(x: i32, y: u32) {
404 do_check_local_name( 404 do_check_local_name(
405 r#" 405 r#"
406fn foo(x: String) { 406fn foo(x: String) {
407 let x : &str = &x<|>; 407 let x : &str = &x$0;
408} 408}
409"#, 409"#,
410 7, 410 7,
@@ -417,7 +417,7 @@ fn foo(x: String) {
417 r" 417 r"
418fn foo(x: String) { 418fn foo(x: String) {
419 let x : &str = &x; 419 let x : &str = &x;
420 x<|> 420 x$0
421} 421}
422", 422",
423 28, 423 28,
@@ -430,7 +430,7 @@ fn foo(x: String) {
430 r" 430 r"
431fn foo() { 431fn foo() {
432 if let Some(&from) = bar() { 432 if let Some(&from) = bar() {
433 from<|>; 433 from$0;
434 } 434 }
435} 435}
436", 436",
@@ -446,7 +446,7 @@ fn foo() {
446fn test() { 446fn test() {
447 let foo: Option<f32> = None; 447 let foo: Option<f32> = None;
448 while let Option::Some(spam) = foo { 448 while let Option::Some(spam) = foo {
449 spam<|> 449 spam$0
450 } 450 }
451} 451}
452"#, 452"#,
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index d1a459066..d3bf5b34c 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -1,6 +1,7 @@
1//! Defines database & queries for name resolution. 1//! Defines database & queries for name resolution.
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use arena::map::ArenaMap;
4use base_db::{salsa, CrateId, SourceDatabase, Upcast}; 5use base_db::{salsa, CrateId, SourceDatabase, Upcast};
5use hir_expand::{db::AstDatabase, HirFileId}; 6use hir_expand::{db::AstDatabase, HirFileId};
6use syntax::SmolStr; 7use syntax::SmolStr;
@@ -16,8 +17,8 @@ use crate::{
16 lang_item::{LangItemTarget, LangItems}, 17 lang_item::{LangItemTarget, LangItems},
17 nameres::CrateDefMap, 18 nameres::CrateDefMap,
18 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, 19 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
19 GenericDefId, ImplId, ImplLoc, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, 20 GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, StaticLoc, StructId,
20 TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, 21 StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
21}; 22};
22 23
23#[salsa::query_group(InternDatabaseStorage)] 24#[salsa::query_group(InternDatabaseStorage)]
@@ -92,6 +93,12 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
92 #[salsa::invoke(GenericParams::generic_params_query)] 93 #[salsa::invoke(GenericParams::generic_params_query)]
93 fn generic_params(&self, def: GenericDefId) -> Arc<GenericParams>; 94 fn generic_params(&self, def: GenericDefId) -> Arc<GenericParams>;
94 95
96 #[salsa::invoke(Attrs::variants_attrs_query)]
97 fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>;
98
99 #[salsa::invoke(Attrs::fields_attrs_query)]
100 fn fields_attrs(&self, def: VariantId) -> Arc<ArenaMap<LocalFieldId, Attrs>>;
101
95 #[salsa::invoke(Attrs::attrs_query)] 102 #[salsa::invoke(Attrs::attrs_query)]
96 fn attrs(&self, def: AttrDefId) -> Attrs; 103 fn attrs(&self, def: AttrDefId) -> Attrs;
97 104
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
index c71266dc0..ab3f059ce 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -133,6 +133,10 @@ impl Diagnostic for InactiveCode {
133// This diagnostic is shown when a procedural macro can not be found. This usually means that 133// This diagnostic is shown when a procedural macro can not be found. This usually means that
134// procedural macro support is simply disabled (and hence is only a weak hint instead of an error), 134// procedural macro support is simply disabled (and hence is only a weak hint instead of an error),
135// but can also indicate project setup problems. 135// but can also indicate project setup problems.
136//
137// If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the
138// `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can
139// enable support for procedural macros (see `rust-analyzer.procMacro.enable`).
136#[derive(Debug, Clone, Eq, PartialEq)] 140#[derive(Debug, Clone, Eq, PartialEq)]
137pub struct UnresolvedProcMacro { 141pub struct UnresolvedProcMacro {
138 pub file: HirFileId, 142 pub file: HirFileId,
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index 6a481769d..76f5721e5 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -1,6 +1,6 @@
1//! This module describes hir-level representation of expressions. 1//! This module describes hir-level representation of expressions.
2//! 2//!
3//! This representaion is: 3//! This representation is:
4//! 4//!
5//! 1. Identity-based. Each expression has an `id`, so we can distinguish 5//! 1. Identity-based. Each expression has an `id`, so we can distinguish
6//! between different `1` in `1 + 1`. 6//! between different `1` in `1 + 1`.
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 02613c4c4..4a212d291 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -410,7 +410,7 @@ mod tests {
410 let code = r#" 410 let code = r#"
411 //- /main.rs 411 //- /main.rs
412 struct S; 412 struct S;
413 <|> 413 $0
414 "#; 414 "#;
415 check_found_path(code, "S", "S", "crate::S", "self::S"); 415 check_found_path(code, "S", "S", "crate::S", "self::S");
416 } 416 }
@@ -420,7 +420,7 @@ mod tests {
420 let code = r#" 420 let code = r#"
421 //- /main.rs 421 //- /main.rs
422 enum E { A } 422 enum E { A }
423 <|> 423 $0
424 "#; 424 "#;
425 check_found_path(code, "E::A", "E::A", "E::A", "E::A"); 425 check_found_path(code, "E::A", "E::A", "E::A", "E::A");
426 } 426 }
@@ -432,7 +432,7 @@ mod tests {
432 mod foo { 432 mod foo {
433 pub struct S; 433 pub struct S;
434 } 434 }
435 <|> 435 $0
436 "#; 436 "#;
437 check_found_path(code, "foo::S", "foo::S", "crate::foo::S", "self::foo::S"); 437 check_found_path(code, "foo::S", "foo::S", "crate::foo::S", "self::foo::S");
438 } 438 }
@@ -446,7 +446,7 @@ mod tests {
446 mod bar; 446 mod bar;
447 struct S; 447 struct S;
448 //- /foo/bar.rs 448 //- /foo/bar.rs
449 <|> 449 $0
450 "#; 450 "#;
451 check_found_path(code, "super::S", "super::S", "crate::foo::S", "super::S"); 451 check_found_path(code, "super::S", "super::S", "crate::foo::S", "super::S");
452 } 452 }
@@ -457,7 +457,7 @@ mod tests {
457 //- /main.rs 457 //- /main.rs
458 mod foo; 458 mod foo;
459 //- /foo.rs 459 //- /foo.rs
460 <|> 460 $0
461 "#; 461 "#;
462 check_found_path(code, "self", "self", "crate::foo", "self"); 462 check_found_path(code, "self", "self", "crate::foo", "self");
463 } 463 }
@@ -468,7 +468,7 @@ mod tests {
468 //- /main.rs 468 //- /main.rs
469 mod foo; 469 mod foo;
470 //- /foo.rs 470 //- /foo.rs
471 <|> 471 $0
472 "#; 472 "#;
473 check_found_path(code, "crate", "crate", "crate", "crate"); 473 check_found_path(code, "crate", "crate", "crate", "crate");
474 } 474 }
@@ -480,7 +480,7 @@ mod tests {
480 mod foo; 480 mod foo;
481 struct S; 481 struct S;
482 //- /foo.rs 482 //- /foo.rs
483 <|> 483 $0
484 "#; 484 "#;
485 check_found_path(code, "crate::S", "crate::S", "crate::S", "crate::S"); 485 check_found_path(code, "crate::S", "crate::S", "crate::S", "crate::S");
486 } 486 }
@@ -489,7 +489,7 @@ mod tests {
489 fn different_crate() { 489 fn different_crate() {
490 let code = r#" 490 let code = r#"
491 //- /main.rs crate:main deps:std 491 //- /main.rs crate:main deps:std
492 <|> 492 $0
493 //- /std.rs crate:std 493 //- /std.rs crate:std
494 pub struct S; 494 pub struct S;
495 "#; 495 "#;
@@ -501,7 +501,7 @@ mod tests {
501 let code = r#" 501 let code = r#"
502 //- /main.rs crate:main deps:std 502 //- /main.rs crate:main deps:std
503 extern crate std as std_renamed; 503 extern crate std as std_renamed;
504 <|> 504 $0
505 //- /std.rs crate:std 505 //- /std.rs crate:std
506 pub struct S; 506 pub struct S;
507 "#; 507 "#;
@@ -523,7 +523,7 @@ mod tests {
523 //- /main.rs crate:main deps:syntax 523 //- /main.rs crate:main deps:syntax
524 524
525 use syntax::ast; 525 use syntax::ast;
526 <|> 526 $0
527 527
528 //- /lib.rs crate:syntax 528 //- /lib.rs crate:syntax
529 pub mod ast { 529 pub mod ast {
@@ -543,7 +543,7 @@ mod tests {
543 let code = r#" 543 let code = r#"
544 //- /main.rs crate:main deps:syntax 544 //- /main.rs crate:main deps:syntax
545 545
546 <|> 546 $0
547 547
548 //- /lib.rs crate:syntax 548 //- /lib.rs crate:syntax
549 pub mod ast { 549 pub mod ast {
@@ -569,7 +569,7 @@ mod tests {
569 mod foo { pub(super) struct S; } 569 mod foo { pub(super) struct S; }
570 pub(crate) use foo::*; 570 pub(crate) use foo::*;
571 } 571 }
572 <|> 572 $0
573 "#; 573 "#;
574 check_found_path(code, "bar::S", "bar::S", "crate::bar::S", "self::bar::S"); 574 check_found_path(code, "bar::S", "bar::S", "crate::bar::S", "self::bar::S");
575 } 575 }
@@ -582,7 +582,7 @@ mod tests {
582 mod foo { pub(super) struct S; } 582 mod foo { pub(super) struct S; }
583 pub(crate) use foo::S as U; 583 pub(crate) use foo::S as U;
584 } 584 }
585 <|> 585 $0
586 "#; 586 "#;
587 check_found_path(code, "bar::U", "bar::U", "crate::bar::U", "self::bar::U"); 587 check_found_path(code, "bar::U", "bar::U", "crate::bar::U", "self::bar::U");
588 } 588 }
@@ -591,7 +591,7 @@ mod tests {
591 fn different_crate_reexport() { 591 fn different_crate_reexport() {
592 let code = r#" 592 let code = r#"
593 //- /main.rs crate:main deps:std 593 //- /main.rs crate:main deps:std
594 <|> 594 $0
595 //- /std.rs crate:std deps:core 595 //- /std.rs crate:std deps:core
596 pub use core::S; 596 pub use core::S;
597 //- /core.rs crate:core 597 //- /core.rs crate:core
@@ -604,7 +604,7 @@ mod tests {
604 fn prelude() { 604 fn prelude() {
605 let code = r#" 605 let code = r#"
606 //- /main.rs crate:main deps:std 606 //- /main.rs crate:main deps:std
607 <|> 607 $0
608 //- /std.rs crate:std 608 //- /std.rs crate:std
609 pub mod prelude { pub struct S; } 609 pub mod prelude { pub struct S; }
610 #[prelude_import] 610 #[prelude_import]
@@ -617,7 +617,7 @@ mod tests {
617 fn enum_variant_from_prelude() { 617 fn enum_variant_from_prelude() {
618 let code = r#" 618 let code = r#"
619 //- /main.rs crate:main deps:std 619 //- /main.rs crate:main deps:std
620 <|> 620 $0
621 //- /std.rs crate:std 621 //- /std.rs crate:std
622 pub mod prelude { 622 pub mod prelude {
623 pub enum Option<T> { Some(T), None } 623 pub enum Option<T> { Some(T), None }
@@ -637,7 +637,7 @@ mod tests {
637 pub mod foo; 637 pub mod foo;
638 pub mod baz; 638 pub mod baz;
639 struct S; 639 struct S;
640 <|> 640 $0
641 //- /foo.rs 641 //- /foo.rs
642 pub mod bar { pub struct S; } 642 pub mod bar { pub struct S; }
643 //- /baz.rs 643 //- /baz.rs
@@ -654,7 +654,7 @@ mod tests {
654 pub mod bar { pub struct S; } 654 pub mod bar { pub struct S; }
655 use bar::S; 655 use bar::S;
656 //- /foo.rs 656 //- /foo.rs
657 <|> 657 $0
658 "#; 658 "#;
659 // crate::S would be shorter, but using private imports seems wrong 659 // crate::S would be shorter, but using private imports seems wrong
660 check_found_path(code, "crate::bar::S", "crate::bar::S", "crate::bar::S", "crate::bar::S"); 660 check_found_path(code, "crate::bar::S", "crate::bar::S", "crate::bar::S", "crate::bar::S");
@@ -668,7 +668,7 @@ mod tests {
668 pub mod bar; 668 pub mod bar;
669 pub mod baz; 669 pub mod baz;
670 //- /bar.rs 670 //- /bar.rs
671 <|> 671 $0
672 //- /foo.rs 672 //- /foo.rs
673 pub use super::baz; 673 pub use super::baz;
674 pub struct S; 674 pub struct S;
@@ -683,7 +683,7 @@ mod tests {
683 mark::check!(prefer_std_paths); 683 mark::check!(prefer_std_paths);
684 let code = r#" 684 let code = r#"
685 //- /main.rs crate:main deps:alloc,std 685 //- /main.rs crate:main deps:alloc,std
686 <|> 686 $0
687 687
688 //- /std.rs crate:std deps:alloc 688 //- /std.rs crate:std deps:alloc
689 pub mod sync { 689 pub mod sync {
@@ -711,7 +711,7 @@ mod tests {
711 //- /main.rs crate:main deps:core,std 711 //- /main.rs crate:main deps:core,std
712 #![no_std] 712 #![no_std]
713 713
714 <|> 714 $0
715 715
716 //- /std.rs crate:std deps:core 716 //- /std.rs crate:std deps:core
717 717
@@ -740,7 +740,7 @@ mod tests {
740 //- /main.rs crate:main deps:alloc,std 740 //- /main.rs crate:main deps:alloc,std
741 #![no_std] 741 #![no_std]
742 742
743 <|> 743 $0
744 744
745 //- /std.rs crate:std deps:alloc 745 //- /std.rs crate:std deps:alloc
746 746
@@ -767,7 +767,7 @@ mod tests {
767 fn prefer_shorter_paths_if_not_alloc() { 767 fn prefer_shorter_paths_if_not_alloc() {
768 let code = r#" 768 let code = r#"
769 //- /main.rs crate:main deps:megaalloc,std 769 //- /main.rs crate:main deps:megaalloc,std
770 <|> 770 $0
771 771
772 //- /std.rs crate:std deps:megaalloc 772 //- /std.rs crate:std deps:megaalloc
773 pub mod sync { 773 pub mod sync {
@@ -790,7 +790,7 @@ mod tests {
790 fn builtins_are_in_scope() { 790 fn builtins_are_in_scope() {
791 let code = r#" 791 let code = r#"
792 //- /main.rs 792 //- /main.rs
793 <|> 793 $0
794 794
795 pub mod primitive { 795 pub mod primitive {
796 pub use u8; 796 pub use u8;
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index bb8fca009..9b5b886c2 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -21,11 +21,11 @@ use crate::{
21 keys, 21 keys,
22 src::{HasChildSource, HasSource}, 22 src::{HasChildSource, HasSource},
23 type_ref::{LifetimeRef, TypeBound, TypeRef}, 23 type_ref::{LifetimeRef, TypeBound, TypeRef},
24 AdtId, GenericDefId, LifetimeParamId, LocalLifetimeParamId, LocalTypeParamId, Lookup, 24 AdtId, ConstParamId, GenericDefId, LifetimeParamId, LocalConstParamId, LocalLifetimeParamId,
25 TypeParamId, 25 LocalTypeParamId, Lookup, TypeParamId,
26}; 26};
27 27
28/// Data about a generic parameter (to a function, struct, impl, ...). 28/// Data about a generic type parameter (to a function, struct, impl, ...).
29#[derive(Clone, PartialEq, Eq, Debug)] 29#[derive(Clone, PartialEq, Eq, Debug)]
30pub struct TypeParamData { 30pub struct TypeParamData {
31 pub name: Option<Name>, 31 pub name: Option<Name>,
@@ -33,12 +33,19 @@ pub struct TypeParamData {
33 pub provenance: TypeParamProvenance, 33 pub provenance: TypeParamProvenance,
34} 34}
35 35
36/// Data about a generic parameter (to a function, struct, impl, ...). 36/// Data about a generic lifetime parameter (to a function, struct, impl, ...).
37#[derive(Clone, PartialEq, Eq, Debug)] 37#[derive(Clone, PartialEq, Eq, Debug)]
38pub struct LifetimeParamData { 38pub struct LifetimeParamData {
39 pub name: Name, 39 pub name: Name,
40} 40}
41 41
42/// Data about a generic const parameter (to a function, struct, impl, ...).
43#[derive(Clone, PartialEq, Eq, Debug)]
44pub struct ConstParamData {
45 pub name: Name,
46 pub ty: TypeRef,
47}
48
42#[derive(Copy, Clone, PartialEq, Eq, Debug)] 49#[derive(Copy, Clone, PartialEq, Eq, Debug)]
43pub enum TypeParamProvenance { 50pub enum TypeParamProvenance {
44 TypeParamList, 51 TypeParamList,
@@ -51,6 +58,7 @@ pub enum TypeParamProvenance {
51pub struct GenericParams { 58pub struct GenericParams {
52 pub types: Arena<TypeParamData>, 59 pub types: Arena<TypeParamData>,
53 pub lifetimes: Arena<LifetimeParamData>, 60 pub lifetimes: Arena<LifetimeParamData>,
61 pub consts: Arena<ConstParamData>,
54 pub where_predicates: Vec<WherePredicate>, 62 pub where_predicates: Vec<WherePredicate>,
55} 63}
56 64
@@ -76,6 +84,7 @@ pub enum WherePredicateTypeTarget {
76pub(crate) struct SourceMap { 84pub(crate) struct SourceMap {
77 pub(crate) type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>, 85 pub(crate) type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>,
78 lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>, 86 lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>,
87 const_params: ArenaMap<LocalConstParamId, ast::ConstParam>,
79} 88}
80 89
81impl GenericParams { 90impl GenericParams {
@@ -268,6 +277,13 @@ impl GenericParams {
268 let lifetime_ref = LifetimeRef::new_name(name); 277 let lifetime_ref = LifetimeRef::new_name(name);
269 self.fill_bounds(&lower_ctx, &lifetime_param, Either::Right(lifetime_ref)); 278 self.fill_bounds(&lower_ctx, &lifetime_param, Either::Right(lifetime_ref));
270 } 279 }
280 for const_param in params.const_params() {
281 let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
282 let ty = const_param.ty().map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it));
283 let param = ConstParamData { name, ty };
284 let param_id = self.consts.alloc(param);
285 sm.const_params.insert(param_id, const_param.clone());
286 }
271 } 287 }
272 288
273 fn fill_where_predicates(&mut self, lower_ctx: &LowerCtx, where_clause: ast::WhereClause) { 289 fn fill_where_predicates(&mut self, lower_ctx: &LowerCtx, where_clause: ast::WhereClause) {
@@ -353,12 +369,16 @@ impl GenericParams {
353 }); 369 });
354 } 370 }
355 371
356 pub fn find_by_name(&self, name: &Name) -> Option<LocalTypeParamId> { 372 pub fn find_type_by_name(&self, name: &Name) -> Option<LocalTypeParamId> {
357 self.types 373 self.types
358 .iter() 374 .iter()
359 .find_map(|(id, p)| if p.name.as_ref() == Some(name) { Some(id) } else { None }) 375 .find_map(|(id, p)| if p.name.as_ref() == Some(name) { Some(id) } else { None })
360 } 376 }
361 377
378 pub fn find_const_by_name(&self, name: &Name) -> Option<LocalConstParamId> {
379 self.consts.iter().find_map(|(id, p)| if p.name == *name { Some(id) } else { None })
380 }
381
362 pub fn find_trait_self_param(&self) -> Option<LocalTypeParamId> { 382 pub fn find_trait_self_param(&self) -> Option<LocalTypeParamId> {
363 self.types.iter().find_map(|(id, p)| { 383 self.types.iter().find_map(|(id, p)| {
364 if p.provenance == TypeParamProvenance::TraitSelf { 384 if p.provenance == TypeParamProvenance::TraitSelf {
@@ -390,6 +410,16 @@ impl HasChildSource<LocalLifetimeParamId> for GenericDefId {
390 } 410 }
391} 411}
392 412
413impl HasChildSource<LocalConstParamId> for GenericDefId {
414 type Value = ast::ConstParam;
415 fn child_source(
416 &self,
417 db: &dyn DefDatabase,
418 ) -> InFile<ArenaMap<LocalConstParamId, Self::Value>> {
419 GenericParams::new(db, *self).1.map(|source_maps| source_maps.const_params)
420 }
421}
422
393impl ChildBySource for GenericDefId { 423impl ChildBySource for GenericDefId {
394 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 424 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
395 let mut res = DynMap::default(); 425 let mut res = DynMap::default();
@@ -406,6 +436,10 @@ impl ChildBySource for GenericDefId {
406 let id = LifetimeParamId { parent: *self, local_id }; 436 let id = LifetimeParamId { parent: *self, local_id };
407 res[keys::LIFETIME_PARAM].insert(sm.with_value(src.clone()), id); 437 res[keys::LIFETIME_PARAM].insert(sm.with_value(src.clone()), id);
408 } 438 }
439 for (local_id, src) in sm.value.const_params.iter() {
440 let id = ConstParamId { parent: *self, local_id };
441 res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id);
442 }
409 res 443 res
410 } 444 }
411} 445}
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index c4dc894df..e5368b293 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -7,9 +7,8 @@ use fst::{self, Streamer};
7use hir_expand::name::Name; 7use hir_expand::name::Name;
8use indexmap::{map::Entry, IndexMap}; 8use indexmap::{map::Entry, IndexMap};
9use itertools::Itertools; 9use itertools::Itertools;
10use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; 10use rustc_hash::{FxHashSet, FxHasher};
11use smallvec::SmallVec; 11use test_utils::mark;
12use syntax::SmolStr;
13 12
14use crate::{ 13use crate::{
15 db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, 14 db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
@@ -25,6 +24,8 @@ pub struct ImportInfo {
25 pub path: ImportPath, 24 pub path: ImportPath,
26 /// The module containing this item. 25 /// The module containing this item.
27 pub container: ModuleId, 26 pub container: ModuleId,
27 /// Whether the import is a trait associated item or not.
28 pub is_trait_assoc_item: bool,
28} 29}
29 30
30#[derive(Debug, Clone, Eq, PartialEq)] 31#[derive(Debug, Clone, Eq, PartialEq)]
@@ -64,10 +65,6 @@ pub struct ImportMap {
64 /// the index of the first one. 65 /// the index of the first one.
65 importables: Vec<ItemInNs>, 66 importables: Vec<ItemInNs>,
66 fst: fst::Map<Vec<u8>>, 67 fst: fst::Map<Vec<u8>>,
67
68 /// Maps names of associated items to the item's ID. Only includes items whose defining trait is
69 /// exported.
70 assoc_map: FxHashMap<SmolStr, SmallVec<[AssocItemId; 1]>>,
71} 68}
72 69
73impl ImportMap { 70impl ImportMap {
@@ -108,14 +105,27 @@ impl ImportMap {
108 105
109 for item in per_ns.iter_items() { 106 for item in per_ns.iter_items() {
110 let path = mk_path(); 107 let path = mk_path();
108 let path_len = path.len();
109 let import_info =
110 ImportInfo { path, container: module, is_trait_assoc_item: false };
111
112 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
113 import_map.collect_trait_assoc_items(
114 db,
115 tr,
116 matches!(item, ItemInNs::Types(_)),
117 &import_info,
118 );
119 }
120
111 match import_map.map.entry(item) { 121 match import_map.map.entry(item) {
112 Entry::Vacant(entry) => { 122 Entry::Vacant(entry) => {
113 entry.insert(ImportInfo { path, container: module }); 123 entry.insert(import_info);
114 } 124 }
115 Entry::Occupied(mut entry) => { 125 Entry::Occupied(mut entry) => {
116 // If the new path is shorter, prefer that one. 126 // If the new path is shorter, prefer that one.
117 if path.len() < entry.get().path.len() { 127 if path_len < entry.get().path.len() {
118 *entry.get_mut() = ImportInfo { path, container: module }; 128 *entry.get_mut() = import_info;
119 } else { 129 } else {
120 continue; 130 continue;
121 } 131 }
@@ -128,11 +138,6 @@ impl ImportMap {
128 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { 138 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
129 worklist.push((mod_id, mk_path())); 139 worklist.push((mod_id, mk_path()));
130 } 140 }
131
132 // If we've added a path to a trait, add the trait's methods to the method map.
133 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
134 import_map.collect_trait_methods(db, tr);
135 }
136 } 141 }
137 } 142 }
138 } 143 }
@@ -153,12 +158,10 @@ impl ImportMap {
153 } 158 }
154 } 159 }
155 160
156 let start = last_batch_start; 161 let key = fst_path(&importables[last_batch_start].1.path);
157 last_batch_start = idx + 1; 162 builder.insert(key, last_batch_start as u64).unwrap();
158
159 let key = fst_path(&importables[start].1.path);
160 163
161 builder.insert(key, start as u64).unwrap(); 164 last_batch_start = idx + 1;
162 } 165 }
163 166
164 import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); 167 import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
@@ -176,10 +179,34 @@ impl ImportMap {
176 self.map.get(&item) 179 self.map.get(&item)
177 } 180 }
178 181
179 fn collect_trait_methods(&mut self, db: &dyn DefDatabase, tr: TraitId) { 182 fn collect_trait_assoc_items(
180 let data = db.trait_data(tr); 183 &mut self,
181 for (name, item) in data.items.iter() { 184 db: &dyn DefDatabase,
182 self.assoc_map.entry(name.to_string().into()).or_default().push(*item); 185 tr: TraitId,
186 is_type_in_ns: bool,
187 original_import_info: &ImportInfo,
188 ) {
189 for (assoc_item_name, item) in &db.trait_data(tr).items {
190 let module_def_id = match item {
191 AssocItemId::FunctionId(f) => ModuleDefId::from(*f),
192 AssocItemId::ConstId(c) => ModuleDefId::from(*c),
193 // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias`
194 // qualifier, ergo no need to store it for imports in import_map
195 AssocItemId::TypeAliasId(_) => {
196 mark::hit!(type_aliases_ignored);
197 continue;
198 }
199 };
200 let assoc_item = if is_type_in_ns {
201 ItemInNs::Types(module_def_id)
202 } else {
203 ItemInNs::Values(module_def_id)
204 };
205
206 let mut assoc_item_info = original_import_info.clone();
207 assoc_item_info.path.segments.push(assoc_item_name.to_owned());
208 assoc_item_info.is_trait_assoc_item = true;
209 self.map.insert(assoc_item, assoc_item_info);
183 } 210 }
184 } 211 }
185} 212}
@@ -302,38 +329,38 @@ impl Query {
302 self.exclude_import_kinds.insert(import_kind); 329 self.exclude_import_kinds.insert(import_kind);
303 self 330 self
304 } 331 }
305}
306 332
307fn contains_query(query: &Query, input_path: &ImportPath, enforce_lowercase: bool) -> bool { 333 fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
308 let mut input = if query.name_only { 334 let mut input = if import.is_trait_assoc_item || self.name_only {
309 input_path.segments.last().unwrap().to_string() 335 import.path.segments.last().unwrap().to_string()
310 } else { 336 } else {
311 input_path.to_string() 337 import.path.to_string()
312 }; 338 };
313 if enforce_lowercase || !query.case_sensitive { 339 if enforce_lowercase || !self.case_sensitive {
314 input.make_ascii_lowercase(); 340 input.make_ascii_lowercase();
315 } 341 }
316 342
317 let query_string = 343 let query_string =
318 if !enforce_lowercase && query.case_sensitive { &query.query } else { &query.lowercased }; 344 if !enforce_lowercase && self.case_sensitive { &self.query } else { &self.lowercased };
319 345
320 match query.search_mode { 346 match self.search_mode {
321 SearchMode::Equals => &input == query_string, 347 SearchMode::Equals => &input == query_string,
322 SearchMode::Contains => input.contains(query_string), 348 SearchMode::Contains => input.contains(query_string),
323 SearchMode::Fuzzy => { 349 SearchMode::Fuzzy => {
324 let mut unchecked_query_chars = query_string.chars(); 350 let mut unchecked_query_chars = query_string.chars();
325 let mut mismatching_query_char = unchecked_query_chars.next(); 351 let mut mismatching_query_char = unchecked_query_chars.next();
326 352
327 for input_char in input.chars() { 353 for input_char in input.chars() {
328 match mismatching_query_char { 354 match mismatching_query_char {
329 None => return true, 355 None => return true,
330 Some(matching_query_char) if matching_query_char == input_char => { 356 Some(matching_query_char) if matching_query_char == input_char => {
331 mismatching_query_char = unchecked_query_chars.next(); 357 mismatching_query_char = unchecked_query_chars.next();
358 }
359 _ => (),
332 } 360 }
333 _ => (),
334 } 361 }
362 mismatching_query_char.is_none()
335 } 363 }
336 mismatching_query_char.is_none()
337 } 364 }
338 } 365 }
339} 366}
@@ -366,13 +393,13 @@ pub fn search_dependencies<'a>(
366 let import_map = &import_maps[indexed_value.index]; 393 let import_map = &import_maps[indexed_value.index];
367 let importables = &import_map.importables[indexed_value.value as usize..]; 394 let importables = &import_map.importables[indexed_value.value as usize..];
368 395
369 // Path shared by the importable items in this group. 396 let common_importable_data = &import_map.map[&importables[0]];
370 let common_importables_path = &import_map.map[&importables[0]].path; 397 if !query.import_matches(common_importable_data, true) {
371 if !contains_query(&query, common_importables_path, true) {
372 continue; 398 continue;
373 } 399 }
374 400
375 let common_importables_path_fst = fst_path(common_importables_path); 401 // Path shared by the importable items in this group.
402 let common_importables_path_fst = fst_path(&common_importable_data.path);
376 // Add the items from this `ModPath` group. Those are all subsequent items in 403 // Add the items from this `ModPath` group. Those are all subsequent items in
377 // `importables` whose paths match `path`. 404 // `importables` whose paths match `path`.
378 let iter = importables 405 let iter = importables
@@ -387,7 +414,7 @@ pub fn search_dependencies<'a>(
387 }) 414 })
388 .filter(|item| { 415 .filter(|item| {
389 !query.case_sensitive // we've already checked the common importables path case-insensitively 416 !query.case_sensitive // we've already checked the common importables path case-insensitively
390 || contains_query(&query, &import_map.map[item].path, false) 417 || query.import_matches(&import_map.map[item], false)
391 }); 418 });
392 res.extend(iter); 419 res.extend(iter);
393 420
@@ -398,19 +425,6 @@ pub fn search_dependencies<'a>(
398 } 425 }
399 } 426 }
400 427
401 // Add all exported associated items whose names match the query (exactly).
402 for map in &import_maps {
403 if let Some(v) = map.assoc_map.get(&*query.query) {
404 res.extend(v.iter().map(|&assoc| {
405 ItemInNs::Types(match assoc {
406 AssocItemId::FunctionId(it) => it.into(),
407 AssocItemId::ConstId(it) => it.into(),
408 AssocItemId::TypeAliasId(it) => it.into(),
409 })
410 }));
411 }
412 }
413
414 res 428 res
415} 429}
416 430
@@ -432,8 +446,9 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
432mod tests { 446mod tests {
433 use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; 447 use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
434 use expect_test::{expect, Expect}; 448 use expect_test::{expect, Expect};
449 use test_utils::mark;
435 450
436 use crate::{data::FunctionData, test_db::TestDB, AssocContainerId, Lookup}; 451 use crate::{test_db::TestDB, AssocContainerId, Lookup};
437 452
438 use super::*; 453 use super::*;
439 454
@@ -450,45 +465,66 @@ mod tests {
450 465
451 let actual = search_dependencies(db.upcast(), krate, query) 466 let actual = search_dependencies(db.upcast(), krate, query)
452 .into_iter() 467 .into_iter()
453 .filter_map(|item| { 468 .filter_map(|dependency| {
454 let mark = match item { 469 let dependency_krate = dependency.krate(db.upcast())?;
455 ItemInNs::Types(ModuleDefId::FunctionId(_)) 470 let dependency_imports = db.import_map(dependency_krate);
456 | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", 471
457 ItemInNs::Types(_) => "t", 472 let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
458 ItemInNs::Values(_) => "v", 473 Some(assoc_item_path) => (assoc_item_path, "a"),
459 ItemInNs::Macros(_) => "m", 474 None => (
475 dependency_imports.path_of(dependency)?.to_string(),
476 match dependency {
477 ItemInNs::Types(ModuleDefId::FunctionId(_))
478 | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
479 ItemInNs::Types(_) => "t",
480 ItemInNs::Values(_) => "v",
481 ItemInNs::Macros(_) => "m",
482 },
483 ),
460 }; 484 };
461 item.krate(db.upcast()).map(|krate| { 485
462 let map = db.import_map(krate); 486 Some(format!(
463 487 "{}::{} ({})\n",
464 let path = match assoc_to_trait(&db, item) { 488 crate_graph[dependency_krate].display_name.as_ref()?,
465 Some(trait_) => { 489 path,
466 let mut full_path = map.path_of(trait_).unwrap().to_string(); 490 mark
467 if let ItemInNs::Types(ModuleDefId::FunctionId(function_id)) 491 ))
468 | ItemInNs::Values(ModuleDefId::FunctionId(function_id)) = item
469 {
470 full_path += &format!(
471 "::{}",
472 FunctionData::fn_data_query(&db, function_id).name
473 );
474 }
475 full_path
476 }
477 None => map.path_of(item).unwrap().to_string(),
478 };
479
480 format!(
481 "{}::{} ({})\n",
482 crate_graph[krate].display_name.as_ref().unwrap(),
483 path,
484 mark
485 )
486 })
487 }) 492 })
488 .collect::<String>(); 493 .collect::<String>();
489 expect.assert_eq(&actual) 494 expect.assert_eq(&actual)
490 } 495 }
491 496
497 fn assoc_item_path(
498 db: &dyn DefDatabase,
499 dependency_imports: &ImportMap,
500 dependency: ItemInNs,
501 ) -> Option<String> {
502 let dependency_assoc_item_id = match dependency {
503 ItemInNs::Types(ModuleDefId::FunctionId(id))
504 | ItemInNs::Values(ModuleDefId::FunctionId(id)) => AssocItemId::from(id),
505 ItemInNs::Types(ModuleDefId::ConstId(id))
506 | ItemInNs::Values(ModuleDefId::ConstId(id)) => AssocItemId::from(id),
507 ItemInNs::Types(ModuleDefId::TypeAliasId(id))
508 | ItemInNs::Values(ModuleDefId::TypeAliasId(id)) => AssocItemId::from(id),
509 _ => return None,
510 };
511
512 let trait_ = assoc_to_trait(db, dependency)?;
513 if let ModuleDefId::TraitId(tr) = trait_.as_module_def_id()? {
514 let trait_data = db.trait_data(tr);
515 let assoc_item_name =
516 trait_data.items.iter().find_map(|(assoc_item_name, assoc_item_id)| {
517 if &dependency_assoc_item_id == assoc_item_id {
518 Some(assoc_item_name)
519 } else {
520 None
521 }
522 })?;
523 return Some(format!("{}::{}", dependency_imports.path_of(trait_)?, assoc_item_name));
524 }
525 None
526 }
527
492 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> { 528 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> {
493 let assoc: AssocItemId = match item { 529 let assoc: AssocItemId = match item {
494 ItemInNs::Types(it) | ItemInNs::Values(it) => match it { 530 ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
@@ -747,6 +783,37 @@ mod tests {
747 } 783 }
748 784
749 #[test] 785 #[test]
786 fn fuzzy_import_trait_and_assoc_items() {
787 mark::check!(type_aliases_ignored);
788 let ra_fixture = r#"
789 //- /main.rs crate:main deps:dep
790 //- /dep.rs crate:dep
791 pub mod fmt {
792 pub trait Display {
793 type FmtTypeAlias;
794 const FMT_CONST: bool;
795
796 fn format_function();
797 fn format_method(&self);
798 }
799 }
800 "#;
801
802 check_search(
803 ra_fixture,
804 "main",
805 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
806 expect![[r#"
807 dep::fmt (t)
808 dep::fmt::Display (t)
809 dep::fmt::Display::FMT_CONST (a)
810 dep::fmt::Display::format_function (a)
811 dep::fmt::Display::format_method (a)
812 "#]],
813 );
814 }
815
816 #[test]
750 fn search_mode() { 817 fn search_mode() {
751 let ra_fixture = r#" 818 let ra_fixture = r#"
752 //- /main.rs crate:main deps:dep 819 //- /main.rs crate:main deps:dep
@@ -782,8 +849,8 @@ mod tests {
782 dep::Fmt (v) 849 dep::Fmt (v)
783 dep::Fmt (m) 850 dep::Fmt (m)
784 dep::fmt::Display (t) 851 dep::fmt::Display (t)
852 dep::fmt::Display::fmt (a)
785 dep::format (f) 853 dep::format (f)
786 dep::fmt::Display::fmt (f)
787 "#]], 854 "#]],
788 ); 855 );
789 856
@@ -796,7 +863,7 @@ mod tests {
796 dep::Fmt (t) 863 dep::Fmt (t)
797 dep::Fmt (v) 864 dep::Fmt (v)
798 dep::Fmt (m) 865 dep::Fmt (m)
799 dep::fmt::Display::fmt (f) 866 dep::fmt::Display::fmt (a)
800 "#]], 867 "#]],
801 ); 868 );
802 869
@@ -810,7 +877,7 @@ mod tests {
810 dep::Fmt (v) 877 dep::Fmt (v)
811 dep::Fmt (m) 878 dep::Fmt (m)
812 dep::fmt::Display (t) 879 dep::fmt::Display (t)
813 dep::fmt::Display::fmt (f) 880 dep::fmt::Display::fmt (a)
814 "#]], 881 "#]],
815 ); 882 );
816 } 883 }
@@ -851,7 +918,7 @@ mod tests {
851 dep::Fmt (v) 918 dep::Fmt (v)
852 dep::Fmt (m) 919 dep::Fmt (m)
853 dep::fmt::Display (t) 920 dep::fmt::Display (t)
854 dep::fmt::Display::fmt (f) 921 dep::fmt::Display::fmt (a)
855 "#]], 922 "#]],
856 ); 923 );
857 924
@@ -864,7 +931,7 @@ mod tests {
864 dep::Fmt (t) 931 dep::Fmt (t)
865 dep::Fmt (v) 932 dep::Fmt (v)
866 dep::Fmt (m) 933 dep::Fmt (m)
867 dep::fmt::Display::fmt (f) 934 dep::fmt::Display::fmt (a)
868 "#]], 935 "#]],
869 ); 936 );
870 } 937 }
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index 62ab3b2bd..2750e1c91 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -10,10 +10,9 @@ use once_cell::sync::Lazy;
10use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
11use test_utils::mark; 11use test_utils::mark;
12 12
13use crate::ModuleId;
14use crate::{ 13use crate::{
15 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, 14 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
16 LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, 15 LocalModuleId, Lookup, MacroDefId, ModuleDefId, ModuleId, TraitId,
17}; 16};
18 17
19#[derive(Copy, Clone)] 18#[derive(Copy, Clone)]
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 100dbf5d6..b6f510731 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -260,6 +260,7 @@ impl GenericParamsStorage {
260 fn alloc(&mut self, params: GenericParams) -> GenericParamsId { 260 fn alloc(&mut self, params: GenericParams) -> GenericParamsId {
261 if params.types.is_empty() 261 if params.types.is_empty()
262 && params.lifetimes.is_empty() 262 && params.lifetimes.is_empty()
263 && params.consts.is_empty()
263 && params.where_predicates.is_empty() 264 && params.where_predicates.is_empty()
264 { 265 {
265 return GenericParamsId::EMPTY; 266 return GenericParamsId::EMPTY;
@@ -269,8 +270,12 @@ impl GenericParamsStorage {
269 } 270 }
270} 271}
271 272
272static EMPTY_GENERICS: GenericParams = 273static EMPTY_GENERICS: GenericParams = GenericParams {
273 GenericParams { types: Arena::new(), lifetimes: Arena::new(), where_predicates: Vec::new() }; 274 types: Arena::new(),
275 lifetimes: Arena::new(),
276 consts: Arena::new(),
277 where_predicates: Vec::new(),
278};
274 279
275#[derive(Default, Debug, Eq, PartialEq)] 280#[derive(Default, Debug, Eq, PartialEq)]
276struct ItemTreeData { 281struct ItemTreeData {
diff --git a/crates/hir_def/src/keys.rs b/crates/hir_def/src/keys.rs
index 9c585de2c..89b3ed868 100644
--- a/crates/hir_def/src/keys.rs
+++ b/crates/hir_def/src/keys.rs
@@ -8,8 +8,8 @@ use syntax::{ast, AstNode, AstPtr};
8 8
9use crate::{ 9use crate::{
10 dyn_map::{DynMap, Policy}, 10 dyn_map::{DynMap, Policy},
11 ConstId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, LifetimeParamId, StaticId, 11 ConstId, ConstParamId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, LifetimeParamId,
12 StructId, TraitId, TypeAliasId, TypeParamId, UnionId, 12 StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId,
13}; 13};
14 14
15pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>; 15pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>;
@@ -29,6 +29,7 @@ pub const TUPLE_FIELD: Key<ast::TupleField, FieldId> = Key::new();
29pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new(); 29pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new();
30pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new(); 30pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new();
31pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new(); 31pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
32pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
32 33
33pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new(); 34pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
34 35
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index ba09a9126..211cb2faf 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -232,6 +232,13 @@ pub struct LifetimeParamId {
232pub type LocalLifetimeParamId = Idx<generics::LifetimeParamData>; 232pub type LocalLifetimeParamId = Idx<generics::LifetimeParamData>;
233 233
234#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 234#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
235pub struct ConstParamId {
236 pub parent: GenericDefId,
237 pub local_id: LocalConstParamId,
238}
239pub type LocalConstParamId = Idx<generics::ConstParamData>;
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
235pub enum ContainerId { 242pub enum ContainerId {
236 ModuleId(ModuleId), 243 ModuleId(ModuleId),
237 DefWithBodyId(DefWithBodyId), 244 DefWithBodyId(DefWithBodyId),
@@ -254,6 +261,15 @@ pub enum AdtId {
254} 261}
255impl_from!(StructId, UnionId, EnumId for AdtId); 262impl_from!(StructId, UnionId, EnumId for AdtId);
256 263
264/// A generic param
265#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
266pub enum GenericParamId {
267 TypeParamId(TypeParamId),
268 LifetimeParamId(LifetimeParamId),
269 ConstParamId(ConstParamId),
270}
271impl_from!(TypeParamId, LifetimeParamId, ConstParamId for GenericParamId);
272
257/// The defs which can be visible in the module. 273/// The defs which can be visible in the module.
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 274#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
259pub enum ModuleDefId { 275pub enum ModuleDefId {
@@ -350,6 +366,7 @@ pub enum AttrDefId {
350 TypeAliasId(TypeAliasId), 366 TypeAliasId(TypeAliasId),
351 MacroDefId(MacroDefId), 367 MacroDefId(MacroDefId),
352 ImplId(ImplId), 368 ImplId(ImplId),
369 GenericParamId(GenericParamId),
353} 370}
354 371
355impl_from!( 372impl_from!(
@@ -363,7 +380,8 @@ impl_from!(
363 TraitId, 380 TraitId,
364 TypeAliasId, 381 TypeAliasId,
365 MacroDefId, 382 MacroDefId,
366 ImplId 383 ImplId,
384 GenericParamId
367 for AttrDefId 385 for AttrDefId
368); 386);
369 387
@@ -488,6 +506,15 @@ impl AttrDefId {
488 AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate, 506 AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate,
489 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate, 507 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate,
490 AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate, 508 AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate,
509 AttrDefId::GenericParamId(it) => {
510 match it {
511 GenericParamId::TypeParamId(it) => it.parent,
512 GenericParamId::LifetimeParamId(it) => it.parent,
513 GenericParamId::ConstParamId(it) => it.parent,
514 }
515 .module(db)
516 .krate
517 }
491 // FIXME: `MacroDefId` should store the defining module, then this can implement 518 // FIXME: `MacroDefId` should store the defining module, then this can implement
492 // `HasModule` 519 // `HasModule`
493 AttrDefId::MacroDefId(it) => it.krate, 520 AttrDefId::MacroDefId(it) => it.krate,
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 9bf358775..5682e122d 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -454,7 +454,7 @@ mod diagnostics {
454 }); 454 });
455 for token in tokens { 455 for token in tokens {
456 if token.kind() == SyntaxKind::IDENT 456 if token.kind() == SyntaxKind::IDENT
457 && token.to_string() == *name 457 && token.text() == name.as_str()
458 { 458 {
459 precise_location = Some(token.text_range()); 459 precise_location = Some(token.text_range());
460 break 'outer; 460 break 'outer;
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index a636ec77d..f027fd48d 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -13,7 +13,7 @@ use hir_expand::{
13 builtin_macro::find_builtin_macro, 13 builtin_macro::find_builtin_macro,
14 name::{AsName, Name}, 14 name::{AsName, Name},
15 proc_macro::ProcMacroExpander, 15 proc_macro::ProcMacroExpander,
16 HirFileId, MacroCallId, MacroDefId, MacroDefKind, 16 HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
17}; 17};
18use hir_expand::{InFile, MacroCallLoc}; 18use hir_expand::{InFile, MacroCallLoc};
19use rustc_hash::{FxHashMap, FxHashSet}; 19use rustc_hash::{FxHashMap, FxHashSet};
@@ -267,7 +267,7 @@ impl DefCollector<'_> {
267 267
268 // Resolve all indeterminate resolved imports again 268 // Resolve all indeterminate resolved imports again
269 // As some of the macros will expand newly import shadowing partial resolved imports 269 // As some of the macros will expand newly import shadowing partial resolved imports
270 // FIXME: We maybe could skip this, if we handle the Indetermine imports in `resolve_imports` 270 // FIXME: We maybe could skip this, if we handle the indeterminate imports in `resolve_imports`
271 // correctly 271 // correctly
272 let partial_resolved = self.resolved_imports.iter().filter_map(|directive| { 272 let partial_resolved = self.resolved_imports.iter().filter_map(|directive| {
273 if let PartialResolvedImport::Indeterminate(_) = directive.status { 273 if let PartialResolvedImport::Indeterminate(_) = directive.status {
@@ -402,7 +402,7 @@ impl DefCollector<'_> {
402 402
403 /// Define a proc macro 403 /// Define a proc macro
404 /// 404 ///
405 /// A proc macro is similar to normal macro scope, but it would not visiable in legacy textual scoped. 405 /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped.
406 /// And unconditionally exported. 406 /// And unconditionally exported.
407 fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) { 407 fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) {
408 self.update( 408 self.update(
@@ -592,7 +592,7 @@ impl DefCollector<'_> {
592 // XXX: urgh, so this works by accident! Here, we look at 592 // XXX: urgh, so this works by accident! Here, we look at
593 // the enum data, and, in theory, this might require us to 593 // the enum data, and, in theory, this might require us to
594 // look back at the crate_def_map, creating a cycle. For 594 // look back at the crate_def_map, creating a cycle. For
595 // example, `enum E { crate::some_macro!(); }`. Luckely, the 595 // example, `enum E { crate::some_macro!(); }`. Luckily, the
596 // only kind of macro that is allowed inside enum is a 596 // only kind of macro that is allowed inside enum is a
597 // `cfg_macro`, and we don't need to run name resolution for 597 // `cfg_macro`, and we don't need to run name resolution for
598 // it, but this is sheer luck! 598 // it, but this is sheer luck!
@@ -655,7 +655,7 @@ impl DefCollector<'_> {
655 &mut self, 655 &mut self,
656 module_id: LocalModuleId, 656 module_id: LocalModuleId,
657 resolutions: &[(Option<Name>, PerNs)], 657 resolutions: &[(Option<Name>, PerNs)],
658 // All resolutions are imported with this visibility; the visibilies in 658 // All resolutions are imported with this visibility; the visibilities in
659 // the `PerNs` values are ignored and overwritten 659 // the `PerNs` values are ignored and overwritten
660 vis: Visibility, 660 vis: Visibility,
661 import_type: ImportType, 661 import_type: ImportType,
@@ -860,6 +860,37 @@ impl DefCollector<'_> {
860 } 860 }
861 861
862 fn finish(mut self) -> CrateDefMap { 862 fn finish(mut self) -> CrateDefMap {
863 // Emit diagnostics for all remaining unexpanded macros.
864
865 for directive in &self.unexpanded_macros {
866 let mut error = None;
867 directive.ast_id.as_call_id_with_errors(
868 self.db,
869 self.def_map.krate,
870 |path| {
871 let resolved_res = self.def_map.resolve_path_fp_with_macro(
872 self.db,
873 ResolveMode::Other,
874 directive.module_id,
875 &path,
876 BuiltinShadowMode::Module,
877 );
878 resolved_res.resolved_def.take_macros()
879 },
880 &mut |e| {
881 error.get_or_insert(e);
882 },
883 );
884
885 if let Some(err) = error {
886 self.def_map.diagnostics.push(DefDiagnostic::macro_error(
887 directive.module_id,
888 MacroCallKind::FnLike(directive.ast_id.ast_id),
889 err.to_string(),
890 ));
891 }
892 }
893
863 // Emit diagnostics for all remaining unresolved imports. 894 // Emit diagnostics for all remaining unresolved imports.
864 895
865 // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't 896 // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs
index 8981fa7c9..509e1bbbc 100644
--- a/crates/hir_def/src/nameres/tests/incremental.rs
+++ b/crates/hir_def/src/nameres/tests/incremental.rs
@@ -28,7 +28,7 @@ fn typing_inside_a_function_should_not_invalidate_def_map() {
28 check_def_map_is_not_recomputed( 28 check_def_map_is_not_recomputed(
29 r" 29 r"
30 //- /lib.rs 30 //- /lib.rs
31 mod foo;<|> 31 mod foo;$0
32 32
33 use crate::foo::bar::Baz; 33 use crate::foo::bar::Baz;
34 34
@@ -81,7 +81,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
81 pub mod bar; 81 pub mod bar;
82 82
83 //- /foo/bar.rs 83 //- /foo/bar.rs
84 <|> 84 $0
85 m!(X); 85 m!(X);
86 ", 86 ",
87 ); 87 );
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs
index e2bf85bbc..3dd7c3cbb 100644
--- a/crates/hir_def/src/path.rs
+++ b/crates/hir_def/src/path.rs
@@ -305,6 +305,7 @@ pub use hir_expand::name as __name;
305macro_rules! __known_path { 305macro_rules! __known_path {
306 (core::iter::IntoIterator) => {}; 306 (core::iter::IntoIterator) => {};
307 (core::result::Result) => {}; 307 (core::result::Result) => {};
308 (core::option::Option) => {};
308 (core::ops::Range) => {}; 309 (core::ops::Range) => {};
309 (core::ops::RangeFrom) => {}; 310 (core::ops::RangeFrom) => {};
310 (core::ops::RangeFull) => {}; 311 (core::ops::RangeFull) => {};
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index 8a01e6eea..9518ac109 100644
--- a/crates/hir_def/src/path/lower.rs
+++ b/crates/hir_def/src/path/lower.rs
@@ -123,7 +123,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
123 // We follow what it did anyway :) 123 // We follow what it did anyway :)
124 if segments.len() == 1 && kind == PathKind::Plain { 124 if segments.len() == 1 && kind == PathKind::Plain {
125 if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { 125 if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
126 if let Some(crate_id) = hygiene.local_inner_macros() { 126 if let Some(crate_id) = hygiene.local_inner_macros(path) {
127 kind = PathKind::DollarCrate(crate_id); 127 kind = PathKind::DollarCrate(crate_id);
128 } 128 }
129 } 129 }
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index 779754ada..61059c349 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -20,14 +20,14 @@ use crate::{
20 path::{ModPath, PathKind}, 20 path::{ModPath, PathKind},
21 per_ns::PerNs, 21 per_ns::PerNs,
22 visibility::{RawVisibility, Visibility}, 22 visibility::{RawVisibility, Visibility},
23 AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId, 23 AdtId, AssocContainerId, ConstId, ConstParamId, ContainerId, DefWithBodyId, EnumId,
24 FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId, 24 EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId,
25 StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId, 25 ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId,
26}; 26};
27 27
28#[derive(Debug, Clone, Default)] 28#[derive(Debug, Clone, Default)]
29pub struct Resolver { 29pub struct Resolver {
30 // FIXME: all usages generally call `.rev`, so maybe reverse once in consturciton? 30 // FIXME: all usages generally call `.rev`, so maybe reverse once in construction?
31 scopes: Vec<Scope>, 31 scopes: Vec<Scope>,
32} 32}
33 33
@@ -93,6 +93,7 @@ pub enum ValueNs {
93 StaticId(StaticId), 93 StaticId(StaticId),
94 StructId(StructId), 94 StructId(StructId),
95 EnumVariantId(EnumVariantId), 95 EnumVariantId(EnumVariantId),
96 GenericParam(ConstParamId),
96} 97}
97 98
98impl Resolver { 99impl Resolver {
@@ -163,7 +164,7 @@ impl Resolver {
163 } 164 }
164 165
165 Scope::GenericParams { params, def } => { 166 Scope::GenericParams { params, def } => {
166 if let Some(local_id) = params.find_by_name(first_name) { 167 if let Some(local_id) = params.find_type_by_name(first_name) {
167 let idx = if path.segments.len() == 1 { None } else { Some(1) }; 168 let idx = if path.segments.len() == 1 { None } else { Some(1) };
168 return Some(( 169 return Some((
169 TypeNs::GenericParam(TypeParamId { local_id, parent: *def }), 170 TypeNs::GenericParam(TypeParamId { local_id, parent: *def }),
@@ -285,11 +286,17 @@ impl Resolver {
285 Scope::ExprScope(_) => continue, 286 Scope::ExprScope(_) => continue,
286 287
287 Scope::GenericParams { params, def } if n_segments > 1 => { 288 Scope::GenericParams { params, def } if n_segments > 1 => {
288 if let Some(local_id) = params.find_by_name(first_name) { 289 if let Some(local_id) = params.find_type_by_name(first_name) {
289 let ty = TypeNs::GenericParam(TypeParamId { local_id, parent: *def }); 290 let ty = TypeNs::GenericParam(TypeParamId { local_id, parent: *def });
290 return Some(ResolveValueResult::Partial(ty, 1)); 291 return Some(ResolveValueResult::Partial(ty, 1));
291 } 292 }
292 } 293 }
294 Scope::GenericParams { params, def } if n_segments == 1 => {
295 if let Some(local_id) = params.find_const_by_name(first_name) {
296 let val = ValueNs::GenericParam(ConstParamId { local_id, parent: *def });
297 return Some(ResolveValueResult::ValueNs(val));
298 }
299 }
293 Scope::GenericParams { .. } => continue, 300 Scope::GenericParams { .. } => continue,
294 301
295 Scope::ImplDefScope(impl_) => { 302 Scope::ImplDefScope(impl_) => {
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index ad378762a..eb257579f 100644
--- a/crates/hir_expand/src/builtin_derive.rs
+++ b/crates/hir_expand/src/builtin_derive.rs
@@ -277,7 +277,7 @@ mod tests {
277 let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap(); 277 let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap();
278 let fixture = format!( 278 let fixture = format!(
279 r#"//- /main.rs crate:main deps:core 279 r#"//- /main.rs crate:main deps:core
280<|> 280$0
281{} 281{}
282//- /lib.rs crate:core 282//- /lib.rs crate:core
283// empty 283// empty
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 6382521fb..80b60d59f 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -259,7 +259,8 @@ fn format_args_expand(
259 } 259 }
260 for arg in &mut args { 260 for arg in &mut args {
261 // Remove `key =`. 261 // Remove `key =`.
262 if matches!(arg.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') { 262 if matches!(arg.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint)
263 {
263 arg.drain(..2); 264 arg.drain(..2);
264 } 265 }
265 } 266 }
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 06f0a3ed9..c62086390 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -8,9 +8,9 @@ use parser::FragmentKind;
8use syntax::{algo::diff, ast::NameOwner, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode}; 8use syntax::{algo::diff, ast::NameOwner, AstNode, GreenNode, Parse, SyntaxKind::*, SyntaxNode};
9 9
10use crate::{ 10use crate::{
11 ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, 11 ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander,
12 HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, 12 EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc,
13 MacroFile, ProcMacroExpander, 13 MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
14}; 14};
15 15
16/// Total limit on the number of tokens produced by any macro invocation. 16/// Total limit on the number of tokens produced by any macro invocation.
@@ -40,7 +40,7 @@ impl TokenExpander {
40 // FIXME switch these to ExpandResult as well 40 // FIXME switch these to ExpandResult as well
41 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), 41 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
42 TokenExpander::ProcMacro(_) => { 42 TokenExpander::ProcMacro(_) => {
43 // We store the result in salsa db to prevent non-determinisc behavior in 43 // We store the result in salsa db to prevent non-deterministic behavior in
44 // some proc-macro implementation 44 // some proc-macro implementation
45 // See #4315 for details 45 // See #4315 for details
46 db.expand_proc_macro(id.into()).into() 46 db.expand_proc_macro(id.into()).into()
@@ -94,6 +94,8 @@ pub trait AstDatabase: SourceDatabase {
94 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; 94 fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId;
95 95
96 fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; 96 fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>;
97
98 fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>;
97} 99}
98 100
99/// This expands the given macro call, but with different arguments. This is 101/// This expands the given macro call, but with different arguments. This is
@@ -369,6 +371,10 @@ fn parse_macro_with_arg(
369 } 371 }
370} 372}
371 373
374fn hygiene_frame(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<HygieneFrame> {
375 Arc::new(HygieneFrame::new(db, file_id))
376}
377
372/// Given a `MacroCallId`, return what `FragmentKind` it belongs to. 378/// Given a `MacroCallId`, return what `FragmentKind` it belongs to.
373/// FIXME: Not completed 379/// FIXME: Not completed
374fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { 380fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
@@ -404,7 +410,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
404 TRY_EXPR => FragmentKind::Expr, 410 TRY_EXPR => FragmentKind::Expr,
405 TUPLE_EXPR => FragmentKind::Expr, 411 TUPLE_EXPR => FragmentKind::Expr,
406 PAREN_EXPR => FragmentKind::Expr, 412 PAREN_EXPR => FragmentKind::Expr,
407 413 ARRAY_EXPR => FragmentKind::Expr,
408 FOR_EXPR => FragmentKind::Expr, 414 FOR_EXPR => FragmentKind::Expr,
409 PATH_EXPR => FragmentKind::Expr, 415 PATH_EXPR => FragmentKind::Expr,
410 CLOSURE_EXPR => FragmentKind::Expr, 416 CLOSURE_EXPR => FragmentKind::Expr,
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index 6354b090d..ae7b51a08 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -218,6 +218,12 @@ fn eager_macro_recur(
218 } 218 }
219 }; 219 };
220 220
221 // check if the whole original sytnax is replaced
222 // Note that SyntaxRewriter cannot replace the root node itself
223 if child.syntax() == &original {
224 return Ok(insert);
225 }
226
221 rewriter.replace(child.syntax(), &insert); 227 rewriter.replace(child.syntax(), &insert);
222 } 228 }
223 229
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index 7ab0a5e52..8db581b77 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -2,65 +2,209 @@
2//! 2//!
3//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at 3//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
4//! this moment, this is horribly incomplete and handles only `$crate`. 4//! this moment, this is horribly incomplete and handles only `$crate`.
5use std::sync::Arc;
6
5use base_db::CrateId; 7use base_db::CrateId;
6use either::Either; 8use either::Either;
7use syntax::ast; 9use mbe::Origin;
10use parser::SyntaxKind;
11use syntax::{ast, AstNode, SyntaxNode, TextRange, TextSize};
8 12
9use crate::{ 13use crate::{
10 db::AstDatabase, 14 db::{self, AstDatabase},
11 name::{AsName, Name}, 15 name::{AsName, Name},
12 HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, 16 HirFileId, HirFileIdRepr, InFile, MacroCallId, MacroCallLoc, MacroDefKind, MacroFile,
13}; 17};
14 18
15#[derive(Clone, Debug)] 19#[derive(Clone, Debug)]
16pub struct Hygiene { 20pub struct Hygiene {
17 // This is what `$crate` expands to 21 frames: Option<HygieneFrames>,
18 def_crate: Option<CrateId>,
19
20 // Indicate this is a local inner macro
21 local_inner: bool,
22} 22}
23 23
24impl Hygiene { 24impl Hygiene {
25 pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { 25 pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
26 let (def_crate, local_inner) = match file_id.0 { 26 Hygiene { frames: Some(HygieneFrames::new(db, file_id.clone())) }
27 HirFileIdRepr::FileId(_) => (None, false),
28 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
29 MacroCallId::LazyMacro(id) => {
30 let loc = db.lookup_intern_macro(id);
31 match loc.def.kind {
32 MacroDefKind::Declarative => (Some(loc.def.krate), loc.def.local_inner),
33 MacroDefKind::BuiltIn(_) => (Some(loc.def.krate), false),
34 MacroDefKind::BuiltInDerive(_) => (None, false),
35 MacroDefKind::BuiltInEager(_) => (None, false),
36 MacroDefKind::ProcMacro(_) => (None, false),
37 }
38 }
39 MacroCallId::EagerMacro(_id) => (None, false),
40 },
41 };
42 Hygiene { def_crate, local_inner }
43 } 27 }
44 28
45 pub fn new_unhygienic() -> Hygiene { 29 pub fn new_unhygienic() -> Hygiene {
46 Hygiene { def_crate: None, local_inner: false } 30 Hygiene { frames: None }
47 } 31 }
48 32
49 // FIXME: this should just return name 33 // FIXME: this should just return name
50 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { 34 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
51 if let Some(def_crate) = self.def_crate { 35 if let Some(frames) = &self.frames {
52 if name_ref.text() == "$crate" { 36 if name_ref.text() == "$crate" {
53 return Either::Right(def_crate); 37 if let Some(krate) = frames.root_crate(name_ref.syntax()) {
38 return Either::Right(krate);
39 }
54 } 40 }
55 } 41 }
42
56 Either::Left(name_ref.as_name()) 43 Either::Left(name_ref.as_name())
57 } 44 }
58 45
59 pub fn local_inner_macros(&self) -> Option<CrateId> { 46 pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> {
60 if self.local_inner { 47 let mut token = path.syntax().first_token()?.text_range();
61 self.def_crate 48 let frames = self.frames.as_ref()?;
62 } else { 49 let mut current = frames.0.clone();
63 None 50
51 loop {
52 let (mapped, origin) = current.expansion.as_ref()?.map_ident_up(token)?;
53 if origin == Origin::Def {
54 return if current.local_inner { frames.root_crate(path.syntax()) } else { None };
55 }
56 current = current.call_site.as_ref()?.clone();
57 token = mapped.value;
58 }
59 }
60}
61
62#[derive(Clone, Debug)]
63struct HygieneFrames(Arc<HygieneFrame>);
64
65#[derive(Clone, Debug, Eq, PartialEq)]
66pub struct HygieneFrame {
67 expansion: Option<HygieneInfo>,
68
69 // Indicate this is a local inner macro
70 local_inner: bool,
71 krate: Option<CrateId>,
72
73 call_site: Option<Arc<HygieneFrame>>,
74 def_site: Option<Arc<HygieneFrame>>,
75}
76
77impl HygieneFrames {
78 fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self {
79 HygieneFrames(Arc::new(HygieneFrame::new(db, file_id)))
80 }
81
82 fn root_crate(&self, node: &SyntaxNode) -> Option<CrateId> {
83 let mut token = node.first_token()?.text_range();
84 let mut result = self.0.krate;
85 let mut current = self.0.clone();
86
87 while let Some((mapped, origin)) =
88 current.expansion.as_ref().and_then(|it| it.map_ident_up(token))
89 {
90 result = current.krate;
91
92 let site = match origin {
93 Origin::Def => &current.def_site,
94 Origin::Call => &current.call_site,
95 };
96
97 let site = match site {
98 None => break,
99 Some(it) => it,
100 };
101
102 current = site.clone();
103 token = mapped.value;
64 } 104 }
105
106 result
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
111struct HygieneInfo {
112 arg_start: InFile<TextSize>,
113 /// The `macro_rules!` arguments.
114 def_start: Option<InFile<TextSize>>,
115
116 macro_def: Arc<(db::TokenExpander, mbe::TokenMap)>,
117 macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
118 exp_map: Arc<mbe::TokenMap>,
119}
120
121impl HygieneInfo {
122 fn map_ident_up(&self, token: TextRange) -> Option<(InFile<TextRange>, Origin)> {
123 let token_id = self.exp_map.token_by_range(token)?;
124
125 let (token_id, origin) = self.macro_def.0.map_id_up(token_id);
126 let (token_map, tt) = match origin {
127 mbe::Origin::Call => (&self.macro_arg.1, self.arg_start),
128 mbe::Origin::Def => (
129 &self.macro_def.1,
130 self.def_start
131 .as_ref()
132 .expect("`Origin::Def` used with non-`macro_rules!` macro")
133 .clone(),
134 ),
135 };
136
137 let range = token_map.range_by_token(token_id)?.by_kind(SyntaxKind::IDENT)?;
138 Some((tt.with_value(range + tt.value), origin))
139 }
140}
141
142fn make_hygiene_info(
143 db: &dyn AstDatabase,
144 macro_file: MacroFile,
145 loc: &MacroCallLoc,
146) -> Option<HygieneInfo> {
147 let arg_tt = loc.kind.arg(db)?;
148
149 let def_offset = loc.def.ast_id.and_then(|id| {
150 let def_tt = match id.to_node(db) {
151 ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(),
152 ast::Macro::MacroDef(_) => return None,
153 };
154 Some(InFile::new(id.file_id, def_tt))
155 });
156
157 let macro_def = db.macro_def(loc.def)?;
158 let (_, exp_map) = db.parse_macro_expansion(macro_file).value?;
159 let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
160
161 Some(HygieneInfo {
162 arg_start: InFile::new(loc.kind.file_id(), arg_tt.text_range().start()),
163 def_start: def_offset,
164 macro_arg,
165 macro_def,
166 exp_map,
167 })
168}
169
170impl HygieneFrame {
171 pub(crate) fn new(db: &dyn AstDatabase, file_id: HirFileId) -> HygieneFrame {
172 let (info, krate, local_inner) = match file_id.0 {
173 HirFileIdRepr::FileId(_) => (None, None, false),
174 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
175 MacroCallId::EagerMacro(_id) => (None, None, false),
176 MacroCallId::LazyMacro(id) => {
177 let loc = db.lookup_intern_macro(id);
178 let info = make_hygiene_info(db, macro_file, &loc);
179 match loc.def.kind {
180 MacroDefKind::Declarative => {
181 (info, Some(loc.def.krate), loc.def.local_inner)
182 }
183 MacroDefKind::BuiltIn(_) => (info, Some(loc.def.krate), false),
184 MacroDefKind::BuiltInDerive(_) => (info, None, false),
185 MacroDefKind::BuiltInEager(_) => (info, None, false),
186 MacroDefKind::ProcMacro(_) => (info, None, false),
187 }
188 }
189 },
190 };
191
192 let info = match info {
193 None => {
194 return HygieneFrame {
195 expansion: None,
196 local_inner,
197 krate,
198 call_site: None,
199 def_site: None,
200 };
201 }
202 Some(it) => it,
203 };
204
205 let def_site = info.def_start.map(|it| db.hygiene_frame(it.file_id));
206 let call_site = Some(db.hygiene_frame(info.arg_start.file_id));
207
208 HygieneFrame { expansion: Some(info), local_inner, krate, call_site, def_site }
65 } 209 }
66} 210}
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index 2f44876a8..95d853b6d 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -164,6 +164,7 @@ pub mod known {
164 future, 164 future,
165 result, 165 result,
166 boxed, 166 boxed,
167 option,
167 // Components of known path (type name) 168 // Components of known path (type name)
168 Iterator, 169 Iterator,
169 IntoIterator, 170 IntoIterator,
@@ -172,6 +173,7 @@ pub mod known {
172 Ok, 173 Ok,
173 Future, 174 Future,
174 Result, 175 Result,
176 Option,
175 Output, 177 Output,
176 Target, 178 Target,
177 Box, 179 Box,
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index 7c77f6ce0..1923daca5 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -58,7 +58,7 @@ impl ProcMacroExpander {
58} 58}
59 59
60fn eat_punct(cursor: &mut Cursor, c: char) -> bool { 60fn eat_punct(cursor: &mut Cursor, c: char) -> bool {
61 if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = cursor.token_tree() { 61 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = cursor.token_tree() {
62 if punct.char == c { 62 if punct.char == c {
63 *cursor = cursor.bump(); 63 *cursor = cursor.bump();
64 return true; 64 return true;
@@ -68,7 +68,7 @@ fn eat_punct(cursor: &mut Cursor, c: char) -> bool {
68} 68}
69 69
70fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool { 70fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool {
71 if let Some(tt::TokenTree::Subtree(subtree)) = cursor.token_tree() { 71 if let Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) = cursor.token_tree() {
72 if Some(kind) == subtree.delimiter_kind() { 72 if Some(kind) == subtree.delimiter_kind() {
73 *cursor = cursor.bump_subtree(); 73 *cursor = cursor.bump_subtree();
74 return true; 74 return true;
@@ -78,7 +78,7 @@ fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool {
78} 78}
79 79
80fn eat_ident(cursor: &mut Cursor, t: &str) -> bool { 80fn eat_ident(cursor: &mut Cursor, t: &str) -> bool {
81 if let Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) = cursor.token_tree() { 81 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) = cursor.token_tree() {
82 if t == ident.text.as_str() { 82 if t == ident.text.as_str() {
83 *cursor = cursor.bump(); 83 *cursor = cursor.bump();
84 return true; 84 return true;
@@ -88,7 +88,7 @@ fn eat_ident(cursor: &mut Cursor, t: &str) -> bool {
88} 88}
89 89
90fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> { 90fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> {
91 let buffer = TokenBuffer::new(&tt.token_trees); 91 let buffer = TokenBuffer::from_tokens(&tt.token_trees);
92 let mut p = buffer.begin(); 92 let mut p = buffer.begin();
93 let mut result = tt::Subtree::default(); 93 let mut result = tt::Subtree::default();
94 94
@@ -106,7 +106,7 @@ fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> {
106 } 106 }
107 } 107 }
108 108
109 result.token_trees.push(curr.token_tree()?.clone()); 109 result.token_trees.push(curr.token_tree()?.cloned());
110 p = curr.bump(); 110 p = curr.bump();
111 } 111 }
112 112
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 965c1780a..b0a453961 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -10,16 +10,16 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13itertools = "0.9.0" 13itertools = "0.10.0"
14arrayvec = "0.5.1" 14arrayvec = "0.5.1"
15smallvec = "1.2.0" 15smallvec = "1.2.0"
16ena = "0.14.0" 16ena = "0.14.0"
17log = "0.4.8" 17log = "0.4.8"
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19scoped-tls = "1" 19scoped-tls = "1"
20chalk-solve = { version = "0.45", default-features = false } 20chalk-solve = { version = "0.47", default-features = false }
21chalk-ir = "0.45" 21chalk-ir = "0.47"
22chalk-recursive = "0.45" 22chalk-recursive = "0.47"
23 23
24stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
25hir_def = { path = "../hir_def", version = "0.0.0" } 25hir_def = { path = "../hir_def", version = "0.0.0" }
@@ -31,7 +31,7 @@ syntax = { path = "../syntax", version = "0.0.0" }
31test_utils = { path = "../test_utils", version = "0.0.0" } 31test_utils = { path = "../test_utils", version = "0.0.0" }
32 32
33[dev-dependencies] 33[dev-dependencies]
34expect-test = "1.0" 34expect-test = "1.1"
35tracing = "0.1" 35tracing = "0.1"
36tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } 36tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
37tracing-tree = { version = "0.1.4" } 37tracing-tree = { version = "0.1.4" }
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index 66bdb8e88..f3567c49e 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -5,8 +5,8 @@ use std::sync::Arc;
5use arena::map::ArenaMap; 5use arena::map::ArenaMap;
6use base_db::{impl_intern_key, salsa, CrateId, Upcast}; 6use base_db::{impl_intern_key, salsa, CrateId, Upcast};
7use hir_def::{ 7use hir_def::{
8 db::DefDatabase, expr::ExprId, DefWithBodyId, FunctionId, GenericDefId, ImplId, LocalFieldId, 8 db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId,
9 TypeParamId, VariantId, 9 LocalFieldId, TypeParamId, VariantId,
10}; 10};
11 11
12use crate::{ 12use crate::{
@@ -37,6 +37,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
37 #[salsa::cycle(crate::lower::impl_self_ty_recover)] 37 #[salsa::cycle(crate::lower::impl_self_ty_recover)]
38 fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>; 38 fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>;
39 39
40 #[salsa::invoke(crate::lower::const_param_ty_query)]
41 fn const_param_ty(&self, def: ConstParamId) -> Ty;
42
40 #[salsa::invoke(crate::lower::impl_trait_query)] 43 #[salsa::invoke(crate::lower::impl_trait_query)]
41 fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>; 44 fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
42 45
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index 14e18f5a1..c67a289f2 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -186,9 +186,10 @@ impl Diagnostic for MissingMatchArms {
186 } 186 }
187} 187}
188 188
189// Diagnostic: missing-ok-in-tail-expr 189// Diagnostic: missing-ok-or-some-in-tail-expr
190// 190//
191// This diagnostic is triggered if block that should return `Result` returns a value not wrapped in `Ok`. 191// This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`,
192// or if a block that should return `Option` returns a value not wrapped in `Some`.
192// 193//
193// Example: 194// Example:
194// 195//
@@ -198,17 +199,19 @@ impl Diagnostic for MissingMatchArms {
198// } 199// }
199// ``` 200// ```
200#[derive(Debug)] 201#[derive(Debug)]
201pub struct MissingOkInTailExpr { 202pub struct MissingOkOrSomeInTailExpr {
202 pub file: HirFileId, 203 pub file: HirFileId,
203 pub expr: AstPtr<ast::Expr>, 204 pub expr: AstPtr<ast::Expr>,
205 // `Some` or `Ok` depending on whether the return type is Result or Option
206 pub required: String,
204} 207}
205 208
206impl Diagnostic for MissingOkInTailExpr { 209impl Diagnostic for MissingOkOrSomeInTailExpr {
207 fn code(&self) -> DiagnosticCode { 210 fn code(&self) -> DiagnosticCode {
208 DiagnosticCode("missing-ok-in-tail-expr") 211 DiagnosticCode("missing-ok-or-some-in-tail-expr")
209 } 212 }
210 fn message(&self) -> String { 213 fn message(&self) -> String {
211 "wrap return expression in Ok".to_string() 214 format!("wrap return expression in {}", self.required)
212 } 215 }
213 fn display_source(&self) -> InFile<SyntaxNodePtr> { 216 fn display_source(&self) -> InFile<SyntaxNodePtr> {
214 InFile { file_id: self.file, value: self.expr.clone().into() } 217 InFile { file_id: self.file, value: self.expr.clone().into() }
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 849415706..107417c27 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -11,8 +11,8 @@ use crate::{
11 db::HirDatabase, 11 db::HirDatabase,
12 diagnostics::{ 12 diagnostics::{
13 match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, 13 match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
14 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields, 14 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
15 RemoveThisSemicolon, 15 MissingPatFields, RemoveThisSemicolon,
16 }, 16 },
17 utils::variant_data, 17 utils::variant_data,
18 ApplicationTy, InferenceResult, Ty, TypeCtor, 18 ApplicationTy, InferenceResult, Ty, TypeCtor,
@@ -156,7 +156,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
156 // FIXME: Due to shortcomings in the current type system implementation, only emit this 156 // FIXME: Due to shortcomings in the current type system implementation, only emit this
157 // diagnostic if there are no type mismatches in the containing function. 157 // diagnostic if there are no type mismatches in the containing function.
158 if self.infer.type_mismatches.iter().next().is_some() { 158 if self.infer.type_mismatches.iter().next().is_some() {
159 return Some(()); 159 return None;
160 } 160 }
161 161
162 let is_method_call = matches!(expr, Expr::MethodCall { .. }); 162 let is_method_call = matches!(expr, Expr::MethodCall { .. });
@@ -170,6 +170,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
170 let mut args = args.clone(); 170 let mut args = args.clone();
171 args.insert(0, *receiver); 171 args.insert(0, *receiver);
172 172
173 let receiver = &self.infer.type_of_expr[*receiver];
174 if receiver.strip_references().is_unknown() {
175 // if the receiver is of unknown type, it's very likely we
176 // don't know enough to correctly resolve the method call.
177 // This is kind of a band-aid for #6975.
178 return None;
179 }
180
173 // FIXME: note that we erase information about substs here. This 181 // FIXME: note that we erase information about substs here. This
174 // is not right, but, luckily, doesn't matter as we care only 182 // is not right, but, luckily, doesn't matter as we care only
175 // about the number of params 183 // about the number of params
@@ -298,27 +306,40 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
298 }; 306 };
299 307
300 let core_result_path = path![core::result::Result]; 308 let core_result_path = path![core::result::Result];
309 let core_option_path = path![core::option::Option];
301 310
302 let resolver = self.owner.resolver(db.upcast()); 311 let resolver = self.owner.resolver(db.upcast());
303 let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) { 312 let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) {
304 Some(it) => it, 313 Some(it) => it,
305 _ => return, 314 _ => return,
306 }; 315 };
316 let core_option_enum = match resolver.resolve_known_enum(db.upcast(), &core_option_path) {
317 Some(it) => it,
318 _ => return,
319 };
307 320
308 let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum)); 321 let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum));
309 let params = match &mismatch.expected { 322 let core_option_ctor = TypeCtor::Adt(AdtId::EnumId(core_option_enum));
323
324 let (params, required) = match &mismatch.expected {
310 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => { 325 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => {
311 parameters 326 (parameters, "Ok".to_string())
327 }
328 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_option_ctor => {
329 (parameters, "Some".to_string())
312 } 330 }
313 _ => return, 331 _ => return,
314 }; 332 };
315 333
316 if params.len() == 2 && params[0] == mismatch.actual { 334 if params.len() > 0 && params[0] == mismatch.actual {
317 let (_, source_map) = db.body_with_source_map(self.owner.into()); 335 let (_, source_map) = db.body_with_source_map(self.owner.into());
318 336
319 if let Ok(source_ptr) = source_map.expr_syntax(id) { 337 if let Ok(source_ptr) = source_map.expr_syntax(id) {
320 self.sink 338 self.sink.push(MissingOkOrSomeInTailExpr {
321 .push(MissingOkInTailExpr { file: source_ptr.file_id, expr: source_ptr.value }); 339 file: source_ptr.file_id,
340 expr: source_ptr.value,
341 required,
342 });
322 } 343 }
323 } 344 }
324 } 345 }
@@ -358,7 +379,7 @@ pub fn record_literal_missing_fields(
358 id: ExprId, 379 id: ExprId,
359 expr: &Expr, 380 expr: &Expr,
360) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> { 381) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> {
361 let (fields, exhausitve) = match expr { 382 let (fields, exhaustive) = match expr {
362 Expr::RecordLit { path: _, fields, spread } => (fields, spread.is_none()), 383 Expr::RecordLit { path: _, fields, spread } => (fields, spread.is_none()),
363 _ => return None, 384 _ => return None,
364 }; 385 };
@@ -379,7 +400,7 @@ pub fn record_literal_missing_fields(
379 if missed_fields.is_empty() { 400 if missed_fields.is_empty() {
380 return None; 401 return None;
381 } 402 }
382 Some((variant_def, missed_fields, exhausitve)) 403 Some((variant_def, missed_fields, exhaustive))
383} 404}
384 405
385pub fn record_pattern_missing_fields( 406pub fn record_pattern_missing_fields(
@@ -505,6 +526,22 @@ fn f() {
505 } 526 }
506 527
507 #[test] 528 #[test]
529 fn method_unknown_receiver() {
530 // note: this is incorrect code, so there might be errors on this in the
531 // future, but we shouldn't emit an argument count diagnostic here
532 check_diagnostics(
533 r#"
534trait Foo { fn method(&self, arg: usize) {} }
535
536fn f() {
537 let x;
538 x.method();
539}
540"#,
541 );
542 }
543
544 #[test]
508 fn tuple_struct() { 545 fn tuple_struct() {
509 check_diagnostics( 546 check_diagnostics(
510 r#" 547 r#"
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index 62c329731..61c47eec8 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -14,7 +14,7 @@
14//! The algorithm implemented here is a modified version of the one described in 14//! The algorithm implemented here is a modified version of the one described in
15//! <http://moscova.inria.fr/~maranget/papers/warn/index.html>. 15//! <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
16//! However, to save future implementors from reading the original paper, we 16//! However, to save future implementors from reading the original paper, we
17//! summarise the algorithm here to hopefully save time and be a little clearer 17//! summarize the algorithm here to hopefully save time and be a little clearer
18//! (without being so rigorous). 18//! (without being so rigorous).
19//! 19//!
20//! The core of the algorithm revolves about a "usefulness" check. In particular, we 20//! The core of the algorithm revolves about a "usefulness" check. In particular, we
@@ -132,7 +132,7 @@
132//! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns). 132//! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns).
133//! That means we're going to check the components from left-to-right, so the algorithm 133//! That means we're going to check the components from left-to-right, so the algorithm
134//! operates principally on the first component of the matrix and new pattern-stack `p`. 134//! operates principally on the first component of the matrix and new pattern-stack `p`.
135//! This algorithm is realised in the `is_useful` function. 135//! This algorithm is realized in the `is_useful` function.
136//! 136//!
137//! Base case (`n = 0`, i.e., an empty tuple pattern): 137//! Base case (`n = 0`, i.e., an empty tuple pattern):
138//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), then 138//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), then
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index 0e827a29e..d2f1b4014 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -1,14 +1,15 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::fmt; 3use std::{borrow::Cow, fmt};
4 4
5use crate::{ 5use crate::{
6 db::HirDatabase, utils::generics, ApplicationTy, CallableDefId, FnSig, GenericPredicate, 6 db::HirDatabase, utils::generics, ApplicationTy, CallableDefId, FnSig, GenericPredicate,
7 Lifetime, Obligation, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, 7 Lifetime, Obligation, OpaqueTy, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
8}; 8};
9use arrayvec::ArrayVec;
9use hir_def::{ 10use hir_def::{
10 find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, 11 db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId,
11 Lookup, ModuleId, 12 AssocContainerId, HasModule, Lookup, ModuleId, TraitId,
12}; 13};
13use hir_expand::name::Name; 14use hir_expand::name::Name;
14 15
@@ -168,7 +169,7 @@ pub enum DisplayTarget {
168 169
169impl DisplayTarget { 170impl DisplayTarget {
170 fn is_source_code(&self) -> bool { 171 fn is_source_code(&self) -> bool {
171 matches!(self, Self::SourceCode {..}) 172 matches!(self, Self::SourceCode { .. })
172 } 173 }
173 fn is_test(&self) -> bool { 174 fn is_test(&self) -> bool {
174 matches!(self, Self::Test) 175 matches!(self, Self::Test)
@@ -257,25 +258,45 @@ impl HirDisplay for ApplicationTy {
257 t.hir_fmt(f)?; 258 t.hir_fmt(f)?;
258 write!(f, "; _]")?; 259 write!(f, "; _]")?;
259 } 260 }
260 TypeCtor::RawPtr(m) => { 261 TypeCtor::RawPtr(m) | TypeCtor::Ref(m) => {
261 let t = self.parameters.as_single(); 262 let t = self.parameters.as_single();
263 let ty_display =
264 t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
262 265
263 write!(f, "*{}", m.as_keyword_for_ptr())?; 266 if matches!(self.ctor, TypeCtor::RawPtr(_)) {
264 if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) { 267 write!(f, "*{}", m.as_keyword_for_ptr())?;
265 write!(f, "(")?;
266 t.hir_fmt(f)?;
267 write!(f, ")")?;
268 } else { 268 } else {
269 t.hir_fmt(f)?; 269 write!(f, "&{}", m.as_keyword_for_ref())?;
270 }
271
272 let datas;
273 let predicates = match t {
274 Ty::Dyn(predicates) if predicates.len() > 1 => {
275 Cow::Borrowed(predicates.as_ref())
276 }
277 &Ty::Opaque(OpaqueTy {
278 opaque_ty_id: OpaqueTyId::ReturnTypeImplTrait(func, idx),
279 ref parameters,
280 }) => {
281 datas =
282 f.db.return_type_impl_traits(func).expect("impl trait id without data");
283 let data = (*datas)
284 .as_ref()
285 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
286 let bounds = data.subst(parameters);
287 Cow::Owned(bounds.value)
288 }
289 _ => Cow::Borrowed(&[][..]),
290 };
291
292 if let [GenericPredicate::Implemented(trait_ref), _] = predicates.as_ref() {
293 let trait_ = trait_ref.trait_;
294 if fn_traits(f.db.upcast(), trait_).any(|it| it == trait_) {
295 return write!(f, "{}", ty_display);
296 }
270 } 297 }
271 }
272 TypeCtor::Ref(m) => {
273 let t = self.parameters.as_single();
274 let ty_display =
275 t.into_displayable(f.db, f.max_size, f.omit_verbose_types, f.display_target);
276 298
277 write!(f, "&{}", m.as_keyword_for_ref())?; 299 if predicates.len() > 1 {
278 if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) {
279 write!(f, "(")?; 300 write!(f, "(")?;
280 write!(f, "{}", ty_display)?; 301 write!(f, "{}", ty_display)?;
281 write!(f, ")")?; 302 write!(f, ")")?;
@@ -595,7 +616,18 @@ impl HirDisplay for FnSig {
595 } 616 }
596} 617}
597 618
598fn write_bounds_like_dyn_trait( 619fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> {
620 let krate = trait_.lookup(db).container.module(db).krate;
621 let fn_traits = [
622 db.lang_item(krate, "fn".into()),
623 db.lang_item(krate, "fn_mut".into()),
624 db.lang_item(krate, "fn_once".into()),
625 ];
626 // FIXME: Replace ArrayVec when into_iter is a thing on arrays
627 ArrayVec::from(fn_traits).into_iter().flatten().flat_map(|it| it.as_trait())
628}
629
630pub fn write_bounds_like_dyn_trait(
599 predicates: &[GenericPredicate], 631 predicates: &[GenericPredicate],
600 f: &mut HirFormatter, 632 f: &mut HirFormatter,
601) -> Result<(), HirDisplayError> { 633) -> Result<(), HirDisplayError> {
@@ -607,10 +639,15 @@ fn write_bounds_like_dyn_trait(
607 // predicate for that trait). 639 // predicate for that trait).
608 let mut first = true; 640 let mut first = true;
609 let mut angle_open = false; 641 let mut angle_open = false;
642 let mut is_fn_trait = false;
610 for p in predicates.iter() { 643 for p in predicates.iter() {
611 match p { 644 match p {
612 GenericPredicate::Implemented(trait_ref) => { 645 GenericPredicate::Implemented(trait_ref) => {
613 if angle_open { 646 let trait_ = trait_ref.trait_;
647 if !is_fn_trait {
648 is_fn_trait = fn_traits(f.db.upcast(), trait_).any(|it| it == trait_);
649 }
650 if !is_fn_trait && angle_open {
614 write!(f, ">")?; 651 write!(f, ">")?;
615 angle_open = false; 652 angle_open = false;
616 } 653 }
@@ -620,14 +657,27 @@ fn write_bounds_like_dyn_trait(
620 // We assume that the self type is $0 (i.e. the 657 // We assume that the self type is $0 (i.e. the
621 // existential) here, which is the only thing that's 658 // existential) here, which is the only thing that's
622 // possible in actual Rust, and hence don't print it 659 // possible in actual Rust, and hence don't print it
623 write!(f, "{}", f.db.trait_data(trait_ref.trait_).name)?; 660 write!(f, "{}", f.db.trait_data(trait_).name)?;
624 if trait_ref.substs.len() > 1 { 661 if let [_, params @ ..] = &*trait_ref.substs.0 {
625 write!(f, "<")?; 662 if is_fn_trait {
626 f.write_joined(&trait_ref.substs[1..], ", ")?; 663 if let Some(args) = params.first().and_then(|it| it.as_tuple()) {
627 // there might be assoc type bindings, so we leave the angle brackets open 664 write!(f, "(")?;
628 angle_open = true; 665 f.write_joined(&*args.0, ", ")?;
666 write!(f, ")")?;
667 }
668 } else if !params.is_empty() {
669 write!(f, "<")?;
670 f.write_joined(params, ", ")?;
671 // there might be assoc type bindings, so we leave the angle brackets open
672 angle_open = true;
673 }
629 } 674 }
630 } 675 }
676 GenericPredicate::Projection(projection_pred) if is_fn_trait => {
677 is_fn_trait = false;
678 write!(f, " -> ")?;
679 projection_pred.ty.hir_fmt(f)?;
680 }
631 GenericPredicate::Projection(projection_pred) => { 681 GenericPredicate::Projection(projection_pred) => {
632 // in types in actual Rust, these will always come 682 // in types in actual Rust, these will always come
633 // after the corresponding Implemented predicate 683 // after the corresponding Implemented predicate
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 70a3f3075..f2fc69b2f 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -648,6 +648,8 @@ impl<'a> InferenceContext<'a> {
648 } 648 }
649 Expr::Array(array) => { 649 Expr::Array(array) => {
650 let elem_ty = match &expected.ty { 650 let elem_ty = match &expected.ty {
651 // FIXME: remove when https://github.com/rust-lang/rust/issues/80501 is fixed
652 #[allow(unreachable_patterns)]
651 ty_app!(TypeCtor::Array, st) | ty_app!(TypeCtor::Slice, st) => { 653 ty_app!(TypeCtor::Array, st) | ty_app!(TypeCtor::Slice, st) => {
652 st.as_single().clone() 654 st.as_single().clone()
653 } 655 }
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 80d7ed10e..5d541104e 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -89,6 +89,7 @@ impl<'a> InferenceContext<'a> {
89 return None; 89 return None;
90 } 90 }
91 } 91 }
92 ValueNs::GenericParam(it) => return Some(self.db.const_param_ty(it)),
92 }; 93 };
93 94
94 let ty = self.db.value_ty(typable); 95 let ty = self.db.value_ty(typable);
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 357bd92f9..e00c7e176 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -791,6 +791,10 @@ impl Ty {
791 matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. })) 791 matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }))
792 } 792 }
793 793
794 pub fn is_unknown(&self) -> bool {
795 matches!(self, Ty::Unknown)
796 }
797
794 /// If this is a `dyn Trait` type, this returns the `Trait` part. 798 /// If this is a `dyn Trait` type, this returns the `Trait` part.
795 pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { 799 pub fn dyn_trait_ref(&self) -> Option<&TraitRef> {
796 match self { 800 match self {
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 8da56cd11..9594cce8b 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -16,9 +16,9 @@ use hir_def::{
16 path::{GenericArg, Path, PathSegment, PathSegments}, 16 path::{GenericArg, Path, PathSegment, PathSegments},
17 resolver::{HasResolver, Resolver, TypeNs}, 17 resolver::{HasResolver, Resolver, TypeNs},
18 type_ref::{TypeBound, TypeRef}, 18 type_ref::{TypeBound, TypeRef},
19 AdtId, AssocContainerId, AssocItemId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, 19 AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId,
20 HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, 20 GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
21 UnionId, VariantId, 21 TypeAliasId, TypeParamId, UnionId, VariantId,
22}; 22};
23use hir_expand::name::Name; 23use hir_expand::name::Name;
24use smallvec::SmallVec; 24use smallvec::SmallVec;
@@ -491,16 +491,16 @@ impl Ty {
491 fn from_hir_path_inner( 491 fn from_hir_path_inner(
492 ctx: &TyLoweringContext<'_>, 492 ctx: &TyLoweringContext<'_>,
493 segment: PathSegment<'_>, 493 segment: PathSegment<'_>,
494 typable: TyDefId, 494 typeable: TyDefId,
495 infer_args: bool, 495 infer_args: bool,
496 ) -> Ty { 496 ) -> Ty {
497 let generic_def = match typable { 497 let generic_def = match typeable {
498 TyDefId::BuiltinType(_) => None, 498 TyDefId::BuiltinType(_) => None,
499 TyDefId::AdtId(it) => Some(it.into()), 499 TyDefId::AdtId(it) => Some(it.into()),
500 TyDefId::TypeAliasId(it) => Some(it.into()), 500 TyDefId::TypeAliasId(it) => Some(it.into()),
501 }; 501 };
502 let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args); 502 let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args);
503 ctx.db.ty(typable).subst(&substs) 503 ctx.db.ty(typeable).subst(&substs)
504 } 504 }
505 505
506 /// Collect generic arguments from a path into a `Substs`. See also 506 /// Collect generic arguments from a path into a `Substs`. See also
@@ -1221,6 +1221,15 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde
1221 Binders::new(generics.len(), Ty::from_hir(&ctx, &impl_data.target_type)) 1221 Binders::new(generics.len(), Ty::from_hir(&ctx, &impl_data.target_type))
1222} 1222}
1223 1223
1224pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
1225 let parent_data = db.generic_params(def.parent);
1226 let data = &parent_data.consts[def.local_id];
1227 let resolver = def.parent.resolver(db.upcast());
1228 let ctx = TyLoweringContext::new(db, &resolver);
1229
1230 Ty::from_hir(&ctx, &data.ty)
1231}
1232
1224pub(crate) fn impl_self_ty_recover( 1233pub(crate) fn impl_self_ty_recover(
1225 db: &dyn HirDatabase, 1234 db: &dyn HirDatabase,
1226 _cycle: &[String], 1235 _cycle: &[String],
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 0a400cb70..3b1675f0b 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -314,7 +314,7 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
314 " 314 "
315 //- /lib.rs 315 //- /lib.rs
316 fn foo() -> i32 { 316 fn foo() -> i32 {
317 <|>1 + 1 317 $01 + 1
318 } 318 }
319 ", 319 ",
320 ); 320 );
diff --git a/crates/hir_ty/src/tests/display_source_code.rs b/crates/hir_ty/src/tests/display_source_code.rs
index b502135d8..3d29021aa 100644
--- a/crates/hir_ty/src/tests/display_source_code.rs
+++ b/crates/hir_ty/src/tests/display_source_code.rs
@@ -39,3 +39,18 @@ fn main() {
39"#, 39"#,
40 ); 40 );
41} 41}
42
43#[test]
44fn render_raw_ptr_impl_ty() {
45 check_types_source_code(
46 r#"
47trait Sized {}
48trait Unpin {}
49fn foo() -> *const (impl Unpin + Sized) { loop {} }
50fn main() {
51 let foo = foo();
52 foo;
53} //^ *const (impl Unpin + Sized)
54"#,
55 );
56}
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index a7656b864..c64f0b5b5 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -371,6 +371,37 @@ expand!();
371} 371}
372 372
373#[test] 373#[test]
374fn infer_macro_with_dollar_crate_in_def_site() {
375 check_types(
376 r#"
377//- /main.rs crate:main deps:foo
378use foo::expand;
379
380macro_rules! list {
381 ($($tt:tt)*) => { $($tt)* }
382}
383
384fn test() {
385 let r = expand!();
386 r;
387 //^ u128
388}
389
390//- /lib.rs crate:foo
391#[macro_export]
392macro_rules! expand {
393 () => { list!($crate::m!()) };
394}
395
396#[macro_export]
397macro_rules! m {
398 () => { 0u128 };
399}
400"#,
401 );
402}
403
404#[test]
374fn infer_type_value_non_legacy_macro_use_as() { 405fn infer_type_value_non_legacy_macro_use_as() {
375 check_infer( 406 check_infer(
376 r#" 407 r#"
@@ -540,6 +571,52 @@ fn bar() -> u32 {0}
540} 571}
541 572
542#[test] 573#[test]
574fn infer_builtin_macros_include_str() {
575 check_types(
576 r#"
577//- /main.rs
578#[rustc_builtin_macro]
579macro_rules! include_str {() => {}}
580
581fn main() {
582 let a = include_str!("foo.rs");
583 a;
584} //^ &str
585
586//- /foo.rs
587hello
588"#,
589 );
590}
591
592#[test]
593fn infer_builtin_macros_include_str_with_lazy_nested() {
594 check_types(
595 r#"
596//- /main.rs
597#[rustc_builtin_macro]
598macro_rules! concat {() => {}}
599#[rustc_builtin_macro]
600macro_rules! include_str {() => {}}
601
602macro_rules! m {
603 ($x:expr) => {
604 concat!("foo", $x)
605 };
606}
607
608fn main() {
609 let a = include_str!(m!(".rs"));
610 a;
611} //^ &str
612
613//- /foo.rs
614hello
615"#,
616 );
617}
618
619#[test]
543#[ignore] 620#[ignore]
544fn include_accidentally_quadratic() { 621fn include_accidentally_quadratic() {
545 let file = project_dir().join("crates/syntax/test_data/accidentally_quadratic"); 622 let file = project_dir().join("crates/syntax/test_data/accidentally_quadratic");
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 307a257b1..cffe8630b 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -326,6 +326,24 @@ fn infer_paren_macro_call() {
326} 326}
327 327
328#[test] 328#[test]
329fn infer_array_macro_call() {
330 check_infer(
331 r#"
332 macro_rules! bar { () => {0u32} }
333 fn test() {
334 let a = [bar!()];
335 }
336 "#,
337 expect![[r#"
338 !0..4 '0u32': u32
339 44..69 '{ ...()]; }': ()
340 54..55 'a': [u32; _]
341 58..66 '[bar!()]': [u32; _]
342 "#]],
343 );
344}
345
346#[test]
329fn bug_1030() { 347fn bug_1030() {
330 check_infer( 348 check_infer(
331 r#" 349 r#"
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index a61282d5a..8d431b920 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -2375,3 +2375,19 @@ fn infer_operator_overload() {
2375 "#]], 2375 "#]],
2376 ); 2376 );
2377} 2377}
2378
2379#[test]
2380fn infer_const_params() {
2381 check_infer(
2382 r#"
2383 fn foo<const FOO: usize>() {
2384 let bar = FOO;
2385 }
2386 "#,
2387 expect![[r#"
2388 27..49 '{ ...FOO; }': ()
2389 37..40 'bar': usize
2390 43..46 'FOO': usize
2391 "#]],
2392 );
2393}
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 41d097519..e5a3f95a6 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3038,16 +3038,16 @@ fn infer_box_fn_arg() {
3038 406..417 '&self.inner': &*mut T 3038 406..417 '&self.inner': &*mut T
3039 407..411 'self': &Box<T> 3039 407..411 'self': &Box<T>
3040 407..417 'self.inner': *mut T 3040 407..417 'self.inner': *mut T
3041 478..575 '{ ...(&s) }': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)> 3041 478..575 '{ ...(&s) }': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)>
3042 488..489 's': Option<i32> 3042 488..489 's': Option<i32>
3043 492..504 'Option::None': Option<i32> 3043 492..504 'Option::None': Option<i32>
3044 514..515 'f': Box<dyn FnOnce<(&Option<i32>,)>> 3044 514..515 'f': Box<dyn FnOnce(&Option<i32>)>
3045 549..562 'box (|ps| {})': Box<|{unknown}| -> ()> 3045 549..562 'box (|ps| {})': Box<|{unknown}| -> ()>
3046 554..561 '|ps| {}': |{unknown}| -> () 3046 554..561 '|ps| {}': |{unknown}| -> ()
3047 555..557 'ps': {unknown} 3047 555..557 'ps': {unknown}
3048 559..561 '{}': () 3048 559..561 '{}': ()
3049 568..569 'f': Box<dyn FnOnce<(&Option<i32>,)>> 3049 568..569 'f': Box<dyn FnOnce(&Option<i32>)>
3050 568..573 'f(&s)': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)> 3050 568..573 'f(&s)': FnOnce::Output<dyn FnOnce(&Option<i32>), (&Option<i32>,)>
3051 570..572 '&s': &Option<i32> 3051 570..572 '&s': &Option<i32>
3052 571..572 's': Option<i32> 3052 571..572 's': Option<i32>
3053 "#]], 3053 "#]],
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index 4d483580d..bb28cca4d 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12[dependencies] 12[dependencies]
13either = "1.5.3" 13either = "1.5.3"
14indexmap = "1.4.0" 14indexmap = "1.4.0"
15itertools = "0.9.0" 15itertools = "0.10.0"
16log = "0.4.8" 16log = "0.4.8"
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18oorandom = "11.1.2" 18oorandom = "11.1.2"
@@ -36,4 +36,4 @@ completion = { path = "../completion", version = "0.0.0" }
36hir = { path = "../hir", version = "0.0.0" } 36hir = { path = "../hir", version = "0.0.0" }
37 37
38[dev-dependencies] 38[dev-dependencies]
39expect-test = "1.0" 39expect-test = "1.1"
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index 60e0cd4ad..e8999a7f3 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -5,10 +5,10 @@ use indexmap::IndexMap;
5use hir::Semantics; 5use hir::Semantics;
6use ide_db::call_info::FnCallNode; 6use ide_db::call_info::FnCallNode;
7use ide_db::RootDatabase; 7use ide_db::RootDatabase;
8use syntax::{ast, match_ast, AstNode, TextRange}; 8use syntax::{ast, AstNode, TextRange};
9 9
10use crate::{ 10use crate::{
11 display::ToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo, 11 display::TryToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo,
12}; 12};
13 13
14#[derive(Debug, Clone)] 14#[derive(Debug, Clone)]
@@ -47,28 +47,23 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
47 47
48 let mut calls = CallLocations::default(); 48 let mut calls = CallLocations::default();
49 49
50 for reference in refs.info.references() { 50 for (&file_id, references) in refs.info.references().iter() {
51 let file_id = reference.file_range.file_id;
52 let file = sema.parse(file_id); 51 let file = sema.parse(file_id);
53 let file = file.syntax(); 52 let file = file.syntax();
54 let token = file.token_at_offset(reference.file_range.range.start()).next()?; 53 for reference in references {
55 let token = sema.descend_into_macros(token); 54 let token = file.token_at_offset(reference.range.start()).next()?;
56 let syntax = token.parent(); 55 let token = sema.descend_into_macros(token);
57 56 let syntax = token.parent();
58 // This target is the containing function 57
59 if let Some(nav) = syntax.ancestors().find_map(|node| { 58 // This target is the containing function
60 match_ast! { 59 if let Some(nav) = syntax.ancestors().find_map(|node| {
61 match node { 60 let fn_ = ast::Fn::cast(node)?;
62 ast::Fn(it) => { 61 let def = sema.to_def(&fn_)?;
63 let def = sema.to_def(&it)?; 62 def.try_to_nav(sema.db)
64 Some(def.to_nav(sema.db)) 63 }) {
65 }, 64 let relative_range = reference.range;
66 _ => None, 65 calls.add(&nav, relative_range);
67 }
68 } 66 }
69 }) {
70 let relative_range = reference.file_range.range;
71 calls.add(&nav, relative_range);
72 } 67 }
73 } 68 }
74 69
@@ -91,29 +86,21 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
91 .filter_map(|node| FnCallNode::with_node_exact(&node)) 86 .filter_map(|node| FnCallNode::with_node_exact(&node))
92 .filter_map(|call_node| { 87 .filter_map(|call_node| {
93 let name_ref = call_node.name_ref()?; 88 let name_ref = call_node.name_ref()?;
94 89 let func_target = match call_node {
95 if let Some(func_target) = match &call_node {
96 FnCallNode::CallExpr(expr) => { 90 FnCallNode::CallExpr(expr) => {
97 //FIXME: Type::as_callable is broken 91 //FIXME: Type::as_callable is broken
98 let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?; 92 let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?;
99 match callable.kind() { 93 match callable.kind() {
100 hir::CallableKind::Function(it) => { 94 hir::CallableKind::Function(it) => it.try_to_nav(db),
101 let fn_def: hir::Function = it.into();
102 let nav = fn_def.to_nav(db);
103 Some(nav)
104 }
105 _ => None, 95 _ => None,
106 } 96 }
107 } 97 }
108 FnCallNode::MethodCallExpr(expr) => { 98 FnCallNode::MethodCallExpr(expr) => {
109 let function = sema.resolve_method_call(&expr)?; 99 let function = sema.resolve_method_call(&expr)?;
110 Some(function.to_nav(db)) 100 function.try_to_nav(db)
111 } 101 }
112 } { 102 }?;
113 Some((func_target, name_ref.syntax().text_range())) 103 Some((func_target, name_ref.syntax().text_range()))
114 } else {
115 None
116 }
117 }) 104 })
118 .for_each(|(nav, range)| calls.add(&nav, range)); 105 .for_each(|(nav, range)| calls.add(&nav, range));
119 106
@@ -178,7 +165,7 @@ mod tests {
178//- /lib.rs 165//- /lib.rs
179fn callee() {} 166fn callee() {}
180fn caller() { 167fn caller() {
181 call<|>ee(); 168 call$0ee();
182} 169}
183"#, 170"#,
184 "callee Function FileId(0) 0..14 3..9", 171 "callee Function FileId(0) 0..14 3..9",
@@ -192,7 +179,7 @@ fn caller() {
192 check_hierarchy( 179 check_hierarchy(
193 r#" 180 r#"
194//- /lib.rs 181//- /lib.rs
195fn call<|>ee() {} 182fn call$0ee() {}
196fn caller() { 183fn caller() {
197 callee(); 184 callee();
198} 185}
@@ -210,7 +197,7 @@ fn caller() {
210//- /lib.rs 197//- /lib.rs
211fn callee() {} 198fn callee() {}
212fn caller() { 199fn caller() {
213 call<|>ee(); 200 call$0ee();
214 callee(); 201 callee();
215} 202}
216"#, 203"#,
@@ -227,7 +214,7 @@ fn caller() {
227//- /lib.rs 214//- /lib.rs
228fn callee() {} 215fn callee() {}
229fn caller1() { 216fn caller1() {
230 call<|>ee(); 217 call$0ee();
231} 218}
232 219
233fn caller2() { 220fn caller2() {
@@ -250,7 +237,7 @@ fn caller2() {
250//- /lib.rs cfg:test 237//- /lib.rs cfg:test
251fn callee() {} 238fn callee() {}
252fn caller1() { 239fn caller1() {
253 call<|>ee(); 240 call$0ee();
254} 241}
255 242
256#[cfg(test)] 243#[cfg(test)]
@@ -281,7 +268,7 @@ mod foo;
281use foo::callee; 268use foo::callee;
282 269
283fn caller() { 270fn caller() {
284 call<|>ee(); 271 call$0ee();
285} 272}
286 273
287//- /foo/mod.rs 274//- /foo/mod.rs
@@ -299,7 +286,7 @@ pub fn callee() {}
299 r#" 286 r#"
300//- /lib.rs 287//- /lib.rs
301fn callee() {} 288fn callee() {}
302fn call<|>er() { 289fn call$0er() {
303 callee(); 290 callee();
304 callee(); 291 callee();
305} 292}
@@ -318,7 +305,7 @@ fn call<|>er() {
318mod foo; 305mod foo;
319use foo::callee; 306use foo::callee;
320 307
321fn call<|>er() { 308fn call$0er() {
322 callee(); 309 callee();
323} 310}
324 311
@@ -337,7 +324,7 @@ pub fn callee() {}
337 r#" 324 r#"
338//- /lib.rs 325//- /lib.rs
339fn caller1() { 326fn caller1() {
340 call<|>er2(); 327 call$0er2();
341} 328}
342 329
343fn caller2() { 330fn caller2() {
@@ -365,7 +352,7 @@ fn a() {
365fn b() {} 352fn b() {}
366 353
367fn main() { 354fn main() {
368 a<|>() 355 a$0()
369} 356}
370"#, 357"#,
371 "a Function FileId(0) 0..18 3..4", 358 "a Function FileId(0) 0..18 3..4",
@@ -376,7 +363,7 @@ fn main() {
376 check_hierarchy( 363 check_hierarchy(
377 r#" 364 r#"
378fn a() { 365fn a() {
379 b<|>() 366 b$0()
380} 367}
381 368
382fn b() {} 369fn b() {}
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 79d126ff2..055c0a79c 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -125,7 +125,7 @@ pub(crate) fn diagnostics(
125 .on::<hir::diagnostics::MissingFields, _>(|d| { 125 .on::<hir::diagnostics::MissingFields, _>(|d| {
126 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 126 res.borrow_mut().push(diagnostic_with_fix(d, &sema));
127 }) 127 })
128 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { 128 .on::<hir::diagnostics::MissingOkOrSomeInTailExpr, _>(|d| {
129 res.borrow_mut().push(diagnostic_with_fix(d, &sema)); 129 res.borrow_mut().push(diagnostic_with_fix(d, &sema));
130 }) 130 })
131 .on::<hir::diagnostics::NoSuchField, _>(|d| { 131 .on::<hir::diagnostics::NoSuchField, _>(|d| {
@@ -305,6 +305,40 @@ mod tests {
305 } 305 }
306 306
307 #[test] 307 #[test]
308 fn test_wrap_return_type_option() {
309 check_fix(
310 r#"
311//- /main.rs crate:main deps:core
312use core::option::Option::{self, Some, None};
313
314fn div(x: i32, y: i32) -> Option<i32> {
315 if y == 0 {
316 return None;
317 }
318 x / y$0
319}
320//- /core/lib.rs crate:core
321pub mod result {
322 pub enum Result<T, E> { Ok(T), Err(E) }
323}
324pub mod option {
325 pub enum Option<T> { Some(T), None }
326}
327"#,
328 r#"
329use core::option::Option::{self, Some, None};
330
331fn div(x: i32, y: i32) -> Option<i32> {
332 if y == 0 {
333 return None;
334 }
335 Some(x / y)
336}
337"#,
338 );
339 }
340
341 #[test]
308 fn test_wrap_return_type() { 342 fn test_wrap_return_type() {
309 check_fix( 343 check_fix(
310 r#" 344 r#"
@@ -315,12 +349,15 @@ fn div(x: i32, y: i32) -> Result<i32, ()> {
315 if y == 0 { 349 if y == 0 {
316 return Err(()); 350 return Err(());
317 } 351 }
318 x / y<|> 352 x / y$0
319} 353}
320//- /core/lib.rs crate:core 354//- /core/lib.rs crate:core
321pub mod result { 355pub mod result {
322 pub enum Result<T, E> { Ok(T), Err(E) } 356 pub enum Result<T, E> { Ok(T), Err(E) }
323} 357}
358pub mod option {
359 pub enum Option<T> { Some(T), None }
360}
324"#, 361"#,
325 r#" 362 r#"
326use core::result::Result::{self, Ok, Err}; 363use core::result::Result::{self, Ok, Err};
@@ -346,12 +383,15 @@ fn div<T>(x: T) -> Result<T, i32> {
346 if x == 0 { 383 if x == 0 {
347 return Err(7); 384 return Err(7);
348 } 385 }
349 <|>x 386 $0x
350} 387}
351//- /core/lib.rs crate:core 388//- /core/lib.rs crate:core
352pub mod result { 389pub mod result {
353 pub enum Result<T, E> { Ok(T), Err(E) } 390 pub enum Result<T, E> { Ok(T), Err(E) }
354} 391}
392pub mod option {
393 pub enum Option<T> { Some(T), None }
394}
355"#, 395"#,
356 r#" 396 r#"
357use core::result::Result::{self, Ok, Err}; 397use core::result::Result::{self, Ok, Err};
@@ -379,12 +419,15 @@ fn div(x: i32, y: i32) -> MyResult<i32> {
379 if y == 0 { 419 if y == 0 {
380 return Err(()); 420 return Err(());
381 } 421 }
382 x <|>/ y 422 x $0/ y
383} 423}
384//- /core/lib.rs crate:core 424//- /core/lib.rs crate:core
385pub mod result { 425pub mod result {
386 pub enum Result<T, E> { Ok(T), Err(E) } 426 pub enum Result<T, E> { Ok(T), Err(E) }
387} 427}
428pub mod option {
429 pub enum Option<T> { Some(T), None }
430}
388"#, 431"#,
389 r#" 432 r#"
390use core::result::Result::{self, Ok, Err}; 433use core::result::Result::{self, Ok, Err};
@@ -414,12 +457,15 @@ fn foo() -> Result<(), i32> { 0 }
414pub mod result { 457pub mod result {
415 pub enum Result<T, E> { Ok(T), Err(E) } 458 pub enum Result<T, E> { Ok(T), Err(E) }
416} 459}
460pub mod option {
461 pub enum Option<T> { Some(T), None }
462}
417"#, 463"#,
418 ); 464 );
419 } 465 }
420 466
421 #[test] 467 #[test]
422 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { 468 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
423 check_no_diagnostics( 469 check_no_diagnostics(
424 r#" 470 r#"
425//- /main.rs crate:main deps:core 471//- /main.rs crate:main deps:core
@@ -433,6 +479,9 @@ fn foo() -> SomeOtherEnum { 0 }
433pub mod result { 479pub mod result {
434 pub enum Result<T, E> { Ok(T), Err(E) } 480 pub enum Result<T, E> { Ok(T), Err(E) }
435} 481}
482pub mod option {
483 pub enum Option<T> { Some(T), None }
484}
436"#, 485"#,
437 ); 486 );
438 } 487 }
@@ -444,7 +493,7 @@ pub mod result {
444struct TestStruct { one: i32, two: i64 } 493struct TestStruct { one: i32, two: i64 }
445 494
446fn test_fn() { 495fn test_fn() {
447 let s = TestStruct {<|>}; 496 let s = TestStruct {$0};
448} 497}
449"#, 498"#,
450 r#" 499 r#"
@@ -464,7 +513,7 @@ fn test_fn() {
464struct TestStruct { one: i32 } 513struct TestStruct { one: i32 }
465 514
466impl TestStruct { 515impl TestStruct {
467 fn test_fn() { let s = Self {<|>}; } 516 fn test_fn() { let s = Self {$0}; }
468} 517}
469"#, 518"#,
470 r#" 519 r#"
@@ -487,7 +536,7 @@ enum Expr {
487 536
488impl Expr { 537impl Expr {
489 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { 538 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
490 Expr::Bin {<|> } 539 Expr::Bin {$0 }
491 } 540 }
492} 541}
493"#, 542"#,
@@ -512,7 +561,7 @@ impl Expr {
512struct TestStruct { one: i32, two: i64 } 561struct TestStruct { one: i32, two: i64 }
513 562
514fn test_fn() { 563fn test_fn() {
515 let s = TestStruct{ two: 2<|> }; 564 let s = TestStruct{ two: 2$0 };
516} 565}
517"#, 566"#,
518 r" 567 r"
@@ -608,7 +657,7 @@ fn here() {}
608macro_rules! id { ($($tt:tt)*) => { $($tt)*}; } 657macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
609 658
610fn main() { 659fn main() {
611 let _x = id![Foo { a: <|>42 }]; 660 let _x = id![Foo { a: $042 }];
612} 661}
613 662
614pub struct Foo { pub a: i32, pub b: i32 } 663pub struct Foo { pub a: i32, pub b: i32 }
@@ -663,7 +712,7 @@ mod a {
663 check_fix( 712 check_fix(
664 r" 713 r"
665 mod b {} 714 mod b {}
666 use {<|>b}; 715 use {$0b};
667 ", 716 ",
668 r" 717 r"
669 mod b {} 718 mod b {}
@@ -673,7 +722,7 @@ mod a {
673 check_fix( 722 check_fix(
674 r" 723 r"
675 mod b {} 724 mod b {}
676 use {b<|>}; 725 use {b$0};
677 ", 726 ",
678 r" 727 r"
679 mod b {} 728 mod b {}
@@ -683,7 +732,7 @@ mod a {
683 check_fix( 732 check_fix(
684 r" 733 r"
685 mod a { mod c {} } 734 mod a { mod c {} }
686 use a::{c<|>}; 735 use a::{c$0};
687 ", 736 ",
688 r" 737 r"
689 mod a { mod c {} } 738 mod a { mod c {} }
@@ -693,7 +742,7 @@ mod a {
693 check_fix( 742 check_fix(
694 r" 743 r"
695 mod a {} 744 mod a {}
696 use a::{self<|>}; 745 use a::{self$0};
697 ", 746 ",
698 r" 747 r"
699 mod a {} 748 mod a {}
@@ -703,7 +752,7 @@ mod a {
703 check_fix( 752 check_fix(
704 r" 753 r"
705 mod a { mod c {} mod d { mod e {} } } 754 mod a { mod c {} mod d { mod e {} } }
706 use a::{c, d::{e<|>}}; 755 use a::{c, d::{e$0}};
707 ", 756 ",
708 r" 757 r"
709 mod a { mod c {} mod d { mod e {} } } 758 mod a { mod c {} mod d { mod e {} } }
@@ -717,7 +766,7 @@ mod a {
717 check_fix( 766 check_fix(
718 r" 767 r"
719fn main() { 768fn main() {
720 Foo { bar: 3, baz<|>: false}; 769 Foo { bar: 3, baz$0: false};
721} 770}
722struct Foo { 771struct Foo {
723 bar: i32 772 bar: i32
@@ -743,7 +792,7 @@ struct Foo {
743mod foo; 792mod foo;
744 793
745fn main() { 794fn main() {
746 foo::Foo { bar: 3, <|>baz: false}; 795 foo::Foo { bar: 3, $0baz: false};
747} 796}
748//- /foo.rs 797//- /foo.rs
749struct Foo { 798struct Foo {
@@ -777,7 +826,7 @@ struct Foo {
777 fn test_rename_incorrect_case() { 826 fn test_rename_incorrect_case() {
778 check_fix( 827 check_fix(
779 r#" 828 r#"
780pub struct test_struct<|> { one: i32 } 829pub struct test_struct$0 { one: i32 }
781 830
782pub fn some_fn(val: test_struct) -> test_struct { 831pub fn some_fn(val: test_struct) -> test_struct {
783 test_struct { one: val.one + 1 } 832 test_struct { one: val.one + 1 }
@@ -794,7 +843,7 @@ pub fn some_fn(val: TestStruct) -> TestStruct {
794 843
795 check_fix( 844 check_fix(
796 r#" 845 r#"
797pub fn some_fn(NonSnakeCase<|>: u8) -> u8 { 846pub fn some_fn(NonSnakeCase$0: u8) -> u8 {
798 NonSnakeCase 847 NonSnakeCase
799} 848}
800"#, 849"#,
@@ -807,7 +856,7 @@ pub fn some_fn(non_snake_case: u8) -> u8 {
807 856
808 check_fix( 857 check_fix(
809 r#" 858 r#"
810pub fn SomeFn<|>(val: u8) -> u8 { 859pub fn SomeFn$0(val: u8) -> u8 {
811 if val != 0 { SomeFn(val - 1) } else { val } 860 if val != 0 { SomeFn(val - 1) } else { val }
812} 861}
813"#, 862"#,
@@ -821,7 +870,7 @@ pub fn some_fn(val: u8) -> u8 {
821 check_fix( 870 check_fix(
822 r#" 871 r#"
823fn some_fn() { 872fn some_fn() {
824 let whatAWeird_Formatting<|> = 10; 873 let whatAWeird_Formatting$0 = 10;
825 another_func(whatAWeird_Formatting); 874 another_func(whatAWeird_Formatting);
826} 875}
827"#, 876"#,
@@ -839,7 +888,7 @@ fn some_fn() {
839 check_no_diagnostics( 888 check_no_diagnostics(
840 r#" 889 r#"
841fn foo() { 890fn foo() {
842 const ANOTHER_ITEM<|>: &str = "some_item"; 891 const ANOTHER_ITEM$0: &str = "some_item";
843} 892}
844"#, 893"#,
845 ); 894 );
@@ -852,7 +901,7 @@ fn foo() {
852pub struct TestStruct; 901pub struct TestStruct;
853 902
854impl TestStruct { 903impl TestStruct {
855 pub fn SomeFn<|>() -> TestStruct { 904 pub fn SomeFn$0() -> TestStruct {
856 TestStruct 905 TestStruct
857 } 906 }
858} 907}
@@ -871,7 +920,7 @@ impl TestStruct {
871 920
872 #[test] 921 #[test]
873 fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() { 922 fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() {
874 let input = r#"fn FOO<|>() {}"#; 923 let input = r#"fn FOO$0() {}"#;
875 let expected = r#"fn foo() {}"#; 924 let expected = r#"fn foo() {}"#;
876 925
877 let (analysis, file_position) = fixture::position(input); 926 let (analysis, file_position) = fixture::position(input);
diff --git a/crates/ide/src/diagnostics/field_shorthand.rs b/crates/ide/src/diagnostics/field_shorthand.rs
index f41bcd619..16c6ea827 100644
--- a/crates/ide/src/diagnostics/field_shorthand.rs
+++ b/crates/ide/src/diagnostics/field_shorthand.rs
@@ -120,7 +120,7 @@ fn main() { A { 0: 0 } }
120struct A { a: &'static str } 120struct A { a: &'static str }
121fn main() { 121fn main() {
122 let a = "haha"; 122 let a = "haha";
123 A { a<|>: a } 123 A { a$0: a }
124} 124}
125"#, 125"#,
126 r#" 126 r#"
@@ -138,7 +138,7 @@ struct A { a: &'static str, b: &'static str }
138fn main() { 138fn main() {
139 let a = "haha"; 139 let a = "haha";
140 let b = "bb"; 140 let b = "bb";
141 A { a<|>: a, b } 141 A { a$0: a, b }
142} 142}
143"#, 143"#,
144 r#" 144 r#"
@@ -171,7 +171,7 @@ fn f(a: A) { let A { 0: 0 } = a; }
171 r#" 171 r#"
172struct A { a: &'static str } 172struct A { a: &'static str }
173fn f(a: A) { 173fn f(a: A) {
174 let A { a<|>: a } = a; 174 let A { a$0: a } = a;
175} 175}
176"#, 176"#,
177 r#" 177 r#"
@@ -186,7 +186,7 @@ fn f(a: A) {
186 r#" 186 r#"
187struct A { a: &'static str, b: &'static str } 187struct A { a: &'static str, b: &'static str }
188fn f(a: A) { 188fn f(a: A) {
189 let A { a<|>: a, b } = a; 189 let A { a$0: a, b } = a;
190} 190}
191"#, 191"#,
192 r#" 192 r#"
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index d79f5c170..d7ad88ed5 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -3,7 +3,7 @@
3use hir::{ 3use hir::{
4 db::AstDatabase, 4 db::AstDatabase,
5 diagnostics::{ 5 diagnostics::{
6 Diagnostic, IncorrectCase, MissingFields, MissingOkInTailExpr, NoSuchField, 6 Diagnostic, IncorrectCase, MissingFields, MissingOkOrSomeInTailExpr, NoSuchField,
7 RemoveThisSemicolon, UnresolvedModule, 7 RemoveThisSemicolon, UnresolvedModule,
8 }, 8 },
9 HasSource, HirDisplay, InFile, Semantics, VariantDef, 9 HasSource, HirDisplay, InFile, Semantics, VariantDef,
@@ -94,15 +94,17 @@ impl DiagnosticWithFix for MissingFields {
94 } 94 }
95} 95}
96 96
97impl DiagnosticWithFix for MissingOkInTailExpr { 97impl DiagnosticWithFix for MissingOkOrSomeInTailExpr {
98 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> { 98 fn fix(&self, sema: &Semantics<RootDatabase>) -> Option<Fix> {
99 let root = sema.db.parse_or_expand(self.file)?; 99 let root = sema.db.parse_or_expand(self.file)?;
100 let tail_expr = self.expr.to_node(&root); 100 let tail_expr = self.expr.to_node(&root);
101 let tail_expr_range = tail_expr.syntax().text_range(); 101 let tail_expr_range = tail_expr.syntax().text_range();
102 let edit = TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax())); 102 let replacement = format!("{}({})", self.required, tail_expr.syntax());
103 let edit = TextEdit::replace(tail_expr_range, replacement);
103 let source_change = 104 let source_change =
104 SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(); 105 SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into();
105 Some(Fix::new("Wrap with ok", source_change, tail_expr_range)) 106 let name = if self.required == "Ok" { "Wrap with Ok" } else { "Wrap with Some" };
107 Some(Fix::new(name, source_change, tail_expr_range))
106 } 108 }
107} 109}
108 110
@@ -156,20 +158,20 @@ fn missing_record_expr_field_fix(
156 let record_fields = match VariantDef::from(def_id) { 158 let record_fields = match VariantDef::from(def_id) {
157 VariantDef::Struct(s) => { 159 VariantDef::Struct(s) => {
158 module = s.module(sema.db); 160 module = s.module(sema.db);
159 let source = s.source(sema.db); 161 let source = s.source(sema.db)?;
160 def_file_id = source.file_id; 162 def_file_id = source.file_id;
161 let fields = source.value.field_list()?; 163 let fields = source.value.field_list()?;
162 record_field_list(fields)? 164 record_field_list(fields)?
163 } 165 }
164 VariantDef::Union(u) => { 166 VariantDef::Union(u) => {
165 module = u.module(sema.db); 167 module = u.module(sema.db);
166 let source = u.source(sema.db); 168 let source = u.source(sema.db)?;
167 def_file_id = source.file_id; 169 def_file_id = source.file_id;
168 source.value.record_field_list()? 170 source.value.record_field_list()?
169 } 171 }
170 VariantDef::Variant(e) => { 172 VariantDef::Variant(e) => {
171 module = e.module(sema.db); 173 module = e.module(sema.db);
172 let source = e.source(sema.db); 174 let source = e.source(sema.db)?;
173 def_file_id = source.file_id; 175 def_file_id = source.file_id;
174 let fields = source.value.field_list()?; 176 let fields = source.value.field_list()?;
175 record_field_list(fields)? 177 record_field_list(fields)?
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 6431e7d6d..4eecae697 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -24,6 +24,7 @@ pub enum SymbolKind {
24 Impl, 24 Impl,
25 Field, 25 Field,
26 TypeParam, 26 TypeParam,
27 ConstParam,
27 LifetimeParam, 28 LifetimeParam,
28 ValueParam, 29 ValueParam,
29 SelfParam, 30 SelfParam,
@@ -209,21 +210,12 @@ impl ToNav for FileSymbol {
209impl TryToNav for Definition { 210impl TryToNav for Definition {
210 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { 211 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
211 match self { 212 match self {
212 Definition::Macro(it) => { 213 Definition::Macro(it) => it.try_to_nav(db),
213 // FIXME: Currently proc-macro do not have ast-node, 214 Definition::Field(it) => it.try_to_nav(db),
214 // such that it does not have source
215 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
216 if it.is_proc_macro() {
217 return None;
218 }
219 Some(it.to_nav(db))
220 }
221 Definition::Field(it) => Some(it.to_nav(db)),
222 Definition::ModuleDef(it) => it.try_to_nav(db), 215 Definition::ModuleDef(it) => it.try_to_nav(db),
223 Definition::SelfType(it) => Some(it.to_nav(db)), 216 Definition::SelfType(it) => it.try_to_nav(db),
224 Definition::Local(it) => Some(it.to_nav(db)), 217 Definition::Local(it) => Some(it.to_nav(db)),
225 Definition::TypeParam(it) => Some(it.to_nav(db)), 218 Definition::GenericParam(it) => it.try_to_nav(db),
226 Definition::LifetimeParam(it) => Some(it.to_nav(db)),
227 Definition::Label(it) => Some(it.to_nav(db)), 219 Definition::Label(it) => Some(it.to_nav(db)),
228 } 220 }
229 } 221 }
@@ -231,18 +223,17 @@ impl TryToNav for Definition {
231 223
232impl TryToNav for hir::ModuleDef { 224impl TryToNav for hir::ModuleDef {
233 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { 225 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
234 let res = match self { 226 match self {
235 hir::ModuleDef::Module(it) => it.to_nav(db), 227 hir::ModuleDef::Module(it) => Some(it.to_nav(db)),
236 hir::ModuleDef::Function(it) => it.to_nav(db), 228 hir::ModuleDef::Function(it) => it.try_to_nav(db),
237 hir::ModuleDef::Adt(it) => it.to_nav(db), 229 hir::ModuleDef::Adt(it) => it.try_to_nav(db),
238 hir::ModuleDef::Variant(it) => it.to_nav(db), 230 hir::ModuleDef::Variant(it) => it.try_to_nav(db),
239 hir::ModuleDef::Const(it) => it.to_nav(db), 231 hir::ModuleDef::Const(it) => it.try_to_nav(db),
240 hir::ModuleDef::Static(it) => it.to_nav(db), 232 hir::ModuleDef::Static(it) => it.try_to_nav(db),
241 hir::ModuleDef::Trait(it) => it.to_nav(db), 233 hir::ModuleDef::Trait(it) => it.try_to_nav(db),
242 hir::ModuleDef::TypeAlias(it) => it.to_nav(db), 234 hir::ModuleDef::TypeAlias(it) => it.try_to_nav(db),
243 hir::ModuleDef::BuiltinType(_) => return None, 235 hir::ModuleDef::BuiltinType(_) => None,
244 }; 236 }
245 Some(res)
246 } 237 }
247} 238}
248 239
@@ -277,13 +268,13 @@ impl ToNavFromAst for hir::Trait {
277 const KIND: SymbolKind = SymbolKind::Trait; 268 const KIND: SymbolKind = SymbolKind::Trait;
278} 269}
279 270
280impl<D> ToNav for D 271impl<D> TryToNav for D
281where 272where
282 D: HasSource + ToNavFromAst + Copy + HasAttrs, 273 D: HasSource + ToNavFromAst + Copy + HasAttrs,
283 D::Ast: ast::NameOwner + ShortLabel, 274 D::Ast: ast::NameOwner + ShortLabel,
284{ 275{
285 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 276 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
286 let src = self.source(db); 277 let src = self.source(db)?;
287 let mut res = NavigationTarget::from_named( 278 let mut res = NavigationTarget::from_named(
288 db, 279 db,
289 src.as_ref().map(|it| it as &dyn ast::NameOwner), 280 src.as_ref().map(|it| it as &dyn ast::NameOwner),
@@ -291,7 +282,7 @@ where
291 ); 282 );
292 res.docs = self.docs(db); 283 res.docs = self.docs(db);
293 res.description = src.value.short_label(); 284 res.description = src.value.short_label();
294 res 285 Some(res)
295 } 286 }
296} 287}
297 288
@@ -310,9 +301,9 @@ impl ToNav for hir::Module {
310 } 301 }
311} 302}
312 303
313impl ToNav for hir::Impl { 304impl TryToNav for hir::Impl {
314 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 305 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
315 let src = self.source(db); 306 let src = self.source(db)?;
316 let derive_attr = self.is_builtin_derive(db); 307 let derive_attr = self.is_builtin_derive(db);
317 let frange = if let Some(item) = &derive_attr { 308 let frange = if let Some(item) = &derive_attr {
318 item.syntax().original_file_range(db) 309 item.syntax().original_file_range(db)
@@ -325,21 +316,21 @@ impl ToNav for hir::Impl {
325 src.value.self_ty().map(|ty| src.with_value(ty.syntax()).original_file_range(db).range) 316 src.value.self_ty().map(|ty| src.with_value(ty.syntax()).original_file_range(db).range)
326 }; 317 };
327 318
328 NavigationTarget::from_syntax( 319 Some(NavigationTarget::from_syntax(
329 frange.file_id, 320 frange.file_id,
330 "impl".into(), 321 "impl".into(),
331 focus_range, 322 focus_range,
332 frange.range, 323 frange.range,
333 SymbolKind::Impl, 324 SymbolKind::Impl,
334 ) 325 ))
335 } 326 }
336} 327}
337 328
338impl ToNav for hir::Field { 329impl TryToNav for hir::Field {
339 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 330 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
340 let src = self.source(db); 331 let src = self.source(db)?;
341 332
342 match &src.value { 333 let field_source = match &src.value {
343 FieldSource::Named(it) => { 334 FieldSource::Named(it) => {
344 let mut res = 335 let mut res =
345 NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field); 336 NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field);
@@ -357,13 +348,14 @@ impl ToNav for hir::Field {
357 SymbolKind::Field, 348 SymbolKind::Field,
358 ) 349 )
359 } 350 }
360 } 351 };
352 Some(field_source)
361 } 353 }
362} 354}
363 355
364impl ToNav for hir::MacroDef { 356impl TryToNav for hir::MacroDef {
365 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 357 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
366 let src = self.source(db); 358 let src = self.source(db)?;
367 log::debug!("nav target {:#?}", src.value.syntax()); 359 log::debug!("nav target {:#?}", src.value.syntax());
368 let mut res = NavigationTarget::from_named( 360 let mut res = NavigationTarget::from_named(
369 db, 361 db,
@@ -371,26 +363,36 @@ impl ToNav for hir::MacroDef {
371 SymbolKind::Macro, 363 SymbolKind::Macro,
372 ); 364 );
373 res.docs = self.docs(db); 365 res.docs = self.docs(db);
374 res 366 Some(res)
375 } 367 }
376} 368}
377 369
378impl ToNav for hir::Adt { 370impl TryToNav for hir::Adt {
379 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 371 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
380 match self { 372 match self {
381 hir::Adt::Struct(it) => it.to_nav(db), 373 hir::Adt::Struct(it) => it.try_to_nav(db),
382 hir::Adt::Union(it) => it.to_nav(db), 374 hir::Adt::Union(it) => it.try_to_nav(db),
383 hir::Adt::Enum(it) => it.to_nav(db), 375 hir::Adt::Enum(it) => it.try_to_nav(db),
384 } 376 }
385 } 377 }
386} 378}
387 379
388impl ToNav for hir::AssocItem { 380impl TryToNav for hir::AssocItem {
389 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 381 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
390 match self { 382 match self {
391 AssocItem::Function(it) => it.to_nav(db), 383 AssocItem::Function(it) => it.try_to_nav(db),
392 AssocItem::Const(it) => it.to_nav(db), 384 AssocItem::Const(it) => it.try_to_nav(db),
393 AssocItem::TypeAlias(it) => it.to_nav(db), 385 AssocItem::TypeAlias(it) => it.try_to_nav(db),
386 }
387 }
388}
389
390impl TryToNav for hir::GenericParam {
391 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
392 match self {
393 hir::GenericParam::TypeParam(it) => it.try_to_nav(db),
394 hir::GenericParam::ConstParam(it) => it.try_to_nav(db),
395 hir::GenericParam::LifetimeParam(it) => it.try_to_nav(db),
394 } 396 }
395 } 397 }
396} 398}
@@ -444,9 +446,9 @@ impl ToNav for hir::Label {
444 } 446 }
445} 447}
446 448
447impl ToNav for hir::TypeParam { 449impl TryToNav for hir::TypeParam {
448 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 450 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
449 let src = self.source(db); 451 let src = self.source(db)?;
450 let full_range = match &src.value { 452 let full_range = match &src.value {
451 Either::Left(it) => it.syntax().text_range(), 453 Either::Left(it) => it.syntax().text_range(),
452 Either::Right(it) => it.syntax().text_range(), 454 Either::Right(it) => it.syntax().text_range(),
@@ -455,7 +457,7 @@ impl ToNav for hir::TypeParam {
455 Either::Left(_) => None, 457 Either::Left(_) => None,
456 Either::Right(it) => it.name().map(|it| it.syntax().text_range()), 458 Either::Right(it) => it.name().map(|it| it.syntax().text_range()),
457 }; 459 };
458 NavigationTarget { 460 Some(NavigationTarget {
459 file_id: src.file_id.original_file(db), 461 file_id: src.file_id.original_file(db),
460 name: self.name(db).to_string().into(), 462 name: self.name(db).to_string().into(),
461 kind: Some(SymbolKind::TypeParam), 463 kind: Some(SymbolKind::TypeParam),
@@ -464,15 +466,15 @@ impl ToNav for hir::TypeParam {
464 container_name: None, 466 container_name: None,
465 description: None, 467 description: None,
466 docs: None, 468 docs: None,
467 } 469 })
468 } 470 }
469} 471}
470 472
471impl ToNav for hir::LifetimeParam { 473impl TryToNav for hir::LifetimeParam {
472 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 474 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
473 let src = self.source(db); 475 let src = self.source(db)?;
474 let full_range = src.value.syntax().text_range(); 476 let full_range = src.value.syntax().text_range();
475 NavigationTarget { 477 Some(NavigationTarget {
476 file_id: src.file_id.original_file(db), 478 file_id: src.file_id.original_file(db),
477 name: self.name(db).to_string().into(), 479 name: self.name(db).to_string().into(),
478 kind: Some(SymbolKind::LifetimeParam), 480 kind: Some(SymbolKind::LifetimeParam),
@@ -481,7 +483,24 @@ impl ToNav for hir::LifetimeParam {
481 container_name: None, 483 container_name: None,
482 description: None, 484 description: None,
483 docs: None, 485 docs: None,
484 } 486 })
487 }
488}
489
490impl TryToNav for hir::ConstParam {
491 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
492 let src = self.source(db)?;
493 let full_range = src.value.syntax().text_range();
494 Some(NavigationTarget {
495 file_id: src.file_id.original_file(db),
496 name: self.name(db).to_string().into(),
497 kind: Some(SymbolKind::ConstParam),
498 full_range,
499 focus_range: src.value.name().map(|n| n.syntax().text_range()),
500 container_name: None,
501 description: None,
502 docs: None,
503 })
485 } 504 }
486} 505}
487 506
diff --git a/crates/ide/src/display/short_label.rs b/crates/ide/src/display/short_label.rs
index ea49d9f97..990f740b8 100644
--- a/crates/ide/src/display/short_label.rs
+++ b/crates/ide/src/display/short_label.rs
@@ -87,6 +87,17 @@ impl ShortLabel for ast::Variant {
87 } 87 }
88} 88}
89 89
90impl ShortLabel for ast::ConstParam {
91 fn short_label(&self) -> Option<String> {
92 let mut buf = "const ".to_owned();
93 buf.push_str(self.name()?.text().as_str());
94 if let Some(type_ref) = self.ty() {
95 format_to!(buf, ": {}", type_ref.syntax());
96 }
97 Some(buf)
98 }
99}
100
90fn short_label_from_ty<T>(node: &T, ty: Option<ast::Type>, prefix: &str) -> Option<String> 101fn short_label_from_ty<T>(node: &T, ty: Option<ast::Type>, prefix: &str) -> Option<String>
91where 102where
92 T: NameOwner + VisibilityOwner, 103 T: NameOwner + VisibilityOwner,
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index e10516f43..de10406bc 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -1,6 +1,6 @@
1//! Resolves and rewrites links in markdown documentation. 1//! Resolves and rewrites links in markdown documentation.
2 2
3use std::{convert::TryFrom, iter::once}; 3use std::{convert::TryFrom, iter::once, ops::Range};
4 4
5use itertools::Itertools; 5use itertools::Itertools;
6use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag}; 6use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
@@ -39,7 +39,7 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Defi
39 if target.contains("://") { 39 if target.contains("://") {
40 (target.to_string(), title.to_string()) 40 (target.to_string(), title.to_string())
41 } else { 41 } else {
42 // Two posibilities: 42 // Two possibilities:
43 // * path-based links: `../../module/struct.MyStruct.html` 43 // * path-based links: `../../module/struct.MyStruct.html`
44 // * module-based links (AKA intra-doc links): `super::super::module::MyStruct` 44 // * module-based links (AKA intra-doc links): `super::super::module::MyStruct`
45 if let Some(rewritten) = rewrite_intra_doc_link(db, *definition, target, title) { 45 if let Some(rewritten) = rewrite_intra_doc_link(db, *definition, target, title) {
@@ -61,6 +61,30 @@ pub(crate) fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Defi
61 out 61 out
62} 62}
63 63
64pub(crate) fn extract_definitions_from_markdown(
65 markdown: &str,
66) -> Vec<(String, Option<hir::Namespace>, Range<usize>)> {
67 let mut res = vec![];
68 let mut cb = |link: BrokenLink| {
69 Some((
70 /*url*/ link.reference.to_owned().into(),
71 /*title*/ link.reference.to_owned().into(),
72 ))
73 };
74 let doc = Parser::new_with_broken_link_callback(markdown, Options::empty(), Some(&mut cb));
75 for (event, range) in doc.into_offset_iter() {
76 match event {
77 Event::Start(Tag::Link(_link_type, ref target, ref title)) => {
78 let link = if target.is_empty() { title } else { target };
79 let (link, ns) = parse_link(link);
80 res.push((link.to_string(), ns, range));
81 }
82 _ => {}
83 }
84 }
85 res
86}
87
64/// Remove all links in markdown documentation. 88/// Remove all links in markdown documentation.
65pub(crate) fn remove_links(markdown: &str) -> String { 89pub(crate) fn remove_links(markdown: &str) -> String {
66 let mut drop_link = false; 90 let mut drop_link = false;
@@ -192,8 +216,7 @@ fn rewrite_intra_doc_link(
192 Definition::Field(it) => it.resolve_doc_path(db, link, ns), 216 Definition::Field(it) => it.resolve_doc_path(db, link, ns),
193 Definition::SelfType(_) 217 Definition::SelfType(_)
194 | Definition::Local(_) 218 | Definition::Local(_)
195 | Definition::TypeParam(_) 219 | Definition::GenericParam(_)
196 | Definition::LifetimeParam(_)
197 | Definition::Label(_) => return None, 220 | Definition::Label(_) => return None,
198 }?; 221 }?;
199 let krate = resolved.module(db)?.krate(); 222 let krate = resolved.module(db)?.krate();
@@ -419,7 +442,7 @@ fn get_symbol_fragment(db: &dyn HirDatabase, field_or_assoc: &FieldOrAssocItem)
419 function.as_assoc_item(db).map(|assoc| assoc.container(db)), 442 function.as_assoc_item(db).map(|assoc| assoc.container(db)),
420 Some(AssocItemContainer::Trait(..)) 443 Some(AssocItemContainer::Trait(..))
421 ); 444 );
422 // This distinction may get more complicated when specialisation is available. 445 // This distinction may get more complicated when specialization is available.
423 // Rustdoc makes this decision based on whether a method 'has defaultness'. 446 // Rustdoc makes this decision based on whether a method 'has defaultness'.
424 // Currently this is only the case for provided trait methods. 447 // Currently this is only the case for provided trait methods.
425 if is_trait_method && !function.has_body(db) { 448 if is_trait_method && !function.has_body(db) {
@@ -463,7 +486,7 @@ mod tests {
463 fn test_doc_url_struct() { 486 fn test_doc_url_struct() {
464 check( 487 check(
465 r#" 488 r#"
466pub struct Fo<|>o; 489pub struct Fo$0o;
467"#, 490"#,
468 expect![[r#"https://docs.rs/test/*/test/struct.Foo.html"#]], 491 expect![[r#"https://docs.rs/test/*/test/struct.Foo.html"#]],
469 ); 492 );
@@ -473,7 +496,7 @@ pub struct Fo<|>o;
473 fn test_doc_url_fn() { 496 fn test_doc_url_fn() {
474 check( 497 check(
475 r#" 498 r#"
476pub fn fo<|>o() {} 499pub fn fo$0o() {}
477"#, 500"#,
478 expect![[r##"https://docs.rs/test/*/test/fn.foo.html#method.foo"##]], 501 expect![[r##"https://docs.rs/test/*/test/fn.foo.html#method.foo"##]],
479 ); 502 );
@@ -486,7 +509,7 @@ pub fn fo<|>o() {}
486pub struct Foo; 509pub struct Foo;
487 510
488impl Foo { 511impl Foo {
489 pub fn met<|>hod() {} 512 pub fn met$0hod() {}
490} 513}
491 514
492"#, 515"#,
@@ -499,7 +522,7 @@ impl Foo {
499 check( 522 check(
500 r#" 523 r#"
501pub trait Bar { 524pub trait Bar {
502 fn met<|>hod() {} 525 fn met$0hod() {}
503} 526}
504 527
505"#, 528"#,
@@ -512,7 +535,7 @@ pub trait Bar {
512 check( 535 check(
513 r#" 536 r#"
514pub trait Foo { 537pub trait Foo {
515 fn met<|>hod(); 538 fn met$0hod();
516} 539}
517 540
518"#, 541"#,
@@ -525,7 +548,7 @@ pub trait Foo {
525 check( 548 check(
526 r#" 549 r#"
527pub struct Foo { 550pub struct Foo {
528 pub fie<|>ld: () 551 pub fie$0ld: ()
529} 552}
530 553
531"#, 554"#,
@@ -538,7 +561,7 @@ pub struct Foo {
538 check( 561 check(
539 r#" 562 r#"
540pub mod foo { 563pub mod foo {
541 pub mod ba<|>r {} 564 pub mod ba$0r {}
542} 565}
543 "#, 566 "#,
544 expect![[r#"https://docs.rs/test/*/test/foo/bar/index.html"#]], 567 expect![[r#"https://docs.rs/test/*/test/foo/bar/index.html"#]],
@@ -563,7 +586,7 @@ pub mod wrapper {
563} 586}
564 587
565fn foo() { 588fn foo() {
566 let bar: wrapper::It<|>em; 589 let bar: wrapper::It$0em;
567} 590}
568 "#, 591 "#,
569 expect![[r#"https://docs.rs/test/*/test/wrapper/module/struct.Item.html"#]], 592 expect![[r#"https://docs.rs/test/*/test/wrapper/module/struct.Item.html"#]],
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 8d75e0f05..ffb3a6f7d 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -144,7 +144,7 @@ macro_rules! foo {
144macro_rules! baz { 144macro_rules! baz {
145 () => { foo!(); } 145 () => { foo!(); }
146} 146}
147f<|>oo!(); 147f$0oo!();
148"#, 148"#,
149 expect![[r#" 149 expect![[r#"
150 foo 150 foo
@@ -165,7 +165,7 @@ macro_rules! foo {
165 } 165 }
166 } 166 }
167} 167}
168f<|>oo!(); 168f$0oo!();
169 "#, 169 "#,
170 expect![[r#" 170 expect![[r#"
171 foo 171 foo
@@ -192,7 +192,7 @@ macro_rules! match_ast {
192} 192}
193 193
194fn main() { 194fn main() {
195 mat<|>ch_ast! { 195 mat$0ch_ast! {
196 match container { 196 match container {
197 ast::TraitDef(it) => {}, 197 ast::TraitDef(it) => {},
198 ast::ImplDef(it) => {}, 198 ast::ImplDef(it) => {},
@@ -226,7 +226,7 @@ macro_rules! match_ast {
226 226
227fn main() { 227fn main() {
228 let p = f(|it| { 228 let p = f(|it| {
229 let res = mat<|>ch_ast! { match c {}}; 229 let res = mat$0ch_ast! { match c {}};
230 Some(res) 230 Some(res)
231 })?; 231 })?;
232} 232}
@@ -250,7 +250,7 @@ macro_rules! foo {
250} 250}
251 251
252fn main() { 252fn main() {
253 let res = fo<|>o!(); 253 let res = fo$0o!();
254} 254}
255"#, 255"#,
256 expect![[r#" 256 expect![[r#"
@@ -272,7 +272,7 @@ macro_rules! foo {
272} 272}
273 273
274fn main() { 274fn main() {
275 let res = fo<|>o!(); 275 let res = fo$0o!();
276} 276}
277"#, 277"#,
278 expect![[r#" 278 expect![[r#"
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index 6f3022dfd..56418c960 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -334,29 +334,29 @@ mod tests {
334 334
335 #[test] 335 #[test]
336 fn test_extend_selection_arith() { 336 fn test_extend_selection_arith() {
337 do_check(r#"fn foo() { <|>1 + 1 }"#, &["1", "1 + 1", "{ 1 + 1 }"]); 337 do_check(r#"fn foo() { $01 + 1 }"#, &["1", "1 + 1", "{ 1 + 1 }"]);
338 } 338 }
339 339
340 #[test] 340 #[test]
341 fn test_extend_selection_list() { 341 fn test_extend_selection_list() {
342 do_check(r#"fn foo(<|>x: i32) {}"#, &["x", "x: i32"]); 342 do_check(r#"fn foo($0x: i32) {}"#, &["x", "x: i32"]);
343 do_check(r#"fn foo(<|>x: i32, y: i32) {}"#, &["x", "x: i32", "x: i32, "]); 343 do_check(r#"fn foo($0x: i32, y: i32) {}"#, &["x", "x: i32", "x: i32, "]);
344 do_check(r#"fn foo(<|>x: i32,y: i32) {}"#, &["x", "x: i32", "x: i32,", "(x: i32,y: i32)"]); 344 do_check(r#"fn foo($0x: i32,y: i32) {}"#, &["x", "x: i32", "x: i32,", "(x: i32,y: i32)"]);
345 do_check(r#"fn foo(x: i32, <|>y: i32) {}"#, &["y", "y: i32", ", y: i32"]); 345 do_check(r#"fn foo(x: i32, $0y: i32) {}"#, &["y", "y: i32", ", y: i32"]);
346 do_check(r#"fn foo(x: i32, <|>y: i32, ) {}"#, &["y", "y: i32", "y: i32, "]); 346 do_check(r#"fn foo(x: i32, $0y: i32, ) {}"#, &["y", "y: i32", "y: i32, "]);
347 do_check(r#"fn foo(x: i32,<|>y: i32) {}"#, &["y", "y: i32", ",y: i32"]); 347 do_check(r#"fn foo(x: i32,$0y: i32) {}"#, &["y", "y: i32", ",y: i32"]);
348 348
349 do_check(r#"const FOO: [usize; 2] = [ 22<|> , 33];"#, &["22", "22 , "]); 349 do_check(r#"const FOO: [usize; 2] = [ 22$0 , 33];"#, &["22", "22 , "]);
350 do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|>];"#, &["33", ", 33"]); 350 do_check(r#"const FOO: [usize; 2] = [ 22 , 33$0];"#, &["33", ", 33"]);
351 do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|> ,];"#, &["33", "33 ,", "[ 22 , 33 ,]"]); 351 do_check(r#"const FOO: [usize; 2] = [ 22 , 33$0 ,];"#, &["33", "33 ,", "[ 22 , 33 ,]"]);
352 352
353 do_check(r#"fn main() { (1, 2<|>) }"#, &["2", ", 2", "(1, 2)"]); 353 do_check(r#"fn main() { (1, 2$0) }"#, &["2", ", 2", "(1, 2)"]);
354 354
355 do_check( 355 do_check(
356 r#" 356 r#"
357const FOO: [usize; 2] = [ 357const FOO: [usize; 2] = [
358 22, 358 22,
359 <|>33, 359 $033,
360]"#, 360]"#,
361 &["33", "33,"], 361 &["33", "33,"],
362 ); 362 );
@@ -365,7 +365,7 @@ const FOO: [usize; 2] = [
365 r#" 365 r#"
366const FOO: [usize; 2] = [ 366const FOO: [usize; 2] = [
367 22 367 22
368 , 33<|>, 368 , 33$0,
369]"#, 369]"#,
370 &["33", "33,"], 370 &["33", "33,"],
371 ); 371 );
@@ -376,7 +376,7 @@ const FOO: [usize; 2] = [
376 do_check( 376 do_check(
377 r#" 377 r#"
378impl S { 378impl S {
379<|> fn foo() { 379$0 fn foo() {
380 380
381 } 381 }
382}"#, 382}"#,
@@ -393,7 +393,7 @@ struct A;
393/// bla 393/// bla
394/// bla 394/// bla
395struct B { 395struct B {
396 <|> 396 $0
397} 397}
398 "#, 398 "#,
399 &["\n \n", "{\n \n}", "/// bla\n/// bla\nstruct B {\n \n}"], 399 &["\n \n", "{\n \n}", "/// bla\n/// bla\nstruct B {\n \n}"],
@@ -407,7 +407,7 @@ struct B {
407fn bar(){} 407fn bar(){}
408 408
409// fn foo() { 409// fn foo() {
410// 1 + <|>1 410// 1 + $01
411// } 411// }
412 412
413// fn foo(){} 413// fn foo(){}
@@ -419,7 +419,7 @@ fn bar(){}
419 r#" 419 r#"
420// #[derive(Debug, Clone, Copy, PartialEq, Eq)] 420// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
421// pub enum Direction { 421// pub enum Direction {
422// <|> Next, 422// $0 Next,
423// Prev 423// Prev
424// } 424// }
425"#, 425"#,
@@ -433,27 +433,27 @@ fn bar(){}
433 r#" 433 r#"
434/* 434/*
435foo 435foo
436_bar1<|>*/ 436_bar1$0*/
437"#, 437"#,
438 &["_bar1", "/*\nfoo\n_bar1*/"], 438 &["_bar1", "/*\nfoo\n_bar1*/"],
439 ); 439 );
440 440
441 do_check(r#"//!<|>foo_2 bar"#, &["foo_2", "//!foo_2 bar"]); 441 do_check(r#"//!$0foo_2 bar"#, &["foo_2", "//!foo_2 bar"]);
442 442
443 do_check(r#"/<|>/foo bar"#, &["//foo bar"]); 443 do_check(r#"/$0/foo bar"#, &["//foo bar"]);
444 } 444 }
445 445
446 #[test] 446 #[test]
447 fn test_extend_selection_prefer_idents() { 447 fn test_extend_selection_prefer_idents() {
448 do_check( 448 do_check(
449 r#" 449 r#"
450fn main() { foo<|>+bar;} 450fn main() { foo$0+bar;}
451"#, 451"#,
452 &["foo", "foo+bar"], 452 &["foo", "foo+bar"],
453 ); 453 );
454 do_check( 454 do_check(
455 r#" 455 r#"
456fn main() { foo+<|>bar;} 456fn main() { foo+$0bar;}
457"#, 457"#,
458 &["bar", "foo+bar"], 458 &["bar", "foo+bar"],
459 ); 459 );
@@ -461,18 +461,18 @@ fn main() { foo+<|>bar;}
461 461
462 #[test] 462 #[test]
463 fn test_extend_selection_prefer_lifetimes() { 463 fn test_extend_selection_prefer_lifetimes() {
464 do_check(r#"fn foo<<|>'a>() {}"#, &["'a", "<'a>"]); 464 do_check(r#"fn foo<$0'a>() {}"#, &["'a", "<'a>"]);
465 do_check(r#"fn foo<'a<|>>() {}"#, &["'a", "<'a>"]); 465 do_check(r#"fn foo<'a$0>() {}"#, &["'a", "<'a>"]);
466 } 466 }
467 467
468 #[test] 468 #[test]
469 fn test_extend_selection_select_first_word() { 469 fn test_extend_selection_select_first_word() {
470 do_check(r#"// foo bar b<|>az quxx"#, &["baz", "// foo bar baz quxx"]); 470 do_check(r#"// foo bar b$0az quxx"#, &["baz", "// foo bar baz quxx"]);
471 do_check( 471 do_check(
472 r#" 472 r#"
473impl S { 473impl S {
474fn foo() { 474fn foo() {
475// hel<|>lo world 475// hel$0lo world
476} 476}
477} 477}
478"#, 478"#,
@@ -486,7 +486,7 @@ fn foo() {
486 r#" 486 r#"
487fn bar(){} 487fn bar(){}
488 488
489" fn f<|>oo() {" 489" fn f$0oo() {"
490"#, 490"#,
491 &["foo", "\" fn foo() {\""], 491 &["foo", "\" fn foo() {\""],
492 ); 492 );
@@ -499,7 +499,7 @@ fn bar(){}
499fn foo<R>() 499fn foo<R>()
500 where 500 where
501 R: req::Request + 'static, 501 R: req::Request + 'static,
502 R::Params: DeserializeOwned<|> + panic::UnwindSafe + 'static, 502 R::Params: DeserializeOwned$0 + panic::UnwindSafe + 'static,
503 R::Result: Serialize + 'static, 503 R::Result: Serialize + 'static,
504"#, 504"#,
505 &[ 505 &[
@@ -510,26 +510,26 @@ fn foo<R>()
510 "R::Params: DeserializeOwned + panic::UnwindSafe + 'static,", 510 "R::Params: DeserializeOwned + panic::UnwindSafe + 'static,",
511 ], 511 ],
512 ); 512 );
513 do_check(r#"fn foo<T>() where T: <|>Copy"#, &["Copy"]); 513 do_check(r#"fn foo<T>() where T: $0Copy"#, &["Copy"]);
514 do_check(r#"fn foo<T>() where T: <|>Copy + Display"#, &["Copy", "Copy + "]); 514 do_check(r#"fn foo<T>() where T: $0Copy + Display"#, &["Copy", "Copy + "]);
515 do_check(r#"fn foo<T>() where T: <|>Copy +Display"#, &["Copy", "Copy +"]); 515 do_check(r#"fn foo<T>() where T: $0Copy +Display"#, &["Copy", "Copy +"]);
516 do_check(r#"fn foo<T>() where T: <|>Copy+Display"#, &["Copy", "Copy+"]); 516 do_check(r#"fn foo<T>() where T: $0Copy+Display"#, &["Copy", "Copy+"]);
517 do_check(r#"fn foo<T>() where T: Copy + <|>Display"#, &["Display", "+ Display"]); 517 do_check(r#"fn foo<T>() where T: Copy + $0Display"#, &["Display", "+ Display"]);
518 do_check(r#"fn foo<T>() where T: Copy + <|>Display + Sync"#, &["Display", "Display + "]); 518 do_check(r#"fn foo<T>() where T: Copy + $0Display + Sync"#, &["Display", "Display + "]);
519 do_check(r#"fn foo<T>() where T: Copy +<|>Display"#, &["Display", "+Display"]); 519 do_check(r#"fn foo<T>() where T: Copy +$0Display"#, &["Display", "+Display"]);
520 } 520 }
521 521
522 #[test] 522 #[test]
523 fn test_extend_trait_bounds_list_inline() { 523 fn test_extend_trait_bounds_list_inline() {
524 do_check(r#"fn foo<T: <|>Copy>() {}"#, &["Copy"]); 524 do_check(r#"fn foo<T: $0Copy>() {}"#, &["Copy"]);
525 do_check(r#"fn foo<T: <|>Copy + Display>() {}"#, &["Copy", "Copy + "]); 525 do_check(r#"fn foo<T: $0Copy + Display>() {}"#, &["Copy", "Copy + "]);
526 do_check(r#"fn foo<T: <|>Copy +Display>() {}"#, &["Copy", "Copy +"]); 526 do_check(r#"fn foo<T: $0Copy +Display>() {}"#, &["Copy", "Copy +"]);
527 do_check(r#"fn foo<T: <|>Copy+Display>() {}"#, &["Copy", "Copy+"]); 527 do_check(r#"fn foo<T: $0Copy+Display>() {}"#, &["Copy", "Copy+"]);
528 do_check(r#"fn foo<T: Copy + <|>Display>() {}"#, &["Display", "+ Display"]); 528 do_check(r#"fn foo<T: Copy + $0Display>() {}"#, &["Display", "+ Display"]);
529 do_check(r#"fn foo<T: Copy + <|>Display + Sync>() {}"#, &["Display", "Display + "]); 529 do_check(r#"fn foo<T: Copy + $0Display + Sync>() {}"#, &["Display", "Display + "]);
530 do_check(r#"fn foo<T: Copy +<|>Display>() {}"#, &["Display", "+Display"]); 530 do_check(r#"fn foo<T: Copy +$0Display>() {}"#, &["Display", "+Display"]);
531 do_check( 531 do_check(
532 r#"fn foo<T: Copy<|> + Display, U: Copy>() {}"#, 532 r#"fn foo<T: Copy$0 + Display, U: Copy>() {}"#,
533 &[ 533 &[
534 "Copy", 534 "Copy",
535 "Copy + ", 535 "Copy + ",
@@ -544,19 +544,19 @@ fn foo<R>()
544 #[test] 544 #[test]
545 fn test_extend_selection_on_tuple_in_type() { 545 fn test_extend_selection_on_tuple_in_type() {
546 do_check( 546 do_check(
547 r#"fn main() { let _: (krate, <|>_crate_def_map, module_id) = (); }"#, 547 r#"fn main() { let _: (krate, $0_crate_def_map, module_id) = (); }"#,
548 &["_crate_def_map", "_crate_def_map, ", "(krate, _crate_def_map, module_id)"], 548 &["_crate_def_map", "_crate_def_map, ", "(krate, _crate_def_map, module_id)"],
549 ); 549 );
550 // white space variations 550 // white space variations
551 do_check( 551 do_check(
552 r#"fn main() { let _: (krate,<|>_crate_def_map,module_id) = (); }"#, 552 r#"fn main() { let _: (krate,$0_crate_def_map,module_id) = (); }"#,
553 &["_crate_def_map", "_crate_def_map,", "(krate,_crate_def_map,module_id)"], 553 &["_crate_def_map", "_crate_def_map,", "(krate,_crate_def_map,module_id)"],
554 ); 554 );
555 do_check( 555 do_check(
556 r#" 556 r#"
557fn main() { let _: ( 557fn main() { let _: (
558 krate, 558 krate,
559 _crate<|>_def_map, 559 _crate$0_def_map,
560 module_id 560 module_id
561) = (); }"#, 561) = (); }"#,
562 &[ 562 &[
@@ -570,19 +570,19 @@ fn main() { let _: (
570 #[test] 570 #[test]
571 fn test_extend_selection_on_tuple_in_rvalue() { 571 fn test_extend_selection_on_tuple_in_rvalue() {
572 do_check( 572 do_check(
573 r#"fn main() { let var = (krate, _crate_def_map<|>, module_id); }"#, 573 r#"fn main() { let var = (krate, _crate_def_map$0, module_id); }"#,
574 &["_crate_def_map", "_crate_def_map, ", "(krate, _crate_def_map, module_id)"], 574 &["_crate_def_map", "_crate_def_map, ", "(krate, _crate_def_map, module_id)"],
575 ); 575 );
576 // white space variations 576 // white space variations
577 do_check( 577 do_check(
578 r#"fn main() { let var = (krate,_crate<|>_def_map,module_id); }"#, 578 r#"fn main() { let var = (krate,_crate$0_def_map,module_id); }"#,
579 &["_crate_def_map", "_crate_def_map,", "(krate,_crate_def_map,module_id)"], 579 &["_crate_def_map", "_crate_def_map,", "(krate,_crate_def_map,module_id)"],
580 ); 580 );
581 do_check( 581 do_check(
582 r#" 582 r#"
583fn main() { let var = ( 583fn main() { let var = (
584 krate, 584 krate,
585 _crate_def_map<|>, 585 _crate_def_map$0,
586 module_id 586 module_id
587); }"#, 587); }"#,
588 &[ 588 &[
@@ -596,19 +596,19 @@ fn main() { let var = (
596 #[test] 596 #[test]
597 fn test_extend_selection_on_tuple_pat() { 597 fn test_extend_selection_on_tuple_pat() {
598 do_check( 598 do_check(
599 r#"fn main() { let (krate, _crate_def_map<|>, module_id) = var; }"#, 599 r#"fn main() { let (krate, _crate_def_map$0, module_id) = var; }"#,
600 &["_crate_def_map", "_crate_def_map, ", "(krate, _crate_def_map, module_id)"], 600 &["_crate_def_map", "_crate_def_map, ", "(krate, _crate_def_map, module_id)"],
601 ); 601 );
602 // white space variations 602 // white space variations
603 do_check( 603 do_check(
604 r#"fn main() { let (krate,_crate<|>_def_map,module_id) = var; }"#, 604 r#"fn main() { let (krate,_crate$0_def_map,module_id) = var; }"#,
605 &["_crate_def_map", "_crate_def_map,", "(krate,_crate_def_map,module_id)"], 605 &["_crate_def_map", "_crate_def_map,", "(krate,_crate_def_map,module_id)"],
606 ); 606 );
607 do_check( 607 do_check(
608 r#" 608 r#"
609fn main() { let ( 609fn main() { let (
610 krate, 610 krate,
611 _crate_def_map<|>, 611 _crate_def_map$0,
612 module_id 612 module_id
613) = var; }"#, 613) = var; }"#,
614 &[ 614 &[
@@ -623,7 +623,7 @@ fn main() { let (
623 fn extend_selection_inside_macros() { 623 fn extend_selection_inside_macros() {
624 do_check( 624 do_check(
625 r#"macro_rules! foo { ($item:item) => {$item} } 625 r#"macro_rules! foo { ($item:item) => {$item} }
626 foo!{fn hello(na<|>me:usize){}}"#, 626 foo!{fn hello(na$0me:usize){}}"#,
627 &[ 627 &[
628 "name", 628 "name",
629 "name:usize", 629 "name:usize",
@@ -640,7 +640,7 @@ fn main() { let (
640 do_check( 640 do_check(
641 r#" macro_rules! foo2 { ($item:item) => {$item} } 641 r#" macro_rules! foo2 { ($item:item) => {$item} }
642 macro_rules! foo { ($item:item) => {foo2!($item);} } 642 macro_rules! foo { ($item:item) => {foo2!($item);} }
643 foo!{fn hello(na<|>me:usize){}}"#, 643 foo!{fn hello(na$0me:usize){}}"#,
644 &[ 644 &[
645 "name", 645 "name",
646 "name:usize", 646 "name:usize",
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs
index eb57f9224..cc8218885 100644
--- a/crates/ide/src/fixture.rs
+++ b/crates/ide/src/fixture.rs
@@ -20,12 +20,12 @@ pub(crate) fn files(ra_fixture: &str) -> (Analysis, Vec<FileId>) {
20 (host.analysis(), change_fixture.files) 20 (host.analysis(), change_fixture.files)
21} 21}
22 22
23/// Creates analysis from a multi-file fixture, returns positions marked with <|>. 23/// Creates analysis from a multi-file fixture, returns positions marked with $0.
24pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) { 24pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
25 let mut host = AnalysisHost::default(); 25 let mut host = AnalysisHost::default();
26 let change_fixture = ChangeFixture::parse(ra_fixture); 26 let change_fixture = ChangeFixture::parse(ra_fixture);
27 host.db.apply_change(change_fixture.change); 27 host.db.apply_change(change_fixture.change);
28 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); 28 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
29 let offset = match range_or_offset { 29 let offset = match range_or_offset {
30 RangeOrOffset::Range(_) => panic!(), 30 RangeOrOffset::Range(_) => panic!(),
31 RangeOrOffset::Offset(it) => it, 31 RangeOrOffset::Offset(it) => it,
@@ -33,12 +33,12 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
33 (host.analysis(), FilePosition { file_id, offset }) 33 (host.analysis(), FilePosition { file_id, offset })
34} 34}
35 35
36/// Creates analysis for a single file, returns range marked with a pair of <|>. 36/// Creates analysis for a single file, returns range marked with a pair of $0.
37pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) { 37pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
38 let mut host = AnalysisHost::default(); 38 let mut host = AnalysisHost::default();
39 let change_fixture = ChangeFixture::parse(ra_fixture); 39 let change_fixture = ChangeFixture::parse(ra_fixture);
40 host.db.apply_change(change_fixture.change); 40 host.db.apply_change(change_fixture.change);
41 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); 41 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
42 let range = match range_or_offset { 42 let range = match range_or_offset {
43 RangeOrOffset::Range(it) => it, 43 RangeOrOffset::Range(it) => it,
44 RangeOrOffset::Offset(_) => panic!(), 44 RangeOrOffset::Offset(_) => panic!(),
@@ -46,12 +46,12 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
46 (host.analysis(), FileRange { file_id, range }) 46 (host.analysis(), FileRange { file_id, range })
47} 47}
48 48
49/// Creates analysis from a multi-file fixture, returns positions marked with <|>. 49/// Creates analysis from a multi-file fixture, returns positions marked with $0.
50pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) { 50pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) {
51 let mut host = AnalysisHost::default(); 51 let mut host = AnalysisHost::default();
52 let change_fixture = ChangeFixture::parse(ra_fixture); 52 let change_fixture = ChangeFixture::parse(ra_fixture);
53 host.db.apply_change(change_fixture.change); 53 host.db.apply_change(change_fixture.change);
54 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); 54 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
55 let offset = match range_or_offset { 55 let offset = match range_or_offset {
56 RangeOrOffset::Range(_) => panic!(), 56 RangeOrOffset::Range(_) => panic!(),
57 RangeOrOffset::Offset(it) => it, 57 RangeOrOffset::Offset(it) => it,
diff --git a/crates/ide/src/fn_references.rs b/crates/ide/src/fn_references.rs
index 5cbbe306e..f6e5a522b 100644
--- a/crates/ide/src/fn_references.rs
+++ b/crates/ide/src/fn_references.rs
@@ -34,7 +34,7 @@ mod tests {
34 fn test_find_all_methods() { 34 fn test_find_all_methods() {
35 let (analysis, pos) = fixture::position( 35 let (analysis, pos) = fixture::position(
36 r#" 36 r#"
37 fn private_fn() {<|>} 37 fn private_fn() {$0}
38 38
39 pub fn pub_fn() {} 39 pub fn pub_fn() {}
40 40
@@ -51,7 +51,7 @@ mod tests {
51 let (analysis, pos) = fixture::position( 51 let (analysis, pos) = fixture::position(
52 r#" 52 r#"
53 trait Foo { 53 trait Foo {
54 fn bar() {<|>} 54 fn bar() {$0}
55 fn baz() {} 55 fn baz() {}
56 } 56 }
57 "#, 57 "#,
@@ -67,7 +67,7 @@ mod tests {
67 r#" 67 r#"
68 //- /lib.rs 68 //- /lib.rs
69 #[test] 69 #[test]
70 fn foo() {<|>} 70 fn foo() {$0}
71 71
72 pub fn pub_fn() {} 72 pub fn pub_fn() {}
73 73
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 912144f8b..cd4afc804 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,14 +1,18 @@
1use either::Either; 1use either::Either;
2use hir::Semantics; 2use hir::{HasAttrs, ModuleDef, Semantics};
3use ide_db::{ 3use ide_db::{
4 base_db::FileId, 4 base_db::FileId,
5 defs::{NameClass, NameRefClass}, 5 defs::{Definition, NameClass, NameRefClass},
6 symbol_index, RootDatabase, 6 symbol_index, RootDatabase,
7}; 7};
8use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 8use syntax::{
9 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T,
10};
9 11
10use crate::{ 12use crate::{
11 display::{ToNav, TryToNav}, 13 display::{ToNav, TryToNav},
14 doc_links::extract_definitions_from_markdown,
15 runnables::doc_owner_to_def,
12 FilePosition, NavigationTarget, RangeInfo, SymbolKind, 16 FilePosition, NavigationTarget, RangeInfo, SymbolKind,
13}; 17};
14 18
@@ -30,6 +34,10 @@ pub(crate) fn goto_definition(
30 let original_token = pick_best(file.token_at_offset(position.offset))?; 34 let original_token = pick_best(file.token_at_offset(position.offset))?;
31 let token = sema.descend_into_macros(original_token.clone()); 35 let token = sema.descend_into_macros(original_token.clone());
32 let parent = token.parent(); 36 let parent = token.parent();
37 if let Some(comment) = ast::Comment::cast(token.clone()) {
38 let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?;
39 return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
40 }
33 41
34 let nav_targets = match_ast! { 42 let nav_targets = match_ast! {
35 match parent { 43 match parent {
@@ -68,11 +76,58 @@ pub(crate) fn goto_definition(
68 Some(RangeInfo::new(original_token.text_range(), nav_targets)) 76 Some(RangeInfo::new(original_token.text_range(), nav_targets))
69} 77}
70 78
79fn def_for_doc_comment(
80 sema: &Semantics<RootDatabase>,
81 position: FilePosition,
82 doc_comment: &ast::Comment,
83) -> Option<hir::ModuleDef> {
84 let parent = doc_comment.syntax().parent();
85 let (link, ns) = extract_positioned_link_from_comment(position, doc_comment)?;
86
87 let def = doc_owner_to_def(sema, parent)?;
88 match def {
89 Definition::ModuleDef(def) => match def {
90 ModuleDef::Module(it) => it.resolve_doc_path(sema.db, &link, ns),
91 ModuleDef::Function(it) => it.resolve_doc_path(sema.db, &link, ns),
92 ModuleDef::Adt(it) => it.resolve_doc_path(sema.db, &link, ns),
93 ModuleDef::Variant(it) => it.resolve_doc_path(sema.db, &link, ns),
94 ModuleDef::Const(it) => it.resolve_doc_path(sema.db, &link, ns),
95 ModuleDef::Static(it) => it.resolve_doc_path(sema.db, &link, ns),
96 ModuleDef::Trait(it) => it.resolve_doc_path(sema.db, &link, ns),
97 ModuleDef::TypeAlias(it) => it.resolve_doc_path(sema.db, &link, ns),
98 ModuleDef::BuiltinType(_) => return None,
99 },
100 Definition::Macro(it) => it.resolve_doc_path(sema.db, &link, ns),
101 Definition::Field(it) => it.resolve_doc_path(sema.db, &link, ns),
102 Definition::SelfType(_)
103 | Definition::Local(_)
104 | Definition::GenericParam(_)
105 | Definition::Label(_) => return None,
106 }
107}
108
109fn extract_positioned_link_from_comment(
110 position: FilePosition,
111 comment: &ast::Comment,
112) -> Option<(String, Option<hir::Namespace>)> {
113 let comment_range = comment.syntax().text_range();
114 let doc_comment = comment.doc_comment()?;
115 let def_links = extract_definitions_from_markdown(doc_comment);
116 let (def_link, ns, _) = def_links.iter().min_by_key(|(_, _, def_link_range)| {
117 let matched_position = comment_range.start() + TextSize::from(def_link_range.start as u32);
118 match position.offset.checked_sub(matched_position) {
119 Some(distance) => distance,
120 None => comment_range.end(),
121 }
122 })?;
123 Some((def_link.to_string(), ns.clone()))
124}
125
71fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 126fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
72 return tokens.max_by_key(priority); 127 return tokens.max_by_key(priority);
73 fn priority(n: &SyntaxToken) -> usize { 128 fn priority(n: &SyntaxToken) -> usize {
74 match n.kind() { 129 match n.kind() {
75 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 2, 130 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | COMMENT => 2,
76 kind if kind.is_trivia() => 0, 131 kind if kind.is_trivia() => 0,
77 _ => 1, 132 _ => 1,
78 } 133 }
@@ -166,7 +221,7 @@ mod tests {
166 check( 221 check(
167 r#" 222 r#"
168 //- /main.rs crate:main deps:std 223 //- /main.rs crate:main deps:std
169 extern crate std<|>; 224 extern crate std$0;
170 //- /std/lib.rs crate:std 225 //- /std/lib.rs crate:std
171 // empty 226 // empty
172 //^ file 227 //^ file
@@ -179,7 +234,7 @@ mod tests {
179 check( 234 check(
180 r#" 235 r#"
181 //- /main.rs crate:main deps:std 236 //- /main.rs crate:main deps:std
182 extern crate std as abc<|>; 237 extern crate std as abc$0;
183 //- /std/lib.rs crate:std 238 //- /std/lib.rs crate:std
184 // empty 239 // empty
185 //^ file 240 //^ file
@@ -193,7 +248,7 @@ mod tests {
193 r#" 248 r#"
194struct Foo; 249struct Foo;
195 //^^^ 250 //^^^
196enum E { X(Foo<|>) } 251enum E { X(Foo$0) }
197"#, 252"#,
198 ); 253 );
199 } 254 }
@@ -204,7 +259,7 @@ enum E { X(Foo<|>) }
204 r#" 259 r#"
205struct Foo; 260struct Foo;
206 //^^^ 261 //^^^
207enum E { X(<|>Foo) } 262enum E { X($0Foo) }
208"#, 263"#,
209 ); 264 );
210 } 265 }
@@ -217,7 +272,7 @@ enum E { X(<|>Foo) }
217use a::Foo; 272use a::Foo;
218mod a; 273mod a;
219mod b; 274mod b;
220enum E { X(Foo<|>) } 275enum E { X(Foo$0) }
221 276
222//- /a.rs 277//- /a.rs
223struct Foo; 278struct Foo;
@@ -233,7 +288,7 @@ struct Foo;
233 check( 288 check(
234 r#" 289 r#"
235//- /lib.rs 290//- /lib.rs
236mod <|>foo; 291mod $0foo;
237 292
238//- /foo.rs 293//- /foo.rs
239// empty 294// empty
@@ -244,7 +299,7 @@ mod <|>foo;
244 check( 299 check(
245 r#" 300 r#"
246//- /lib.rs 301//- /lib.rs
247mod <|>foo; 302mod $0foo;
248 303
249//- /foo/mod.rs 304//- /foo/mod.rs
250// empty 305// empty
@@ -260,7 +315,7 @@ mod <|>foo;
260macro_rules! foo { () => { () } } 315macro_rules! foo { () => { () } }
261 //^^^ 316 //^^^
262fn bar() { 317fn bar() {
263 <|>foo!(); 318 $0foo!();
264} 319}
265"#, 320"#,
266 ); 321 );
@@ -273,7 +328,7 @@ fn bar() {
273//- /lib.rs 328//- /lib.rs
274use foo::foo; 329use foo::foo;
275fn bar() { 330fn bar() {
276 <|>foo!(); 331 $0foo!();
277} 332}
278 333
279//- /foo/lib.rs 334//- /foo/lib.rs
@@ -289,7 +344,7 @@ macro_rules! foo { () => { () } }
289 check( 344 check(
290 r#" 345 r#"
291//- /lib.rs 346//- /lib.rs
292use foo::foo<|>; 347use foo::foo$0;
293 348
294//- /foo/lib.rs 349//- /foo/lib.rs
295#[macro_export] 350#[macro_export]
@@ -312,7 +367,7 @@ define_fn!(foo);
312 //^^^ 367 //^^^
313 368
314fn bar() { 369fn bar() {
315 <|>foo(); 370 $0foo();
316} 371}
317"#, 372"#,
318 ); 373 );
@@ -331,7 +386,7 @@ macro_rules! define_fn {
331//^^^^^^^^^^^^^ 386//^^^^^^^^^^^^^
332 387
333fn bar() { 388fn bar() {
334 <|>foo(); 389 $0foo();
335} 390}
336"#, 391"#,
337 ); 392 );
@@ -347,7 +402,7 @@ macro_rules! foo {() => {0}}
347 402
348fn bar() { 403fn bar() {
349 match (0,1) { 404 match (0,1) {
350 (<|>foo!(), _) => {} 405 ($0foo!(), _) => {}
351 } 406 }
352} 407}
353"#, 408"#,
@@ -363,7 +418,7 @@ macro_rules! foo {() => {0}}
363 //^^^ 418 //^^^
364fn bar() { 419fn bar() {
365 match 0 { 420 match 0 {
366 <|>foo!() => {} 421 $0foo!() => {}
367 } 422 }
368} 423}
369"#, 424"#,
@@ -375,7 +430,7 @@ fn bar() {
375 check( 430 check(
376 r#" 431 r#"
377//- /lib.rs crate:main deps:foo 432//- /lib.rs crate:main deps:foo
378use foo as bar<|>; 433use foo as bar$0;
379 434
380//- /foo/lib.rs crate:foo 435//- /foo/lib.rs crate:foo
381// empty 436// empty
@@ -389,7 +444,7 @@ use foo as bar<|>;
389 check( 444 check(
390 r#" 445 r#"
391//- /lib.rs crate:main deps:foo 446//- /lib.rs crate:main deps:foo
392use foo::foo as bar<|>; 447use foo::foo as bar$0;
393 448
394//- /foo/lib.rs crate:foo 449//- /foo/lib.rs crate:foo
395#[macro_export] 450#[macro_export]
@@ -410,7 +465,7 @@ impl Foo {
410} 465}
411 466
412fn bar(foo: &Foo) { 467fn bar(foo: &Foo) {
413 foo.frobnicate<|>(); 468 foo.frobnicate$0();
414} 469}
415"#, 470"#,
416 ); 471 );
@@ -425,7 +480,7 @@ struct Foo {
425} //^^^^ 480} //^^^^
426 481
427fn bar(foo: &Foo) { 482fn bar(foo: &Foo) {
428 foo.spam<|>; 483 foo.spam$0;
429} 484}
430"#, 485"#,
431 ); 486 );
@@ -442,7 +497,7 @@ struct Foo {
442 497
443fn bar() -> Foo { 498fn bar() -> Foo {
444 Foo { 499 Foo {
445 spam<|>: 0, 500 spam$0: 0,
446 } 501 }
447} 502}
448"#, 503"#,
@@ -459,7 +514,7 @@ struct Foo {
459} //^^^^ 514} //^^^^
460 515
461fn bar(foo: Foo) -> Foo { 516fn bar(foo: Foo) -> Foo {
462 let Foo { spam<|>: _, } = foo 517 let Foo { spam$0: _, } = foo
463} 518}
464"#, 519"#,
465 ); 520 );
@@ -474,7 +529,7 @@ struct Foo { spam: u32 }
474 //^^^^ 529 //^^^^
475 530
476fn bar() -> Foo { 531fn bar() -> Foo {
477 Foo { spam<|>: m!() } 532 Foo { spam$0: m!() }
478} 533}
479", 534",
480 ); 535 );
@@ -489,7 +544,7 @@ struct Foo(u32);
489 544
490fn bar() { 545fn bar() {
491 let foo = Foo(0); 546 let foo = Foo(0);
492 foo.<|>0; 547 foo.$00;
493} 548}
494"#, 549"#,
495 ); 550 );
@@ -505,7 +560,7 @@ impl Foo {
505} //^^^^^^^^^^ 560} //^^^^^^^^^^
506 561
507fn bar(foo: &Foo) { 562fn bar(foo: &Foo) {
508 Foo::frobnicate<|>(); 563 Foo::frobnicate$0();
509} 564}
510"#, 565"#,
511 ); 566 );
@@ -520,7 +575,7 @@ trait Foo {
520} //^^^^^^^^^^ 575} //^^^^^^^^^^
521 576
522fn bar() { 577fn bar() {
523 Foo::frobnicate<|>(); 578 Foo::frobnicate$0();
524} 579}
525"#, 580"#,
526 ); 581 );
@@ -537,7 +592,7 @@ trait Trait {
537impl Trait for Foo {} 592impl Trait for Foo {}
538 593
539fn bar() { 594fn bar() {
540 Foo::frobnicate<|>(); 595 Foo::frobnicate$0();
541} 596}
542"#, 597"#,
543 ); 598 );
@@ -551,7 +606,7 @@ struct Foo;
551impl Foo { 606impl Foo {
552 //^^^ 607 //^^^
553 pub fn new() -> Self { 608 pub fn new() -> Self {
554 Self<|> {} 609 Self$0 {}
555 } 610 }
556} 611}
557"#, 612"#,
@@ -561,7 +616,7 @@ impl Foo {
561struct Foo; 616struct Foo;
562impl Foo { 617impl Foo {
563 //^^^ 618 //^^^
564 pub fn new() -> Self<|> { 619 pub fn new() -> Self$0 {
565 Self {} 620 Self {}
566 } 621 }
567} 622}
@@ -573,7 +628,7 @@ impl Foo {
573enum Foo { A } 628enum Foo { A }
574impl Foo { 629impl Foo {
575 //^^^ 630 //^^^
576 pub fn new() -> Self<|> { 631 pub fn new() -> Self$0 {
577 Foo::A 632 Foo::A
578 } 633 }
579} 634}
@@ -585,7 +640,7 @@ impl Foo {
585enum Foo { A } 640enum Foo { A }
586impl Foo { 641impl Foo {
587 //^^^ 642 //^^^
588 pub fn thing(a: &Self<|>) { 643 pub fn thing(a: &Self$0) {
589 } 644 }
590} 645}
591"#, 646"#,
@@ -603,7 +658,7 @@ trait Make {
603impl Make for Foo { 658impl Make for Foo {
604 //^^^ 659 //^^^
605 fn new() -> Self { 660 fn new() -> Self {
606 Self<|> {} 661 Self$0 {}
607 } 662 }
608} 663}
609"#, 664"#,
@@ -617,7 +672,7 @@ trait Make {
617} 672}
618impl Make for Foo { 673impl Make for Foo {
619 //^^^ 674 //^^^
620 fn new() -> Self<|> { 675 fn new() -> Self$0 {
621 Self {} 676 Self {}
622 } 677 }
623} 678}
@@ -629,7 +684,7 @@ impl Make for Foo {
629 fn goto_def_when_used_on_definition_name_itself() { 684 fn goto_def_when_used_on_definition_name_itself() {
630 check( 685 check(
631 r#" 686 r#"
632struct Foo<|> { value: u32 } 687struct Foo$0 { value: u32 }
633 //^^^ 688 //^^^
634 "#, 689 "#,
635 ); 690 );
@@ -637,21 +692,21 @@ struct Foo<|> { value: u32 }
637 check( 692 check(
638 r#" 693 r#"
639struct Foo { 694struct Foo {
640 field<|>: string, 695 field$0: string,
641} //^^^^^ 696} //^^^^^
642"#, 697"#,
643 ); 698 );
644 699
645 check( 700 check(
646 r#" 701 r#"
647fn foo_test<|>() { } 702fn foo_test$0() { }
648 //^^^^^^^^ 703 //^^^^^^^^
649"#, 704"#,
650 ); 705 );
651 706
652 check( 707 check(
653 r#" 708 r#"
654enum Foo<|> { Variant } 709enum Foo$0 { Variant }
655 //^^^ 710 //^^^
656"#, 711"#,
657 ); 712 );
@@ -660,7 +715,7 @@ enum Foo<|> { Variant }
660 r#" 715 r#"
661enum Foo { 716enum Foo {
662 Variant1, 717 Variant1,
663 Variant2<|>, 718 Variant2$0,
664 //^^^^^^^^ 719 //^^^^^^^^
665 Variant3, 720 Variant3,
666} 721}
@@ -669,35 +724,35 @@ enum Foo {
669 724
670 check( 725 check(
671 r#" 726 r#"
672static INNER<|>: &str = ""; 727static INNER$0: &str = "";
673 //^^^^^ 728 //^^^^^
674"#, 729"#,
675 ); 730 );
676 731
677 check( 732 check(
678 r#" 733 r#"
679const INNER<|>: &str = ""; 734const INNER$0: &str = "";
680 //^^^^^ 735 //^^^^^
681"#, 736"#,
682 ); 737 );
683 738
684 check( 739 check(
685 r#" 740 r#"
686type Thing<|> = Option<()>; 741type Thing$0 = Option<()>;
687 //^^^^^ 742 //^^^^^
688"#, 743"#,
689 ); 744 );
690 745
691 check( 746 check(
692 r#" 747 r#"
693trait Foo<|> { } 748trait Foo$0 { }
694 //^^^ 749 //^^^
695"#, 750"#,
696 ); 751 );
697 752
698 check( 753 check(
699 r#" 754 r#"
700mod bar<|> { } 755mod bar$0 { }
701 //^^^ 756 //^^^
702"#, 757"#,
703 ); 758 );
@@ -714,7 +769,7 @@ fn foo() {}
714 //^^^ 769 //^^^
715id! { 770id! {
716 fn bar() { 771 fn bar() {
717 fo<|>o(); 772 fo$0o();
718 } 773 }
719} 774}
720mod confuse_index { fn foo(); } 775mod confuse_index { fn foo(); }
@@ -743,7 +798,7 @@ pub mod __export {
743fn foo() -> i8 {} 798fn foo() -> i8 {}
744 //^^^ 799 //^^^
745fn test() { 800fn test() {
746 format!("{}", fo<|>o()) 801 format!("{}", fo$0o())
747} 802}
748"#, 803"#,
749 ); 804 );
@@ -761,7 +816,7 @@ macro_rules! include {}
761//^^^^^^^^^^^^^^^^^^^ 816//^^^^^^^^^^^^^^^^^^^
762 817
763fn f() { 818fn f() {
764 foo<|>(); 819 foo$0();
765} 820}
766 821
767mod confuse_index { 822mod confuse_index {
@@ -778,7 +833,7 @@ fn foo() {}
778 fn goto_for_type_param() { 833 fn goto_for_type_param() {
779 check( 834 check(
780 r#" 835 r#"
781struct Foo<T: Clone> { t: <|>T } 836struct Foo<T: Clone> { t: $0T }
782 //^ 837 //^
783"#, 838"#,
784 ); 839 );
@@ -796,7 +851,7 @@ fn foo() {
796 let x = 1; 851 let x = 1;
797 //^ 852 //^
798 id!({ 853 id!({
799 let y = <|>x; 854 let y = $0x;
800 let z = y; 855 let z = y;
801 }); 856 });
802} 857}
@@ -814,7 +869,7 @@ fn foo() {
814 id!({ 869 id!({
815 let y = x; 870 let y = x;
816 //^ 871 //^
817 let z = <|>y; 872 let z = $0y;
818 }); 873 });
819} 874}
820"#, 875"#,
@@ -829,7 +884,7 @@ fn main() {
829 fn foo() { 884 fn foo() {
830 let x = 92; 885 let x = 92;
831 //^ 886 //^
832 <|>x; 887 $0x;
833 } 888 }
834} 889}
835"#, 890"#,
@@ -843,7 +898,7 @@ fn main() {
843fn bar() { 898fn bar() {
844 macro_rules! foo { () => { () } } 899 macro_rules! foo { () => { () } }
845 //^^^ 900 //^^^
846 <|>foo!(); 901 $0foo!();
847} 902}
848"#, 903"#,
849 ); 904 );
@@ -857,7 +912,7 @@ struct Foo { x: i32 }
857fn main() { 912fn main() {
858 let x = 92; 913 let x = 92;
859 //^ 914 //^
860 Foo { x<|> }; 915 Foo { x$0 };
861} 916}
862"#, 917"#,
863 ) 918 )
@@ -872,7 +927,7 @@ enum Foo {
872} //^ 927} //^
873fn baz(foo: Foo) { 928fn baz(foo: Foo) {
874 match foo { 929 match foo {
875 Foo::Bar { x<|> } => x 930 Foo::Bar { x$0 } => x
876 }; 931 };
877} 932}
878"#, 933"#,
@@ -887,7 +942,7 @@ enum Foo { Bar }
887 //^^^ 942 //^^^
888impl Foo { 943impl Foo {
889 fn baz(self) { 944 fn baz(self) {
890 match self { Self::Bar<|> => {} } 945 match self { Self::Bar$0 => {} }
891 } 946 }
892} 947}
893"#, 948"#,
@@ -902,7 +957,7 @@ enum Foo { Bar { val: i32 } }
902 //^^^ 957 //^^^
903impl Foo { 958impl Foo {
904 fn baz(self) -> i32 { 959 fn baz(self) -> i32 {
905 match self { Self::Bar<|> { val } => {} } 960 match self { Self::Bar$0 { val } => {} }
906 } 961 }
907} 962}
908"#, 963"#,
@@ -916,7 +971,7 @@ impl Foo {
916enum Foo { Bar } 971enum Foo { Bar }
917 //^^^ 972 //^^^
918impl Foo { 973impl Foo {
919 fn baz(self) { Self::Bar<|>; } 974 fn baz(self) { Self::Bar$0; }
920} 975}
921"#, 976"#,
922 ); 977 );
@@ -929,7 +984,7 @@ impl Foo {
929enum Foo { Bar { val: i32 } } 984enum Foo { Bar { val: i32 } }
930 //^^^ 985 //^^^
931impl Foo { 986impl Foo {
932 fn baz(self) { Self::Bar<|> {val: 4}; } 987 fn baz(self) { Self::Bar$0 {val: 4}; }
933} 988}
934"#, 989"#,
935 ); 990 );
@@ -939,7 +994,7 @@ impl Foo {
939 fn goto_def_for_type_alias_generic_parameter() { 994 fn goto_def_for_type_alias_generic_parameter() {
940 check( 995 check(
941 r#" 996 r#"
942type Alias<T> = T<|>; 997type Alias<T> = T$0;
943 //^ 998 //^
944"#, 999"#,
945 ) 1000 )
@@ -950,7 +1005,7 @@ type Alias<T> = T<|>;
950 check( 1005 check(
951 r#" 1006 r#"
952//- /lib.rs 1007//- /lib.rs
953foo::module<|>::mac!(); 1008foo::module$0::mac!();
954 1009
955//- /foo/lib.rs 1010//- /foo/lib.rs
956pub mod module { 1011pub mod module {
@@ -972,7 +1027,7 @@ trait Iterator {
972 //^^^^ 1027 //^^^^
973} 1028}
974 1029
975fn f() -> impl Iterator<Item<|> = u8> {} 1030fn f() -> impl Iterator<Item$0 = u8> {}
976"#, 1031"#,
977 ); 1032 );
978 } 1033 }
@@ -987,7 +1042,7 @@ trait Iterator {
987 type B; 1042 type B;
988} 1043}
989 1044
990fn f() -> impl Iterator<A<|> = u8, B = ()> {} 1045fn f() -> impl Iterator<A$0 = u8, B = ()> {}
991"#, 1046"#,
992 ); 1047 );
993 check( 1048 check(
@@ -998,7 +1053,7 @@ trait Iterator {
998 //^ 1053 //^
999} 1054}
1000 1055
1001fn f() -> impl Iterator<A = u8, B<|> = ()> {} 1056fn f() -> impl Iterator<A = u8, B$0 = ()> {}
1002"#, 1057"#,
1003 ); 1058 );
1004 } 1059 }
@@ -1012,7 +1067,7 @@ trait Iterator {
1012 //^^^^ 1067 //^^^^
1013} 1068}
1014 1069
1015fn g() -> <() as Iterator<Item<|> = ()>>::Item {} 1070fn g() -> <() as Iterator<Item$0 = ()>>::Item {}
1016"#, 1071"#,
1017 ); 1072 );
1018 } 1073 }
@@ -1027,7 +1082,7 @@ trait Iterator {
1027 type B; 1082 type B;
1028} 1083}
1029 1084
1030fn g() -> <() as Iterator<A<|> = (), B = u8>>::B {} 1085fn g() -> <() as Iterator<A$0 = (), B = u8>>::B {}
1031"#, 1086"#,
1032 ); 1087 );
1033 check( 1088 check(
@@ -1038,7 +1093,7 @@ trait Iterator {
1038 //^ 1093 //^
1039} 1094}
1040 1095
1041fn g() -> <() as Iterator<A = (), B<|> = u8>>::A {} 1096fn g() -> <() as Iterator<A = (), B$0 = u8>>::A {}
1042"#, 1097"#,
1043 ); 1098 );
1044 } 1099 }
@@ -1052,7 +1107,7 @@ struct Foo {}
1052impl Foo { 1107impl Foo {
1053 fn bar(self: &Foo) { 1108 fn bar(self: &Foo) {
1054 //^^^^ 1109 //^^^^
1055 let foo = sel<|>f; 1110 let foo = sel$0f;
1056 } 1111 }
1057}"#, 1112}"#,
1058 ) 1113 )
@@ -1065,7 +1120,7 @@ impl Foo {
1065struct Foo {} 1120struct Foo {}
1066 1121
1067impl Foo { 1122impl Foo {
1068 fn bar(&self<|>) { 1123 fn bar(&self$0) {
1069 //^^^^ 1124 //^^^^
1070 } 1125 }
1071}"#, 1126}"#,
@@ -1076,7 +1131,7 @@ impl Foo {
1076 fn goto_lifetime_param_on_decl() { 1131 fn goto_lifetime_param_on_decl() {
1077 check( 1132 check(
1078 r#" 1133 r#"
1079fn foo<'foobar<|>>(_: &'foobar ()) { 1134fn foo<'foobar$0>(_: &'foobar ()) {
1080 //^^^^^^^ 1135 //^^^^^^^
1081}"#, 1136}"#,
1082 ) 1137 )
@@ -1086,7 +1141,7 @@ fn foo<'foobar<|>>(_: &'foobar ()) {
1086 fn goto_lifetime_param_decl() { 1141 fn goto_lifetime_param_decl() {
1087 check( 1142 check(
1088 r#" 1143 r#"
1089fn foo<'foobar>(_: &'foobar<|> ()) { 1144fn foo<'foobar>(_: &'foobar$0 ()) {
1090 //^^^^^^^ 1145 //^^^^^^^
1091}"#, 1146}"#,
1092 ) 1147 )
@@ -1097,7 +1152,7 @@ fn foo<'foobar>(_: &'foobar<|> ()) {
1097 check( 1152 check(
1098 r#" 1153 r#"
1099fn foo<'foobar>(_: &'foobar ()) { 1154fn foo<'foobar>(_: &'foobar ()) {
1100 fn foo<'foobar>(_: &'foobar<|> ()) {} 1155 fn foo<'foobar>(_: &'foobar$0 ()) {}
1101 //^^^^^^^ 1156 //^^^^^^^
1102}"#, 1157}"#,
1103 ) 1158 )
@@ -1108,13 +1163,13 @@ fn foo<'foobar>(_: &'foobar ()) {
1108 fn goto_lifetime_hrtb() { 1163 fn goto_lifetime_hrtb() {
1109 check( 1164 check(
1110 r#"trait Foo<T> {} 1165 r#"trait Foo<T> {}
1111fn foo<T>() where for<'a> T: Foo<&'a<|> (u8, u16)>, {} 1166fn foo<T>() where for<'a> T: Foo<&'a$0 (u8, u16)>, {}
1112 //^^ 1167 //^^
1113"#, 1168"#,
1114 ); 1169 );
1115 check( 1170 check(
1116 r#"trait Foo<T> {} 1171 r#"trait Foo<T> {}
1117fn foo<T>() where for<'a<|>> T: Foo<&'a (u8, u16)>, {} 1172fn foo<T>() where for<'a$0> T: Foo<&'a (u8, u16)>, {}
1118 //^^ 1173 //^^
1119"#, 1174"#,
1120 ); 1175 );
@@ -1125,7 +1180,7 @@ fn foo<T>() where for<'a<|>> T: Foo<&'a (u8, u16)>, {}
1125 fn goto_lifetime_hrtb_for_type() { 1180 fn goto_lifetime_hrtb_for_type() {
1126 check( 1181 check(
1127 r#"trait Foo<T> {} 1182 r#"trait Foo<T> {}
1128fn foo<T>() where T: for<'a> Foo<&'a<|> (u8, u16)>, {} 1183fn foo<T>() where T: for<'a> Foo<&'a$0 (u8, u16)>, {}
1129 //^^ 1184 //^^
1130"#, 1185"#,
1131 ); 1186 );
@@ -1139,10 +1194,40 @@ fn foo<'foo>(_: &'foo ()) {
1139 'foo: { 1194 'foo: {
1140 //^^^^ 1195 //^^^^
1141 'bar: loop { 1196 'bar: loop {
1142 break 'foo<|>; 1197 break 'foo$0;
1143 } 1198 }
1144 } 1199 }
1145}"#, 1200}"#,
1146 ) 1201 )
1147 } 1202 }
1203
1204 #[test]
1205 fn goto_def_for_intra_doc_link_same_file() {
1206 check(
1207 r#"
1208/// Blah, [`bar`](bar) .. [`foo`](foo)$0 has [`bar`](bar)
1209pub fn bar() { }
1210
1211/// You might want to see [`std::fs::read()`] too.
1212pub fn foo() { }
1213 //^^^
1214
1215}"#,
1216 )
1217 }
1218
1219 #[test]
1220 fn goto_def_for_intra_doc_link_inner() {
1221 check(
1222 r#"
1223//- /main.rs
1224mod m;
1225struct S;
1226 //^
1227
1228//- /m.rs
1229//! [`super::S$0`]
1230"#,
1231 )
1232 }
1148} 1233}
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 6eac39639..761a98b2c 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -2,7 +2,7 @@ use hir::{Crate, Impl, Semantics};
2use ide_db::RootDatabase; 2use ide_db::RootDatabase;
3use syntax::{algo::find_node_at_offset, ast, AstNode}; 3use syntax::{algo::find_node_at_offset, ast, AstNode};
4 4
5use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 5use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
6 6
7// Feature: Go to Implementation 7// Feature: Go to Implementation
8// 8//
@@ -55,7 +55,7 @@ fn impls_for_def(
55 impls 55 impls
56 .into_iter() 56 .into_iter()
57 .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db))) 57 .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db)))
58 .map(|imp| imp.to_nav(sema.db)) 58 .filter_map(|imp| imp.try_to_nav(sema.db))
59 .collect(), 59 .collect(),
60 ) 60 )
61} 61}
@@ -69,7 +69,7 @@ fn impls_for_trait(
69 69
70 let impls = Impl::for_trait(sema.db, krate, tr); 70 let impls = Impl::for_trait(sema.db, krate, tr);
71 71
72 Some(impls.into_iter().map(|imp| imp.to_nav(sema.db)).collect()) 72 Some(impls.into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect())
73} 73}
74 74
75#[cfg(test)] 75#[cfg(test)]
@@ -107,7 +107,7 @@ mod tests {
107 fn goto_implementation_works() { 107 fn goto_implementation_works() {
108 check( 108 check(
109 r#" 109 r#"
110struct Foo<|>; 110struct Foo$0;
111impl Foo {} 111impl Foo {}
112 //^^^ 112 //^^^
113"#, 113"#,
@@ -118,7 +118,7 @@ impl Foo {}
118 fn goto_implementation_works_multiple_blocks() { 118 fn goto_implementation_works_multiple_blocks() {
119 check( 119 check(
120 r#" 120 r#"
121struct Foo<|>; 121struct Foo$0;
122impl Foo {} 122impl Foo {}
123 //^^^ 123 //^^^
124impl Foo {} 124impl Foo {}
@@ -131,7 +131,7 @@ impl Foo {}
131 fn goto_implementation_works_multiple_mods() { 131 fn goto_implementation_works_multiple_mods() {
132 check( 132 check(
133 r#" 133 r#"
134struct Foo<|>; 134struct Foo$0;
135mod a { 135mod a {
136 impl super::Foo {} 136 impl super::Foo {}
137 //^^^^^^^^^^ 137 //^^^^^^^^^^
@@ -149,7 +149,7 @@ mod b {
149 check( 149 check(
150 r#" 150 r#"
151//- /lib.rs 151//- /lib.rs
152struct Foo<|>; 152struct Foo$0;
153mod a; 153mod a;
154mod b; 154mod b;
155//- /a.rs 155//- /a.rs
@@ -166,7 +166,7 @@ impl crate::Foo {}
166 fn goto_implementation_for_trait() { 166 fn goto_implementation_for_trait() {
167 check( 167 check(
168 r#" 168 r#"
169trait T<|> {} 169trait T$0 {}
170struct Foo; 170struct Foo;
171impl T for Foo {} 171impl T for Foo {}
172 //^^^ 172 //^^^
@@ -179,7 +179,7 @@ impl T for Foo {}
179 check( 179 check(
180 r#" 180 r#"
181//- /lib.rs 181//- /lib.rs
182trait T<|> {}; 182trait T$0 {};
183struct Foo; 183struct Foo;
184mod a; 184mod a;
185mod b; 185mod b;
@@ -199,7 +199,7 @@ impl crate::T for crate::Foo {}
199 r#" 199 r#"
200//- /lib.rs 200//- /lib.rs
201trait T {} 201trait T {}
202struct Foo<|>; 202struct Foo$0;
203impl Foo {} 203impl Foo {}
204 //^^^ 204 //^^^
205impl T for Foo {} 205impl T for Foo {}
@@ -216,7 +216,7 @@ impl T for &Foo {}
216 r#" 216 r#"
217 #[derive(Copy)] 217 #[derive(Copy)]
218//^^^^^^^^^^^^^^^ 218//^^^^^^^^^^^^^^^
219struct Foo<|>; 219struct Foo$0;
220 220
221mod marker { 221mod marker {
222 trait Copy {} 222 trait Copy {}
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index aba6bf5dc..369a59820 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -1,7 +1,7 @@
1use ide_db::RootDatabase; 1use ide_db::RootDatabase;
2use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 2use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
3 3
4use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 4use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
5 5
6// Feature: Go to Type Definition 6// Feature: Go to Type Definition
7// 7//
@@ -37,7 +37,7 @@ pub(crate) fn goto_type_definition(
37 37
38 let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?; 38 let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?;
39 39
40 let nav = adt_def.to_nav(db); 40 let nav = adt_def.try_to_nav(db)?;
41 Some(RangeInfo::new(node.text_range(), vec![nav])) 41 Some(RangeInfo::new(node.text_range(), vec![nav]))
42} 42}
43 43
@@ -76,7 +76,7 @@ mod tests {
76struct Foo; 76struct Foo;
77 //^^^ 77 //^^^
78fn foo() { 78fn foo() {
79 let f: Foo; f<|> 79 let f: Foo; f$0
80} 80}
81"#, 81"#,
82 ); 82 );
@@ -89,7 +89,7 @@ fn foo() {
89struct Foo; 89struct Foo;
90 //^^^ 90 //^^^
91fn foo() { 91fn foo() {
92 let f: &Foo; f<|> 92 let f: &Foo; f$0
93} 93}
94"#, 94"#,
95 ); 95 );
@@ -103,7 +103,7 @@ macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
103struct Foo {} 103struct Foo {}
104 //^^^ 104 //^^^
105id! { 105id! {
106 fn bar() { let f<|> = Foo {}; } 106 fn bar() { let f$0 = Foo {}; }
107} 107}
108"#, 108"#,
109 ); 109 );
@@ -115,7 +115,7 @@ id! {
115 r#" 115 r#"
116struct Foo; 116struct Foo;
117 //^^^ 117 //^^^
118fn foo(<|>f: Foo) {} 118fn foo($0f: Foo) {}
119"#, 119"#,
120 ); 120 );
121 } 121 }
@@ -129,7 +129,7 @@ struct Foo;
129struct Bar(Foo); 129struct Bar(Foo);
130fn foo() { 130fn foo() {
131 let bar = Bar(Foo); 131 let bar = Bar(Foo);
132 bar.<|>0; 132 bar.$00;
133} 133}
134"#, 134"#,
135 ); 135 );
@@ -142,7 +142,7 @@ fn foo() {
142struct Foo; 142struct Foo;
143 //^^^ 143 //^^^
144impl Foo { 144impl Foo {
145 fn f(&self<|>) {} 145 fn f(&self$0) {}
146} 146}
147"#, 147"#,
148 ) 148 )
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 73245fbe7..317b6f011 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1,6 +1,6 @@
1use hir::{ 1use hir::{
2 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasAttrs, HasSource, HirDisplay, Module, 2 Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource,
3 ModuleDef, ModuleSource, Semantics, 3 HirDisplay, Module, ModuleDef, ModuleSource, Semantics,
4}; 4};
5use ide_db::base_db::SourceDatabase; 5use ide_db::base_db::SourceDatabase;
6use ide_db::{ 6use ide_db::{
@@ -13,11 +13,11 @@ use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset,
13use test_utils::mark; 13use test_utils::mark;
14 14
15use crate::{ 15use crate::{
16 display::{macro_label, ShortLabel, ToNav, TryToNav}, 16 display::{macro_label, ShortLabel, TryToNav},
17 doc_links::{remove_links, rewrite_links}, 17 doc_links::{remove_links, rewrite_links},
18 markdown_remove::remove_markdown, 18 markdown_remove::remove_markdown,
19 markup::Markup, 19 markup::Markup,
20 runnables::runnable, 20 runnables::{runnable_fn, runnable_mod},
21 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, 21 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
22}; 22};
23 23
@@ -31,19 +31,6 @@ pub struct HoverConfig {
31 pub markdown: bool, 31 pub markdown: bool,
32} 32}
33 33
34impl Default for HoverConfig {
35 fn default() -> Self {
36 Self {
37 implementations: true,
38 run: true,
39 debug: true,
40 goto_type_def: true,
41 links_in_hover: true,
42 markdown: true,
43 }
44 }
45}
46
47impl HoverConfig { 34impl HoverConfig {
48 pub const NO_ACTIONS: Self = Self { 35 pub const NO_ACTIONS: Self = Self {
49 implementations: false, 36 implementations: false,
@@ -70,7 +57,7 @@ impl HoverConfig {
70#[derive(Debug, Clone)] 57#[derive(Debug, Clone)]
71pub enum HoverAction { 58pub enum HoverAction {
72 Runnable(Runnable), 59 Runnable(Runnable),
73 Implementaion(FilePosition), 60 Implementation(FilePosition),
74 GoToType(Vec<HoverGotoTypeData>), 61 GoToType(Vec<HoverGotoTypeData>),
75} 62}
76 63
@@ -109,17 +96,20 @@ pub(crate) fn hover(
109 match node { 96 match node {
110 ast::Name(name) => NameClass::classify(&sema, &name).and_then(|d| d.defined(sema.db)), 97 ast::Name(name) => NameClass::classify(&sema, &name).and_then(|d| d.defined(sema.db)),
111 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), 98 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
99 ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime)
100 .map_or_else(|| NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced(sema.db)), |d| d.defined(sema.db)),
112 _ => None, 101 _ => None,
113 } 102 }
114 }; 103 };
115 if let Some(definition) = definition { 104 if let Some(definition) = definition {
116 if let Some(markup) = hover_for_definition(db, definition) { 105 if let Some(markup) = hover_for_definition(db, definition) {
106 let markup = markup.as_str();
117 let markup = if !markdown { 107 let markup = if !markdown {
118 remove_markdown(&markup.as_str()) 108 remove_markdown(markup)
119 } else if links_in_hover { 109 } else if links_in_hover {
120 rewrite_links(db, &markup.as_str(), &definition) 110 rewrite_links(db, markup, &definition)
121 } else { 111 } else {
122 remove_links(&markup.as_str()) 112 remove_links(markup)
123 }; 113 };
124 res.markup = Markup::from(markup); 114 res.markup = Markup::from(markup);
125 if let Some(action) = show_implementations_action(db, definition) { 115 if let Some(action) = show_implementations_action(db, definition) {
@@ -173,22 +163,19 @@ pub(crate) fn hover(
173 163
174fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 164fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
175 fn to_action(nav_target: NavigationTarget) -> HoverAction { 165 fn to_action(nav_target: NavigationTarget) -> HoverAction {
176 HoverAction::Implementaion(FilePosition { 166 HoverAction::Implementation(FilePosition {
177 file_id: nav_target.file_id, 167 file_id: nav_target.file_id,
178 offset: nav_target.focus_or_full_range().start(), 168 offset: nav_target.focus_or_full_range().start(),
179 }) 169 })
180 } 170 }
181 171
182 match def { 172 let adt = match def {
183 Definition::ModuleDef(it) => match it { 173 Definition::ModuleDef(ModuleDef::Trait(it)) => return it.try_to_nav(db).map(to_action),
184 ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))), 174 Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it),
185 ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))), 175 Definition::SelfType(it) => it.target_ty(db).as_adt(),
186 ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))),
187 ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))),
188 _ => None,
189 },
190 _ => None, 176 _ => None,
191 } 177 }?;
178 adt.try_to_nav(db).map(to_action)
192} 179}
193 180
194fn runnable_action( 181fn runnable_action(
@@ -199,21 +186,20 @@ fn runnable_action(
199 match def { 186 match def {
200 Definition::ModuleDef(it) => match it { 187 Definition::ModuleDef(it) => match it {
201 ModuleDef::Module(it) => match it.definition_source(sema.db).value { 188 ModuleDef::Module(it) => match it.definition_source(sema.db).value {
202 ModuleSource::Module(it) => runnable(&sema, it.syntax().clone(), file_id) 189 ModuleSource::Module(it) => {
203 .map(|it| HoverAction::Runnable(it)), 190 runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it))
191 }
204 _ => None, 192 _ => None,
205 }, 193 },
206 ModuleDef::Function(it) => { 194 ModuleDef::Function(func) => {
207 let src = it.source(sema.db); 195 let src = func.source(sema.db)?;
208 if src.file_id != file_id.into() { 196 if src.file_id != file_id.into() {
209 mark::hit!(hover_macro_generated_struct_fn_doc_comment); 197 mark::hit!(hover_macro_generated_struct_fn_doc_comment);
210 mark::hit!(hover_macro_generated_struct_fn_doc_attr); 198 mark::hit!(hover_macro_generated_struct_fn_doc_attr);
211
212 return None; 199 return None;
213 } 200 }
214 201
215 runnable(&sema, src.value.syntax().clone(), file_id) 202 runnable_fn(&sema, func).map(HoverAction::Runnable)
216 .map(|it| HoverAction::Runnable(it))
217 } 203 }
218 _ => None, 204 _ => None,
219 }, 205 },
@@ -222,45 +208,46 @@ fn runnable_action(
222} 208}
223 209
224fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 210fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
225 match def { 211 let mut targets: Vec<ModuleDef> = Vec::new();
226 Definition::Local(it) => { 212 let mut push_new_def = |item: ModuleDef| {
227 let mut targets: Vec<ModuleDef> = Vec::new(); 213 if !targets.contains(&item) {
228 let mut push_new_def = |item: ModuleDef| { 214 targets.push(item);
229 if !targets.contains(&item) {
230 targets.push(item);
231 }
232 };
233
234 it.ty(db).walk(db, |t| {
235 if let Some(adt) = t.as_adt() {
236 push_new_def(adt.into());
237 } else if let Some(trait_) = t.as_dyn_trait() {
238 push_new_def(trait_.into());
239 } else if let Some(traits) = t.as_impl_traits(db) {
240 traits.into_iter().for_each(|it| push_new_def(it.into()));
241 } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
242 push_new_def(trait_.into());
243 }
244 });
245
246 let targets = targets
247 .into_iter()
248 .filter_map(|it| {
249 Some(HoverGotoTypeData {
250 mod_path: render_path(
251 db,
252 it.module(db)?,
253 it.name(db).map(|name| name.to_string()),
254 ),
255 nav: it.try_to_nav(db)?,
256 })
257 })
258 .collect();
259
260 Some(HoverAction::GoToType(targets))
261 } 215 }
262 _ => None, 216 };
217
218 if let Definition::GenericParam(GenericParam::TypeParam(it)) = def {
219 it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
220 } else {
221 let ty = match def {
222 Definition::Local(it) => it.ty(db),
223 Definition::GenericParam(GenericParam::ConstParam(it)) => it.ty(db),
224 _ => return None,
225 };
226
227 ty.walk(db, |t| {
228 if let Some(adt) = t.as_adt() {
229 push_new_def(adt.into());
230 } else if let Some(trait_) = t.as_dyn_trait() {
231 push_new_def(trait_.into());
232 } else if let Some(traits) = t.as_impl_traits(db) {
233 traits.into_iter().for_each(|it| push_new_def(it.into()));
234 } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
235 push_new_def(trait_.into());
236 }
237 });
263 } 238 }
239
240 let targets = targets
241 .into_iter()
242 .filter_map(|it| {
243 Some(HoverGotoTypeData {
244 mod_path: render_path(db, it.module(db)?, it.name(db).map(|name| name.to_string())),
245 nav: it.try_to_nav(db)?,
246 })
247 })
248 .collect();
249
250 Some(HoverAction::GoToType(targets))
264} 251}
265 252
266fn hover_markup( 253fn hover_markup(
@@ -324,17 +311,11 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
324 let mod_path = definition_mod_path(db, &def); 311 let mod_path = definition_mod_path(db, &def);
325 return match def { 312 return match def {
326 Definition::Macro(it) => { 313 Definition::Macro(it) => {
327 // FIXME: Currently proc-macro do not have ast-node, 314 let label = macro_label(&it.source(db)?.value);
328 // such that it does not have source
329 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
330 if it.is_proc_macro() {
331 return None;
332 }
333 let label = macro_label(&it.source(db).value);
334 from_def_source_labeled(db, it, Some(label), mod_path) 315 from_def_source_labeled(db, it, Some(label), mod_path)
335 } 316 }
336 Definition::Field(def) => { 317 Definition::Field(def) => {
337 let src = def.source(db).value; 318 let src = def.source(db)?.value;
338 if let FieldSource::Named(it) = src { 319 if let FieldSource::Named(it) = src {
339 from_def_source_labeled(db, def, it.short_label(), mod_path) 320 from_def_source_labeled(db, def, it.short_label(), mod_path)
340 } else { 321 } else {
@@ -360,9 +341,9 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
360 ModuleDef::Static(it) => from_def_source(db, it, mod_path), 341 ModuleDef::Static(it) => from_def_source(db, it, mod_path),
361 ModuleDef::Trait(it) => from_def_source(db, it, mod_path), 342 ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
362 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), 343 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
363 ModuleDef::BuiltinType(it) => return Some(it.to_string().into()), 344 ModuleDef::BuiltinType(it) => Some(Markup::fenced_block(&it)),
364 }, 345 },
365 Definition::Local(it) => return Some(Markup::fenced_block(&it.ty(db).display(db))), 346 Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))),
366 Definition::SelfType(impl_def) => { 347 Definition::SelfType(impl_def) => {
367 impl_def.target_ty(db).as_adt().and_then(|adt| match adt { 348 impl_def.target_ty(db).as_adt().and_then(|adt| match adt {
368 Adt::Struct(it) => from_def_source(db, it, mod_path), 349 Adt::Struct(it) => from_def_source(db, it, mod_path),
@@ -370,10 +351,12 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
370 Adt::Enum(it) => from_def_source(db, it, mod_path), 351 Adt::Enum(it) => from_def_source(db, it, mod_path),
371 }) 352 })
372 } 353 }
373 Definition::TypeParam(_) | Definition::LifetimeParam(_) | Definition::Label(_) => { 354 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
374 // FIXME: Hover for generic param 355 Definition::GenericParam(it) => match it {
375 None 356 GenericParam::TypeParam(it) => Some(Markup::fenced_block(&it.display(db))),
376 } 357 GenericParam::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))),
358 GenericParam::ConstParam(it) => from_def_source(db, it, None),
359 },
377 }; 360 };
378 361
379 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> 362 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
@@ -381,7 +364,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
381 D: HasSource<Ast = A> + HasAttrs + Copy, 364 D: HasSource<Ast = A> + HasAttrs + Copy,
382 A: ShortLabel, 365 A: ShortLabel,
383 { 366 {
384 let short_label = def.source(db).value.short_label(); 367 let short_label = def.source(db)?.value.short_label();
385 from_def_source_labeled(db, def, short_label, mod_path) 368 from_def_source_labeled(db, def, short_label, mod_path)
386 } 369 }
387 370
@@ -403,7 +386,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
403 return tokens.max_by_key(priority); 386 return tokens.max_by_key(priority);
404 fn priority(n: &SyntaxToken) -> usize { 387 fn priority(n: &SyntaxToken) -> usize {
405 match n.kind() { 388 match n.kind() {
406 IDENT | INT_NUMBER => 3, 389 IDENT | INT_NUMBER | LIFETIME_IDENT => 3,
407 T!['('] | T![')'] => 2, 390 T!['('] | T![')'] => 2,
408 kind if kind.is_trivia() => 0, 391 kind if kind.is_trivia() => 0,
409 _ => 1, 392 _ => 1,
@@ -471,7 +454,7 @@ mod tests {
471pub fn foo() -> u32 { 1 } 454pub fn foo() -> u32 { 1 }
472 455
473fn main() { 456fn main() {
474 let foo_test = foo()<|>; 457 let foo_test = foo()$0;
475} 458}
476"#, 459"#,
477 expect![[r#" 460 expect![[r#"
@@ -490,7 +473,7 @@ fn main() {
490pub fn foo() -> u32 { 1 } 473pub fn foo() -> u32 { 1 }
491 474
492fn main() { 475fn main() {
493 let foo_test = foo()<|>; 476 let foo_test = foo()$0;
494} 477}
495"#, 478"#,
496 expect![[r#" 479 expect![[r#"
@@ -520,7 +503,7 @@ fn main() {
520 Option::Some(*memo + value) 503 Option::Some(*memo + value)
521 }; 504 };
522 let number = 5u32; 505 let number = 5u32;
523 let mut iter<|> = scan(OtherStruct { i: num }, closure, number); 506 let mut iter$0 = scan(OtherStruct { i: num }, closure, number);
524} 507}
525"#, 508"#,
526 expect![[r#" 509 expect![[r#"
@@ -540,7 +523,7 @@ fn main() {
540 r#" 523 r#"
541pub fn foo() -> u32 { 1 } 524pub fn foo() -> u32 { 1 }
542 525
543fn main() { let foo_test = fo<|>o(); } 526fn main() { let foo_test = fo$0o(); }
544"#, 527"#,
545 expect![[r#" 528 expect![[r#"
546 *foo* 529 *foo*
@@ -572,7 +555,7 @@ mod a;
572mod b; 555mod b;
573mod c; 556mod c;
574 557
575fn main() { let foo_test = fo<|>o(); } 558fn main() { let foo_test = fo$0o(); }
576 "#, 559 "#,
577 expect![[r#" 560 expect![[r#"
578 *foo* 561 *foo*
@@ -589,7 +572,7 @@ fn main() { let foo_test = fo<|>o(); }
589 r#" 572 r#"
590pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { } 573pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
591 574
592fn main() { let foo_test = fo<|>o(); } 575fn main() { let foo_test = fo$0o(); }
593 "#, 576 "#,
594 expect![[r#" 577 expect![[r#"
595 *foo* 578 *foo*
@@ -609,7 +592,7 @@ fn main() { let foo_test = fo<|>o(); }
609 fn hover_shows_fn_signature_on_fn_name() { 592 fn hover_shows_fn_signature_on_fn_name() {
610 check( 593 check(
611 r#" 594 r#"
612pub fn foo<|>(a: u32, b: u32) -> u32 {} 595pub fn foo$0(a: u32, b: u32) -> u32 {}
613 596
614fn main() { } 597fn main() { }
615"#, 598"#,
@@ -637,7 +620,7 @@ fn main() { }
637/// # 620/// #
638/// foo(Path::new("hello, world!")) 621/// foo(Path::new("hello, world!"))
639/// ``` 622/// ```
640pub fn foo<|>(_: &Path) {} 623pub fn foo$0(_: &Path) {}
641 624
642fn main() { } 625fn main() { }
643"#, 626"#,
@@ -670,7 +653,7 @@ fn main() { }
670 check( 653 check(
671 r##" 654 r##"
672#[doc = r#"Raw string doc attr"#] 655#[doc = r#"Raw string doc attr"#]
673pub fn foo<|>(_: &Path) {} 656pub fn foo$0(_: &Path) {}
674 657
675fn main() { } 658fn main() { }
676"##, 659"##,
@@ -700,7 +683,7 @@ fn main() { }
700struct Foo { field_a: u32 } 683struct Foo { field_a: u32 }
701 684
702fn main() { 685fn main() {
703 let foo = Foo { field_a<|>: 0, }; 686 let foo = Foo { field_a$0: 0, };
704} 687}
705"#, 688"#,
706 expect![[r#" 689 expect![[r#"
@@ -719,7 +702,7 @@ fn main() {
719 // Hovering over the field in the definition 702 // Hovering over the field in the definition
720 check( 703 check(
721 r#" 704 r#"
722struct Foo { field_a<|>: u32 } 705struct Foo { field_a$0: u32 }
723 706
724fn main() { 707fn main() {
725 let foo = Foo { field_a: 0 }; 708 let foo = Foo { field_a: 0 };
@@ -742,7 +725,7 @@ fn main() {
742 #[test] 725 #[test]
743 fn hover_const_static() { 726 fn hover_const_static() {
744 check( 727 check(
745 r#"const foo<|>: u32 = 123;"#, 728 r#"const foo$0: u32 = 123;"#,
746 expect![[r#" 729 expect![[r#"
747 *foo* 730 *foo*
748 731
@@ -756,7 +739,7 @@ fn main() {
756 "#]], 739 "#]],
757 ); 740 );
758 check( 741 check(
759 r#"static foo<|>: u32 = 456;"#, 742 r#"static foo$0: u32 = 456;"#,
760 expect![[r#" 743 expect![[r#"
761 *foo* 744 *foo*
762 745
@@ -778,7 +761,7 @@ fn main() {
778struct Test<K, T = u8> { k: K, t: T } 761struct Test<K, T = u8> { k: K, t: T }
779 762
780fn main() { 763fn main() {
781 let zz<|> = Test { t: 23u8, k: 33 }; 764 let zz$0 = Test { t: 23u8, k: 33 };
782}"#, 765}"#,
783 expect![[r#" 766 expect![[r#"
784 *zz* 767 *zz*
@@ -797,7 +780,7 @@ fn main() {
797enum Option<T> { Some(T) } 780enum Option<T> { Some(T) }
798use Option::Some; 781use Option::Some;
799 782
800fn main() { So<|>me(12); } 783fn main() { So$0me(12); }
801"#, 784"#,
802 expect![[r#" 785 expect![[r#"
803 *Some* 786 *Some*
@@ -817,7 +800,7 @@ fn main() { So<|>me(12); }
817enum Option<T> { Some(T) } 800enum Option<T> { Some(T) }
818use Option::Some; 801use Option::Some;
819 802
820fn main() { let b<|>ar = Some(12); } 803fn main() { let b$0ar = Some(12); }
821"#, 804"#,
822 expect![[r#" 805 expect![[r#"
823 *bar* 806 *bar*
@@ -835,7 +818,7 @@ fn main() { let b<|>ar = Some(12); }
835 r#" 818 r#"
836enum Option<T> { 819enum Option<T> {
837 /// The None variant 820 /// The None variant
838 Non<|>e 821 Non$0e
839} 822}
840"#, 823"#,
841 expect![[r#" 824 expect![[r#"
@@ -862,7 +845,7 @@ enum Option<T> {
862 Some(T) 845 Some(T)
863} 846}
864fn main() { 847fn main() {
865 let s = Option::Som<|>e(12); 848 let s = Option::Som$0e(12);
866} 849}
867"#, 850"#,
868 expect![[r#" 851 expect![[r#"
@@ -886,7 +869,7 @@ fn main() {
886 #[test] 869 #[test]
887 fn hover_for_local_variable() { 870 fn hover_for_local_variable() {
888 check( 871 check(
889 r#"fn func(foo: i32) { fo<|>o; }"#, 872 r#"fn func(foo: i32) { fo$0o; }"#,
890 expect![[r#" 873 expect![[r#"
891 *foo* 874 *foo*
892 875
@@ -900,7 +883,7 @@ fn main() {
900 #[test] 883 #[test]
901 fn hover_for_local_variable_pat() { 884 fn hover_for_local_variable_pat() {
902 check( 885 check(
903 r#"fn func(fo<|>o: i32) {}"#, 886 r#"fn func(fo$0o: i32) {}"#,
904 expect![[r#" 887 expect![[r#"
905 *foo* 888 *foo*
906 889
@@ -914,7 +897,7 @@ fn main() {
914 #[test] 897 #[test]
915 fn hover_local_var_edge() { 898 fn hover_local_var_edge() {
916 check( 899 check(
917 r#"fn func(foo: i32) { if true { <|>foo; }; }"#, 900 r#"fn func(foo: i32) { if true { $0foo; }; }"#,
918 expect![[r#" 901 expect![[r#"
919 *foo* 902 *foo*
920 903
@@ -928,7 +911,7 @@ fn main() {
928 #[test] 911 #[test]
929 fn hover_for_param_edge() { 912 fn hover_for_param_edge() {
930 check( 913 check(
931 r#"fn func(<|>foo: i32) {}"#, 914 r#"fn func($0foo: i32) {}"#,
932 expect![[r#" 915 expect![[r#"
933 *foo* 916 *foo*
934 917
@@ -948,7 +931,7 @@ fn main() {
948 trait DerefMut { 931 trait DerefMut {
949 type Target: ?Sized; 932 type Target: ?Sized;
950 } 933 }
951 fn f(_x<|>: impl Deref<Target=u8> + DerefMut<Target=u8>) {}"#, 934 fn f(_x$0: impl Deref<Target=u8> + DerefMut<Target=u8>) {}"#,
952 expect![[r#" 935 expect![[r#"
953 *_x* 936 *_x*
954 937
@@ -969,7 +952,7 @@ impl Thing {
969 fn new() -> Thing { Thing { x: 0 } } 952 fn new() -> Thing { Thing { x: 0 } }
970} 953}
971 954
972fn main() { let foo_<|>test = Thing::new(); } 955fn main() { let foo_$0test = Thing::new(); }
973 "#, 956 "#,
974 expect![[r#" 957 expect![[r#"
975 *foo_test* 958 *foo_test*
@@ -993,7 +976,7 @@ mod wrapper {
993 } 976 }
994} 977}
995 978
996fn main() { let foo_test = wrapper::Thing::new<|>(); } 979fn main() { let foo_test = wrapper::Thing::new$0(); }
997"#, 980"#,
998 expect![[r#" 981 expect![[r#"
999 *new* 982 *new*
@@ -1020,7 +1003,7 @@ impl X {
1020 1003
1021fn main() { 1004fn main() {
1022 match 1 { 1005 match 1 {
1023 X::C<|> => {}, 1006 X::C$0 => {},
1024 2 => {}, 1007 2 => {},
1025 _ => {} 1008 _ => {}
1026 }; 1009 };
@@ -1046,7 +1029,7 @@ fn main() {
1046 r#" 1029 r#"
1047struct Thing { x: u32 } 1030struct Thing { x: u32 }
1048impl Thing { 1031impl Thing {
1049 fn new() -> Self { Self<|> { x: 0 } } 1032 fn new() -> Self { Self$0 { x: 0 } }
1050} 1033}
1051"#, 1034"#,
1052 expect![[r#" 1035 expect![[r#"
@@ -1065,7 +1048,7 @@ impl Thing {
1065 r#" 1048 r#"
1066struct Thing { x: u32 } 1049struct Thing { x: u32 }
1067impl Thing { 1050impl Thing {
1068 fn new() -> Self<|> { Self { x: 0 } } 1051 fn new() -> Self$0 { Self { x: 0 } }
1069} 1052}
1070"#, 1053"#,
1071 expect![[r#" 1054 expect![[r#"
@@ -1084,7 +1067,7 @@ impl Thing {
1084 r#" 1067 r#"
1085enum Thing { A } 1068enum Thing { A }
1086impl Thing { 1069impl Thing {
1087 pub fn new() -> Self<|> { Thing::A } 1070 pub fn new() -> Self$0 { Thing::A }
1088} 1071}
1089"#, 1072"#,
1090 expect![[r#" 1073 expect![[r#"
@@ -1103,7 +1086,7 @@ impl Thing {
1103 r#" 1086 r#"
1104 enum Thing { A } 1087 enum Thing { A }
1105 impl Thing { 1088 impl Thing {
1106 pub fn thing(a: Self<|>) {} 1089 pub fn thing(a: Self$0) {}
1107 } 1090 }
1108 "#, 1091 "#,
1109 expect![[r#" 1092 expect![[r#"
@@ -1128,7 +1111,7 @@ fn x() {}
1128 1111
1129fn y() { 1112fn y() {
1130 let x = 0i32; 1113 let x = 0i32;
1131 x<|>; 1114 x$0;
1132} 1115}
1133"#, 1116"#,
1134 expect![[r#" 1117 expect![[r#"
@@ -1147,7 +1130,7 @@ fn y() {
1147 r#" 1130 r#"
1148macro_rules! foo { () => {} } 1131macro_rules! foo { () => {} }
1149 1132
1150fn f() { fo<|>o!(); } 1133fn f() { fo$0o!(); }
1151"#, 1134"#,
1152 expect![[r#" 1135 expect![[r#"
1153 *foo* 1136 *foo*
@@ -1166,10 +1149,13 @@ fn f() { fo<|>o!(); }
1166 #[test] 1149 #[test]
1167 fn test_hover_tuple_field() { 1150 fn test_hover_tuple_field() {
1168 check( 1151 check(
1169 r#"struct TS(String, i32<|>);"#, 1152 r#"struct TS(String, i32$0);"#,
1170 expect![[r#" 1153 expect![[r#"
1171 *i32* 1154 *i32*
1155
1156 ```rust
1172 i32 1157 i32
1158 ```
1173 "#]], 1159 "#]],
1174 ) 1160 )
1175 } 1161 }
@@ -1181,7 +1167,7 @@ fn f() { fo<|>o!(); }
1181macro_rules! id { ($($tt:tt)*) => { $($tt)* } } 1167macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
1182fn foo() {} 1168fn foo() {}
1183id! { 1169id! {
1184 fn bar() { fo<|>o(); } 1170 fn bar() { fo$0o(); }
1185} 1171}
1186"#, 1172"#,
1187 expect![[r#" 1173 expect![[r#"
@@ -1203,7 +1189,7 @@ id! {
1203 check( 1189 check(
1204 r#" 1190 r#"
1205macro_rules! id { ($($tt:tt)*) => { $($tt)* } } 1191macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
1206fn foo(bar:u32) { let a = id!(ba<|>r); } 1192fn foo(bar:u32) { let a = id!(ba$0r); }
1207"#, 1193"#,
1208 expect![[r#" 1194 expect![[r#"
1209 *bar* 1195 *bar*
@@ -1221,7 +1207,7 @@ fn foo(bar:u32) { let a = id!(ba<|>r); }
1221 r#" 1207 r#"
1222macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } } 1208macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
1223macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } } 1209macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
1224fn foo(bar:u32) { let a = id!(ba<|>r); } 1210fn foo(bar:u32) { let a = id!(ba$0r); }
1225"#, 1211"#,
1226 expect![[r#" 1212 expect![[r#"
1227 *bar* 1213 *bar*
@@ -1240,7 +1226,7 @@ fn foo(bar:u32) { let a = id!(ba<|>r); }
1240macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } } 1226macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
1241macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } } 1227macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
1242fn bar() -> u32 { 0 } 1228fn bar() -> u32 { 0 }
1243fn foo() { let a = id!([0u32, bar(<|>)] ); } 1229fn foo() { let a = id!([0u32, bar($0)] ); }
1244"#, 1230"#,
1245 expect![[r#" 1231 expect![[r#"
1246 *bar()* 1232 *bar()*
@@ -1258,7 +1244,7 @@ fn foo() { let a = id!([0u32, bar(<|>)] ); }
1258macro_rules! arr { ($($tt:tt)*) => { [$($tt)*)] } } 1244macro_rules! arr { ($($tt:tt)*) => { [$($tt)*)] } }
1259fn foo() { 1245fn foo() {
1260 let mastered_for_itunes = ""; 1246 let mastered_for_itunes = "";
1261 let _ = arr!("Tr<|>acks", &mastered_for_itunes); 1247 let _ = arr!("Tr$0acks", &mastered_for_itunes);
1262} 1248}
1263"#, 1249"#,
1264 expect![[r#" 1250 expect![[r#"
@@ -1279,7 +1265,7 @@ macro_rules! assert {}
1279 1265
1280fn bar() -> bool { true } 1266fn bar() -> bool { true }
1281fn foo() { 1267fn foo() {
1282 assert!(ba<|>r()); 1268 assert!(ba$0r());
1283} 1269}
1284"#, 1270"#,
1285 expect![[r#" 1271 expect![[r#"
@@ -1304,7 +1290,7 @@ fn foo() {
1304 macro_rules! format {} 1290 macro_rules! format {}
1305 1291
1306 fn foo() { 1292 fn foo() {
1307 format!("hel<|>lo {}", 0); 1293 format!("hel$0lo {}", 0);
1308 } 1294 }
1309 "#, 1295 "#,
1310 ); 1296 );
@@ -1317,7 +1303,7 @@ fn foo() {
1317/// <- `\u{3000}` here 1303/// <- `\u{3000}` here
1318fn foo() { } 1304fn foo() { }
1319 1305
1320fn bar() { fo<|>o(); } 1306fn bar() { fo$0o(); }
1321", 1307",
1322 expect![[r#" 1308 expect![[r#"
1323 *foo* 1309 *foo*
@@ -1340,7 +1326,7 @@ fn bar() { fo<|>o(); }
1340 #[test] 1326 #[test]
1341 fn test_hover_function_show_qualifiers() { 1327 fn test_hover_function_show_qualifiers() {
1342 check( 1328 check(
1343 r#"async fn foo<|>() {}"#, 1329 r#"async fn foo$0() {}"#,
1344 expect![[r#" 1330 expect![[r#"
1345 *foo* 1331 *foo*
1346 1332
@@ -1354,7 +1340,7 @@ fn bar() { fo<|>o(); }
1354 "#]], 1340 "#]],
1355 ); 1341 );
1356 check( 1342 check(
1357 r#"pub const unsafe fn foo<|>() {}"#, 1343 r#"pub const unsafe fn foo$0() {}"#,
1358 expect![[r#" 1344 expect![[r#"
1359 *foo* 1345 *foo*
1360 1346
@@ -1368,7 +1354,7 @@ fn bar() { fo<|>o(); }
1368 "#]], 1354 "#]],
1369 ); 1355 );
1370 check( 1356 check(
1371 r#"pub(crate) async unsafe extern "C" fn foo<|>() {}"#, 1357 r#"pub(crate) async unsafe extern "C" fn foo$0() {}"#,
1372 expect![[r#" 1358 expect![[r#"
1373 *foo* 1359 *foo*
1374 1360
@@ -1386,10 +1372,10 @@ fn bar() { fo<|>o(); }
1386 #[test] 1372 #[test]
1387 fn test_hover_trait_show_qualifiers() { 1373 fn test_hover_trait_show_qualifiers() {
1388 check_actions( 1374 check_actions(
1389 r"unsafe trait foo<|>() {}", 1375 r"unsafe trait foo$0() {}",
1390 expect![[r#" 1376 expect![[r#"
1391 [ 1377 [
1392 Implementaion( 1378 Implementation(
1393 FilePosition { 1379 FilePosition {
1394 file_id: FileId( 1380 file_id: FileId(
1395 0, 1381 0,
@@ -1407,7 +1393,7 @@ fn bar() { fo<|>o(); }
1407 check( 1393 check(
1408 r#" 1394 r#"
1409//- /main.rs crate:main deps:std 1395//- /main.rs crate:main deps:std
1410extern crate st<|>d; 1396extern crate st$0d;
1411//- /std/lib.rs crate:std 1397//- /std/lib.rs crate:std
1412//! Standard library for this test 1398//! Standard library for this test
1413//! 1399//!
@@ -1425,7 +1411,7 @@ extern crate st<|>d;
1425 check( 1411 check(
1426 r#" 1412 r#"
1427//- /main.rs crate:main deps:std 1413//- /main.rs crate:main deps:std
1428extern crate std as ab<|>c; 1414extern crate std as ab$0c;
1429//- /std/lib.rs crate:std 1415//- /std/lib.rs crate:std
1430//! Standard library for this test 1416//! Standard library for this test
1431//! 1417//!
@@ -1446,7 +1432,7 @@ extern crate std as ab<|>c;
1446 fn test_hover_mod_with_same_name_as_function() { 1432 fn test_hover_mod_with_same_name_as_function() {
1447 check( 1433 check(
1448 r#" 1434 r#"
1449use self::m<|>y::Bar; 1435use self::m$0y::Bar;
1450mod my { pub struct Bar; } 1436mod my { pub struct Bar; }
1451 1437
1452fn my() {} 1438fn my() {}
@@ -1472,7 +1458,7 @@ fn my() {}
1472/// bar docs 1458/// bar docs
1473struct Bar; 1459struct Bar;
1474 1460
1475fn foo() { let bar = Ba<|>r; } 1461fn foo() { let bar = Ba$0r; }
1476"#, 1462"#,
1477 expect![[r#" 1463 expect![[r#"
1478 *Bar* 1464 *Bar*
@@ -1499,7 +1485,7 @@ fn foo() { let bar = Ba<|>r; }
1499#[doc = "bar docs"] 1485#[doc = "bar docs"]
1500struct Bar; 1486struct Bar;
1501 1487
1502fn foo() { let bar = Ba<|>r; } 1488fn foo() { let bar = Ba$0r; }
1503"#, 1489"#,
1504 expect![[r#" 1490 expect![[r#"
1505 *Bar* 1491 *Bar*
@@ -1528,7 +1514,7 @@ fn foo() { let bar = Ba<|>r; }
1528#[doc = "bar docs 2"] 1514#[doc = "bar docs 2"]
1529struct Bar; 1515struct Bar;
1530 1516
1531fn foo() { let bar = Ba<|>r; } 1517fn foo() { let bar = Ba$0r; }
1532"#, 1518"#,
1533 expect![[r#" 1519 expect![[r#"
1534 *Bar* 1520 *Bar*
@@ -1556,7 +1542,7 @@ fn foo() { let bar = Ba<|>r; }
1556 r#" 1542 r#"
1557pub struct Foo; 1543pub struct Foo;
1558/// [Foo](struct.Foo.html) 1544/// [Foo](struct.Foo.html)
1559pub struct B<|>ar 1545pub struct B$0ar
1560"#, 1546"#,
1561 expect![[r#" 1547 expect![[r#"
1562 *Bar* 1548 *Bar*
@@ -1582,7 +1568,7 @@ pub struct B<|>ar
1582 r#" 1568 r#"
1583pub struct Foo; 1569pub struct Foo;
1584/// [struct Foo](struct.Foo.html) 1570/// [struct Foo](struct.Foo.html)
1585pub struct B<|>ar 1571pub struct B$0ar
1586"#, 1572"#,
1587 expect![[r#" 1573 expect![[r#"
1588 *Bar* 1574 *Bar*
@@ -1610,7 +1596,7 @@ pub struct B<|>ar
1610pub struct Foo; 1596pub struct Foo;
1611pub struct Bar { 1597pub struct Bar {
1612 /// [Foo](struct.Foo.html) 1598 /// [Foo](struct.Foo.html)
1613 fie<|>ld: () 1599 fie$0ld: ()
1614} 1600}
1615"#, 1601"#,
1616 expect![[r#" 1602 expect![[r#"
@@ -1639,7 +1625,7 @@ pub mod foo {
1639 pub struct Foo; 1625 pub struct Foo;
1640} 1626}
1641/// [Foo](foo::Foo) 1627/// [Foo](foo::Foo)
1642pub struct B<|>ar 1628pub struct B$0ar
1643"#, 1629"#,
1644 expect![[r#" 1630 expect![[r#"
1645 *Bar* 1631 *Bar*
@@ -1669,7 +1655,7 @@ pub mod foo {
1669 pub struct Foo; 1655 pub struct Foo;
1670} 1656}
1671/// [Foo](foo::Foo) 1657/// [Foo](foo::Foo)
1672pub struct B<|>ar 1658pub struct B$0ar
1673"#, 1659"#,
1674 expect![[r#" 1660 expect![[r#"
1675 *Bar* 1661 *Bar*
@@ -1695,7 +1681,7 @@ pub struct B<|>ar
1695 r#" 1681 r#"
1696pub struct Foo; 1682pub struct Foo;
1697/// [Foo] 1683/// [Foo]
1698pub struct B<|>ar 1684pub struct B$0ar
1699"#, 1685"#,
1700 expect![[r#" 1686 expect![[r#"
1701 *Bar* 1687 *Bar*
@@ -1721,7 +1707,7 @@ pub struct B<|>ar
1721 r#" 1707 r#"
1722pub struct Foo; 1708pub struct Foo;
1723/// [`Foo`] 1709/// [`Foo`]
1724pub struct B<|>ar 1710pub struct B$0ar
1725"#, 1711"#,
1726 expect![[r#" 1712 expect![[r#"
1727 *Bar* 1713 *Bar*
@@ -1748,7 +1734,7 @@ pub struct B<|>ar
1748pub struct Foo; 1734pub struct Foo;
1749fn Foo() {} 1735fn Foo() {}
1750/// [Foo()] 1736/// [Foo()]
1751pub struct B<|>ar 1737pub struct B$0ar
1752"#, 1738"#,
1753 expect![[r#" 1739 expect![[r#"
1754 *Bar* 1740 *Bar*
@@ -1774,7 +1760,7 @@ pub struct B<|>ar
1774 r#" 1760 r#"
1775pub struct Foo; 1761pub struct Foo;
1776/// [`struct Foo`] 1762/// [`struct Foo`]
1777pub struct B<|>ar 1763pub struct B$0ar
1778"#, 1764"#,
1779 expect![[r#" 1765 expect![[r#"
1780 *Bar* 1766 *Bar*
@@ -1800,7 +1786,7 @@ pub struct B<|>ar
1800 r#" 1786 r#"
1801pub struct Foo; 1787pub struct Foo;
1802/// [`struct@Foo`] 1788/// [`struct@Foo`]
1803pub struct B<|>ar 1789pub struct B$0ar
1804"#, 1790"#,
1805 expect![[r#" 1791 expect![[r#"
1806 *Bar* 1792 *Bar*
@@ -1828,7 +1814,7 @@ pub struct Foo;
1828/// [my Foo][foo] 1814/// [my Foo][foo]
1829/// 1815///
1830/// [foo]: Foo 1816/// [foo]: Foo
1831pub struct B<|>ar 1817pub struct B$0ar
1832"#, 1818"#,
1833 expect![[r#" 1819 expect![[r#"
1834 *Bar* 1820 *Bar*
@@ -1854,7 +1840,7 @@ pub struct B<|>ar
1854 r#" 1840 r#"
1855pub struct Foo; 1841pub struct Foo;
1856/// [external](https://www.google.com) 1842/// [external](https://www.google.com)
1857pub struct B<|>ar 1843pub struct B$0ar
1858"#, 1844"#,
1859 expect![[r#" 1845 expect![[r#"
1860 *Bar* 1846 *Bar*
@@ -1881,7 +1867,7 @@ pub struct B<|>ar
1881 r#" 1867 r#"
1882pub struct Foo; 1868pub struct Foo;
1883/// [baz](Baz) 1869/// [baz](Baz)
1884pub struct B<|>ar 1870pub struct B$0ar
1885"#, 1871"#,
1886 expect![[r#" 1872 expect![[r#"
1887 *Bar* 1873 *Bar*
@@ -1907,7 +1893,7 @@ pub struct B<|>ar
1907 r#" 1893 r#"
1908enum E { 1894enum E {
1909 /// [E] 1895 /// [E]
1910 V<|> { field: i32 } 1896 V$0 { field: i32 }
1911} 1897}
1912"#, 1898"#,
1913 expect![[r#" 1899 expect![[r#"
@@ -1934,7 +1920,7 @@ enum E {
1934 r#" 1920 r#"
1935struct S { 1921struct S {
1936 /// [`S`] 1922 /// [`S`]
1937 field<|>: i32 1923 field$0: i32
1938} 1924}
1939"#, 1925"#,
1940 expect![[r#" 1926 expect![[r#"
@@ -1962,16 +1948,16 @@ struct S {
1962/// Test cases: 1948/// Test cases:
1963/// case 1. bare URL: https://www.example.com/ 1949/// case 1. bare URL: https://www.example.com/
1964/// case 2. inline URL with title: [example](https://www.example.com/) 1950/// case 2. inline URL with title: [example](https://www.example.com/)
1965/// case 3. code refrence: [`Result`] 1951/// case 3. code reference: [`Result`]
1966/// case 4. code refrence but miss footnote: [`String`] 1952/// case 4. code reference but miss footnote: [`String`]
1967/// case 5. autolink: <http://www.example.com/> 1953/// case 5. autolink: <http://www.example.com/>
1968/// case 6. email address: <[email protected]> 1954/// case 6. email address: <[email protected]>
1969/// case 7. refrence: [example][example] 1955/// case 7. reference: [example][example]
1970/// case 8. collapsed link: [example][] 1956/// case 8. collapsed link: [example][]
1971/// case 9. shortcut link: [example] 1957/// case 9. shortcut link: [example]
1972/// case 10. inline without URL: [example]() 1958/// case 10. inline without URL: [example]()
1973/// case 11. refrence: [foo][foo] 1959/// case 11. reference: [foo][foo]
1974/// case 12. refrence: [foo][bar] 1960/// case 12. reference: [foo][bar]
1975/// case 13. collapsed link: [foo][] 1961/// case 13. collapsed link: [foo][]
1976/// case 14. shortcut link: [foo] 1962/// case 14. shortcut link: [foo]
1977/// case 15. inline without URL: [foo]() 1963/// case 15. inline without URL: [foo]()
@@ -1980,7 +1966,7 @@ struct S {
1980/// 1966///
1981/// [`Result`]: ../../std/result/enum.Result.html 1967/// [`Result`]: ../../std/result/enum.Result.html
1982/// [^example]: https://www.example.com/ 1968/// [^example]: https://www.example.com/
1983pub fn fo<|>o() {} 1969pub fn fo$0o() {}
1984"#, 1970"#,
1985 expect![[r#" 1971 expect![[r#"
1986 *foo* 1972 *foo*
@@ -1998,16 +1984,16 @@ pub fn fo<|>o() {}
1998 Test cases: 1984 Test cases:
1999 case 1. bare URL: https://www.example.com/ 1985 case 1. bare URL: https://www.example.com/
2000 case 2. inline URL with title: [example](https://www.example.com/) 1986 case 2. inline URL with title: [example](https://www.example.com/)
2001 case 3. code refrence: `Result` 1987 case 3. code reference: `Result`
2002 case 4. code refrence but miss footnote: `String` 1988 case 4. code reference but miss footnote: `String`
2003 case 5. autolink: http://www.example.com/ 1989 case 5. autolink: http://www.example.com/
2004 case 6. email address: [email protected] 1990 case 6. email address: [email protected]
2005 case 7. refrence: example 1991 case 7. reference: example
2006 case 8. collapsed link: example 1992 case 8. collapsed link: example
2007 case 9. shortcut link: example 1993 case 9. shortcut link: example
2008 case 10. inline without URL: example 1994 case 10. inline without URL: example
2009 case 11. refrence: foo 1995 case 11. reference: foo
2010 case 12. refrence: foo 1996 case 12. reference: foo
2011 case 13. collapsed link: foo 1997 case 13. collapsed link: foo
2012 case 14. shortcut link: foo 1998 case 14. shortcut link: foo
2013 case 15. inline without URL: foo 1999 case 15. inline without URL: foo
@@ -2037,7 +2023,7 @@ macro_rules! bar {
2037 2023
2038bar!(); 2024bar!();
2039 2025
2040fn foo() { let bar = Bar; bar.fo<|>o(); } 2026fn foo() { let bar = Bar; bar.fo$0o(); }
2041"#, 2027"#,
2042 expect![[r#" 2028 expect![[r#"
2043 *foo* 2029 *foo*
@@ -2075,7 +2061,7 @@ macro_rules! bar {
2075 2061
2076bar!(); 2062bar!();
2077 2063
2078fn foo() { let bar = Bar; bar.fo<|>o(); } 2064fn foo() { let bar = Bar; bar.fo$0o(); }
2079"#, 2065"#,
2080 expect![[r#" 2066 expect![[r#"
2081 *foo* 2067 *foo*
@@ -2098,10 +2084,10 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2098 #[test] 2084 #[test]
2099 fn test_hover_trait_has_impl_action() { 2085 fn test_hover_trait_has_impl_action() {
2100 check_actions( 2086 check_actions(
2101 r#"trait foo<|>() {}"#, 2087 r#"trait foo$0() {}"#,
2102 expect![[r#" 2088 expect![[r#"
2103 [ 2089 [
2104 Implementaion( 2090 Implementation(
2105 FilePosition { 2091 FilePosition {
2106 file_id: FileId( 2092 file_id: FileId(
2107 0, 2093 0,
@@ -2117,10 +2103,10 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2117 #[test] 2103 #[test]
2118 fn test_hover_struct_has_impl_action() { 2104 fn test_hover_struct_has_impl_action() {
2119 check_actions( 2105 check_actions(
2120 r"struct foo<|>() {}", 2106 r"struct foo$0() {}",
2121 expect![[r#" 2107 expect![[r#"
2122 [ 2108 [
2123 Implementaion( 2109 Implementation(
2124 FilePosition { 2110 FilePosition {
2125 file_id: FileId( 2111 file_id: FileId(
2126 0, 2112 0,
@@ -2136,10 +2122,10 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2136 #[test] 2122 #[test]
2137 fn test_hover_union_has_impl_action() { 2123 fn test_hover_union_has_impl_action() {
2138 check_actions( 2124 check_actions(
2139 r#"union foo<|>() {}"#, 2125 r#"union foo$0() {}"#,
2140 expect![[r#" 2126 expect![[r#"
2141 [ 2127 [
2142 Implementaion( 2128 Implementation(
2143 FilePosition { 2129 FilePosition {
2144 file_id: FileId( 2130 file_id: FileId(
2145 0, 2131 0,
@@ -2155,10 +2141,10 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2155 #[test] 2141 #[test]
2156 fn test_hover_enum_has_impl_action() { 2142 fn test_hover_enum_has_impl_action() {
2157 check_actions( 2143 check_actions(
2158 r"enum foo<|>() { A, B }", 2144 r"enum foo$0() { A, B }",
2159 expect![[r#" 2145 expect![[r#"
2160 [ 2146 [
2161 Implementaion( 2147 Implementation(
2162 FilePosition { 2148 FilePosition {
2163 file_id: FileId( 2149 file_id: FileId(
2164 0, 2150 0,
@@ -2172,11 +2158,30 @@ fn foo() { let bar = Bar; bar.fo<|>o(); }
2172 } 2158 }
2173 2159
2174 #[test] 2160 #[test]
2161 fn test_hover_self_has_impl_action() {
2162 check_actions(
2163 r#"struct foo where Self$0:;"#,
2164 expect![[r#"
2165 [
2166 Implementation(
2167 FilePosition {
2168 file_id: FileId(
2169 0,
2170 ),
2171 offset: 7,
2172 },
2173 ),
2174 ]
2175 "#]],
2176 );
2177 }
2178
2179 #[test]
2175 fn test_hover_test_has_action() { 2180 fn test_hover_test_has_action() {
2176 check_actions( 2181 check_actions(
2177 r#" 2182 r#"
2178#[test] 2183#[test]
2179fn foo_<|>test() {} 2184fn foo_$0test() {}
2180"#, 2185"#,
2181 expect![[r#" 2186 expect![[r#"
2182 [ 2187 [
@@ -2211,7 +2216,7 @@ fn foo_<|>test() {}
2211 fn test_hover_test_mod_has_action() { 2216 fn test_hover_test_mod_has_action() {
2212 check_actions( 2217 check_actions(
2213 r#" 2218 r#"
2214mod tests<|> { 2219mod tests$0 {
2215 #[test] 2220 #[test]
2216 fn foo_test() {} 2221 fn foo_test() {}
2217} 2222}
@@ -2246,7 +2251,7 @@ mod tests<|> {
2246 r#" 2251 r#"
2247struct S{ f1: u32 } 2252struct S{ f1: u32 }
2248 2253
2249fn main() { let s<|>t = S{ f1:0 }; } 2254fn main() { let s$0t = S{ f1:0 }; }
2250 "#, 2255 "#,
2251 expect![[r#" 2256 expect![[r#"
2252 [ 2257 [
@@ -2279,7 +2284,7 @@ fn main() { let s<|>t = S{ f1:0 }; }
2279struct Arg(u32); 2284struct Arg(u32);
2280struct S<T>{ f1: T } 2285struct S<T>{ f1: T }
2281 2286
2282fn main() { let s<|>t = S{ f1:Arg(0) }; } 2287fn main() { let s$0t = S{ f1:Arg(0) }; }
2283"#, 2288"#,
2284 expect![[r#" 2289 expect![[r#"
2285 [ 2290 [
@@ -2325,7 +2330,7 @@ fn main() { let s<|>t = S{ f1:Arg(0) }; }
2325struct Arg(u32); 2330struct Arg(u32);
2326struct S<T>{ f1: T } 2331struct S<T>{ f1: T }
2327 2332
2328fn main() { let s<|>t = S{ f1: S{ f1: Arg(0) } }; } 2333fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
2329 "#, 2334 "#,
2330 expect![[r#" 2335 expect![[r#"
2331 [ 2336 [
@@ -2374,7 +2379,7 @@ mod M {
2374 pub struct C(u32); 2379 pub struct C(u32);
2375} 2380}
2376 2381
2377fn main() { let s<|>t = (A(1), B(2), M::C(3) ); } 2382fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
2378"#, 2383"#,
2379 expect![[r#" 2384 expect![[r#"
2380 [ 2385 [
@@ -2433,7 +2438,7 @@ fn main() { let s<|>t = (A(1), B(2), M::C(3) ); }
2433trait Foo {} 2438trait Foo {}
2434fn foo() -> impl Foo {} 2439fn foo() -> impl Foo {}
2435 2440
2436fn main() { let s<|>t = foo(); } 2441fn main() { let s$0t = foo(); }
2437"#, 2442"#,
2438 expect![[r#" 2443 expect![[r#"
2439 [ 2444 [
@@ -2467,7 +2472,7 @@ trait Foo<T> {}
2467struct S; 2472struct S;
2468fn foo() -> impl Foo<S> {} 2473fn foo() -> impl Foo<S> {}
2469 2474
2470fn main() { let s<|>t = foo(); } 2475fn main() { let s$0t = foo(); }
2471"#, 2476"#,
2472 expect![[r#" 2477 expect![[r#"
2473 [ 2478 [
@@ -2514,7 +2519,7 @@ trait Foo {}
2514trait Bar {} 2519trait Bar {}
2515fn foo() -> impl Foo + Bar {} 2520fn foo() -> impl Foo + Bar {}
2516 2521
2517fn main() { let s<|>t = foo(); } 2522fn main() { let s$0t = foo(); }
2518 "#, 2523 "#,
2519 expect![[r#" 2524 expect![[r#"
2520 [ 2525 [
@@ -2564,7 +2569,7 @@ struct S2 {}
2564 2569
2565fn foo() -> impl Foo<S1> + Bar<S2> {} 2570fn foo() -> impl Foo<S1> + Bar<S2> {}
2566 2571
2567fn main() { let s<|>t = foo(); } 2572fn main() { let s$0t = foo(); }
2568"#, 2573"#,
2569 expect![[r#" 2574 expect![[r#"
2570 [ 2575 [
@@ -2634,7 +2639,7 @@ fn main() { let s<|>t = foo(); }
2634 check_actions( 2639 check_actions(
2635 r#" 2640 r#"
2636trait Foo {} 2641trait Foo {}
2637fn foo(ar<|>g: &impl Foo) {} 2642fn foo(ar$0g: &impl Foo) {}
2638"#, 2643"#,
2639 expect![[r#" 2644 expect![[r#"
2640 [ 2645 [
@@ -2668,7 +2673,7 @@ trait Foo {}
2668trait Bar<T> {} 2673trait Bar<T> {}
2669struct S{} 2674struct S{}
2670 2675
2671fn foo(ar<|>g: &impl Foo + Bar<S>) {} 2676fn foo(ar$0g: &impl Foo + Bar<S>) {}
2672"#, 2677"#,
2673 expect![[r#" 2678 expect![[r#"
2674 [ 2679 [
@@ -2726,7 +2731,7 @@ fn foo(ar<|>g: &impl Foo + Bar<S>) {}
2726 r#" 2731 r#"
2727struct S; 2732struct S;
2728fn foo() { 2733fn foo() {
2729 let fo<|>o = async { S }; 2734 let fo$0o = async { S };
2730} 2735}
2731 2736
2732#[prelude_import] use future::*; 2737#[prelude_import] use future::*;
@@ -2778,7 +2783,7 @@ mod future {
2778 r#" 2783 r#"
2779trait Foo<T> {} 2784trait Foo<T> {}
2780struct S {} 2785struct S {}
2781fn foo(ar<|>g: &impl Foo<S>) {} 2786fn foo(ar$0g: &impl Foo<S>) {}
2782"#, 2787"#,
2783 expect![[r#" 2788 expect![[r#"
2784 [ 2789 [
@@ -2828,7 +2833,7 @@ impl Foo for S {}
2828struct B<T>{} 2833struct B<T>{}
2829fn foo() -> B<dyn Foo> {} 2834fn foo() -> B<dyn Foo> {}
2830 2835
2831fn main() { let s<|>t = foo(); } 2836fn main() { let s$0t = foo(); }
2832"#, 2837"#,
2833 expect![[r#" 2838 expect![[r#"
2834 [ 2839 [
@@ -2872,7 +2877,7 @@ fn main() { let s<|>t = foo(); }
2872 check_actions( 2877 check_actions(
2873 r#" 2878 r#"
2874trait Foo {} 2879trait Foo {}
2875fn foo(ar<|>g: &dyn Foo) {} 2880fn foo(ar$0g: &dyn Foo) {}
2876"#, 2881"#,
2877 expect![[r#" 2882 expect![[r#"
2878 [ 2883 [
@@ -2904,7 +2909,7 @@ fn foo(ar<|>g: &dyn Foo) {}
2904 r#" 2909 r#"
2905trait Foo<T> {} 2910trait Foo<T> {}
2906struct S {} 2911struct S {}
2907fn foo(ar<|>g: &dyn Foo<S>) {} 2912fn foo(ar$0g: &dyn Foo<S>) {}
2908"#, 2913"#,
2909 expect![[r#" 2914 expect![[r#"
2910 [ 2915 [
@@ -2952,7 +2957,7 @@ trait DynTrait<T> {}
2952struct B<T> {} 2957struct B<T> {}
2953struct S {} 2958struct S {}
2954 2959
2955fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} 2960fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
2956 "#, 2961 "#,
2957 expect![[r#" 2962 expect![[r#"
2958 [ 2963 [
@@ -3033,7 +3038,7 @@ impl Foo for S { type Item = Bar; }
3033 3038
3034fn test() -> impl Foo { S {} } 3039fn test() -> impl Foo { S {} }
3035 3040
3036fn main() { let s<|>t = test().get(); } 3041fn main() { let s$0t = test().get(); }
3037"#, 3042"#,
3038 expect![[r#" 3043 expect![[r#"
3039 [ 3044 [
@@ -3060,6 +3065,71 @@ fn main() { let s<|>t = test().get(); }
3060 } 3065 }
3061 3066
3062 #[test] 3067 #[test]
3068 fn test_hover_const_param_has_goto_type_action() {
3069 check_actions(
3070 r#"
3071struct Bar;
3072struct Foo<const BAR: Bar>;
3073
3074impl<const BAR: Bar> Foo<BAR$0> {}
3075"#,
3076 expect![[r#"
3077 [
3078 GoToType(
3079 [
3080 HoverGotoTypeData {
3081 mod_path: "test::Bar",
3082 nav: NavigationTarget {
3083 file_id: FileId(
3084 0,
3085 ),
3086 full_range: 0..11,
3087 focus_range: 7..10,
3088 name: "Bar",
3089 kind: Struct,
3090 description: "struct Bar",
3091 },
3092 },
3093 ],
3094 ),
3095 ]
3096 "#]],
3097 );
3098 }
3099
3100 #[test]
3101 fn test_hover_type_param_has_goto_type_action() {
3102 check_actions(
3103 r#"
3104trait Foo {}
3105
3106fn foo<T: Foo>(t: T$0){}
3107"#,
3108 expect![[r#"
3109 [
3110 GoToType(
3111 [
3112 HoverGotoTypeData {
3113 mod_path: "test::Foo",
3114 nav: NavigationTarget {
3115 file_id: FileId(
3116 0,
3117 ),
3118 full_range: 0..12,
3119 focus_range: 6..9,
3120 name: "Foo",
3121 kind: Trait,
3122 description: "trait Foo",
3123 },
3124 },
3125 ],
3126 ),
3127 ]
3128 "#]],
3129 );
3130 }
3131
3132 #[test]
3063 fn hover_displays_normalized_crate_names() { 3133 fn hover_displays_normalized_crate_names() {
3064 check( 3134 check(
3065 r#" 3135 r#"
@@ -3073,7 +3143,7 @@ pub mod wrapper {
3073} 3143}
3074 3144
3075//- /main.rs crate:main deps:name-with-dashes 3145//- /main.rs crate:main deps:name-with-dashes
3076fn main() { let foo_test = name_with_dashes::wrapper::Thing::new<|>(); } 3146fn main() { let foo_test = name_with_dashes::wrapper::Thing::new$0(); }
3077"#, 3147"#,
3078 expect![[r#" 3148 expect![[r#"
3079 *new* 3149 *new*
@@ -3099,7 +3169,7 @@ struct S {
3099 3169
3100fn main() { 3170fn main() {
3101 let s = S { f: 0 }; 3171 let s = S { f: 0 };
3102 let S { f<|> } = &s; 3172 let S { f$0 } = &s;
3103} 3173}
3104"#, 3174"#,
3105 expect![[r#" 3175 expect![[r#"
@@ -3118,7 +3188,7 @@ fn main() {
3118 r#" 3188 r#"
3119struct Foo {} 3189struct Foo {}
3120impl Foo { 3190impl Foo {
3121 fn bar(&sel<|>f) {} 3191 fn bar(&sel$0f) {}
3122} 3192}
3123"#, 3193"#,
3124 expect![[r#" 3194 expect![[r#"
@@ -3137,7 +3207,7 @@ impl Foo {
3137struct Arc<T>(T); 3207struct Arc<T>(T);
3138struct Foo {} 3208struct Foo {}
3139impl Foo { 3209impl Foo {
3140 fn bar(sel<|>f: Arc<Foo>) {} 3210 fn bar(sel$0f: Arc<Foo>) {}
3141} 3211}
3142"#, 3212"#,
3143 expect![[r#" 3213 expect![[r#"
@@ -3154,7 +3224,7 @@ impl Foo {
3154 check( 3224 check(
3155 r#" 3225 r#"
3156/// Be quick; 3226/// Be quick;
3157mod Foo<|> { 3227mod Foo$0 {
3158 //! time is mana 3228 //! time is mana
3159 3229
3160 /// This comment belongs to the function 3230 /// This comment belongs to the function
@@ -3185,7 +3255,7 @@ mod Foo<|> {
3185 check( 3255 check(
3186 r#" 3256 r#"
3187#[doc = "Be quick;"] 3257#[doc = "Be quick;"]
3188mod Foo<|> { 3258mod Foo$0 {
3189 #![doc = "time is mana"] 3259 #![doc = "time is mana"]
3190 3260
3191 #[doc = "This comment belongs to the function"] 3261 #[doc = "This comment belongs to the function"]
@@ -3216,9 +3286,105 @@ mod Foo<|> {
3216 check_hover_no_result( 3286 check_hover_no_result(
3217 r#" 3287 r#"
3218fn no_hover() { 3288fn no_hover() {
3219 // no<|>hover 3289 // no$0hover
3220} 3290}
3221"#, 3291"#,
3222 ); 3292 );
3223 } 3293 }
3294
3295 #[test]
3296 fn hover_label() {
3297 check(
3298 r#"
3299fn foo() {
3300 'label$0: loop {}
3301}
3302"#,
3303 expect![[r#"
3304 *'label*
3305
3306 ```rust
3307 'label
3308 ```
3309 "#]],
3310 );
3311 }
3312
3313 #[test]
3314 fn hover_lifetime() {
3315 check(
3316 r#"fn foo<'lifetime>(_: &'lifetime$0 ()) {}"#,
3317 expect![[r#"
3318 *'lifetime*
3319
3320 ```rust
3321 'lifetime
3322 ```
3323 "#]],
3324 );
3325 }
3326
3327 #[test]
3328 fn hover_type_param() {
3329 check(
3330 r#"
3331struct Foo<T>(T);
3332trait Copy {}
3333trait Clone {}
3334trait Sized {}
3335impl<T: Copy + Clone> Foo<T$0> where T: Sized {}
3336"#,
3337 expect![[r#"
3338 *T*
3339
3340 ```rust
3341 T: Copy + Clone + Sized
3342 ```
3343 "#]],
3344 );
3345 check(
3346 r#"
3347struct Foo<T>(T);
3348impl<T> Foo<T$0> {}
3349"#,
3350 expect![[r#"
3351 *T*
3352
3353 ```rust
3354 T
3355 ```
3356 "#]],
3357 );
3358 // lifetimes aren't being substituted yet
3359 check(
3360 r#"
3361struct Foo<T>(T);
3362impl<T: 'static> Foo<T$0> {}
3363"#,
3364 expect![[r#"
3365 *T*
3366
3367 ```rust
3368 T: {error}
3369 ```
3370 "#]],
3371 );
3372 }
3373
3374 #[test]
3375 fn hover_const_param() {
3376 check(
3377 r#"
3378struct Foo<const LEN: usize>;
3379impl<const LEN: usize> Foo<LEN$0> {}
3380"#,
3381 expect![[r#"
3382 *LEN*
3383
3384 ```rust
3385 const LEN: usize
3386 ```
3387 "#]],
3388 );
3389 }
3224} 3390}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 65df7979c..a2039fcc7 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -18,12 +18,6 @@ pub struct InlayHintsConfig {
18 pub max_length: Option<usize>, 18 pub max_length: Option<usize>,
19} 19}
20 20
21impl Default for InlayHintsConfig {
22 fn default() -> Self {
23 Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None }
24 }
25}
26
27#[derive(Clone, Debug, PartialEq, Eq)] 21#[derive(Clone, Debug, PartialEq, Eq)]
28pub enum InlayKind { 22pub enum InlayKind {
29 TypeHint, 23 TypeHint,
@@ -359,9 +353,25 @@ fn is_argument_similar_to_param_name(
359 } 353 }
360 match get_string_representation(argument) { 354 match get_string_representation(argument) {
361 None => false, 355 None => false,
362 Some(repr) => { 356 Some(argument_string) => {
363 let argument_string = repr.trim_start_matches('_'); 357 let num_leading_underscores =
364 argument_string.starts_with(param_name) || argument_string.ends_with(param_name) 358 argument_string.bytes().take_while(|&c| c == b'_').count();
359
360 // Does the argument name begin with the parameter name? Ignore leading underscores.
361 let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
362 let starts_with_pattern = param_name.bytes().all(
363 |expected| matches!(arg_bytes.next(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
364 );
365
366 if starts_with_pattern {
367 return true;
368 }
369
370 // Does the argument name end with the parameter name?
371 let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
372 param_name.bytes().rev().all(
373 |expected| matches!(arg_bytes.next_back(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
374 )
365 } 375 }
366 } 376 }
367} 377}
@@ -433,8 +443,15 @@ mod tests {
433 443
434 use crate::{fixture, inlay_hints::InlayHintsConfig}; 444 use crate::{fixture, inlay_hints::InlayHintsConfig};
435 445
446 const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
447 type_hints: true,
448 parameter_hints: true,
449 chaining_hints: true,
450 max_length: None,
451 };
452
436 fn check(ra_fixture: &str) { 453 fn check(ra_fixture: &str) {
437 check_with_config(InlayHintsConfig::default(), ra_fixture); 454 check_with_config(TEST_CONFIG, ra_fixture);
438 } 455 }
439 456
440 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { 457 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
@@ -748,7 +765,7 @@ fn main() {
748 #[test] 765 #[test]
749 fn hint_truncation() { 766 fn hint_truncation() {
750 check_with_config( 767 check_with_config(
751 InlayHintsConfig { max_length: Some(8), ..Default::default() }, 768 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
752 r#" 769 r#"
753struct Smol<T>(T); 770struct Smol<T>(T);
754 771
@@ -831,7 +848,7 @@ fn main() {
831 #[test] 848 #[test]
832 fn omitted_parameters_hints_heuristics() { 849 fn omitted_parameters_hints_heuristics() {
833 check_with_config( 850 check_with_config(
834 InlayHintsConfig { max_length: Some(8), ..Default::default() }, 851 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
835 r#" 852 r#"
836fn map(f: i32) {} 853fn map(f: i32) {}
837fn filter(predicate: i32) {} 854fn filter(predicate: i32) {}
@@ -900,6 +917,9 @@ fn main() {
900 twiddle(true); 917 twiddle(true);
901 doo(true); 918 doo(true);
902 919
920 const TWIDDLE_UPPERCASE: bool = true;
921 twiddle(TWIDDLE_UPPERCASE);
922
903 let mut param_begin: Param = Param {}; 923 let mut param_begin: Param = Param {};
904 different_order(&param_begin); 924 different_order(&param_begin);
905 different_order(&mut param_begin); 925 different_order(&mut param_begin);
@@ -924,7 +944,7 @@ fn main() {
924 #[test] 944 #[test]
925 fn unit_structs_have_no_type_hints() { 945 fn unit_structs_have_no_type_hints() {
926 check_with_config( 946 check_with_config(
927 InlayHintsConfig { max_length: Some(8), ..Default::default() }, 947 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
928 r#" 948 r#"
929enum Result<T, E> { Ok(T), Err(E) } 949enum Result<T, E> { Ok(T), Err(E) }
930use Result::*; 950use Result::*;
@@ -1381,4 +1401,41 @@ fn main() {
1381"#, 1401"#,
1382 ) 1402 )
1383 } 1403 }
1404
1405 #[test]
1406 fn fn_hints() {
1407 check(
1408 r#"
1409trait Sized {}
1410
1411fn foo() -> impl Fn() { loop {} }
1412fn foo1() -> impl Fn(f64) { loop {} }
1413fn foo2() -> impl Fn(f64, f64) { loop {} }
1414fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
1415fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
1416fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
1417fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
1418fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
1419
1420fn main() {
1421 let foo = foo();
1422 // ^^^ impl Fn()
1423 let foo = foo1();
1424 // ^^^ impl Fn(f64)
1425 let foo = foo2();
1426 // ^^^ impl Fn(f64, f64)
1427 let foo = foo3();
1428 // ^^^ impl Fn(f64, f64) -> u32
1429 let foo = foo4();
1430 // ^^^ &dyn Fn(f64, f64) -> u32
1431 let foo = foo5();
1432 // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
1433 let foo = foo6();
1434 // ^^^ impl Fn(f64, f64) -> u32 + Sized
1435 let foo = foo7();
1436 // ^^^ *const (impl Fn(f64, f64) -> u32 + Sized)
1437}
1438"#,
1439 )
1440 }
1384} 1441}
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index b5a6f66fd..05380f2a1 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -104,7 +104,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS
104 // Special case that turns something like: 104 // Special case that turns something like:
105 // 105 //
106 // ``` 106 // ```
107 // my_function({<|> 107 // my_function({$0
108 // <some-expr> 108 // <some-expr>
109 // }) 109 // })
110 // ``` 110 // ```
@@ -116,7 +116,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS
116 // ditto for 116 // ditto for
117 // 117 //
118 // ``` 118 // ```
119 // use foo::{<|> 119 // use foo::{$0
120 // bar 120 // bar
121 // }; 121 // };
122 // ``` 122 // ```
@@ -198,8 +198,8 @@ mod tests {
198 198
199 use super::*; 199 use super::*;
200 200
201 fn check_join_lines(before: &str, after: &str) { 201 fn check_join_lines(ra_fixture_before: &str, ra_fixture_after: &str) {
202 let (before_cursor_pos, before) = extract_offset(before); 202 let (before_cursor_pos, before) = extract_offset(ra_fixture_before);
203 let file = SourceFile::parse(&before).ok().unwrap(); 203 let file = SourceFile::parse(&before).ok().unwrap();
204 204
205 let range = TextRange::empty(before_cursor_pos); 205 let range = TextRange::empty(before_cursor_pos);
@@ -214,7 +214,7 @@ mod tests {
214 .apply_to_offset(before_cursor_pos) 214 .apply_to_offset(before_cursor_pos)
215 .expect("cursor position is affected by the edit"); 215 .expect("cursor position is affected by the edit");
216 let actual = add_cursor(&actual, actual_cursor_pos); 216 let actual = add_cursor(&actual, actual_cursor_pos);
217 assert_eq_text!(after, &actual); 217 assert_eq_text!(ra_fixture_after, &actual);
218 } 218 }
219 219
220 #[test] 220 #[test]
@@ -222,13 +222,13 @@ mod tests {
222 check_join_lines( 222 check_join_lines(
223 r" 223 r"
224fn foo() { 224fn foo() {
225 <|>foo(1, 225 $0foo(1,
226 ) 226 )
227} 227}
228", 228",
229 r" 229 r"
230fn foo() { 230fn foo() {
231 <|>foo(1) 231 $0foo(1)
232} 232}
233", 233",
234 ); 234 );
@@ -239,14 +239,14 @@ fn foo() {
239 check_join_lines( 239 check_join_lines(
240 r" 240 r"
241pub fn reparse(&self, edit: &AtomTextEdit) -> File { 241pub fn reparse(&self, edit: &AtomTextEdit) -> File {
242 <|>self.incremental_reparse(edit).unwrap_or_else(|| { 242 $0self.incremental_reparse(edit).unwrap_or_else(|| {
243 self.full_reparse(edit) 243 self.full_reparse(edit)
244 }) 244 })
245} 245}
246", 246",
247 r" 247 r"
248pub fn reparse(&self, edit: &AtomTextEdit) -> File { 248pub fn reparse(&self, edit: &AtomTextEdit) -> File {
249 <|>self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit)) 249 $0self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
250} 250}
251", 251",
252 ); 252 );
@@ -257,13 +257,13 @@ pub fn reparse(&self, edit: &AtomTextEdit) -> File {
257 check_join_lines( 257 check_join_lines(
258 r" 258 r"
259fn foo() { 259fn foo() {
260 foo(<|>{ 260 foo($0{
261 92 261 92
262 }) 262 })
263}", 263}",
264 r" 264 r"
265fn foo() { 265fn foo() {
266 foo(<|>92) 266 foo($092)
267}", 267}",
268 ); 268 );
269 } 269 }
@@ -274,7 +274,7 @@ fn foo() {
274 fn foo() { 274 fn foo() {
275 loop { 275 loop {
276 match x { 276 match x {
277 92 => <|>{ 277 92 => $0{
278 continue; 278 continue;
279 } 279 }
280 } 280 }
@@ -285,7 +285,7 @@ fn foo() {
285 fn foo() { 285 fn foo() {
286 loop { 286 loop {
287 match x { 287 match x {
288 92 => <|>continue, 288 92 => $0continue,
289 } 289 }
290 } 290 }
291 } 291 }
@@ -299,7 +299,7 @@ fn foo() {
299 r" 299 r"
300fn foo(e: Result<U, V>) { 300fn foo(e: Result<U, V>) {
301 match e { 301 match e {
302 Ok(u) => <|>{ 302 Ok(u) => $0{
303 u.foo() 303 u.foo()
304 } 304 }
305 Err(v) => v, 305 Err(v) => v,
@@ -308,7 +308,7 @@ fn foo(e: Result<U, V>) {
308 r" 308 r"
309fn foo(e: Result<U, V>) { 309fn foo(e: Result<U, V>) {
310 match e { 310 match e {
311 Ok(u) => <|>u.foo(), 311 Ok(u) => $0u.foo(),
312 Err(v) => v, 312 Err(v) => v,
313 } 313 }
314}", 314}",
@@ -321,7 +321,7 @@ fn foo(e: Result<U, V>) {
321 r" 321 r"
322fn foo() { 322fn foo() {
323 match ty { 323 match ty {
324 <|> Some(ty) => { 324 $0 Some(ty) => {
325 match ty { 325 match ty {
326 _ => false, 326 _ => false,
327 } 327 }
@@ -333,7 +333,7 @@ fn foo() {
333 r" 333 r"
334fn foo() { 334fn foo() {
335 match ty { 335 match ty {
336 <|> Some(ty) => match ty { 336 $0 Some(ty) => match ty {
337 _ => false, 337 _ => false,
338 }, 338 },
339 _ => true, 339 _ => true,
@@ -350,7 +350,7 @@ fn foo() {
350 r" 350 r"
351fn foo(e: Result<U, V>) { 351fn foo(e: Result<U, V>) {
352 match e { 352 match e {
353 Ok(u) => <|>{ 353 Ok(u) => $0{
354 u.foo() 354 u.foo()
355 }, 355 },
356 Err(v) => v, 356 Err(v) => v,
@@ -359,7 +359,7 @@ fn foo(e: Result<U, V>) {
359 r" 359 r"
360fn foo(e: Result<U, V>) { 360fn foo(e: Result<U, V>) {
361 match e { 361 match e {
362 Ok(u) => <|>u.foo(), 362 Ok(u) => $0u.foo(),
363 Err(v) => v, 363 Err(v) => v,
364 } 364 }
365}", 365}",
@@ -370,7 +370,7 @@ fn foo(e: Result<U, V>) {
370 r" 370 r"
371fn foo(e: Result<U, V>) { 371fn foo(e: Result<U, V>) {
372 match e { 372 match e {
373 Ok(u) => <|>{ 373 Ok(u) => $0{
374 u.foo() 374 u.foo()
375 } , 375 } ,
376 Err(v) => v, 376 Err(v) => v,
@@ -379,7 +379,7 @@ fn foo(e: Result<U, V>) {
379 r" 379 r"
380fn foo(e: Result<U, V>) { 380fn foo(e: Result<U, V>) {
381 match e { 381 match e {
382 Ok(u) => <|>u.foo() , 382 Ok(u) => $0u.foo() ,
383 Err(v) => v, 383 Err(v) => v,
384 } 384 }
385}", 385}",
@@ -390,7 +390,7 @@ fn foo(e: Result<U, V>) {
390 r" 390 r"
391fn foo(e: Result<U, V>) { 391fn foo(e: Result<U, V>) {
392 match e { 392 match e {
393 Ok(u) => <|>{ 393 Ok(u) => $0{
394 u.foo() 394 u.foo()
395 } 395 }
396 , 396 ,
@@ -400,7 +400,7 @@ fn foo(e: Result<U, V>) {
400 r" 400 r"
401fn foo(e: Result<U, V>) { 401fn foo(e: Result<U, V>) {
402 match e { 402 match e {
403 Ok(u) => <|>u.foo() 403 Ok(u) => $0u.foo()
404 , 404 ,
405 Err(v) => v, 405 Err(v) => v,
406 } 406 }
@@ -414,13 +414,13 @@ fn foo(e: Result<U, V>) {
414 check_join_lines( 414 check_join_lines(
415 r" 415 r"
416fn foo() { 416fn foo() {
417 let x = (<|>{ 417 let x = ($0{
418 4 418 4
419 },); 419 },);
420}", 420}",
421 r" 421 r"
422fn foo() { 422fn foo() {
423 let x = (<|>4,); 423 let x = ($04,);
424}", 424}",
425 ); 425 );
426 426
@@ -428,13 +428,13 @@ fn foo() {
428 check_join_lines( 428 check_join_lines(
429 r" 429 r"
430fn foo() { 430fn foo() {
431 let x = (<|>{ 431 let x = ($0{
432 4 432 4
433 } ,); 433 } ,);
434}", 434}",
435 r" 435 r"
436fn foo() { 436fn foo() {
437 let x = (<|>4 ,); 437 let x = ($04 ,);
438}", 438}",
439 ); 439 );
440 440
@@ -442,14 +442,14 @@ fn foo() {
442 check_join_lines( 442 check_join_lines(
443 r" 443 r"
444fn foo() { 444fn foo() {
445 let x = (<|>{ 445 let x = ($0{
446 4 446 4
447 } 447 }
448 ,); 448 ,);
449}", 449}",
450 r" 450 r"
451fn foo() { 451fn foo() {
452 let x = (<|>4 452 let x = ($04
453 ,); 453 ,);
454}", 454}",
455 ); 455 );
@@ -460,11 +460,11 @@ fn foo() {
460 // No space after the '{' 460 // No space after the '{'
461 check_join_lines( 461 check_join_lines(
462 r" 462 r"
463<|>use syntax::{ 463$0use syntax::{
464 TextSize, TextRange, 464 TextSize, TextRange,
465};", 465};",
466 r" 466 r"
467<|>use syntax::{TextSize, TextRange, 467$0use syntax::{TextSize, TextRange,
468};", 468};",
469 ); 469 );
470 } 470 }
@@ -475,11 +475,11 @@ fn foo() {
475 check_join_lines( 475 check_join_lines(
476 r" 476 r"
477use syntax::{ 477use syntax::{
478<|> TextSize, TextRange 478$0 TextSize, TextRange
479};", 479};",
480 r" 480 r"
481use syntax::{ 481use syntax::{
482<|> TextSize, TextRange};", 482$0 TextSize, TextRange};",
483 ); 483 );
484 } 484 }
485 485
@@ -489,11 +489,11 @@ use syntax::{
489 check_join_lines( 489 check_join_lines(
490 r" 490 r"
491use syntax::{ 491use syntax::{
492<|> TextSize, TextRange, 492$0 TextSize, TextRange,
493};", 493};",
494 r" 494 r"
495use syntax::{ 495use syntax::{
496<|> TextSize, TextRange};", 496$0 TextSize, TextRange};",
497 ); 497 );
498 } 498 }
499 499
@@ -502,14 +502,14 @@ use syntax::{
502 check_join_lines( 502 check_join_lines(
503 r" 503 r"
504use syntax::{ 504use syntax::{
505 algo::<|>{ 505 algo::$0{
506 find_token_at_offset, 506 find_token_at_offset,
507 }, 507 },
508 ast, 508 ast,
509};", 509};",
510 r" 510 r"
511use syntax::{ 511use syntax::{
512 algo::<|>find_token_at_offset, 512 algo::$0find_token_at_offset,
513 ast, 513 ast,
514};", 514};",
515 ); 515 );
@@ -520,13 +520,13 @@ use syntax::{
520 check_join_lines( 520 check_join_lines(
521 r" 521 r"
522fn foo() { 522fn foo() {
523 // Hello<|> 523 // Hello$0
524 // world! 524 // world!
525} 525}
526", 526",
527 r" 527 r"
528fn foo() { 528fn foo() {
529 // Hello<|> world! 529 // Hello$0 world!
530} 530}
531", 531",
532 ); 532 );
@@ -537,13 +537,13 @@ fn foo() {
537 check_join_lines( 537 check_join_lines(
538 r" 538 r"
539fn foo() { 539fn foo() {
540 /// Hello<|> 540 /// Hello$0
541 /// world! 541 /// world!
542} 542}
543", 543",
544 r" 544 r"
545fn foo() { 545fn foo() {
546 /// Hello<|> world! 546 /// Hello$0 world!
547} 547}
548", 548",
549 ); 549 );
@@ -554,13 +554,13 @@ fn foo() {
554 check_join_lines( 554 check_join_lines(
555 r" 555 r"
556fn foo() { 556fn foo() {
557 //! Hello<|> 557 //! Hello$0
558 //! world! 558 //! world!
559} 559}
560", 560",
561 r" 561 r"
562fn foo() { 562fn foo() {
563 //! Hello<|> world! 563 //! Hello$0 world!
564} 564}
565", 565",
566 ); 566 );
@@ -571,13 +571,13 @@ fn foo() {
571 check_join_lines( 571 check_join_lines(
572 r" 572 r"
573fn foo() { 573fn foo() {
574 // Hello<|> 574 // Hello$0
575 /* world! */ 575 /* world! */
576} 576}
577", 577",
578 r" 578 r"
579fn foo() { 579fn foo() {
580 // Hello<|> world! */ 580 // Hello$0 world! */
581} 581}
582", 582",
583 ); 583 );
@@ -588,7 +588,7 @@ fn foo() {
588 check_join_lines( 588 check_join_lines(
589 r" 589 r"
590fn foo() { 590fn foo() {
591 // The<|> 591 // The$0
592 /* quick 592 /* quick
593 brown 593 brown
594 fox! */ 594 fox! */
@@ -596,7 +596,7 @@ fn foo() {
596", 596",
597 r" 597 r"
598fn foo() { 598fn foo() {
599 // The<|> quick 599 // The$0 quick
600 brown 600 brown
601 fox! */ 601 fox! */
602} 602}
@@ -604,8 +604,8 @@ fn foo() {
604 ); 604 );
605 } 605 }
606 606
607 fn check_join_lines_sel(before: &str, after: &str) { 607 fn check_join_lines_sel(ra_fixture_before: &str, ra_fixture_after: &str) {
608 let (sel, before) = extract_range(before); 608 let (sel, before) = extract_range(ra_fixture_before);
609 let parse = SourceFile::parse(&before); 609 let parse = SourceFile::parse(&before);
610 let result = join_lines(&parse.tree(), sel); 610 let result = join_lines(&parse.tree(), sel);
611 let actual = { 611 let actual = {
@@ -613,7 +613,7 @@ fn foo() {
613 result.apply(&mut actual); 613 result.apply(&mut actual);
614 actual 614 actual
615 }; 615 };
616 assert_eq_text!(after, &actual); 616 assert_eq_text!(ra_fixture_after, &actual);
617 } 617 }
618 618
619 #[test] 619 #[test]
@@ -621,10 +621,10 @@ fn foo() {
621 check_join_lines_sel( 621 check_join_lines_sel(
622 r" 622 r"
623fn foo() { 623fn foo() {
624 <|>foo(1, 624 $0foo(1,
625 2, 625 2,
626 3, 626 3,
627 <|>) 627 $0)
628} 628}
629 ", 629 ",
630 r" 630 r"
@@ -639,9 +639,9 @@ fn foo() {
639 fn test_join_lines_selection_struct() { 639 fn test_join_lines_selection_struct() {
640 check_join_lines_sel( 640 check_join_lines_sel(
641 r" 641 r"
642struct Foo <|>{ 642struct Foo $0{
643 f: u32, 643 f: u32,
644}<|> 644}$0
645 ", 645 ",
646 r" 646 r"
647struct Foo { f: u32 } 647struct Foo { f: u32 }
@@ -654,9 +654,9 @@ struct Foo { f: u32 }
654 check_join_lines_sel( 654 check_join_lines_sel(
655 r" 655 r"
656fn foo() { 656fn foo() {
657 join(<|>type_params.type_params() 657 join($0type_params.type_params()
658 .filter_map(|it| it.name()) 658 .filter_map(|it| it.name())
659 .map(|it| it.text())<|>) 659 .map(|it| it.text())$0)
660}", 660}",
661 r" 661 r"
662fn foo() { 662fn foo() {
@@ -671,9 +671,9 @@ fn foo() {
671 r" 671 r"
672pub fn handle_find_matching_brace() { 672pub fn handle_find_matching_brace() {
673 params.offsets 673 params.offsets
674 .map(|offset| <|>{ 674 .map(|offset| $0{
675 world.analysis().matching_brace(&file, offset).unwrap_or(offset) 675 world.analysis().matching_brace(&file, offset).unwrap_or(offset)
676 }<|>) 676 }$0)
677 .collect(); 677 .collect();
678}", 678}",
679 r" 679 r"
@@ -691,7 +691,7 @@ pub fn handle_find_matching_brace() {
691 r" 691 r"
692fn main() { 692fn main() {
693 let _ = { 693 let _ = {
694 // <|>foo 694 // $0foo
695 // bar 695 // bar
696 92 696 92
697 }; 697 };
@@ -700,7 +700,7 @@ fn main() {
700 r" 700 r"
701fn main() { 701fn main() {
702 let _ = { 702 let _ = {
703 // <|>foo bar 703 // $0foo bar
704 92 704 92
705 }; 705 };
706} 706}
@@ -712,12 +712,12 @@ fn main() {
712 fn join_lines_mandatory_blocks_block() { 712 fn join_lines_mandatory_blocks_block() {
713 check_join_lines( 713 check_join_lines(
714 r" 714 r"
715<|>fn foo() { 715$0fn foo() {
716 92 716 92
717} 717}
718 ", 718 ",
719 r" 719 r"
720<|>fn foo() { 92 720$0fn foo() { 92
721} 721}
722 ", 722 ",
723 ); 723 );
@@ -725,14 +725,14 @@ fn main() {
725 check_join_lines( 725 check_join_lines(
726 r" 726 r"
727fn foo() { 727fn foo() {
728 <|>if true { 728 $0if true {
729 92 729 92
730 } 730 }
731} 731}
732 ", 732 ",
733 r" 733 r"
734fn foo() { 734fn foo() {
735 <|>if true { 92 735 $0if true { 92
736 } 736 }
737} 737}
738 ", 738 ",
@@ -741,14 +741,14 @@ fn foo() {
741 check_join_lines( 741 check_join_lines(
742 r" 742 r"
743fn foo() { 743fn foo() {
744 <|>loop { 744 $0loop {
745 92 745 92
746 } 746 }
747} 747}
748 ", 748 ",
749 r" 749 r"
750fn foo() { 750fn foo() {
751 <|>loop { 92 751 $0loop { 92
752 } 752 }
753} 753}
754 ", 754 ",
@@ -757,14 +757,14 @@ fn foo() {
757 check_join_lines( 757 check_join_lines(
758 r" 758 r"
759fn foo() { 759fn foo() {
760 <|>unsafe { 760 $0unsafe {
761 92 761 92
762 } 762 }
763} 763}
764 ", 764 ",
765 r" 765 r"
766fn foo() { 766fn foo() {
767 <|>unsafe { 92 767 $0unsafe { 92
768 } 768 }
769} 769}
770 ", 770 ",
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index b3331f03f..1e03832ec 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -31,6 +31,7 @@ mod folding_ranges;
31mod goto_definition; 31mod goto_definition;
32mod goto_implementation; 32mod goto_implementation;
33mod goto_type_definition; 33mod goto_type_definition;
34mod view_hir;
34mod hover; 35mod hover;
35mod inlay_hints; 36mod inlay_hints;
36mod join_lines; 37mod join_lines;
@@ -75,14 +76,14 @@ pub use crate::{
75 references::{rename::RenameError, Declaration, ReferenceSearchResult}, 76 references::{rename::RenameError, Declaration, ReferenceSearchResult},
76 runnables::{Runnable, RunnableKind, TestId}, 77 runnables::{Runnable, RunnableKind, TestId},
77 syntax_highlighting::{ 78 syntax_highlighting::{
78 tags::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}, 79 tags::{Highlight, HlMod, HlMods, HlPunct, HlTag},
79 HighlightedRange, 80 HlRange,
80 }, 81 },
81}; 82};
82pub use assists::{Assist, AssistConfig, AssistId, AssistKind}; 83pub use assists::{Assist, AssistConfig, AssistId, AssistKind, InsertUseConfig};
83pub use completion::{ 84pub use completion::{
84 CompletionConfig, CompletionItem, CompletionItemKind, CompletionResolveCapability, 85 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit,
85 CompletionScore, ImportEdit, InsertTextFormat, 86 InsertTextFormat,
86}; 87};
87pub use hir::{Documentation, Semantics}; 88pub use hir::{Documentation, Semantics};
88pub use ide_db::base_db::{ 89pub use ide_db::base_db::{
@@ -91,7 +92,7 @@ pub use ide_db::base_db::{
91}; 92};
92pub use ide_db::{ 93pub use ide_db::{
93 call_info::CallInfo, 94 call_info::CallInfo,
94 search::{Reference, ReferenceAccess, ReferenceKind}, 95 search::{FileReference, ReferenceAccess, ReferenceKind},
95}; 96};
96pub use ide_db::{ 97pub use ide_db::{
97 label::Label, 98 label::Label,
@@ -271,6 +272,10 @@ impl Analysis {
271 self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) 272 self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range))
272 } 273 }
273 274
275 pub fn view_hir(&self, position: FilePosition) -> Cancelable<String> {
276 self.with_db(|db| view_hir::view_hir(&db, position))
277 }
278
274 pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> { 279 pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> {
275 self.with_db(|db| expand_macro::expand_macro(db, position)) 280 self.with_db(|db| expand_macro::expand_macro(db, position))
276 } 281 }
@@ -444,12 +449,12 @@ impl Analysis {
444 } 449 }
445 450
446 /// Computes syntax highlighting for the given file 451 /// Computes syntax highlighting for the given file
447 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 452 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HlRange>> {
448 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) 453 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
449 } 454 }
450 455
451 /// Computes syntax highlighting for the given file range. 456 /// Computes syntax highlighting for the given file range.
452 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> { 457 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HlRange>> {
453 self.with_db(|db| { 458 self.with_db(|db| {
454 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false) 459 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
455 }) 460 })
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs
index d70248afe..1bfa1439d 100644
--- a/crates/ide/src/matching_brace.rs
+++ b/crates/ide/src/matching_brace.rs
@@ -58,15 +58,15 @@ mod tests {
58 assert_eq_text!(after, &actual); 58 assert_eq_text!(after, &actual);
59 } 59 }
60 60
61 do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }"); 61 do_check("struct Foo { a: i32, }$0", "struct Foo $0{ a: i32, }");
62 do_check("fn main() { |x: i32|<|> x * 2;}", "fn main() { <|>|x: i32| x * 2;}"); 62 do_check("fn main() { |x: i32|$0 x * 2;}", "fn main() { $0|x: i32| x * 2;}");
63 do_check("fn main() { <|>|x: i32| x * 2;}", "fn main() { |x: i32<|>| x * 2;}"); 63 do_check("fn main() { $0|x: i32| x * 2;}", "fn main() { |x: i32$0| x * 2;}");
64 64
65 { 65 {
66 mark::check!(pipes_not_braces); 66 mark::check!(pipes_not_braces);
67 do_check( 67 do_check(
68 "fn main() { match 92 { 1 | 2 |<|> 3 => 92 } }", 68 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }",
69 "fn main() { match 92 { 1 | 2 |<|> 3 => 92 } }", 69 "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }",
70 ); 70 );
71 } 71 }
72 } 72 }
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs
index be344a09b..d343638fb 100644
--- a/crates/ide/src/parent_module.rs
+++ b/crates/ide/src/parent_module.rs
@@ -74,7 +74,7 @@ mod tests {
74 //- /lib.rs 74 //- /lib.rs
75 mod foo; 75 mod foo;
76 //- /foo.rs 76 //- /foo.rs
77 <|>// empty 77 $0// empty
78 ", 78 ",
79 ); 79 );
80 let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); 80 let nav = analysis.parent_module(pos).unwrap().pop().unwrap();
@@ -90,7 +90,7 @@ mod tests {
90 mod foo; 90 mod foo;
91 91
92 //- /foo.rs 92 //- /foo.rs
93 mod <|>bar; 93 mod $0bar;
94 94
95 //- /foo/bar.rs 95 //- /foo/bar.rs
96 // empty 96 // empty
@@ -107,7 +107,7 @@ mod tests {
107 //- /lib.rs 107 //- /lib.rs
108 mod foo { 108 mod foo {
109 mod bar { 109 mod bar {
110 mod baz { <|> } 110 mod baz { $0 }
111 } 111 }
112 } 112 }
113 ", 113 ",
@@ -123,7 +123,7 @@ mod tests {
123//- /main.rs 123//- /main.rs
124mod foo; 124mod foo;
125//- /foo.rs 125//- /foo.rs
126<|> 126$0
127"#, 127"#,
128 ); 128 );
129 assert_eq!(analysis.crate_for(file_id).unwrap().len(), 1); 129 assert_eq!(analysis.crate_for(file_id).unwrap().len(), 1);
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 21b2d7ca1..7d4757e02 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -3,7 +3,7 @@
3//! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we 3//! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we
4//! try to resolve the direct tree parent of this element, otherwise we 4//! try to resolve the direct tree parent of this element, otherwise we
5//! already have a definition and just need to get its HIR together with 5//! already have a definition and just need to get its HIR together with
6//! some information that is needed for futher steps of searching. 6//! some information that is needed for further steps of searching.
7//! After that, we collect files that might contain references and look 7//! After that, we collect files that might contain references and look
8//! for text occurrences of the identifier. If there's an `ast::NameRef` 8//! for text occurrences of the identifier. If there's an `ast::NameRef`
9//! at the index that the match starts at and its tree parent is 9//! at the index that the match starts at and its tree parent is
@@ -13,15 +13,15 @@ pub(crate) mod rename;
13 13
14use hir::Semantics; 14use hir::Semantics;
15use ide_db::{ 15use ide_db::{
16 base_db::FileId,
16 defs::{Definition, NameClass, NameRefClass}, 17 defs::{Definition, NameClass, NameRefClass},
17 search::Reference, 18 search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope, UsageSearchResult},
18 search::{ReferenceAccess, ReferenceKind, SearchScope},
19 RootDatabase, 19 RootDatabase,
20}; 20};
21use syntax::{ 21use syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, NameOwner}, 23 ast::{self, NameOwner},
24 match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset, 24 match_ast, AstNode, SyntaxNode, TextRange, TokenAtOffset, T,
25}; 25};
26 26
27use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, SymbolKind}; 27use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo, SymbolKind};
@@ -29,7 +29,7 @@ use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeI
29#[derive(Debug, Clone)] 29#[derive(Debug, Clone)]
30pub struct ReferenceSearchResult { 30pub struct ReferenceSearchResult {
31 declaration: Declaration, 31 declaration: Declaration,
32 references: Vec<Reference>, 32 references: UsageSearchResult,
33} 33}
34 34
35#[derive(Debug, Clone)] 35#[derive(Debug, Clone)]
@@ -48,10 +48,21 @@ impl ReferenceSearchResult {
48 &self.declaration.nav 48 &self.declaration.nav
49 } 49 }
50 50
51 pub fn references(&self) -> &[Reference] { 51 pub fn references(&self) -> &UsageSearchResult {
52 &self.references 52 &self.references
53 } 53 }
54 54
55 pub fn references_with_declaration(mut self) -> UsageSearchResult {
56 let decl_ref = FileReference {
57 range: self.declaration.nav.focus_or_full_range(),
58 kind: self.declaration.kind,
59 access: self.declaration.access,
60 };
61 let file_id = self.declaration.nav.file_id;
62 self.references.references.entry(file_id).or_default().push(decl_ref);
63 self.references
64 }
65
55 /// Total number of references 66 /// Total number of references
56 /// At least 1 since all valid references should 67 /// At least 1 since all valid references should
57 /// Have a declaration 68 /// Have a declaration
@@ -63,21 +74,11 @@ impl ReferenceSearchResult {
63// allow turning ReferenceSearchResult into an iterator 74// allow turning ReferenceSearchResult into an iterator
64// over References 75// over References
65impl IntoIterator for ReferenceSearchResult { 76impl IntoIterator for ReferenceSearchResult {
66 type Item = Reference; 77 type Item = (FileId, Vec<FileReference>);
67 type IntoIter = std::vec::IntoIter<Reference>; 78 type IntoIter = std::collections::hash_map::IntoIter<FileId, Vec<FileReference>>;
68 79
69 fn into_iter(mut self) -> Self::IntoIter { 80 fn into_iter(self) -> Self::IntoIter {
70 let mut v = Vec::with_capacity(self.len()); 81 self.references_with_declaration().into_iter()
71 v.push(Reference {
72 file_range: FileRange {
73 file_id: self.declaration.nav.file_id,
74 range: self.declaration.nav.focus_or_full_range(),
75 },
76 kind: self.declaration.kind,
77 access: self.declaration.access,
78 });
79 v.append(&mut self.references);
80 v.into_iter()
81 } 82 }
82} 83}
83 84
@@ -109,13 +110,12 @@ pub(crate) fn find_all_refs(
109 110
110 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; 111 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?;
111 112
112 let references = def 113 let mut usages = def.usages(sema).set_scope(search_scope).all();
113 .usages(sema) 114 usages
114 .set_scope(search_scope) 115 .references
115 .all() 116 .values_mut()
116 .into_iter() 117 .for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind));
117 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) 118 usages.references.retain(|_, it| !it.is_empty());
118 .collect();
119 119
120 let nav = def.try_to_nav(sema.db)?; 120 let nav = def.try_to_nav(sema.db)?;
121 let decl_range = nav.focus_or_full_range(); 121 let decl_range = nav.focus_or_full_range();
@@ -130,13 +130,16 @@ pub(crate) fn find_all_refs(
130 kind = ReferenceKind::FieldShorthandForLocal; 130 kind = ReferenceKind::FieldShorthandForLocal;
131 } 131 }
132 } 132 }
133 } else if matches!(def, Definition::LifetimeParam(_) | Definition::Label(_)) { 133 } else if matches!(
134 def,
135 Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_)
136 ) {
134 kind = ReferenceKind::Lifetime; 137 kind = ReferenceKind::Lifetime;
135 }; 138 };
136 139
137 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; 140 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
138 141
139 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) 142 Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references: usages }))
140} 143}
141 144
142fn find_name( 145fn find_name(
@@ -200,7 +203,7 @@ fn get_struct_def_name_for_struct_literal_search(
200 position: FilePosition, 203 position: FilePosition,
201) -> Option<ast::Name> { 204) -> Option<ast::Name> {
202 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { 205 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) {
203 if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { 206 if right.kind() != T!['{'] && right.kind() != T!['('] {
204 return None; 207 return None;
205 } 208 }
206 if let Some(name) = 209 if let Some(name) =
@@ -227,7 +230,7 @@ fn get_enum_def_name_for_struct_literal_search(
227 position: FilePosition, 230 position: FilePosition,
228) -> Option<ast::Name> { 231) -> Option<ast::Name> {
229 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) { 232 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) {
230 if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN { 233 if right.kind() != T!['{'] && right.kind() != T!['('] {
231 return None; 234 return None;
232 } 235 }
233 if let Some(name) = 236 if let Some(name) =
@@ -252,8 +255,8 @@ fn try_find_self_references(
252 syntax: &SyntaxNode, 255 syntax: &SyntaxNode,
253 position: FilePosition, 256 position: FilePosition,
254) -> Option<RangeInfo<ReferenceSearchResult>> { 257) -> Option<RangeInfo<ReferenceSearchResult>> {
255 let self_token = 258 let FilePosition { file_id, offset } = position;
256 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)?; 259 let self_token = syntax.token_at_offset(offset).find(|t| t.kind() == T![self])?;
257 let parent = self_token.parent(); 260 let parent = self_token.parent();
258 match_ast! { 261 match_ast! {
259 match parent { 262 match parent {
@@ -274,7 +277,7 @@ fn try_find_self_references(
274 277
275 let declaration = Declaration { 278 let declaration = Declaration {
276 nav: NavigationTarget { 279 nav: NavigationTarget {
277 file_id: position.file_id, 280 file_id,
278 full_range: self_param.syntax().text_range(), 281 full_range: self_param.syntax().text_range(),
279 focus_range: Some(param_self_token.text_range()), 282 focus_range: Some(param_self_token.text_range()),
280 name: param_self_token.text().clone(), 283 name: param_self_token.text().clone(),
@@ -290,7 +293,7 @@ fn try_find_self_references(
290 ReferenceAccess::Read 293 ReferenceAccess::Read
291 }), 294 }),
292 }; 295 };
293 let references = function 296 let refs = function
294 .body() 297 .body()
295 .map(|body| { 298 .map(|body| {
296 body.syntax() 299 body.syntax()
@@ -304,14 +307,16 @@ fn try_find_self_references(
304 None 307 None
305 } 308 }
306 }) 309 })
307 .map(|token| Reference { 310 .map(|token| FileReference {
308 file_range: FileRange { file_id: position.file_id, range: token.text_range() }, 311 range: token.text_range(),
309 kind: ReferenceKind::SelfKw, 312 kind: ReferenceKind::SelfKw,
310 access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration 313 access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration
311 }) 314 })
312 .collect() 315 .collect()
313 }) 316 })
314 .unwrap_or_default(); 317 .unwrap_or_default();
318 let mut references = UsageSearchResult::default();
319 references.references.insert(file_id, refs);
315 320
316 Some(RangeInfo::new( 321 Some(RangeInfo::new(
317 param_self_token.text_range(), 322 param_self_token.text_range(),
@@ -331,7 +336,7 @@ mod tests {
331 fn test_struct_literal_after_space() { 336 fn test_struct_literal_after_space() {
332 check( 337 check(
333 r#" 338 r#"
334struct Foo <|>{ 339struct Foo $0{
335 a: i32, 340 a: i32,
336} 341}
337impl Foo { 342impl Foo {
@@ -354,7 +359,7 @@ fn main() {
354 fn test_struct_literal_before_space() { 359 fn test_struct_literal_before_space() {
355 check( 360 check(
356 r#" 361 r#"
357struct Foo<|> {} 362struct Foo$0 {}
358 fn main() { 363 fn main() {
359 let f: Foo; 364 let f: Foo;
360 f = Foo {}; 365 f = Foo {};
@@ -373,7 +378,7 @@ struct Foo<|> {}
373 fn test_struct_literal_with_generic_type() { 378 fn test_struct_literal_with_generic_type() {
374 check( 379 check(
375 r#" 380 r#"
376struct Foo<T> <|>{} 381struct Foo<T> $0{}
377 fn main() { 382 fn main() {
378 let f: Foo::<i32>; 383 let f: Foo::<i32>;
379 f = Foo {}; 384 f = Foo {};
@@ -391,7 +396,7 @@ struct Foo<T> <|>{}
391 fn test_struct_literal_for_tuple() { 396 fn test_struct_literal_for_tuple() {
392 check( 397 check(
393 r#" 398 r#"
394struct Foo<|>(i32); 399struct Foo$0(i32);
395 400
396fn main() { 401fn main() {
397 let f: Foo; 402 let f: Foo;
@@ -410,7 +415,7 @@ fn main() {
410 fn test_enum_after_space() { 415 fn test_enum_after_space() {
411 check( 416 check(
412 r#" 417 r#"
413enum Foo <|>{ 418enum Foo $0{
414 A, 419 A,
415 B, 420 B,
416} 421}
@@ -431,7 +436,7 @@ fn main() {
431 fn test_enum_before_space() { 436 fn test_enum_before_space() {
432 check( 437 check(
433 r#" 438 r#"
434enum Foo<|> { 439enum Foo$0 {
435 A, 440 A,
436 B, 441 B,
437} 442}
@@ -453,7 +458,7 @@ fn main() {
453 fn test_enum_with_generic_type() { 458 fn test_enum_with_generic_type() {
454 check( 459 check(
455 r#" 460 r#"
456enum Foo<T> <|>{ 461enum Foo<T> $0{
457 A(T), 462 A(T),
458 B, 463 B,
459} 464}
@@ -474,7 +479,7 @@ fn main() {
474 fn test_enum_for_tuple() { 479 fn test_enum_for_tuple() {
475 check( 480 check(
476 r#" 481 r#"
477enum Foo<|>{ 482enum Foo$0{
478 A(i8), 483 A(i8),
479 B(i8), 484 B(i8),
480} 485}
@@ -498,7 +503,7 @@ fn main() {
498fn main() { 503fn main() {
499 let mut i = 1; 504 let mut i = 1;
500 let j = 1; 505 let j = 1;
501 i = i<|> + j; 506 i = i$0 + j;
502 507
503 { 508 {
504 i = 0; 509 i = 0;
@@ -522,7 +527,7 @@ fn main() {
522 check( 527 check(
523 r#" 528 r#"
524fn foo() { 529fn foo() {
525 let spam<|> = 92; 530 let spam$0 = 92;
526 spam + spam 531 spam + spam
527} 532}
528fn bar() { 533fn bar() {
@@ -543,7 +548,7 @@ fn bar() {
543 fn test_find_all_refs_for_param_inside() { 548 fn test_find_all_refs_for_param_inside() {
544 check( 549 check(
545 r#" 550 r#"
546fn foo(i : u32) -> u32 { i<|> } 551fn foo(i : u32) -> u32 { i$0 }
547"#, 552"#,
548 expect![[r#" 553 expect![[r#"
549 i ValueParam FileId(0) 7..8 Other 554 i ValueParam FileId(0) 7..8 Other
@@ -557,7 +562,7 @@ fn foo(i : u32) -> u32 { i<|> }
557 fn test_find_all_refs_for_fn_param() { 562 fn test_find_all_refs_for_fn_param() {
558 check( 563 check(
559 r#" 564 r#"
560fn foo(i<|> : u32) -> u32 { i } 565fn foo(i$0 : u32) -> u32 { i }
561"#, 566"#,
562 expect![[r#" 567 expect![[r#"
563 i ValueParam FileId(0) 7..8 Other 568 i ValueParam FileId(0) 7..8 Other
@@ -573,7 +578,7 @@ fn foo(i<|> : u32) -> u32 { i }
573 r#" 578 r#"
574//- /lib.rs 579//- /lib.rs
575struct Foo { 580struct Foo {
576 pub spam<|>: u32, 581 pub spam$0: u32,
577} 582}
578 583
579fn main(s: Foo) { 584fn main(s: Foo) {
@@ -594,7 +599,7 @@ fn main(s: Foo) {
594 r#" 599 r#"
595struct Foo; 600struct Foo;
596impl Foo { 601impl Foo {
597 fn f<|>(&self) { } 602 fn f$0(&self) { }
598} 603}
599"#, 604"#,
600 expect![[r#" 605 expect![[r#"
@@ -610,7 +615,7 @@ impl Foo {
610 r#" 615 r#"
611enum Foo { 616enum Foo {
612 A, 617 A,
613 B<|>, 618 B$0,
614 C, 619 C,
615} 620}
616"#, 621"#,
@@ -627,7 +632,7 @@ enum Foo {
627 r#" 632 r#"
628enum Foo { 633enum Foo {
629 A, 634 A,
630 B { field<|>: u8 }, 635 B { field$0: u8 },
631 C, 636 C,
632} 637}
633"#, 638"#,
@@ -669,7 +674,7 @@ pub struct Bar {
669} 674}
670 675
671fn f() { 676fn f() {
672 let i = foo::Foo<|> { n: 5 }; 677 let i = foo::Foo$0 { n: 5 };
673} 678}
674"#, 679"#,
675 expect![[r#" 680 expect![[r#"
@@ -689,7 +694,7 @@ fn f() {
689 check( 694 check(
690 r#" 695 r#"
691//- /lib.rs 696//- /lib.rs
692mod foo<|>; 697mod foo$0;
693 698
694use foo::Foo; 699use foo::Foo;
695 700
@@ -726,7 +731,7 @@ fn f() {
726} 731}
727 732
728//- /foo/some.rs 733//- /foo/some.rs
729pub(super) struct Foo<|> { 734pub(super) struct Foo$0 {
730 pub n: u32, 735 pub n: u32,
731} 736}
732"#, 737"#,
@@ -746,7 +751,7 @@ pub(super) struct Foo<|> {
746 mod foo; 751 mod foo;
747 mod bar; 752 mod bar;
748 753
749 pub fn quux<|>() {} 754 pub fn quux$0() {}
750 755
751 //- /foo.rs 756 //- /foo.rs
752 fn f() { super::quux(); } 757 fn f() { super::quux(); }
@@ -782,7 +787,7 @@ pub(super) struct Foo<|> {
782 check( 787 check(
783 r#" 788 r#"
784#[macro_export] 789#[macro_export]
785macro_rules! m1<|> { () => (()) } 790macro_rules! m1$0 { () => (()) }
786 791
787fn foo() { 792fn foo() {
788 m1(); 793 m1();
@@ -803,7 +808,7 @@ fn foo() {
803 check( 808 check(
804 r#" 809 r#"
805fn foo() { 810fn foo() {
806 let mut i<|> = 0; 811 let mut i$0 = 0;
807 i = i + 1; 812 i = i + 1;
808} 813}
809"#, 814"#,
@@ -826,7 +831,7 @@ struct S {
826 831
827fn foo() { 832fn foo() {
828 let mut s = S{f: 0}; 833 let mut s = S{f: 0};
829 s.f<|> = 0; 834 s.f$0 = 0;
830} 835}
831"#, 836"#,
832 expect![[r#" 837 expect![[r#"
@@ -843,7 +848,7 @@ fn foo() {
843 check( 848 check(
844 r#" 849 r#"
845fn foo() { 850fn foo() {
846 let i<|>; 851 let i$0;
847 i = 1; 852 i = 1;
848} 853}
849"#, 854"#,
@@ -863,7 +868,7 @@ mod foo {
863 pub struct Foo; 868 pub struct Foo;
864 869
865 impl Foo { 870 impl Foo {
866 pub fn new<|>() -> Foo { Foo } 871 pub fn new$0() -> Foo { Foo }
867 } 872 }
868} 873}
869 874
@@ -886,7 +891,7 @@ fn main() {
886//- /lib.rs 891//- /lib.rs
887mod foo { mod bar; } 892mod foo { mod bar; }
888 893
889fn f<|>() {} 894fn f$0() {}
890 895
891//- /foo/bar.rs 896//- /foo/bar.rs
892use crate::f; 897use crate::f;
@@ -907,7 +912,7 @@ fn g() { f(); }
907 check( 912 check(
908 r#" 913 r#"
909struct S { 914struct S {
910 field<|>: u8, 915 field$0: u8,
911} 916}
912 917
913fn f(s: S) { 918fn f(s: S) {
@@ -930,7 +935,7 @@ fn f(s: S) {
930 r#" 935 r#"
931enum En { 936enum En {
932 Variant { 937 Variant {
933 field<|>: u8, 938 field$0: u8,
934 } 939 }
935} 940}
936 941
@@ -955,7 +960,7 @@ fn f(e: En) {
955mod m { 960mod m {
956 pub enum En { 961 pub enum En {
957 Variant { 962 Variant {
958 field<|>: u8, 963 field$0: u8,
959 } 964 }
960 } 965 }
961} 966}
@@ -980,7 +985,7 @@ struct Foo { bar: i32 }
980 985
981impl Foo { 986impl Foo {
982 fn foo(self) { 987 fn foo(self) {
983 let x = self<|>.bar; 988 let x = self$0.bar;
984 if true { 989 if true {
985 let _ = match () { 990 let _ = match () {
986 () => self, 991 () => self,
@@ -1016,12 +1021,14 @@ impl Foo {
1016 actual += "\n\n"; 1021 actual += "\n\n";
1017 } 1022 }
1018 1023
1019 for r in &refs.references { 1024 for (file_id, references) in refs.references {
1020 format_to!(actual, "{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind); 1025 for r in references {
1021 if let Some(access) = r.access { 1026 format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind);
1022 format_to!(actual, " {:?}", access); 1027 if let Some(access) = r.access {
1028 format_to!(actual, " {:?}", access);
1029 }
1030 actual += "\n";
1023 } 1031 }
1024 actual += "\n";
1025 } 1032 }
1026 expect.assert_eq(&actual) 1033 expect.assert_eq(&actual)
1027 } 1034 }
@@ -1032,7 +1039,7 @@ impl Foo {
1032 r#" 1039 r#"
1033trait Foo<'a> {} 1040trait Foo<'a> {}
1034impl<'a> Foo<'a> for &'a () {} 1041impl<'a> Foo<'a> for &'a () {}
1035fn foo<'a, 'b: 'a>(x: &'a<|> ()) -> &'a () where &'a (): Foo<'a> { 1042fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> {
1036 fn bar<'a>(_: &'a ()) {} 1043 fn bar<'a>(_: &'a ()) {}
1037 x 1044 x
1038} 1045}
@@ -1053,7 +1060,7 @@ fn foo<'a, 'b: 'a>(x: &'a<|> ()) -> &'a () where &'a (): Foo<'a> {
1053 fn test_find_lifetimes_type_alias() { 1060 fn test_find_lifetimes_type_alias() {
1054 check( 1061 check(
1055 r#" 1062 r#"
1056type Foo<'a, T> where T: 'a<|> = &'a T; 1063type Foo<'a, T> where T: 'a$0 = &'a T;
1057"#, 1064"#,
1058 expect![[r#" 1065 expect![[r#"
1059 'a LifetimeParam FileId(0) 9..11 9..11 Lifetime 1066 'a LifetimeParam FileId(0) 9..11 9..11 Lifetime
@@ -1072,7 +1079,7 @@ trait Foo<'a> {
1072 fn foo() -> &'a (); 1079 fn foo() -> &'a ();
1073} 1080}
1074impl<'a> Foo<'a> for &'a () { 1081impl<'a> Foo<'a> for &'a () {
1075 fn foo() -> &'a<|> () { 1082 fn foo() -> &'a$0 () {
1076 unimplemented!() 1083 unimplemented!()
1077 } 1084 }
1078} 1085}
@@ -1093,7 +1100,7 @@ impl<'a> Foo<'a> for &'a () {
1093 r#" 1100 r#"
1094macro_rules! foo {($i:ident) => {$i} } 1101macro_rules! foo {($i:ident) => {$i} }
1095fn main() { 1102fn main() {
1096 let a<|> = "test"; 1103 let a$0 = "test";
1097 foo!(a); 1104 foo!(a);
1098} 1105}
1099"#, 1106"#,
@@ -1112,7 +1119,7 @@ fn main() {
1112macro_rules! foo {($i:ident) => {$i} } 1119macro_rules! foo {($i:ident) => {$i} }
1113fn main() { 1120fn main() {
1114 let a = "test"; 1121 let a = "test";
1115 foo!(a<|>); 1122 foo!(a$0);
1116} 1123}
1117"#, 1124"#,
1118 expect![[r#" 1125 expect![[r#"
@@ -1130,7 +1137,7 @@ fn main() {
1130fn foo<'a>() -> &'a () { 1137fn foo<'a>() -> &'a () {
1131 'a: loop { 1138 'a: loop {
1132 'b: loop { 1139 'b: loop {
1133 continue 'a<|>; 1140 continue 'a$0;
1134 } 1141 }
1135 break 'a; 1142 break 'a;
1136 } 1143 }
@@ -1144,4 +1151,20 @@ fn foo<'a>() -> &'a () {
1144 "#]], 1151 "#]],
1145 ); 1152 );
1146 } 1153 }
1154
1155 #[test]
1156 fn test_find_const_param() {
1157 check(
1158 r#"
1159fn foo<const FOO$0: usize>() -> usize {
1160 FOO
1161}
1162"#,
1163 expect![[r#"
1164 FOO ConstParam FileId(0) 7..23 13..16 Other
1165
1166 FileId(0) 42..45 Other
1167 "#]],
1168 );
1169 }
1147} 1170}
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 854bf194e..c3ae568c2 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -1,29 +1,30 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2use std::{ 2use std::{
3 convert::TryInto, 3 convert::TryInto,
4 error::Error,
5 fmt::{self, Display}, 4 fmt::{self, Display},
6}; 5};
7 6
8use hir::{Module, ModuleDef, ModuleSource, Semantics}; 7use hir::{Module, ModuleDef, ModuleSource, Semantics};
9use ide_db::base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt};
10use ide_db::{ 8use ide_db::{
9 base_db::{AnchoredPathBuf, FileId, FileRange, SourceDatabaseExt},
11 defs::{Definition, NameClass, NameRefClass}, 10 defs::{Definition, NameClass, NameRefClass},
11 search::FileReference,
12 RootDatabase, 12 RootDatabase,
13}; 13};
14use syntax::{ 14use syntax::{
15 algo::find_node_at_offset, 15 algo::find_node_at_offset,
16 ast::{self, NameOwner}, 16 ast::{self, NameOwner},
17 lex_single_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken, 17 lex_single_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken, T,
18}; 18};
19use test_utils::mark; 19use test_utils::mark;
20use text_edit::TextEdit; 20use text_edit::TextEdit;
21 21
22use crate::{ 22use crate::{
23 references::find_all_refs, FilePosition, FileSystemEdit, RangeInfo, Reference, ReferenceKind, 23 FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, ReferenceSearchResult, SourceChange,
24 SourceChange, SourceFileEdit, TextRange, TextSize, 24 SourceFileEdit, TextRange, TextSize,
25}; 25};
26 26
27type RenameResult<T> = Result<T, RenameError>;
27#[derive(Debug)] 28#[derive(Debug)]
28pub struct RenameError(pub(crate) String); 29pub struct RenameError(pub(crate) String);
29 30
@@ -33,26 +34,30 @@ impl fmt::Display for RenameError {
33 } 34 }
34} 35}
35 36
36impl Error for RenameError {} 37macro_rules! format_err {
38 ($fmt:expr) => {RenameError(format!($fmt))};
39 ($fmt:expr, $($arg:tt)+) => {RenameError(format!($fmt, $($arg)+))}
40}
41
42macro_rules! bail {
43 ($($tokens:tt)*) => {return Err(format_err!($($tokens)*))}
44}
37 45
38pub(crate) fn prepare_rename( 46pub(crate) fn prepare_rename(
39 db: &RootDatabase, 47 db: &RootDatabase,
40 position: FilePosition, 48 position: FilePosition,
41) -> Result<RangeInfo<()>, RenameError> { 49) -> RenameResult<RangeInfo<()>> {
42 let sema = Semantics::new(db); 50 let sema = Semantics::new(db);
43 let source_file = sema.parse(position.file_id); 51 let source_file = sema.parse(position.file_id);
44 let syntax = source_file.syntax(); 52 let syntax = source_file.syntax();
45 if let Some(module) = find_module_at_offset(&sema, position, syntax) { 53 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
46 rename_mod(&sema, position, module, "dummy") 54 rename_mod(&sema, position, module, "dummy")
47 } else if let Some(self_token) = 55 } else if let Some(self_token) =
48 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 56 syntax.token_at_offset(position.offset).find(|t| t.kind() == T![self])
49 { 57 {
50 rename_self_to_param(&sema, position, self_token, "dummy") 58 rename_self_to_param(&sema, position, self_token, "dummy")
51 } else { 59 } else {
52 let range = match find_all_refs(&sema, position, None) { 60 let RangeInfo { range, .. } = find_all_refs(&sema, position)?;
53 Some(RangeInfo { range, .. }) => range,
54 None => return Err(RenameError("No references found at position".to_string())),
55 };
56 Ok(RangeInfo::new(range, SourceChange::from(vec![]))) 61 Ok(RangeInfo::new(range, SourceChange::from(vec![])))
57 } 62 }
58 .map(|info| RangeInfo::new(info.range, ())) 63 .map(|info| RangeInfo::new(info.range, ()))
@@ -62,7 +67,7 @@ pub(crate) fn rename(
62 db: &RootDatabase, 67 db: &RootDatabase,
63 position: FilePosition, 68 position: FilePosition,
64 new_name: &str, 69 new_name: &str,
65) -> Result<RangeInfo<SourceChange>, RenameError> { 70) -> RenameResult<RangeInfo<SourceChange>> {
66 let sema = Semantics::new(db); 71 let sema = Semantics::new(db);
67 rename_with_semantics(&sema, position, new_name) 72 rename_with_semantics(&sema, position, new_name)
68} 73}
@@ -71,42 +76,18 @@ pub(crate) fn rename_with_semantics(
71 sema: &Semantics<RootDatabase>, 76 sema: &Semantics<RootDatabase>,
72 position: FilePosition, 77 position: FilePosition,
73 new_name: &str, 78 new_name: &str,
74) -> Result<RangeInfo<SourceChange>, RenameError> { 79) -> RenameResult<RangeInfo<SourceChange>> {
75 let is_lifetime_name = match lex_single_syntax_kind(new_name) {
76 Some(res) => match res {
77 (SyntaxKind::IDENT, _) => false,
78 (SyntaxKind::UNDERSCORE, _) => false,
79 (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position),
80 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => true,
81 (SyntaxKind::LIFETIME_IDENT, _) => {
82 return Err(RenameError(format!(
83 "Invalid name `{0}`: Cannot rename lifetime to {0}",
84 new_name
85 )))
86 }
87 (_, Some(syntax_error)) => {
88 return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error)))
89 }
90 (_, None) => {
91 return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name)))
92 }
93 },
94 None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))),
95 };
96
97 let source_file = sema.parse(position.file_id); 80 let source_file = sema.parse(position.file_id);
98 let syntax = source_file.syntax(); 81 let syntax = source_file.syntax();
99 // this is here to prevent lifetime renames from happening on modules and self 82
100 if is_lifetime_name { 83 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
101 rename_reference(&sema, position, new_name, is_lifetime_name)
102 } else if let Some(module) = find_module_at_offset(&sema, position, syntax) {
103 rename_mod(&sema, position, module, new_name) 84 rename_mod(&sema, position, module, new_name)
104 } else if let Some(self_token) = 85 } else if let Some(self_token) =
105 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 86 syntax.token_at_offset(position.offset).find(|t| t.kind() == T![self])
106 { 87 {
107 rename_self_to_param(&sema, position, self_token, new_name) 88 rename_self_to_param(&sema, position, self_token, new_name)
108 } else { 89 } else {
109 rename_reference(&sema, position, new_name, is_lifetime_name) 90 rename_reference(&sema, position, new_name)
110 } 91 }
111} 92}
112 93
@@ -127,6 +108,33 @@ pub(crate) fn will_rename_file(
127 Some(change) 108 Some(change)
128} 109}
129 110
111#[derive(Debug, PartialEq)]
112enum IdentifierKind {
113 Ident,
114 Lifetime,
115 ToSelf,
116 Underscore,
117}
118
119fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> {
120 match lex_single_syntax_kind(new_name) {
121 Some(res) => match res {
122 (SyntaxKind::IDENT, _) => Ok(IdentifierKind::Ident),
123 (T![_], _) => Ok(IdentifierKind::Underscore),
124 (T![self], _) => Ok(IdentifierKind::ToSelf),
125 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => {
126 Ok(IdentifierKind::Lifetime)
127 }
128 (SyntaxKind::LIFETIME_IDENT, _) => {
129 bail!("Invalid name `{0}`: Cannot rename lifetime to {0}", new_name)
130 }
131 (_, Some(syntax_error)) => bail!("Invalid name `{}`: {}", new_name, syntax_error),
132 (_, None) => bail!("Invalid name `{}`: not an identifier", new_name),
133 },
134 None => bail!("Invalid name `{}`: not an identifier", new_name),
135 }
136}
137
130fn find_module_at_offset( 138fn find_module_at_offset(
131 sema: &Semantics<RootDatabase>, 139 sema: &Semantics<RootDatabase>,
132 position: FilePosition, 140 position: FilePosition,
@@ -155,39 +163,54 @@ fn find_module_at_offset(
155 Some(module) 163 Some(module)
156} 164}
157 165
158fn source_edit_from_reference( 166fn find_all_refs(
167 sema: &Semantics<RootDatabase>,
168 position: FilePosition,
169) -> RenameResult<RangeInfo<ReferenceSearchResult>> {
170 crate::references::find_all_refs(sema, position, None)
171 .ok_or_else(|| format_err!("No references found at position"))
172}
173
174fn source_edit_from_references(
159 sema: &Semantics<RootDatabase>, 175 sema: &Semantics<RootDatabase>,
160 reference: Reference, 176 file_id: FileId,
177 references: &[FileReference],
161 new_name: &str, 178 new_name: &str,
162) -> SourceFileEdit { 179) -> SourceFileEdit {
163 let mut replacement_text = String::new(); 180 let mut edit = TextEdit::builder();
164 let range = match reference.kind { 181 for reference in references {
165 ReferenceKind::FieldShorthandForField => { 182 let mut replacement_text = String::new();
166 mark::hit!(test_rename_struct_field_for_shorthand); 183 let range = match reference.kind {
167 replacement_text.push_str(new_name); 184 ReferenceKind::FieldShorthandForField => {
168 replacement_text.push_str(": "); 185 mark::hit!(test_rename_struct_field_for_shorthand);
169 TextRange::new(reference.file_range.range.start(), reference.file_range.range.start()) 186 replacement_text.push_str(new_name);
170 } 187 replacement_text.push_str(": ");
171 ReferenceKind::FieldShorthandForLocal => { 188 TextRange::new(reference.range.start(), reference.range.start())
172 mark::hit!(test_rename_local_for_field_shorthand); 189 }
173 replacement_text.push_str(": "); 190 ReferenceKind::FieldShorthandForLocal => {
174 replacement_text.push_str(new_name); 191 mark::hit!(test_rename_local_for_field_shorthand);
175 TextRange::new(reference.file_range.range.end(), reference.file_range.range.end()) 192 replacement_text.push_str(": ");
176 } 193 replacement_text.push_str(new_name);
177 ReferenceKind::RecordFieldExprOrPat => { 194 TextRange::new(reference.range.end(), reference.range.end())
178 mark::hit!(test_rename_field_expr_pat); 195 }
179 replacement_text.push_str(new_name); 196 ReferenceKind::RecordFieldExprOrPat => {
180 edit_text_range_for_record_field_expr_or_pat(sema, reference.file_range, new_name) 197 mark::hit!(test_rename_field_expr_pat);
181 } 198 replacement_text.push_str(new_name);
182 _ => { 199 edit_text_range_for_record_field_expr_or_pat(
183 replacement_text.push_str(new_name); 200 sema,
184 reference.file_range.range 201 FileRange { file_id, range: reference.range },
185 } 202 new_name,
186 }; 203 )
187 SourceFileEdit { 204 }
188 file_id: reference.file_range.file_id, 205 _ => {
189 edit: TextEdit::replace(range, replacement_text), 206 replacement_text.push_str(new_name);
207 reference.range
208 }
209 };
210 edit.replace(range, replacement_text);
190 } 211 }
212
213 SourceFileEdit { file_id, edit: edit.finish() }
191} 214}
192 215
193fn edit_text_range_for_record_field_expr_or_pat( 216fn edit_text_range_for_record_field_expr_or_pat(
@@ -223,7 +246,10 @@ fn rename_mod(
223 position: FilePosition, 246 position: FilePosition,
224 module: Module, 247 module: Module,
225 new_name: &str, 248 new_name: &str,
226) -> Result<RangeInfo<SourceChange>, RenameError> { 249) -> RenameResult<RangeInfo<SourceChange>> {
250 if IdentifierKind::Ident != check_identifier(new_name)? {
251 bail!("Invalid name `{0}`: cannot rename module to {0}", new_name);
252 }
227 let mut source_file_edits = Vec::new(); 253 let mut source_file_edits = Vec::new();
228 let mut file_system_edits = Vec::new(); 254 let mut file_system_edits = Vec::new();
229 255
@@ -254,12 +280,10 @@ fn rename_mod(
254 source_file_edits.push(edit); 280 source_file_edits.push(edit);
255 } 281 }
256 282
257 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None) 283 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
258 .ok_or_else(|| RenameError("No references found at position".to_string()))?; 284 let ref_edits = refs.references().iter().map(|(&file_id, references)| {
259 let ref_edits = refs 285 source_edit_from_references(sema, file_id, references, new_name)
260 .references 286 });
261 .into_iter()
262 .map(|reference| source_edit_from_reference(sema, reference, new_name));
263 source_file_edits.extend(ref_edits); 287 source_file_edits.extend(ref_edits);
264 288
265 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits))) 289 Ok(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
@@ -274,27 +298,26 @@ fn rename_to_self(
274 298
275 let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset) 299 let (fn_def, fn_ast) = find_node_at_offset::<ast::Fn>(syn, position.offset)
276 .and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast))) 300 .and_then(|fn_ast| sema.to_def(&fn_ast).zip(Some(fn_ast)))
277 .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?; 301 .ok_or_else(|| format_err!("No surrounding method declaration found"))?;
278 let param_range = fn_ast 302 let param_range = fn_ast
279 .param_list() 303 .param_list()
280 .and_then(|p| p.params().next()) 304 .and_then(|p| p.params().next())
281 .ok_or_else(|| RenameError("Method has no parameters".to_string()))? 305 .ok_or_else(|| format_err!("Method has no parameters"))?
282 .syntax() 306 .syntax()
283 .text_range(); 307 .text_range();
284 if !param_range.contains(position.offset) { 308 if !param_range.contains(position.offset) {
285 return Err(RenameError("Only the first parameter can be self".to_string())); 309 bail!("Only the first parameter can be self");
286 } 310 }
287 311
288 let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset) 312 let impl_block = find_node_at_offset::<ast::Impl>(syn, position.offset)
289 .and_then(|def| sema.to_def(&def)) 313 .and_then(|def| sema.to_def(&def))
290 .ok_or_else(|| RenameError("No impl block found for function".to_string()))?; 314 .ok_or_else(|| format_err!("No impl block found for function"))?;
291 if fn_def.self_param(sema.db).is_some() { 315 if fn_def.self_param(sema.db).is_some() {
292 return Err(RenameError("Method already has a self parameter".to_string())); 316 bail!("Method already has a self parameter");
293 } 317 }
294 318
295 let params = fn_def.assoc_fn_params(sema.db); 319 let params = fn_def.assoc_fn_params(sema.db);
296 let first_param = 320 let first_param = params.first().ok_or_else(|| format_err!("Method has no parameters"))?;
297 params.first().ok_or_else(|| RenameError("Method has no parameters".into()))?;
298 let first_param_ty = first_param.ty(); 321 let first_param_ty = first_param.ty();
299 let impl_ty = impl_block.target_ty(sema.db); 322 let impl_ty = impl_block.target_ty(sema.db);
300 let (ty, self_param) = if impl_ty.remove_ref().is_some() { 323 let (ty, self_param) = if impl_ty.remove_ref().is_some() {
@@ -307,23 +330,17 @@ fn rename_to_self(
307 }; 330 };
308 331
309 if ty != impl_ty { 332 if ty != impl_ty {
310 return Err(RenameError("Parameter type differs from impl block type".to_string())); 333 bail!("Parameter type differs from impl block type");
311 } 334 }
312 335
313 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None) 336 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
314 .ok_or_else(|| RenameError("No reference found at position".to_string()))?;
315
316 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
317 .into_iter()
318 .partition(|reference| param_range.intersect(reference.file_range.range).is_some());
319
320 if param_ref.is_empty() {
321 return Err(RenameError("Parameter to rename not found".to_string()));
322 }
323 337
324 let mut edits = usages 338 let mut edits = refs
325 .into_iter() 339 .references()
326 .map(|reference| source_edit_from_reference(sema, reference, "self")) 340 .iter()
341 .map(|(&file_id, references)| {
342 source_edit_from_references(sema, file_id, references, "self")
343 })
327 .collect::<Vec<_>>(); 344 .collect::<Vec<_>>();
328 345
329 edits.push(SourceFileEdit { 346 edits.push(SourceFileEdit {
@@ -367,12 +384,22 @@ fn rename_self_to_param(
367 self_token: SyntaxToken, 384 self_token: SyntaxToken,
368 new_name: &str, 385 new_name: &str,
369) -> Result<RangeInfo<SourceChange>, RenameError> { 386) -> Result<RangeInfo<SourceChange>, RenameError> {
387 let ident_kind = check_identifier(new_name)?;
388 match ident_kind {
389 IdentifierKind::Lifetime => bail!("Invalid name `{}`: not an identifier", new_name),
390 IdentifierKind::ToSelf => {
391 // no-op
392 mark::hit!(rename_self_to_self);
393 return Ok(RangeInfo::new(self_token.text_range(), SourceChange::default()));
394 }
395 _ => (),
396 }
370 let source_file = sema.parse(position.file_id); 397 let source_file = sema.parse(position.file_id);
371 let syn = source_file.syntax(); 398 let syn = source_file.syntax();
372 399
373 let text = sema.db.file_text(position.file_id); 400 let text = sema.db.file_text(position.file_id);
374 let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset) 401 let fn_def = find_node_at_offset::<ast::Fn>(syn, position.offset)
375 .ok_or_else(|| RenameError("No surrounding method declaration found".to_string()))?; 402 .ok_or_else(|| format_err!("No surrounding method declaration found"))?;
376 let search_range = fn_def.syntax().text_range(); 403 let search_range = fn_def.syntax().text_range();
377 404
378 let mut edits: Vec<SourceFileEdit> = vec![]; 405 let mut edits: Vec<SourceFileEdit> = vec![];
@@ -382,12 +409,10 @@ fn rename_self_to_param(
382 if !search_range.contains_inclusive(offset) { 409 if !search_range.contains_inclusive(offset) {
383 continue; 410 continue;
384 } 411 }
385 if let Some(ref usage) = 412 if let Some(ref usage) = syn.token_at_offset(offset).find(|t| t.kind() == T![self]) {
386 syn.token_at_offset(offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
387 {
388 let edit = if let Some(ref self_param) = ast::SelfParam::cast(usage.parent()) { 413 let edit = if let Some(ref self_param) = ast::SelfParam::cast(usage.parent()) {
389 text_edit_from_self_param(syn, self_param, new_name) 414 text_edit_from_self_param(syn, self_param, new_name)
390 .ok_or_else(|| RenameError("No target type found".to_string()))? 415 .ok_or_else(|| format_err!("No target type found"))?
391 } else { 416 } else {
392 TextEdit::replace(usage.text_range(), String::from(new_name)) 417 TextEdit::replace(usage.text_range(), String::from(new_name))
393 }; 418 };
@@ -395,6 +420,10 @@ fn rename_self_to_param(
395 } 420 }
396 } 421 }
397 422
423 if edits.len() > 1 && ident_kind == IdentifierKind::Underscore {
424 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
425 }
426
398 let range = ast::SelfParam::cast(self_token.parent()) 427 let range = ast::SelfParam::cast(self_token.parent())
399 .map_or(self_token.text_range(), |p| p.syntax().text_range()); 428 .map_or(self_token.text_range(), |p| p.syntax().text_range());
400 429
@@ -405,35 +434,43 @@ fn rename_reference(
405 sema: &Semantics<RootDatabase>, 434 sema: &Semantics<RootDatabase>,
406 position: FilePosition, 435 position: FilePosition,
407 new_name: &str, 436 new_name: &str,
408 is_lifetime_name: bool,
409) -> Result<RangeInfo<SourceChange>, RenameError> { 437) -> Result<RangeInfo<SourceChange>, RenameError> {
410 let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) { 438 let ident_kind = check_identifier(new_name)?;
411 Some(range_info) => range_info, 439 let RangeInfo { range, info: refs } = find_all_refs(sema, position)?;
412 None => return Err(RenameError("No references found at position".to_string())), 440
413 }; 441 match (ident_kind, &refs.declaration.kind) {
414 442 (IdentifierKind::ToSelf, ReferenceKind::Lifetime)
415 match (refs.declaration.kind == ReferenceKind::Lifetime, is_lifetime_name) { 443 | (IdentifierKind::Underscore, ReferenceKind::Lifetime)
416 (true, false) => { 444 | (IdentifierKind::Ident, ReferenceKind::Lifetime) => {
417 return Err(RenameError(format!( 445 mark::hit!(rename_not_a_lifetime_ident_ref);
418 "Invalid name `{}`: not a lifetime identifier", 446 bail!("Invalid name `{}`: not a lifetime identifier", new_name)
419 new_name
420 )))
421 } 447 }
422 (false, true) => { 448 (IdentifierKind::Lifetime, ReferenceKind::Lifetime) => mark::hit!(rename_lifetime),
423 return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))) 449 (IdentifierKind::Lifetime, _) => {
450 mark::hit!(rename_not_an_ident_ref);
451 bail!("Invalid name `{}`: not an identifier", new_name)
424 } 452 }
425 _ => (), 453 (IdentifierKind::ToSelf, ReferenceKind::SelfKw) => {
454 unreachable!("rename_self_to_param should've been called instead")
455 }
456 (IdentifierKind::ToSelf, _) => {
457 mark::hit!(rename_to_self);
458 return rename_to_self(sema, position);
459 }
460 (IdentifierKind::Underscore, _) if !refs.references.is_empty() => {
461 mark::hit!(rename_underscore_multiple);
462 bail!("Cannot rename reference to `_` as it is being referenced multiple times")
463 }
464 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident),
426 } 465 }
427 466
428 let edit = refs 467 let edit = refs
429 .into_iter() 468 .into_iter()
430 .map(|reference| source_edit_from_reference(sema, reference, new_name)) 469 .map(|(file_id, references)| {
470 source_edit_from_references(sema, file_id, &references, new_name)
471 })
431 .collect::<Vec<_>>(); 472 .collect::<Vec<_>>();
432 473
433 if edit.is_empty() {
434 return Err(RenameError("No references found at position".to_string()));
435 }
436
437 Ok(RangeInfo::new(range, SourceChange::from(edit))) 474 Ok(RangeInfo::new(range, SourceChange::from(edit)))
438} 475}
439 476
@@ -462,9 +499,11 @@ mod tests {
462 text_edit_builder.replace(indel.delete, indel.insert); 499 text_edit_builder.replace(indel.delete, indel.insert);
463 } 500 }
464 } 501 }
465 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string(); 502 if let Some(file_id) = file_id {
466 text_edit_builder.finish().apply(&mut result); 503 let mut result = analysis.file_text(file_id).unwrap().to_string();
467 assert_eq_text!(ra_fixture_after, &*result); 504 text_edit_builder.finish().apply(&mut result);
505 assert_eq_text!(ra_fixture_after, &*result);
506 }
468 } 507 }
469 Err(err) => { 508 Err(err) => {
470 if ra_fixture_after.starts_with("error:") { 509 if ra_fixture_after.starts_with("error:") {
@@ -493,19 +532,19 @@ mod tests {
493 532
494 #[test] 533 #[test]
495 fn test_rename_to_underscore() { 534 fn test_rename_to_underscore() {
496 check("_", r#"fn main() { let i<|> = 1; }"#, r#"fn main() { let _ = 1; }"#); 535 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#);
497 } 536 }
498 537
499 #[test] 538 #[test]
500 fn test_rename_to_raw_identifier() { 539 fn test_rename_to_raw_identifier() {
501 check("r#fn", r#"fn main() { let i<|> = 1; }"#, r#"fn main() { let r#fn = 1; }"#); 540 check("r#fn", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let r#fn = 1; }"#);
502 } 541 }
503 542
504 #[test] 543 #[test]
505 fn test_rename_to_invalid_identifier1() { 544 fn test_rename_to_invalid_identifier1() {
506 check( 545 check(
507 "invalid!", 546 "invalid!",
508 r#"fn main() { let i<|> = 1; }"#, 547 r#"fn main() { let i$0 = 1; }"#,
509 "error: Invalid name `invalid!`: not an identifier", 548 "error: Invalid name `invalid!`: not an identifier",
510 ); 549 );
511 } 550 }
@@ -514,7 +553,7 @@ mod tests {
514 fn test_rename_to_invalid_identifier2() { 553 fn test_rename_to_invalid_identifier2() {
515 check( 554 check(
516 "multiple tokens", 555 "multiple tokens",
517 r#"fn main() { let i<|> = 1; }"#, 556 r#"fn main() { let i$0 = 1; }"#,
518 "error: Invalid name `multiple tokens`: not an identifier", 557 "error: Invalid name `multiple tokens`: not an identifier",
519 ); 558 );
520 } 559 }
@@ -523,38 +562,60 @@ mod tests {
523 fn test_rename_to_invalid_identifier3() { 562 fn test_rename_to_invalid_identifier3() {
524 check( 563 check(
525 "let", 564 "let",
526 r#"fn main() { let i<|> = 1; }"#, 565 r#"fn main() { let i$0 = 1; }"#,
527 "error: Invalid name `let`: not an identifier", 566 "error: Invalid name `let`: not an identifier",
528 ); 567 );
529 } 568 }
530 569
531 #[test] 570 #[test]
532 fn test_rename_to_invalid_identifier_lifetime() { 571 fn test_rename_to_invalid_identifier_lifetime() {
572 mark::check!(rename_not_an_ident_ref);
533 check( 573 check(
534 "'foo", 574 "'foo",
535 r#"fn main() { let i<|> = 1; }"#, 575 r#"fn main() { let i$0 = 1; }"#,
536 "error: Invalid name `'foo`: not an identifier", 576 "error: Invalid name `'foo`: not an identifier",
537 ); 577 );
538 } 578 }
539 579
540 #[test] 580 #[test]
541 fn test_rename_to_invalid_identifier_lifetime2() { 581 fn test_rename_to_invalid_identifier_lifetime2() {
582 mark::check!(rename_not_a_lifetime_ident_ref);
542 check( 583 check(
543 "foo", 584 "foo",
544 r#"fn main<'a>(_: &'a<|> ()) {}"#, 585 r#"fn main<'a>(_: &'a$0 ()) {}"#,
545 "error: Invalid name `foo`: not a lifetime identifier", 586 "error: Invalid name `foo`: not a lifetime identifier",
546 ); 587 );
547 } 588 }
548 589
549 #[test] 590 #[test]
591 fn test_rename_to_underscore_invalid() {
592 mark::check!(rename_underscore_multiple);
593 check(
594 "_",
595 r#"fn main(foo$0: ()) {foo;}"#,
596 "error: Cannot rename reference to `_` as it is being referenced multiple times",
597 );
598 }
599
600 #[test]
601 fn test_rename_mod_invalid() {
602 check(
603 "'foo",
604 r#"mod foo$0 {}"#,
605 "error: Invalid name `'foo`: cannot rename module to 'foo",
606 );
607 }
608
609 #[test]
550 fn test_rename_for_local() { 610 fn test_rename_for_local() {
611 mark::check!(rename_ident);
551 check( 612 check(
552 "k", 613 "k",
553 r#" 614 r#"
554fn main() { 615fn main() {
555 let mut i = 1; 616 let mut i = 1;
556 let j = 1; 617 let j = 1;
557 i = i<|> + j; 618 i = i$0 + j;
558 619
559 { i = 0; } 620 { i = 0; }
560 621
@@ -579,7 +640,7 @@ fn main() {
579 fn test_rename_unresolved_reference() { 640 fn test_rename_unresolved_reference() {
580 check( 641 check(
581 "new_name", 642 "new_name",
582 r#"fn main() { let _ = unresolved_ref<|>; }"#, 643 r#"fn main() { let _ = unresolved_ref$0; }"#,
583 "error: No references found at position", 644 "error: No references found at position",
584 ); 645 );
585 } 646 }
@@ -591,7 +652,7 @@ fn main() {
591 r#" 652 r#"
592macro_rules! foo {($i:ident) => {$i} } 653macro_rules! foo {($i:ident) => {$i} }
593fn main() { 654fn main() {
594 let a<|> = "test"; 655 let a$0 = "test";
595 foo!(a); 656 foo!(a);
596} 657}
597"#, 658"#,
@@ -613,7 +674,7 @@ fn main() {
613macro_rules! foo {($i:ident) => {$i} } 674macro_rules! foo {($i:ident) => {$i} }
614fn main() { 675fn main() {
615 let a = "test"; 676 let a = "test";
616 foo!(a<|>); 677 foo!(a$0);
617} 678}
618"#, 679"#,
619 r#" 680 r#"
@@ -634,7 +695,7 @@ fn main() {
634macro_rules! define_fn {($id:ident) => { fn $id{} }} 695macro_rules! define_fn {($id:ident) => { fn $id{} }}
635define_fn!(foo); 696define_fn!(foo);
636fn main() { 697fn main() {
637 fo<|>o(); 698 fo$0o();
638} 699}
639"#, 700"#,
640 r#" 701 r#"
@@ -653,7 +714,7 @@ fn main() {
653 "bar", 714 "bar",
654 r#" 715 r#"
655macro_rules! define_fn {($id:ident) => { fn $id{} }} 716macro_rules! define_fn {($id:ident) => { fn $id{} }}
656define_fn!(fo<|>o); 717define_fn!(fo$0o);
657fn main() { 718fn main() {
658 foo(); 719 foo();
659} 720}
@@ -670,17 +731,17 @@ fn main() {
670 731
671 #[test] 732 #[test]
672 fn test_rename_for_param_inside() { 733 fn test_rename_for_param_inside() {
673 check("j", r#"fn foo(i : u32) -> u32 { i<|> }"#, r#"fn foo(j : u32) -> u32 { j }"#); 734 check("j", r#"fn foo(i : u32) -> u32 { i$0 }"#, r#"fn foo(j : u32) -> u32 { j }"#);
674 } 735 }
675 736
676 #[test] 737 #[test]
677 fn test_rename_refs_for_fn_param() { 738 fn test_rename_refs_for_fn_param() {
678 check("j", r#"fn foo(i<|> : u32) -> u32 { i }"#, r#"fn foo(j : u32) -> u32 { j }"#); 739 check("j", r#"fn foo(i$0 : u32) -> u32 { i }"#, r#"fn foo(j : u32) -> u32 { j }"#);
679 } 740 }
680 741
681 #[test] 742 #[test]
682 fn test_rename_for_mut_param() { 743 fn test_rename_for_mut_param() {
683 check("j", r#"fn foo(mut i<|> : u32) -> u32 { i }"#, r#"fn foo(mut j : u32) -> u32 { j }"#); 744 check("j", r#"fn foo(mut i$0 : u32) -> u32 { i }"#, r#"fn foo(mut j : u32) -> u32 { j }"#);
684 } 745 }
685 746
686 #[test] 747 #[test]
@@ -688,7 +749,7 @@ fn main() {
688 check( 749 check(
689 "j", 750 "j",
690 r#" 751 r#"
691struct Foo { i<|>: i32 } 752struct Foo { i$0: i32 }
692 753
693impl Foo { 754impl Foo {
694 fn new(i: i32) -> Self { 755 fn new(i: i32) -> Self {
@@ -714,7 +775,7 @@ impl Foo {
714 check( 775 check(
715 "j", 776 "j",
716 r#" 777 r#"
717struct Foo { i<|>: i32 } 778struct Foo { i$0: i32 }
718 779
719impl Foo { 780impl Foo {
720 fn new(i: i32) -> Self { 781 fn new(i: i32) -> Self {
@@ -743,7 +804,7 @@ impl Foo {
743struct Foo { i: i32 } 804struct Foo { i: i32 }
744 805
745impl Foo { 806impl Foo {
746 fn new(i<|>: i32) -> Self { 807 fn new(i$0: i32) -> Self {
747 Self { i } 808 Self { i }
748 } 809 }
749} 810}
@@ -765,7 +826,7 @@ impl Foo {
765 check( 826 check(
766 "j", 827 "j",
767 r#" 828 r#"
768struct Foo { i<|>: i32 } 829struct Foo { i$0: i32 }
769struct Bar { i: i32 } 830struct Bar { i: i32 }
770 831
771impl Bar { 832impl Bar {
@@ -794,7 +855,7 @@ impl Bar {
794 r#" 855 r#"
795struct Foo { i: i32 } 856struct Foo { i: i32 }
796 857
797fn baz(i<|>: i32) -> Self { 858fn baz(i$0: i32) -> Self {
798 let x = Foo { i }; 859 let x = Foo { i };
799 { 860 {
800 let i = 0; 861 let i = 0;
@@ -825,7 +886,7 @@ fn baz(j: i32) -> Self {
825mod bar; 886mod bar;
826 887
827//- /bar.rs 888//- /bar.rs
828mod foo<|>; 889mod foo$0;
829 890
830//- /bar/foo.rs 891//- /bar/foo.rs
831// empty 892// empty
@@ -883,7 +944,7 @@ fn main() {}
883pub struct FooContent; 944pub struct FooContent;
884 945
885//- /bar.rs 946//- /bar.rs
886use crate::foo<|>::FooContent; 947use crate::foo$0::FooContent;
887"#, 948"#,
888 expect![[r#" 949 expect![[r#"
889 RangeInfo { 950 RangeInfo {
@@ -943,9 +1004,9 @@ use crate::foo<|>::FooContent;
943 "foo2", 1004 "foo2",
944 r#" 1005 r#"
945//- /lib.rs 1006//- /lib.rs
946mod fo<|>o; 1007mod fo$0o;
947//- /foo/mod.rs 1008//- /foo/mod.rs
948// emtpy 1009// empty
949"#, 1010"#,
950 expect![[r#" 1011 expect![[r#"
951 RangeInfo { 1012 RangeInfo {
@@ -992,10 +1053,10 @@ mod fo<|>o;
992 "bar", 1053 "bar",
993 r#" 1054 r#"
994//- /lib.rs 1055//- /lib.rs
995mod outer { mod fo<|>o; } 1056mod outer { mod fo$0o; }
996 1057
997//- /outer/foo.rs 1058//- /outer/foo.rs
998// emtpy 1059// empty
999"#, 1060"#,
1000 expect![[r#" 1061 expect![[r#"
1001 RangeInfo { 1062 RangeInfo {
@@ -1041,7 +1102,7 @@ mod outer { mod fo<|>o; }
1041 check( 1102 check(
1042 "baz", 1103 "baz",
1043 r#" 1104 r#"
1044mod <|>foo { pub fn bar() {} } 1105mod $0foo { pub fn bar() {} }
1045 1106
1046fn main() { foo::bar(); } 1107fn main() { foo::bar(); }
1047"#, 1108"#,
@@ -1065,7 +1126,7 @@ fn f() {
1065} 1126}
1066 1127
1067//- /bar.rs 1128//- /bar.rs
1068pub mod foo<|>; 1129pub mod foo$0;
1069 1130
1070//- /bar/foo.rs 1131//- /bar/foo.rs
1071// pub fn fun() {} 1132// pub fn fun() {}
@@ -1128,7 +1189,7 @@ pub mod foo<|>;
1128 "Baz", 1189 "Baz",
1129 r#" 1190 r#"
1130mod foo { 1191mod foo {
1131 pub enum Foo { Bar<|> } 1192 pub enum Foo { Bar$0 }
1132} 1193}
1133 1194
1134fn func(f: foo::Foo) { 1195fn func(f: foo::Foo) {
@@ -1157,7 +1218,7 @@ fn func(f: foo::Foo) {
1157 "baz", 1218 "baz",
1158 r#" 1219 r#"
1159mod foo { 1220mod foo {
1160 pub struct Foo { pub bar<|>: uint } 1221 pub struct Foo { pub bar$0: uint }
1161} 1222}
1162 1223
1163fn foo(f: foo::Foo) { 1224fn foo(f: foo::Foo) {
@@ -1178,13 +1239,14 @@ fn foo(f: foo::Foo) {
1178 1239
1179 #[test] 1240 #[test]
1180 fn test_parameter_to_self() { 1241 fn test_parameter_to_self() {
1242 mark::check!(rename_to_self);
1181 check( 1243 check(
1182 "self", 1244 "self",
1183 r#" 1245 r#"
1184struct Foo { i: i32 } 1246struct Foo { i: i32 }
1185 1247
1186impl Foo { 1248impl Foo {
1187 fn f(foo<|>: &mut Foo) -> i32 { 1249 fn f(foo$0: &mut Foo) -> i32 {
1188 foo.i 1250 foo.i
1189 } 1251 }
1190} 1252}
@@ -1205,7 +1267,7 @@ impl Foo {
1205struct Foo { i: i32 } 1267struct Foo { i: i32 }
1206 1268
1207impl Foo { 1269impl Foo {
1208 fn f(foo<|>: Foo) -> i32 { 1270 fn f(foo$0: Foo) -> i32 {
1209 foo.i 1271 foo.i
1210 } 1272 }
1211} 1273}
@@ -1229,7 +1291,7 @@ impl Foo {
1229 r#" 1291 r#"
1230struct Foo { i: i32 } 1292struct Foo { i: i32 }
1231 1293
1232fn f(foo<|>: &mut Foo) -> i32 { 1294fn f(foo$0: &mut Foo) -> i32 {
1233 foo.i 1295 foo.i
1234} 1296}
1235"#, 1297"#,
@@ -1242,7 +1304,7 @@ struct Foo { i: i32 }
1242struct Bar; 1304struct Bar;
1243 1305
1244impl Bar { 1306impl Bar {
1245 fn f(foo<|>: &mut Foo) -> i32 { 1307 fn f(foo$0: &mut Foo) -> i32 {
1246 foo.i 1308 foo.i
1247 } 1309 }
1248} 1310}
@@ -1258,7 +1320,7 @@ impl Bar {
1258 r#" 1320 r#"
1259struct Foo { i: i32 } 1321struct Foo { i: i32 }
1260impl Foo { 1322impl Foo {
1261 fn f(x: (), foo<|>: &mut Foo) -> i32 { 1323 fn f(x: (), foo$0: &mut Foo) -> i32 {
1262 foo.i 1324 foo.i
1263 } 1325 }
1264} 1326}
@@ -1274,7 +1336,7 @@ impl Foo {
1274 r#" 1336 r#"
1275struct Foo { i: i32 } 1337struct Foo { i: i32 }
1276impl &Foo { 1338impl &Foo {
1277 fn f(foo<|>: &Foo) -> i32 { 1339 fn f(foo$0: &Foo) -> i32 {
1278 foo.i 1340 foo.i
1279 } 1341 }
1280} 1342}
@@ -1298,7 +1360,7 @@ impl &Foo {
1298struct Foo { i: i32 } 1360struct Foo { i: i32 }
1299 1361
1300impl Foo { 1362impl Foo {
1301 fn f(&mut <|>self) -> i32 { 1363 fn f(&mut $0self) -> i32 {
1302 self.i 1364 self.i
1303 } 1365 }
1304} 1366}
@@ -1323,7 +1385,7 @@ impl Foo {
1323struct Foo { i: i32 } 1385struct Foo { i: i32 }
1324 1386
1325impl Foo { 1387impl Foo {
1326 fn f(<|>self) -> i32 { 1388 fn f($0self) -> i32 {
1327 self.i 1389 self.i
1328 } 1390 }
1329} 1391}
@@ -1350,7 +1412,7 @@ struct Foo { i: i32 }
1350impl Foo { 1412impl Foo {
1351 fn f(&self) -> i32 { 1413 fn f(&self) -> i32 {
1352 let self_var = 1; 1414 let self_var = 1;
1353 self<|>.i 1415 self$0.i
1354 } 1416 }
1355} 1417}
1356"#, 1418"#,
@@ -1373,7 +1435,7 @@ impl Foo {
1373 check( 1435 check(
1374 "bar", 1436 "bar",
1375 r#" 1437 r#"
1376struct Foo { i<|>: i32 } 1438struct Foo { i$0: i32 }
1377 1439
1378fn foo(bar: i32) -> Foo { 1440fn foo(bar: i32) -> Foo {
1379 Foo { i: bar } 1441 Foo { i: bar }
@@ -1394,7 +1456,7 @@ fn foo(bar: i32) -> Foo {
1394 check( 1456 check(
1395 "baz", 1457 "baz",
1396 r#" 1458 r#"
1397struct Foo { i<|>: i32 } 1459struct Foo { i$0: i32 }
1398 1460
1399fn foo(foo: Foo) { 1461fn foo(foo: Foo) {
1400 let Foo { i: baz } = foo; 1462 let Foo { i: baz } = foo;
@@ -1433,7 +1495,7 @@ struct Foo {
1433 1495
1434fn foo(foo: Foo) { 1496fn foo(foo: Foo) {
1435 let Foo { i: b } = foo; 1497 let Foo { i: b } = foo;
1436 let _ = b<|>; 1498 let _ = b$0;
1437} 1499}
1438"#, 1500"#,
1439 expected_fixture, 1501 expected_fixture,
@@ -1447,7 +1509,7 @@ struct Foo {
1447 1509
1448fn foo(foo: Foo) { 1510fn foo(foo: Foo) {
1449 let Foo { i } = foo; 1511 let Foo { i } = foo;
1450 let _ = i<|>; 1512 let _ = i$0;
1451} 1513}
1452"#, 1514"#,
1453 expected_fixture, 1515 expected_fixture,
@@ -1464,7 +1526,7 @@ struct Foo {
1464} 1526}
1465 1527
1466fn foo(Foo { i }: foo) -> i32 { 1528fn foo(Foo { i }: foo) -> i32 {
1467 i<|> 1529 i$0
1468} 1530}
1469"#, 1531"#,
1470 r#" 1532 r#"
@@ -1481,6 +1543,7 @@ fn foo(Foo { i: bar }: foo) -> i32 {
1481 1543
1482 #[test] 1544 #[test]
1483 fn test_rename_lifetimes() { 1545 fn test_rename_lifetimes() {
1546 mark::check!(rename_lifetime);
1484 check( 1547 check(
1485 "'yeeee", 1548 "'yeeee",
1486 r#" 1549 r#"
@@ -1488,7 +1551,7 @@ trait Foo<'a> {
1488 fn foo() -> &'a (); 1551 fn foo() -> &'a ();
1489} 1552}
1490impl<'a> Foo<'a> for &'a () { 1553impl<'a> Foo<'a> for &'a () {
1491 fn foo() -> &'a<|> () { 1554 fn foo() -> &'a$0 () {
1492 unimplemented!() 1555 unimplemented!()
1493 } 1556 }
1494} 1557}
@@ -1520,7 +1583,7 @@ fn main() {
1520 let test_variable = CustomOption::Some(22); 1583 let test_variable = CustomOption::Some(22);
1521 1584
1522 match test_variable { 1585 match test_variable {
1523 CustomOption::Some(foo<|>) if foo == 11 => {} 1586 CustomOption::Some(foo$0) if foo == 11 => {}
1524 _ => (), 1587 _ => (),
1525 } 1588 }
1526}"#, 1589}"#,
@@ -1549,7 +1612,7 @@ fn main() {
1549fn foo<'a>() -> &'a () { 1612fn foo<'a>() -> &'a () {
1550 'a: { 1613 'a: {
1551 'b: loop { 1614 'b: loop {
1552 break 'a<|>; 1615 break 'a$0;
1553 } 1616 }
1554 } 1617 }
1555} 1618}
@@ -1565,4 +1628,24 @@ fn foo<'a>() -> &'a () {
1565"#, 1628"#,
1566 ) 1629 )
1567 } 1630 }
1631
1632 #[test]
1633 fn test_self_to_self() {
1634 mark::check!(rename_self_to_self);
1635 check(
1636 "self",
1637 r#"
1638struct Foo;
1639impl Foo {
1640 fn foo(self$0) {}
1641}
1642"#,
1643 r#"
1644struct Foo;
1645impl Foo {
1646 fn foo(self) {}
1647}
1648"#,
1649 )
1650 }
1568} 1651}
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 891183266..f5ee7de86 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -2,11 +2,11 @@ use std::fmt;
2 2
3use assists::utils::test_related_attribute; 3use assists::utils::test_related_attribute;
4use cfg::CfgExpr; 4use cfg::CfgExpr;
5use hir::{AsAssocItem, HasAttrs, InFile, Semantics}; 5use hir::{AsAssocItem, HasAttrs, HasSource, Semantics};
6use ide_db::RootDatabase; 6use ide_db::{defs::Definition, RootDatabase};
7use itertools::Itertools; 7use itertools::Itertools;
8use syntax::{ 8use syntax::{
9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, 9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner},
10 match_ast, SyntaxNode, 10 match_ast, SyntaxNode,
11}; 11};
12 12
@@ -96,41 +96,45 @@ impl Runnable {
96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
97 let sema = Semantics::new(db); 97 let sema = Semantics::new(db);
98 let source_file = sema.parse(file_id); 98 let source_file = sema.parse(file_id);
99 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect() 99 source_file
100} 100 .syntax()
101 101 .descendants()
102pub(crate) fn runnable( 102 .filter_map(|item| {
103 sema: &Semantics<RootDatabase>, 103 let runnable = match_ast! {
104 item: SyntaxNode, 104 match item {
105 file_id: FileId, 105 ast::Fn(func) => {
106) -> Option<Runnable> { 106 let def = sema.to_def(&func)?;
107 let runnable_item = match_ast! { 107 runnable_fn(&sema, def)
108 match (item.clone()) { 108 },
109 ast::Fn(it) => runnable_fn(sema, it, file_id), 109 ast::Module(it) => runnable_mod(&sema, it),
110 ast::Module(it) => runnable_mod(sema, it), 110 _ => None,
111 _ => None, 111 }
112 } 112 };
113 }; 113 runnable.or_else(|| match doc_owner_to_def(&sema, item)? {
114 runnable_item.or_else(|| runnable_doctest(sema, item)) 114 Definition::ModuleDef(def) => module_def_doctest(&sema, def),
115 _ => None,
116 })
117 })
118 .collect()
115} 119}
116 120
117fn runnable_fn(sema: &Semantics<RootDatabase>, func: ast::Fn, file_id: FileId) -> Option<Runnable> { 121pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
118 let def = sema.to_def(&func)?; 122 let func = def.source(sema.db)?;
119 let name_string = func.name()?.text().to_string(); 123 let name_string = def.name(sema.db).to_string();
120 124
121 let kind = if name_string == "main" { 125 let kind = if name_string == "main" {
122 RunnableKind::Bin 126 RunnableKind::Bin
123 } else { 127 } else {
124 let canonical_path = sema.to_def(&func).and_then(|def| { 128 let canonical_path = {
125 let def: hir::ModuleDef = def.into(); 129 let def: hir::ModuleDef = def.into();
126 def.canonical_path(sema.db) 130 def.canonical_path(sema.db)
127 }); 131 };
128 let test_id = canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name_string)); 132 let test_id = canonical_path.map(TestId::Path).unwrap_or(TestId::Name(name_string));
129 133
130 if test_related_attribute(&func).is_some() { 134 if test_related_attribute(&func.value).is_some() {
131 let attr = TestAttr::from_fn(&func); 135 let attr = TestAttr::from_fn(&func.value);
132 RunnableKind::Test { test_id, attr } 136 RunnableKind::Test { test_id, attr }
133 } else if func.has_atom_attr("bench") { 137 } else if func.value.has_atom_attr("bench") {
134 RunnableKind::Bench { test_id } 138 RunnableKind::Bench { test_id }
135 } else { 139 } else {
136 return None; 140 return None;
@@ -139,27 +143,56 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, func: ast::Fn, file_id: FileId) -
139 143
140 let nav = NavigationTarget::from_named( 144 let nav = NavigationTarget::from_named(
141 sema.db, 145 sema.db,
142 InFile::new(file_id.into(), &func), 146 func.as_ref().map(|it| it as &dyn ast::NameOwner),
143 SymbolKind::Function, 147 SymbolKind::Function,
144 ); 148 );
145 let cfg = def.attrs(sema.db).cfg(); 149 let cfg = def.attrs(sema.db).cfg();
146 Some(Runnable { nav, kind, cfg }) 150 Some(Runnable { nav, kind, cfg })
147} 151}
148 152
149fn runnable_doctest(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { 153pub(crate) fn runnable_mod(
150 match_ast! { 154 sema: &Semantics<RootDatabase>,
155 module: ast::Module,
156) -> Option<Runnable> {
157 if !has_test_function_or_multiple_test_submodules(&module) {
158 return None;
159 }
160 let module_def = sema.to_def(&module)?;
161
162 let path = module_def
163 .path_to_root(sema.db)
164 .into_iter()
165 .rev()
166 .filter_map(|it| it.name(sema.db))
167 .join("::");
168
169 let def = sema.to_def(&module)?;
170 let attrs = def.attrs(sema.db);
171 let cfg = attrs.cfg();
172 let nav = module_def.to_nav(sema.db);
173 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg })
174}
175
176// FIXME: figure out a proper API here.
177pub(crate) fn doc_owner_to_def(
178 sema: &Semantics<RootDatabase>,
179 item: SyntaxNode,
180) -> Option<Definition> {
181 let res: hir::ModuleDef = match_ast! {
151 match item { 182 match item {
152 ast::Fn(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 183 ast::SourceFile(it) => sema.scope(&item).module()?.into(),
153 ast::Struct(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 184 ast::Fn(it) => sema.to_def(&it)?.into(),
154 ast::Enum(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 185 ast::Struct(it) => sema.to_def(&it)?.into(),
155 ast::Union(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 186 ast::Enum(it) => sema.to_def(&it)?.into(),
156 ast::Trait(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 187 ast::Union(it) => sema.to_def(&it)?.into(),
157 ast::Const(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 188 ast::Trait(it) => sema.to_def(&it)?.into(),
158 ast::Static(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 189 ast::Const(it) => sema.to_def(&it)?.into(),
159 ast::TypeAlias(it) => module_def_doctest(sema, sema.to_def(&it)?.into()), 190 ast::Static(it) => sema.to_def(&it)?.into(),
160 _ => None, 191 ast::TypeAlias(it) => sema.to_def(&it)?.into(),
192 _ => return None,
161 } 193 }
162 } 194 };
195 Some(Definition::ModuleDef(res))
163} 196}
164 197
165fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> { 198fn module_def_doctest(sema: &Semantics<RootDatabase>, def: hir::ModuleDef) -> Option<Runnable> {
@@ -230,7 +263,7 @@ impl TestAttr {
230 263
231const RUSTDOC_FENCE: &str = "```"; 264const RUSTDOC_FENCE: &str = "```";
232const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = 265const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
233 &["", "rust", "should_panic", "edition2015", "edition2018"]; 266 &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
234 267
235fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool { 268fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
236 attrs.docs().map_or(false, |doc| { 269 attrs.docs().map_or(false, |doc| {
@@ -254,26 +287,6 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
254 }) 287 })
255} 288}
256 289
257fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> {
258 if !has_test_function_or_multiple_test_submodules(&module) {
259 return None;
260 }
261 let module_def = sema.to_def(&module)?;
262
263 let path = module_def
264 .path_to_root(sema.db)
265 .into_iter()
266 .rev()
267 .filter_map(|it| it.name(sema.db))
268 .join("::");
269
270 let def = sema.to_def(&module)?;
271 let attrs = def.attrs(sema.db);
272 let cfg = attrs.cfg();
273 let nav = module_def.to_nav(sema.db);
274 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg })
275}
276
277// We could create runnables for modules with number_of_test_submodules > 0, 290// We could create runnables for modules with number_of_test_submodules > 0,
278// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already 291// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already
279fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { 292fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool {
@@ -330,7 +343,7 @@ mod tests {
330 check( 343 check(
331 r#" 344 r#"
332//- /lib.rs 345//- /lib.rs
333<|> 346$0
334fn main() {} 347fn main() {}
335 348
336#[test] 349#[test]
@@ -426,7 +439,7 @@ fn bench() {}
426 check( 439 check(
427 r#" 440 r#"
428//- /lib.rs 441//- /lib.rs
429<|> 442$0
430fn main() {} 443fn main() {}
431 444
432/// ``` 445/// ```
@@ -574,7 +587,7 @@ struct StructWithRunnable(String);
574 check( 587 check(
575 r#" 588 r#"
576//- /lib.rs 589//- /lib.rs
577<|> 590$0
578fn main() {} 591fn main() {}
579 592
580struct Data; 593struct Data;
@@ -626,7 +639,7 @@ impl Data {
626 check( 639 check(
627 r#" 640 r#"
628//- /lib.rs 641//- /lib.rs
629<|> 642$0
630mod test_mod { 643mod test_mod {
631 #[test] 644 #[test]
632 fn test_foo1() {} 645 fn test_foo1() {}
@@ -680,7 +693,7 @@ mod test_mod {
680 check( 693 check(
681 r#" 694 r#"
682//- /lib.rs 695//- /lib.rs
683<|> 696$0
684mod root_tests { 697mod root_tests {
685 mod nested_tests_0 { 698 mod nested_tests_0 {
686 mod nested_tests_1 { 699 mod nested_tests_1 {
@@ -820,7 +833,7 @@ mod root_tests {
820 check( 833 check(
821 r#" 834 r#"
822//- /lib.rs crate:foo cfg:feature=foo 835//- /lib.rs crate:foo cfg:feature=foo
823<|> 836$0
824#[test] 837#[test]
825#[cfg(feature = "foo")] 838#[cfg(feature = "foo")]
826fn test_foo1() {} 839fn test_foo1() {}
@@ -865,7 +878,7 @@ fn test_foo1() {}
865 check( 878 check(
866 r#" 879 r#"
867//- /lib.rs crate:foo cfg:feature=foo,feature=bar 880//- /lib.rs crate:foo cfg:feature=foo,feature=bar
868<|> 881$0
869#[test] 882#[test]
870#[cfg(all(feature = "foo", feature = "bar"))] 883#[cfg(all(feature = "foo", feature = "bar"))]
871fn test_foo1() {} 884fn test_foo1() {}
@@ -920,7 +933,7 @@ fn test_foo1() {}
920 check( 933 check(
921 r#" 934 r#"
922//- /lib.rs 935//- /lib.rs
923<|> 936$0
924mod test_mod { 937mod test_mod {
925 fn foo1() {} 938 fn foo1() {}
926} 939}
@@ -939,7 +952,7 @@ mod test_mod {
939//- /lib.rs 952//- /lib.rs
940mod foo; 953mod foo;
941//- /foo.rs 954//- /foo.rs
942struct Foo;<|> 955struct Foo;$0
943impl Foo { 956impl Foo {
944 /// ``` 957 /// ```
945 /// let x = 5; 958 /// let x = 5;
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 5ad96581b..f2d4da78d 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -1,35 +1,39 @@
1pub(crate) mod tags;
2
3mod highlights;
4mod injector;
5
6mod highlight;
1mod format; 7mod format;
2mod html;
3mod injection;
4mod macro_rules; 8mod macro_rules;
5pub(crate) mod tags; 9mod inject;
10
11mod html;
6#[cfg(test)] 12#[cfg(test)]
7mod tests; 13mod tests;
8 14
9use hir::{AsAssocItem, Local, Name, Semantics, VariantDef}; 15use hir::{Name, Semantics};
10use ide_db::{ 16use ide_db::RootDatabase;
11 defs::{Definition, NameClass, NameRefClass},
12 RootDatabase,
13};
14use rustc_hash::FxHashMap; 17use rustc_hash::FxHashMap;
15use syntax::{ 18use syntax::{
16 ast::{self, HasFormatSpecifier}, 19 ast::{self, HasFormatSpecifier},
17 AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, 20 AstNode, AstToken, Direction, NodeOrToken,
18 SyntaxKind::{self, *}, 21 SyntaxKind::*,
19 SyntaxNode, SyntaxToken, TextRange, WalkEvent, T, 22 SyntaxNode, TextRange, WalkEvent, T,
20}; 23};
21 24
22use crate::{ 25use crate::{
23 syntax_highlighting::{ 26 syntax_highlighting::{
24 format::FormatStringHighlighter, macro_rules::MacroRulesHighlighter, tags::Highlight, 27 format::highlight_format_string, highlights::Highlights,
28 macro_rules::MacroRulesHighlighter, tags::Highlight,
25 }, 29 },
26 FileId, HighlightModifier, HighlightTag, SymbolKind, 30 FileId, HlMod, HlTag, SymbolKind,
27}; 31};
28 32
29pub(crate) use html::highlight_as_html; 33pub(crate) use html::highlight_as_html;
30 34
31#[derive(Debug, Clone)] 35#[derive(Debug, Clone, Copy)]
32pub struct HighlightedRange { 36pub struct HlRange {
33 pub range: TextRange, 37 pub range: TextRange,
34 pub highlight: Highlight, 38 pub highlight: Highlight,
35 pub binding_hash: Option<u64>, 39 pub binding_hash: Option<u64>,
@@ -49,7 +53,7 @@ pub(crate) fn highlight(
49 file_id: FileId, 53 file_id: FileId,
50 range_to_highlight: Option<TextRange>, 54 range_to_highlight: Option<TextRange>,
51 syntactic_name_ref_highlighting: bool, 55 syntactic_name_ref_highlighting: bool,
52) -> Vec<HighlightedRange> { 56) -> Vec<HlRange> {
53 let _p = profile::span("highlight"); 57 let _p = profile::span("highlight");
54 let sema = Semantics::new(db); 58 let sema = Semantics::new(db);
55 59
@@ -68,28 +72,30 @@ pub(crate) fn highlight(
68 } 72 }
69 }; 73 };
70 74
75 let mut hl = highlights::Highlights::new(range_to_highlight);
76 traverse(&mut hl, &sema, &root, range_to_highlight, syntactic_name_ref_highlighting);
77 hl.to_vec()
78}
79
80fn traverse(
81 hl: &mut Highlights,
82 sema: &Semantics<RootDatabase>,
83 root: &SyntaxNode,
84 range_to_highlight: TextRange,
85 syntactic_name_ref_highlighting: bool,
86) {
71 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); 87 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
72 // We use a stack for the DFS traversal below.
73 // When we leave a node, the we use it to flatten the highlighted ranges.
74 let mut stack = HighlightedRangeStack::new();
75 88
76 let mut current_macro_call: Option<ast::MacroCall> = None; 89 let mut current_macro_call: Option<ast::MacroCall> = None;
77 let mut current_macro_rules: Option<ast::MacroRules> = None; 90 let mut current_macro_rules: Option<ast::MacroRules> = None;
78 let mut format_string_highlighter = FormatStringHighlighter::default();
79 let mut macro_rules_highlighter = MacroRulesHighlighter::default(); 91 let mut macro_rules_highlighter = MacroRulesHighlighter::default();
80 let mut inside_attribute = false; 92 let mut inside_attribute = false;
81 93
82 // Walk all nodes, keeping track of whether we are inside a macro or not. 94 // Walk all nodes, keeping track of whether we are inside a macro or not.
83 // If in macro, expand it first and highlight the expanded code. 95 // If in macro, expand it first and highlight the expanded code.
84 for event in root.preorder_with_tokens() { 96 for event in root.preorder_with_tokens() {
85 match &event {
86 WalkEvent::Enter(_) => stack.push(),
87 WalkEvent::Leave(_) => stack.pop(),
88 };
89
90 let event_range = match &event { 97 let event_range = match &event {
91 WalkEvent::Enter(it) => it.text_range(), 98 WalkEvent::Enter(it) | WalkEvent::Leave(it) => it.text_range(),
92 WalkEvent::Leave(it) => it.text_range(),
93 }; 99 };
94 100
95 // Element outside of the viewport, no need to highlight 101 // Element outside of the viewport, no need to highlight
@@ -101,9 +107,9 @@ pub(crate) fn highlight(
101 match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) { 107 match event.clone().map(|it| it.into_node().and_then(ast::MacroCall::cast)) {
102 WalkEvent::Enter(Some(mc)) => { 108 WalkEvent::Enter(Some(mc)) => {
103 if let Some(range) = macro_call_range(&mc) { 109 if let Some(range) = macro_call_range(&mc) {
104 stack.add(HighlightedRange { 110 hl.add(HlRange {
105 range, 111 range,
106 highlight: HighlightTag::Symbol(SymbolKind::Macro).into(), 112 highlight: HlTag::Symbol(SymbolKind::Macro).into(),
107 binding_hash: None, 113 binding_hash: None,
108 }); 114 });
109 } 115 }
@@ -113,7 +119,6 @@ pub(crate) fn highlight(
113 WalkEvent::Leave(Some(mc)) => { 119 WalkEvent::Leave(Some(mc)) => {
114 assert_eq!(current_macro_call, Some(mc)); 120 assert_eq!(current_macro_call, Some(mc));
115 current_macro_call = None; 121 current_macro_call = None;
116 format_string_highlighter = FormatStringHighlighter::default();
117 } 122 }
118 _ => (), 123 _ => (),
119 } 124 }
@@ -131,33 +136,24 @@ pub(crate) fn highlight(
131 } 136 }
132 _ => (), 137 _ => (),
133 } 138 }
134
135 match &event { 139 match &event {
136 // Check for Rust code in documentation
137 WalkEvent::Leave(NodeOrToken::Node(node)) => {
138 if ast::Attr::can_cast(node.kind()) {
139 inside_attribute = false
140 }
141 if let Some((doctest, range_mapping, new_comments)) =
142 injection::extract_doc_comments(node)
143 {
144 injection::highlight_doc_comment(
145 doctest,
146 range_mapping,
147 new_comments,
148 &mut stack,
149 );
150 }
151 }
152 WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => { 140 WalkEvent::Enter(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
153 inside_attribute = true 141 inside_attribute = true
154 } 142 }
143 WalkEvent::Leave(NodeOrToken::Node(node)) if ast::Attr::can_cast(node.kind()) => {
144 inside_attribute = false
145 }
155 _ => (), 146 _ => (),
156 } 147 }
157 148
158 let element = match event { 149 let element = match event {
159 WalkEvent::Enter(it) => it, 150 WalkEvent::Enter(it) => it,
160 WalkEvent::Leave(_) => continue, 151 WalkEvent::Leave(it) => {
152 if let Some(node) = it.as_node() {
153 inject::doc_comment(hl, node);
154 }
155 continue;
156 }
161 }; 157 };
162 158
163 let range = element.text_range(); 159 let range = element.text_range();
@@ -177,8 +173,6 @@ pub(crate) fn highlight(
177 let token = sema.descend_into_macros(token.clone()); 173 let token = sema.descend_into_macros(token.clone());
178 let parent = token.parent(); 174 let parent = token.parent();
179 175
180 format_string_highlighter.check_for_format_string(&parent);
181
182 // We only care Name and Name_ref 176 // We only care Name and Name_ref
183 match (token.kind(), parent.kind()) { 177 match (token.kind(), parent.kind()) {
184 (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), 178 (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(),
@@ -191,213 +185,45 @@ pub(crate) fn highlight(
191 if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) { 185 if let Some(token) = element.as_token().cloned().and_then(ast::String::cast) {
192 if token.is_raw() { 186 if token.is_raw() {
193 let expanded = element_to_highlight.as_token().unwrap().clone(); 187 let expanded = element_to_highlight.as_token().unwrap().clone();
194 if injection::highlight_injection(&mut stack, &sema, token, expanded).is_some() { 188 if inject::ra_fixture(hl, &sema, token, expanded).is_some() {
195 continue; 189 continue;
196 } 190 }
197 } 191 }
198 } 192 }
199 193
200 if let Some((mut highlight, binding_hash)) = highlight_element( 194 if let Some(_) = macro_rules_highlighter.highlight(element_to_highlight.clone()) {
195 continue;
196 }
197
198 if let Some((mut highlight, binding_hash)) = highlight::element(
201 &sema, 199 &sema,
202 &mut bindings_shadow_count, 200 &mut bindings_shadow_count,
203 syntactic_name_ref_highlighting, 201 syntactic_name_ref_highlighting,
204 element_to_highlight.clone(), 202 element_to_highlight.clone(),
205 ) { 203 ) {
206 if inside_attribute { 204 if inside_attribute {
207 highlight = highlight | HighlightModifier::Attribute; 205 highlight = highlight | HlMod::Attribute;
208 }
209
210 if macro_rules_highlighter.highlight(element_to_highlight.clone()).is_none() {
211 stack.add(HighlightedRange { range, highlight, binding_hash });
212 } 206 }
213 207
214 if let Some(string) = 208 hl.add(HlRange { range, highlight, binding_hash });
215 element_to_highlight.as_token().cloned().and_then(ast::String::cast)
216 {
217 format_string_highlighter.highlight_format_string(&mut stack, &string, range);
218 // Highlight escape sequences
219 if let Some(char_ranges) = string.char_ranges() {
220 stack.push();
221 for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) {
222 if string.text()[piece_range.start().into()..].starts_with('\\') {
223 stack.add(HighlightedRange {
224 range: piece_range + range.start(),
225 highlight: HighlightTag::EscapeSequence.into(),
226 binding_hash: None,
227 });
228 }
229 }
230 stack.pop_and_inject(None);
231 }
232 }
233 } 209 }
234 }
235
236 stack.flattened()
237}
238
239#[derive(Debug)]
240struct HighlightedRangeStack {
241 stack: Vec<Vec<HighlightedRange>>,
242}
243
244/// We use a stack to implement the flattening logic for the highlighted
245/// syntax ranges.
246impl HighlightedRangeStack {
247 fn new() -> Self {
248 Self { stack: vec![Vec::new()] }
249 }
250
251 fn push(&mut self) {
252 self.stack.push(Vec::new());
253 }
254
255 /// Flattens the highlighted ranges.
256 ///
257 /// For example `#[cfg(feature = "foo")]` contains the nested ranges:
258 /// 1) parent-range: Attribute [0, 23)
259 /// 2) child-range: String [16, 21)
260 ///
261 /// The following code implements the flattening, for our example this results to:
262 /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]`
263 fn pop(&mut self) {
264 let children = self.stack.pop().unwrap();
265 let prev = self.stack.last_mut().unwrap();
266 let needs_flattening = !children.is_empty()
267 && !prev.is_empty()
268 && prev.last().unwrap().range.contains_range(children.first().unwrap().range);
269 if !needs_flattening {
270 prev.extend(children);
271 } else {
272 let mut parent = prev.pop().unwrap();
273 for ele in children {
274 assert!(parent.range.contains_range(ele.range));
275
276 let cloned = Self::intersect(&mut parent, &ele);
277 if !parent.range.is_empty() {
278 prev.push(parent);
279 }
280 prev.push(ele);
281 parent = cloned;
282 }
283 if !parent.range.is_empty() {
284 prev.push(parent);
285 }
286 }
287 }
288
289 /// Intersects the `HighlightedRange` `parent` with `child`.
290 /// `parent` is mutated in place, becoming the range before `child`.
291 /// Returns the range (of the same type as `parent`) *after* `child`.
292 fn intersect(parent: &mut HighlightedRange, child: &HighlightedRange) -> HighlightedRange {
293 assert!(parent.range.contains_range(child.range));
294
295 let mut cloned = parent.clone();
296 parent.range = TextRange::new(parent.range.start(), child.range.start());
297 cloned.range = TextRange::new(child.range.end(), cloned.range.end());
298 210
299 cloned 211 if let Some(string) = element_to_highlight.as_token().cloned().and_then(ast::String::cast) {
300 } 212 highlight_format_string(hl, &string, range);
301 213 // Highlight escape sequences
302 /// Remove the `HighlightRange` of `parent` that's currently covered by `child`. 214 if let Some(char_ranges) = string.char_ranges() {
303 fn intersect_partial(parent: &mut HighlightedRange, child: &HighlightedRange) { 215 for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) {
304 assert!( 216 if string.text()[piece_range.start().into()..].starts_with('\\') {
305 parent.range.start() <= child.range.start() 217 hl.add(HlRange {
306 && parent.range.end() >= child.range.start() 218 range: piece_range + range.start(),
307 && child.range.end() > parent.range.end() 219 highlight: HlTag::EscapeSequence.into(),
308 ); 220 binding_hash: None,
309 221 });
310 parent.range = TextRange::new(parent.range.start(), child.range.start());
311 }
312
313 /// Similar to `pop`, but can modify arbitrary prior ranges (where `pop`)
314 /// can only modify the last range currently on the stack.
315 /// Can be used to do injections that span multiple ranges, like the
316 /// doctest injection below.
317 /// If `overwrite_parent` is non-optional, the highlighting of the parent range
318 /// is overwritten with the argument.
319 ///
320 /// Note that `pop` can be simulated by `pop_and_inject(false)` but the
321 /// latter is computationally more expensive.
322 fn pop_and_inject(&mut self, overwrite_parent: Option<Highlight>) {
323 let mut children = self.stack.pop().unwrap();
324 let prev = self.stack.last_mut().unwrap();
325 children.sort_by_key(|range| range.range.start());
326 prev.sort_by_key(|range| range.range.start());
327
328 for child in children {
329 if let Some(idx) =
330 prev.iter().position(|parent| parent.range.contains_range(child.range))
331 {
332 if let Some(tag) = overwrite_parent {
333 prev[idx].highlight = tag;
334 }
335
336 let cloned = Self::intersect(&mut prev[idx], &child);
337 let insert_idx = if prev[idx].range.is_empty() {
338 prev.remove(idx);
339 idx
340 } else {
341 idx + 1
342 };
343 prev.insert(insert_idx, child);
344 if !cloned.range.is_empty() {
345 prev.insert(insert_idx + 1, cloned);
346 }
347 } else {
348 let maybe_idx =
349 prev.iter().position(|parent| parent.range.contains(child.range.start()));
350 match (overwrite_parent, maybe_idx) {
351 (Some(_), Some(idx)) => {
352 Self::intersect_partial(&mut prev[idx], &child);
353 let insert_idx = if prev[idx].range.is_empty() {
354 prev.remove(idx);
355 idx
356 } else {
357 idx + 1
358 };
359 prev.insert(insert_idx, child);
360 }
361 (_, None) => {
362 let idx = prev
363 .binary_search_by_key(&child.range.start(), |range| range.range.start())
364 .unwrap_or_else(|x| x);
365 prev.insert(idx, child);
366 }
367 _ => {
368 unreachable!("child range should be completely contained in parent range");
369 } 222 }
370 } 223 }
371 } 224 }
372 } 225 }
373 } 226 }
374
375 fn add(&mut self, range: HighlightedRange) {
376 self.stack
377 .last_mut()
378 .expect("during DFS traversal, the stack must not be empty")
379 .push(range)
380 }
381
382 fn flattened(mut self) -> Vec<HighlightedRange> {
383 assert_eq!(
384 self.stack.len(),
385 1,
386 "after DFS traversal, the stack should only contain a single element"
387 );
388 let mut res = self.stack.pop().unwrap();
389 res.sort_by_key(|range| range.range.start());
390 // Check that ranges are sorted and disjoint
391 for (left, right) in res.iter().zip(res.iter().skip(1)) {
392 assert!(
393 left.range.end() <= right.range.start(),
394 "left: {:#?}, right: {:#?}",
395 left,
396 right
397 );
398 }
399 res
400 }
401} 227}
402 228
403fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> { 229fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
@@ -415,523 +241,3 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
415 241
416 Some(TextRange::new(range_start, range_end)) 242 Some(TextRange::new(range_start, range_end))
417} 243}
418
419/// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly.
420fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool {
421 while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) {
422 if parent.kind() != *kind {
423 return false;
424 }
425
426 // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value
427 // in the same pattern is unstable: rust-lang/rust#68354.
428 node = node.parent().unwrap().into();
429 kinds = rest;
430 }
431
432 // Only true if we matched all expected kinds
433 kinds.len() == 0
434}
435
436fn is_consumed_lvalue(
437 node: NodeOrToken<SyntaxNode, SyntaxToken>,
438 local: &Local,
439 db: &RootDatabase,
440) -> bool {
441 // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming.
442 parents_match(node, &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST]) && !local.ty(db).is_copy(db)
443}
444
445fn highlight_element(
446 sema: &Semantics<RootDatabase>,
447 bindings_shadow_count: &mut FxHashMap<Name, u32>,
448 syntactic_name_ref_highlighting: bool,
449 element: SyntaxElement,
450) -> Option<(Highlight, Option<u64>)> {
451 let db = sema.db;
452 let mut binding_hash = None;
453 let highlight: Highlight = match element.kind() {
454 FN => {
455 bindings_shadow_count.clear();
456 return None;
457 }
458
459 // Highlight definitions depending on the "type" of the definition.
460 NAME => {
461 let name = element.into_node().and_then(ast::Name::cast).unwrap();
462 let name_kind = NameClass::classify(sema, &name);
463
464 if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind {
465 if let Some(name) = local.name(db) {
466 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
467 *shadow_count += 1;
468 binding_hash = Some(calc_binding_hash(&name, *shadow_count))
469 }
470 };
471
472 match name_kind {
473 Some(NameClass::ExternCrate(_)) => HighlightTag::Symbol(SymbolKind::Module).into(),
474 Some(NameClass::Definition(def)) => {
475 highlight_def(db, def) | HighlightModifier::Definition
476 }
477 Some(NameClass::ConstReference(def)) => highlight_def(db, def),
478 Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
479 let mut h = HighlightTag::Symbol(SymbolKind::Field).into();
480 if let Definition::Field(field) = field_ref {
481 if let VariantDef::Union(_) = field.parent_def(db) {
482 h |= HighlightModifier::Unsafe;
483 }
484 }
485
486 h
487 }
488 None => highlight_name_by_syntax(name) | HighlightModifier::Definition,
489 }
490 }
491
492 // Highlight references like the definitions they resolve to
493 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => {
494 // even though we track whether we are in an attribute or not we still need this special case
495 // as otherwise we would emit unresolved references for name refs inside attributes
496 Highlight::from(HighlightTag::Symbol(SymbolKind::Function))
497 }
498 NAME_REF => {
499 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
500 highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| {
501 match NameRefClass::classify(sema, &name_ref) {
502 Some(name_kind) => match name_kind {
503 NameRefClass::ExternCrate(_) => {
504 HighlightTag::Symbol(SymbolKind::Module).into()
505 }
506 NameRefClass::Definition(def) => {
507 if let Definition::Local(local) = &def {
508 if let Some(name) = local.name(db) {
509 let shadow_count =
510 bindings_shadow_count.entry(name.clone()).or_default();
511 binding_hash = Some(calc_binding_hash(&name, *shadow_count))
512 }
513 };
514
515 let mut h = highlight_def(db, def);
516
517 if let Definition::Local(local) = &def {
518 if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) {
519 h |= HighlightModifier::Consuming;
520 }
521 }
522
523 if let Some(parent) = name_ref.syntax().parent() {
524 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) {
525 if let Definition::Field(field) = def {
526 if let VariantDef::Union(_) = field.parent_def(db) {
527 h |= HighlightModifier::Unsafe;
528 }
529 }
530 }
531 }
532
533 h
534 }
535 NameRefClass::FieldShorthand { .. } => {
536 HighlightTag::Symbol(SymbolKind::Field).into()
537 }
538 },
539 None if syntactic_name_ref_highlighting => {
540 highlight_name_ref_by_syntax(name_ref, sema)
541 }
542 None => HighlightTag::UnresolvedReference.into(),
543 }
544 })
545 }
546
547 // Simple token-based highlighting
548 COMMENT => {
549 let comment = element.into_token().and_then(ast::Comment::cast)?;
550 let h = HighlightTag::Comment;
551 match comment.kind().doc {
552 Some(_) => h | HighlightModifier::Documentation,
553 None => h.into(),
554 }
555 }
556 STRING | BYTE_STRING => HighlightTag::StringLiteral.into(),
557 ATTR => HighlightTag::Attribute.into(),
558 INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(),
559 BYTE => HighlightTag::ByteLiteral.into(),
560 CHAR => HighlightTag::CharLiteral.into(),
561 QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow,
562 LIFETIME => {
563 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap();
564
565 match NameClass::classify_lifetime(sema, &lifetime) {
566 Some(NameClass::Definition(def)) => {
567 highlight_def(db, def) | HighlightModifier::Definition
568 }
569 None => match NameRefClass::classify_lifetime(sema, &lifetime) {
570 Some(NameRefClass::Definition(def)) => highlight_def(db, def),
571 _ => Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)),
572 },
573 _ => {
574 Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam))
575 | HighlightModifier::Definition
576 }
577 }
578 }
579 p if p.is_punct() => match p {
580 T![&] => {
581 let h = HighlightTag::Operator.into();
582 let is_unsafe = element
583 .parent()
584 .and_then(ast::RefExpr::cast)
585 .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr))
586 .unwrap_or(false);
587 if is_unsafe {
588 h | HighlightModifier::Unsafe
589 } else {
590 h
591 }
592 }
593 T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => {
594 HighlightTag::Operator.into()
595 }
596 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
597 HighlightTag::Symbol(SymbolKind::Macro).into()
598 }
599 T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => {
600 HighlightTag::BuiltinType.into()
601 }
602 T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => {
603 HighlightTag::Keyword.into()
604 }
605 T![*] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
606 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
607
608 let expr = prefix_expr.expr()?;
609 let ty = sema.type_of_expr(&expr)?;
610 if ty.is_raw_ptr() {
611 HighlightTag::Operator | HighlightModifier::Unsafe
612 } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() {
613 HighlightTag::Operator.into()
614 } else {
615 HighlightTag::Punctuation.into()
616 }
617 }
618 T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
619 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
620
621 let expr = prefix_expr.expr()?;
622 match expr {
623 ast::Expr::Literal(_) => HighlightTag::NumericLiteral,
624 _ => HighlightTag::Operator,
625 }
626 .into()
627 }
628 _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
629 HighlightTag::Operator.into()
630 }
631 _ if element.parent().and_then(ast::BinExpr::cast).is_some() => {
632 HighlightTag::Operator.into()
633 }
634 _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => {
635 HighlightTag::Operator.into()
636 }
637 _ if element.parent().and_then(ast::RangePat::cast).is_some() => {
638 HighlightTag::Operator.into()
639 }
640 _ if element.parent().and_then(ast::RestPat::cast).is_some() => {
641 HighlightTag::Operator.into()
642 }
643 _ if element.parent().and_then(ast::Attr::cast).is_some() => {
644 HighlightTag::Attribute.into()
645 }
646 _ => HighlightTag::Punctuation.into(),
647 },
648
649 k if k.is_keyword() => {
650 let h = Highlight::new(HighlightTag::Keyword);
651 match k {
652 T![break]
653 | T![continue]
654 | T![else]
655 | T![if]
656 | T![loop]
657 | T![match]
658 | T![return]
659 | T![while]
660 | T![in] => h | HighlightModifier::ControlFlow,
661 T![for] if !is_child_of_impl(&element) => h | HighlightModifier::ControlFlow,
662 T![unsafe] => h | HighlightModifier::Unsafe,
663 T![true] | T![false] => HighlightTag::BoolLiteral.into(),
664 T![self] => {
665 let self_param_is_mut = element
666 .parent()
667 .and_then(ast::SelfParam::cast)
668 .and_then(|p| p.mut_token())
669 .is_some();
670 let self_path = &element
671 .parent()
672 .as_ref()
673 .and_then(SyntaxNode::parent)
674 .and_then(ast::Path::cast)
675 .and_then(|p| sema.resolve_path(&p));
676 let mut h = HighlightTag::Symbol(SymbolKind::SelfParam).into();
677 if self_param_is_mut
678 || matches!(self_path,
679 Some(hir::PathResolution::Local(local))
680 if local.is_self(db)
681 && (local.is_mut(db) || local.ty(db).is_mutable_reference())
682 )
683 {
684 h |= HighlightModifier::Mutable
685 }
686
687 if let Some(hir::PathResolution::Local(local)) = self_path {
688 if is_consumed_lvalue(element, &local, db) {
689 h |= HighlightModifier::Consuming;
690 }
691 }
692
693 h
694 }
695 T![ref] => element
696 .parent()
697 .and_then(ast::IdentPat::cast)
698 .and_then(|ident_pat| {
699 if sema.is_unsafe_ident_pat(&ident_pat) {
700 Some(HighlightModifier::Unsafe)
701 } else {
702 None
703 }
704 })
705 .map(|modifier| h | modifier)
706 .unwrap_or(h),
707 _ => h,
708 }
709 }
710
711 _ => return None,
712 };
713
714 return Some((highlight, binding_hash));
715
716 fn calc_binding_hash(name: &Name, shadow_count: u32) -> u64 {
717 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
718 use std::{collections::hash_map::DefaultHasher, hash::Hasher};
719
720 let mut hasher = DefaultHasher::new();
721 x.hash(&mut hasher);
722 hasher.finish()
723 }
724
725 hash((name, shadow_count))
726 }
727}
728
729fn is_child_of_impl(element: &SyntaxElement) -> bool {
730 match element.parent() {
731 Some(e) => e.kind() == IMPL,
732 _ => false,
733 }
734}
735
736fn highlight_func_by_name_ref(
737 sema: &Semantics<RootDatabase>,
738 name_ref: &ast::NameRef,
739) -> Option<Highlight> {
740 let method_call = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
741 highlight_method_call(sema, &method_call)
742}
743
744fn highlight_method_call(
745 sema: &Semantics<RootDatabase>,
746 method_call: &ast::MethodCallExpr,
747) -> Option<Highlight> {
748 let func = sema.resolve_method_call(&method_call)?;
749 let mut h = HighlightTag::Symbol(SymbolKind::Function).into();
750 h |= HighlightModifier::Associated;
751 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
752 h |= HighlightModifier::Unsafe;
753 }
754 if let Some(self_param) = func.self_param(sema.db) {
755 match self_param.access(sema.db) {
756 hir::Access::Shared => (),
757 hir::Access::Exclusive => h |= HighlightModifier::Mutable,
758 hir::Access::Owned => {
759 if let Some(receiver_ty) =
760 method_call.receiver().and_then(|it| sema.type_of_expr(&it))
761 {
762 if !receiver_ty.is_copy(sema.db) {
763 h |= HighlightModifier::Consuming
764 }
765 }
766 }
767 }
768 }
769 Some(h)
770}
771
772fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
773 match def {
774 Definition::Macro(_) => HighlightTag::Symbol(SymbolKind::Macro),
775 Definition::Field(_) => HighlightTag::Symbol(SymbolKind::Field),
776 Definition::ModuleDef(def) => match def {
777 hir::ModuleDef::Module(_) => HighlightTag::Symbol(SymbolKind::Module),
778 hir::ModuleDef::Function(func) => {
779 let mut h = Highlight::new(HighlightTag::Symbol(SymbolKind::Function));
780 if func.as_assoc_item(db).is_some() {
781 h |= HighlightModifier::Associated;
782 if func.self_param(db).is_none() {
783 h |= HighlightModifier::Static
784 }
785 }
786 if func.is_unsafe(db) {
787 h |= HighlightModifier::Unsafe;
788 }
789 return h;
790 }
791 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Symbol(SymbolKind::Struct),
792 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Symbol(SymbolKind::Enum),
793 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Symbol(SymbolKind::Union),
794 hir::ModuleDef::Variant(_) => HighlightTag::Symbol(SymbolKind::Variant),
795 hir::ModuleDef::Const(konst) => {
796 let mut h = Highlight::new(HighlightTag::Symbol(SymbolKind::Const));
797 if konst.as_assoc_item(db).is_some() {
798 h |= HighlightModifier::Associated
799 }
800 return h;
801 }
802 hir::ModuleDef::Trait(_) => HighlightTag::Symbol(SymbolKind::Trait),
803 hir::ModuleDef::TypeAlias(type_) => {
804 let mut h = Highlight::new(HighlightTag::Symbol(SymbolKind::TypeAlias));
805 if type_.as_assoc_item(db).is_some() {
806 h |= HighlightModifier::Associated
807 }
808 return h;
809 }
810 hir::ModuleDef::BuiltinType(_) => HighlightTag::BuiltinType,
811 hir::ModuleDef::Static(s) => {
812 let mut h = Highlight::new(HighlightTag::Symbol(SymbolKind::Static));
813 if s.is_mut(db) {
814 h |= HighlightModifier::Mutable;
815 h |= HighlightModifier::Unsafe;
816 }
817 return h;
818 }
819 },
820 Definition::SelfType(_) => HighlightTag::Symbol(SymbolKind::Impl),
821 Definition::TypeParam(_) => HighlightTag::Symbol(SymbolKind::TypeParam),
822 Definition::Local(local) => {
823 let tag = if local.is_param(db) {
824 HighlightTag::Symbol(SymbolKind::ValueParam)
825 } else {
826 HighlightTag::Symbol(SymbolKind::Local)
827 };
828 let mut h = Highlight::new(tag);
829 if local.is_mut(db) || local.ty(db).is_mutable_reference() {
830 h |= HighlightModifier::Mutable;
831 }
832 if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) {
833 h |= HighlightModifier::Callable;
834 }
835 return h;
836 }
837 Definition::LifetimeParam(_) => HighlightTag::Symbol(SymbolKind::LifetimeParam),
838 Definition::Label(_) => HighlightTag::Symbol(SymbolKind::Label),
839 }
840 .into()
841}
842
843fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
844 let default = HighlightTag::UnresolvedReference;
845
846 let parent = match name.syntax().parent() {
847 Some(it) => it,
848 _ => return default.into(),
849 };
850
851 let tag = match parent.kind() {
852 STRUCT => HighlightTag::Symbol(SymbolKind::Struct),
853 ENUM => HighlightTag::Symbol(SymbolKind::Enum),
854 VARIANT => HighlightTag::Symbol(SymbolKind::Variant),
855 UNION => HighlightTag::Symbol(SymbolKind::Union),
856 TRAIT => HighlightTag::Symbol(SymbolKind::Trait),
857 TYPE_ALIAS => HighlightTag::Symbol(SymbolKind::TypeAlias),
858 TYPE_PARAM => HighlightTag::Symbol(SymbolKind::TypeParam),
859 RECORD_FIELD => HighlightTag::Symbol(SymbolKind::Field),
860 MODULE => HighlightTag::Symbol(SymbolKind::Module),
861 FN => HighlightTag::Symbol(SymbolKind::Function),
862 CONST => HighlightTag::Symbol(SymbolKind::Const),
863 STATIC => HighlightTag::Symbol(SymbolKind::Static),
864 IDENT_PAT => HighlightTag::Symbol(SymbolKind::Local),
865 _ => default,
866 };
867
868 tag.into()
869}
870
871fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabase>) -> Highlight {
872 let default = HighlightTag::UnresolvedReference;
873
874 let parent = match name.syntax().parent() {
875 Some(it) => it,
876 _ => return default.into(),
877 };
878
879 match parent.kind() {
880 METHOD_CALL_EXPR => {
881 return ast::MethodCallExpr::cast(parent)
882 .and_then(|method_call| highlight_method_call(sema, &method_call))
883 .unwrap_or_else(|| HighlightTag::Symbol(SymbolKind::Function).into());
884 }
885 FIELD_EXPR => {
886 let h = HighlightTag::Symbol(SymbolKind::Field);
887 let is_union = ast::FieldExpr::cast(parent)
888 .and_then(|field_expr| {
889 let field = sema.resolve_field(&field_expr)?;
890 Some(if let VariantDef::Union(_) = field.parent_def(sema.db) {
891 true
892 } else {
893 false
894 })
895 })
896 .unwrap_or(false);
897 if is_union {
898 h | HighlightModifier::Unsafe
899 } else {
900 h.into()
901 }
902 }
903 PATH_SEGMENT => {
904 let path = match parent.parent().and_then(ast::Path::cast) {
905 Some(it) => it,
906 _ => return default.into(),
907 };
908 let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) {
909 Some(it) => it,
910 _ => {
911 // within path, decide whether it is module or adt by checking for uppercase name
912 return if name.text().chars().next().unwrap_or_default().is_uppercase() {
913 HighlightTag::Symbol(SymbolKind::Struct)
914 } else {
915 HighlightTag::Symbol(SymbolKind::Module)
916 }
917 .into();
918 }
919 };
920 let parent = match expr.syntax().parent() {
921 Some(it) => it,
922 None => return default.into(),
923 };
924
925 match parent.kind() {
926 CALL_EXPR => HighlightTag::Symbol(SymbolKind::Function).into(),
927 _ => if name.text().chars().next().unwrap_or_default().is_uppercase() {
928 HighlightTag::Symbol(SymbolKind::Struct)
929 } else {
930 HighlightTag::Symbol(SymbolKind::Const)
931 }
932 .into(),
933 }
934 }
935 _ => default.into(),
936 }
937}
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs
index 26416022b..a74ca844b 100644
--- a/crates/ide/src/syntax_highlighting/format.rs
+++ b/crates/ide/src/syntax_highlighting/format.rs
@@ -1,65 +1,51 @@
1//! Syntax highlighting for format macro strings. 1//! Syntax highlighting for format macro strings.
2use syntax::{ 2use syntax::{
3 ast::{self, FormatSpecifier, HasFormatSpecifier}, 3 ast::{self, FormatSpecifier, HasFormatSpecifier},
4 AstNode, AstToken, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, 4 AstNode, AstToken, TextRange,
5}; 5};
6 6
7use crate::{ 7use crate::{syntax_highlighting::highlights::Highlights, HlRange, HlTag, SymbolKind};
8 syntax_highlighting::HighlightedRangeStack, HighlightTag, HighlightedRange, SymbolKind,
9};
10
11#[derive(Default)]
12pub(super) struct FormatStringHighlighter {
13 format_string: Option<SyntaxElement>,
14}
15 8
16impl FormatStringHighlighter { 9pub(super) fn highlight_format_string(
17 pub(super) fn check_for_format_string(&mut self, parent: &SyntaxNode) { 10 stack: &mut Highlights,
18 // Check if macro takes a format string and remember it for highlighting later. 11 string: &ast::String,
19 // The macros that accept a format string expand to a compiler builtin macros 12 range: TextRange,
20 // `format_args` and `format_args_nl`. 13) {
21 if let Some(name) = parent 14 if is_format_string(string).is_none() {
22 .parent() 15 return;
23 .and_then(ast::MacroCall::cast)
24 .and_then(|mc| mc.path())
25 .and_then(|p| p.segment())
26 .and_then(|s| s.name_ref())
27 {
28 match name.text().as_str() {
29 "format_args" | "format_args_nl" => {
30 self.format_string = parent
31 .children_with_tokens()
32 .filter(|t| t.kind() != SyntaxKind::WHITESPACE)
33 .nth(1)
34 .filter(|e| ast::String::can_cast(e.kind()))
35 }
36 _ => {}
37 }
38 }
39 } 16 }
40 pub(super) fn highlight_format_string( 17
41 &self, 18 string.lex_format_specifier(|piece_range, kind| {
42 range_stack: &mut HighlightedRangeStack, 19 if let Some(highlight) = highlight_format_specifier(kind) {
43 string: &impl HasFormatSpecifier, 20 stack.add(HlRange {
44 range: TextRange, 21 range: piece_range + range.start(),
45 ) { 22 highlight: highlight.into(),
46 if self.format_string.as_ref() == Some(&SyntaxElement::from(string.syntax().clone())) { 23 binding_hash: None,
47 range_stack.push();
48 string.lex_format_specifier(|piece_range, kind| {
49 if let Some(highlight) = highlight_format_specifier(kind) {
50 range_stack.add(HighlightedRange {
51 range: piece_range + range.start(),
52 highlight: highlight.into(),
53 binding_hash: None,
54 });
55 }
56 }); 24 });
57 range_stack.pop();
58 } 25 }
26 });
27}
28
29fn is_format_string(string: &ast::String) -> Option<()> {
30 let parent = string.syntax().parent();
31
32 let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?;
33 if !matches!(name.text().as_str(), "format_args" | "format_args_nl") {
34 return None;
35 }
36
37 let first_literal = parent
38 .children_with_tokens()
39 .filter_map(|it| it.as_token().cloned().and_then(ast::String::cast))
40 .next()?;
41 if &first_literal != string {
42 return None;
59 } 43 }
44
45 Some(())
60} 46}
61 47
62fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> { 48fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HlTag> {
63 Some(match kind { 49 Some(match kind {
64 FormatSpecifier::Open 50 FormatSpecifier::Open
65 | FormatSpecifier::Close 51 | FormatSpecifier::Close
@@ -71,8 +57,10 @@ fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> {
71 | FormatSpecifier::DollarSign 57 | FormatSpecifier::DollarSign
72 | FormatSpecifier::Dot 58 | FormatSpecifier::Dot
73 | FormatSpecifier::Asterisk 59 | FormatSpecifier::Asterisk
74 | FormatSpecifier::QuestionMark => HighlightTag::FormatSpecifier, 60 | FormatSpecifier::QuestionMark => HlTag::FormatSpecifier,
75 FormatSpecifier::Integer | FormatSpecifier::Zero => HighlightTag::NumericLiteral, 61
76 FormatSpecifier::Identifier => HighlightTag::Symbol(SymbolKind::Local), 62 FormatSpecifier::Integer | FormatSpecifier::Zero => HlTag::NumericLiteral,
63
64 FormatSpecifier::Identifier => HlTag::Symbol(SymbolKind::Local),
77 }) 65 })
78} 66}
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
new file mode 100644
index 000000000..34bae49a8
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -0,0 +1,530 @@
1//! Computes color for a single element.
2
3use hir::{AsAssocItem, Semantics, VariantDef};
4use ide_db::{
5 defs::{Definition, NameClass, NameRefClass},
6 RootDatabase,
7};
8use rustc_hash::FxHashMap;
9use syntax::{
10 ast, AstNode, AstToken, NodeOrToken, SyntaxElement,
11 SyntaxKind::{self, *},
12 SyntaxNode, SyntaxToken, T,
13};
14
15use crate::{syntax_highlighting::tags::HlPunct, Highlight, HlMod, HlTag, SymbolKind};
16
17pub(super) fn element(
18 sema: &Semantics<RootDatabase>,
19 bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
20 syntactic_name_ref_highlighting: bool,
21 element: SyntaxElement,
22) -> Option<(Highlight, Option<u64>)> {
23 let db = sema.db;
24 let mut binding_hash = None;
25 let highlight: Highlight = match element.kind() {
26 FN => {
27 bindings_shadow_count.clear();
28 return None;
29 }
30
31 // Highlight definitions depending on the "type" of the definition.
32 NAME => {
33 let name = element.into_node().and_then(ast::Name::cast).unwrap();
34 let name_kind = NameClass::classify(sema, &name);
35
36 if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind {
37 if let Some(name) = local.name(db) {
38 let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
39 *shadow_count += 1;
40 binding_hash = Some(calc_binding_hash(&name, *shadow_count))
41 }
42 };
43
44 match name_kind {
45 Some(NameClass::ExternCrate(_)) => HlTag::Symbol(SymbolKind::Module).into(),
46 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition,
47 Some(NameClass::ConstReference(def)) => highlight_def(db, def),
48 Some(NameClass::PatFieldShorthand { field_ref, .. }) => {
49 let mut h = HlTag::Symbol(SymbolKind::Field).into();
50 if let Definition::Field(field) = field_ref {
51 if let VariantDef::Union(_) = field.parent_def(db) {
52 h |= HlMod::Unsafe;
53 }
54 }
55
56 h
57 }
58 None => highlight_name_by_syntax(name) | HlMod::Definition,
59 }
60 }
61
62 // Highlight references like the definitions they resolve to
63 NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => {
64 // even though we track whether we are in an attribute or not we still need this special case
65 // as otherwise we would emit unresolved references for name refs inside attributes
66 Highlight::from(HlTag::Symbol(SymbolKind::Function))
67 }
68 NAME_REF => {
69 let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
70 highlight_func_by_name_ref(sema, &name_ref).unwrap_or_else(|| {
71 match NameRefClass::classify(sema, &name_ref) {
72 Some(name_kind) => match name_kind {
73 NameRefClass::ExternCrate(_) => HlTag::Symbol(SymbolKind::Module).into(),
74 NameRefClass::Definition(def) => {
75 if let Definition::Local(local) = &def {
76 if let Some(name) = local.name(db) {
77 let shadow_count =
78 bindings_shadow_count.entry(name.clone()).or_default();
79 binding_hash = Some(calc_binding_hash(&name, *shadow_count))
80 }
81 };
82
83 let mut h = highlight_def(db, def);
84
85 if let Definition::Local(local) = &def {
86 if is_consumed_lvalue(name_ref.syntax().clone().into(), local, db) {
87 h |= HlMod::Consuming;
88 }
89 }
90
91 if let Some(parent) = name_ref.syntax().parent() {
92 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) {
93 if let Definition::Field(field) = def {
94 if let VariantDef::Union(_) = field.parent_def(db) {
95 h |= HlMod::Unsafe;
96 }
97 }
98 }
99 }
100
101 h
102 }
103 NameRefClass::FieldShorthand { .. } => {
104 HlTag::Symbol(SymbolKind::Field).into()
105 }
106 },
107 None if syntactic_name_ref_highlighting => {
108 highlight_name_ref_by_syntax(name_ref, sema)
109 }
110 None => HlTag::UnresolvedReference.into(),
111 }
112 })
113 }
114
115 // Simple token-based highlighting
116 COMMENT => {
117 let comment = element.into_token().and_then(ast::Comment::cast)?;
118 let h = HlTag::Comment;
119 match comment.kind().doc {
120 Some(_) => h | HlMod::Documentation,
121 None => h.into(),
122 }
123 }
124 STRING | BYTE_STRING => HlTag::StringLiteral.into(),
125 ATTR => HlTag::Attribute.into(),
126 INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
127 BYTE => HlTag::ByteLiteral.into(),
128 CHAR => HlTag::CharLiteral.into(),
129 QUESTION => Highlight::new(HlTag::Operator) | HlMod::ControlFlow,
130 LIFETIME => {
131 let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap();
132
133 match NameClass::classify_lifetime(sema, &lifetime) {
134 Some(NameClass::Definition(def)) => highlight_def(db, def) | HlMod::Definition,
135 None => match NameRefClass::classify_lifetime(sema, &lifetime) {
136 Some(NameRefClass::Definition(def)) => highlight_def(db, def),
137 _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)),
138 },
139 _ => Highlight::new(HlTag::Symbol(SymbolKind::LifetimeParam)) | HlMod::Definition,
140 }
141 }
142 p if p.is_punct() => match p {
143 T![&] => {
144 let h = HlTag::Operator.into();
145 let is_unsafe = element
146 .parent()
147 .and_then(ast::RefExpr::cast)
148 .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr))
149 .unwrap_or(false);
150 if is_unsafe {
151 h | HlMod::Unsafe
152 } else {
153 h
154 }
155 }
156 T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => HlTag::Operator.into(),
157 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
158 HlTag::Symbol(SymbolKind::Macro).into()
159 }
160 T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => {
161 HlTag::BuiltinType.into()
162 }
163 T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => {
164 HlTag::Keyword.into()
165 }
166 T![*] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
167 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
168
169 let expr = prefix_expr.expr()?;
170 let ty = sema.type_of_expr(&expr)?;
171 if ty.is_raw_ptr() {
172 HlTag::Operator | HlMod::Unsafe
173 } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() {
174 HlTag::Operator.into()
175 } else {
176 HlTag::Punctuation(HlPunct::Other).into()
177 }
178 }
179 T![-] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
180 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
181
182 let expr = prefix_expr.expr()?;
183 match expr {
184 ast::Expr::Literal(_) => HlTag::NumericLiteral,
185 _ => HlTag::Operator,
186 }
187 .into()
188 }
189 _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
190 HlTag::Operator.into()
191 }
192 _ if element.parent().and_then(ast::BinExpr::cast).is_some() => HlTag::Operator.into(),
193 _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => {
194 HlTag::Operator.into()
195 }
196 _ if element.parent().and_then(ast::RangePat::cast).is_some() => HlTag::Operator.into(),
197 _ if element.parent().and_then(ast::RestPat::cast).is_some() => HlTag::Operator.into(),
198 _ if element.parent().and_then(ast::Attr::cast).is_some() => HlTag::Attribute.into(),
199 kind => HlTag::Punctuation(match kind {
200 T!['['] | T![']'] => HlPunct::Bracket,
201 T!['{'] | T!['}'] => HlPunct::Brace,
202 T!['('] | T![')'] => HlPunct::Parenthesis,
203 T![<] | T![>] => HlPunct::Angle,
204 T![,] => HlPunct::Comma,
205 T![:] => HlPunct::Colon,
206 T![;] => HlPunct::Semi,
207 T![.] => HlPunct::Dot,
208 _ => HlPunct::Other,
209 })
210 .into(),
211 },
212
213 k if k.is_keyword() => {
214 let h = Highlight::new(HlTag::Keyword);
215 match k {
216 T![break]
217 | T![continue]
218 | T![else]
219 | T![if]
220 | T![loop]
221 | T![match]
222 | T![return]
223 | T![while]
224 | T![in] => h | HlMod::ControlFlow,
225 T![for] if !is_child_of_impl(&element) => h | HlMod::ControlFlow,
226 T![unsafe] => h | HlMod::Unsafe,
227 T![true] | T![false] => HlTag::BoolLiteral.into(),
228 T![self] => {
229 let self_param_is_mut = element
230 .parent()
231 .and_then(ast::SelfParam::cast)
232 .and_then(|p| p.mut_token())
233 .is_some();
234 let self_path = &element
235 .parent()
236 .as_ref()
237 .and_then(SyntaxNode::parent)
238 .and_then(ast::Path::cast)
239 .and_then(|p| sema.resolve_path(&p));
240 let mut h = HlTag::Symbol(SymbolKind::SelfParam).into();
241 if self_param_is_mut
242 || matches!(self_path,
243 Some(hir::PathResolution::Local(local))
244 if local.is_self(db)
245 && (local.is_mut(db) || local.ty(db).is_mutable_reference())
246 )
247 {
248 h |= HlMod::Mutable
249 }
250
251 if let Some(hir::PathResolution::Local(local)) = self_path {
252 if is_consumed_lvalue(element, &local, db) {
253 h |= HlMod::Consuming;
254 }
255 }
256
257 h
258 }
259 T![ref] => element
260 .parent()
261 .and_then(ast::IdentPat::cast)
262 .and_then(|ident_pat| {
263 if sema.is_unsafe_ident_pat(&ident_pat) {
264 Some(HlMod::Unsafe)
265 } else {
266 None
267 }
268 })
269 .map(|modifier| h | modifier)
270 .unwrap_or(h),
271 _ => h,
272 }
273 }
274
275 _ => return None,
276 };
277
278 return Some((highlight, binding_hash));
279
280 fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 {
281 fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
282 use std::{collections::hash_map::DefaultHasher, hash::Hasher};
283
284 let mut hasher = DefaultHasher::new();
285 x.hash(&mut hasher);
286 hasher.finish()
287 }
288
289 hash((name, shadow_count))
290 }
291}
292
293fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
294 match def {
295 Definition::Macro(_) => HlTag::Symbol(SymbolKind::Macro),
296 Definition::Field(_) => HlTag::Symbol(SymbolKind::Field),
297 Definition::ModuleDef(def) => match def {
298 hir::ModuleDef::Module(_) => HlTag::Symbol(SymbolKind::Module),
299 hir::ModuleDef::Function(func) => {
300 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Function));
301 if func.as_assoc_item(db).is_some() {
302 h |= HlMod::Associated;
303 if func.self_param(db).is_none() {
304 h |= HlMod::Static
305 }
306 }
307 if func.is_unsafe(db) {
308 h |= HlMod::Unsafe;
309 }
310 return h;
311 }
312 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HlTag::Symbol(SymbolKind::Struct),
313 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HlTag::Symbol(SymbolKind::Enum),
314 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HlTag::Symbol(SymbolKind::Union),
315 hir::ModuleDef::Variant(_) => HlTag::Symbol(SymbolKind::Variant),
316 hir::ModuleDef::Const(konst) => {
317 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Const));
318 if konst.as_assoc_item(db).is_some() {
319 h |= HlMod::Associated
320 }
321 return h;
322 }
323 hir::ModuleDef::Trait(_) => HlTag::Symbol(SymbolKind::Trait),
324 hir::ModuleDef::TypeAlias(type_) => {
325 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias));
326 if type_.as_assoc_item(db).is_some() {
327 h |= HlMod::Associated
328 }
329 return h;
330 }
331 hir::ModuleDef::BuiltinType(_) => HlTag::BuiltinType,
332 hir::ModuleDef::Static(s) => {
333 let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Static));
334 if s.is_mut(db) {
335 h |= HlMod::Mutable;
336 h |= HlMod::Unsafe;
337 }
338 return h;
339 }
340 },
341 Definition::SelfType(_) => HlTag::Symbol(SymbolKind::Impl),
342 Definition::GenericParam(it) => match it {
343 hir::GenericParam::TypeParam(_) => HlTag::Symbol(SymbolKind::TypeParam),
344 hir::GenericParam::ConstParam(_) => HlTag::Symbol(SymbolKind::ConstParam),
345 hir::GenericParam::LifetimeParam(_) => HlTag::Symbol(SymbolKind::LifetimeParam),
346 },
347 Definition::Local(local) => {
348 let tag = if local.is_param(db) {
349 HlTag::Symbol(SymbolKind::ValueParam)
350 } else {
351 HlTag::Symbol(SymbolKind::Local)
352 };
353 let mut h = Highlight::new(tag);
354 if local.is_mut(db) || local.ty(db).is_mutable_reference() {
355 h |= HlMod::Mutable;
356 }
357 if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) {
358 h |= HlMod::Callable;
359 }
360 return h;
361 }
362 Definition::Label(_) => HlTag::Symbol(SymbolKind::Label),
363 }
364 .into()
365}
366
367fn highlight_func_by_name_ref(
368 sema: &Semantics<RootDatabase>,
369 name_ref: &ast::NameRef,
370) -> Option<Highlight> {
371 let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
372 highlight_method_call(sema, &mc)
373}
374
375fn highlight_method_call(
376 sema: &Semantics<RootDatabase>,
377 method_call: &ast::MethodCallExpr,
378) -> Option<Highlight> {
379 let func = sema.resolve_method_call(&method_call)?;
380 let mut h = HlTag::Symbol(SymbolKind::Function).into();
381 h |= HlMod::Associated;
382 if func.is_unsafe(sema.db) || sema.is_unsafe_method_call(&method_call) {
383 h |= HlMod::Unsafe;
384 }
385 if let Some(self_param) = func.self_param(sema.db) {
386 match self_param.access(sema.db) {
387 hir::Access::Shared => (),
388 hir::Access::Exclusive => h |= HlMod::Mutable,
389 hir::Access::Owned => {
390 if let Some(receiver_ty) =
391 method_call.receiver().and_then(|it| sema.type_of_expr(&it))
392 {
393 if !receiver_ty.is_copy(sema.db) {
394 h |= HlMod::Consuming
395 }
396 }
397 }
398 }
399 }
400 Some(h)
401}
402
403fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
404 let default = HlTag::UnresolvedReference;
405
406 let parent = match name.syntax().parent() {
407 Some(it) => it,
408 _ => return default.into(),
409 };
410
411 let tag = match parent.kind() {
412 STRUCT => HlTag::Symbol(SymbolKind::Struct),
413 ENUM => HlTag::Symbol(SymbolKind::Enum),
414 VARIANT => HlTag::Symbol(SymbolKind::Variant),
415 UNION => HlTag::Symbol(SymbolKind::Union),
416 TRAIT => HlTag::Symbol(SymbolKind::Trait),
417 TYPE_ALIAS => HlTag::Symbol(SymbolKind::TypeAlias),
418 TYPE_PARAM => HlTag::Symbol(SymbolKind::TypeParam),
419 RECORD_FIELD => HlTag::Symbol(SymbolKind::Field),
420 MODULE => HlTag::Symbol(SymbolKind::Module),
421 FN => HlTag::Symbol(SymbolKind::Function),
422 CONST => HlTag::Symbol(SymbolKind::Const),
423 STATIC => HlTag::Symbol(SymbolKind::Static),
424 IDENT_PAT => HlTag::Symbol(SymbolKind::Local),
425 _ => default,
426 };
427
428 tag.into()
429}
430
431fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabase>) -> Highlight {
432 let default = HlTag::UnresolvedReference;
433
434 let parent = match name.syntax().parent() {
435 Some(it) => it,
436 _ => return default.into(),
437 };
438
439 match parent.kind() {
440 METHOD_CALL_EXPR => {
441 return ast::MethodCallExpr::cast(parent)
442 .and_then(|it| highlight_method_call(sema, &it))
443 .unwrap_or_else(|| HlTag::Symbol(SymbolKind::Function).into());
444 }
445 FIELD_EXPR => {
446 let h = HlTag::Symbol(SymbolKind::Field);
447 let is_union = ast::FieldExpr::cast(parent)
448 .and_then(|field_expr| {
449 let field = sema.resolve_field(&field_expr)?;
450 Some(if let VariantDef::Union(_) = field.parent_def(sema.db) {
451 true
452 } else {
453 false
454 })
455 })
456 .unwrap_or(false);
457 if is_union {
458 h | HlMod::Unsafe
459 } else {
460 h.into()
461 }
462 }
463 PATH_SEGMENT => {
464 let path = match parent.parent().and_then(ast::Path::cast) {
465 Some(it) => it,
466 _ => return default.into(),
467 };
468 let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) {
469 Some(it) => it,
470 _ => {
471 // within path, decide whether it is module or adt by checking for uppercase name
472 return if name.text().chars().next().unwrap_or_default().is_uppercase() {
473 HlTag::Symbol(SymbolKind::Struct)
474 } else {
475 HlTag::Symbol(SymbolKind::Module)
476 }
477 .into();
478 }
479 };
480 let parent = match expr.syntax().parent() {
481 Some(it) => it,
482 None => return default.into(),
483 };
484
485 match parent.kind() {
486 CALL_EXPR => HlTag::Symbol(SymbolKind::Function).into(),
487 _ => if name.text().chars().next().unwrap_or_default().is_uppercase() {
488 HlTag::Symbol(SymbolKind::Struct)
489 } else {
490 HlTag::Symbol(SymbolKind::Const)
491 }
492 .into(),
493 }
494 }
495 _ => default.into(),
496 }
497}
498
499fn is_consumed_lvalue(
500 node: NodeOrToken<SyntaxNode, SyntaxToken>,
501 local: &hir::Local,
502 db: &RootDatabase,
503) -> bool {
504 // When lvalues are passed as arguments and they're not Copy, then mark them as Consuming.
505 parents_match(node, &[PATH_SEGMENT, PATH, PATH_EXPR, ARG_LIST]) && !local.ty(db).is_copy(db)
506}
507
508/// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly.
509fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool {
510 while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) {
511 if parent.kind() != *kind {
512 return false;
513 }
514
515 // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value
516 // in the same pattern is unstable: rust-lang/rust#68354.
517 node = node.parent().unwrap().into();
518 kinds = rest;
519 }
520
521 // Only true if we matched all expected kinds
522 kinds.len() == 0
523}
524
525fn is_child_of_impl(element: &SyntaxElement) -> bool {
526 match element.parent() {
527 Some(e) => e.kind() == IMPL,
528 _ => false,
529 }
530}
diff --git a/crates/ide/src/syntax_highlighting/highlights.rs b/crates/ide/src/syntax_highlighting/highlights.rs
new file mode 100644
index 000000000..882a685a5
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/highlights.rs
@@ -0,0 +1,92 @@
1//! Collects a tree of highlighted ranges and flattens it.
2use std::iter;
3
4use stdx::equal_range_by;
5use syntax::TextRange;
6
7use crate::{HlRange, HlTag};
8
9pub(super) struct Highlights {
10 root: Node,
11}
12
13struct Node {
14 hl_range: HlRange,
15 nested: Vec<Node>,
16}
17
18impl Highlights {
19 pub(super) fn new(range: TextRange) -> Highlights {
20 Highlights {
21 root: Node::new(HlRange { range, highlight: HlTag::None.into(), binding_hash: None }),
22 }
23 }
24
25 pub(super) fn add(&mut self, hl_range: HlRange) {
26 self.root.add(hl_range);
27 }
28
29 pub(super) fn to_vec(self) -> Vec<HlRange> {
30 let mut res = Vec::new();
31 self.root.flatten(&mut res);
32 res
33 }
34}
35
36impl Node {
37 fn new(hl_range: HlRange) -> Node {
38 Node { hl_range, nested: Vec::new() }
39 }
40
41 fn add(&mut self, hl_range: HlRange) {
42 assert!(self.hl_range.range.contains_range(hl_range.range));
43
44 // Fast path
45 if let Some(last) = self.nested.last_mut() {
46 if last.hl_range.range.contains_range(hl_range.range) {
47 return last.add(hl_range);
48 }
49 if last.hl_range.range.end() <= hl_range.range.start() {
50 return self.nested.push(Node::new(hl_range));
51 }
52 }
53
54 let overlapping =
55 equal_range_by(&self.nested, |n| TextRange::ordering(n.hl_range.range, hl_range.range));
56
57 if overlapping.len() == 1
58 && self.nested[overlapping.start].hl_range.range.contains_range(hl_range.range)
59 {
60 return self.nested[overlapping.start].add(hl_range);
61 }
62
63 let nested = self
64 .nested
65 .splice(overlapping.clone(), iter::once(Node::new(hl_range)))
66 .collect::<Vec<_>>();
67 self.nested[overlapping.start].nested = nested;
68 }
69
70 fn flatten(&self, acc: &mut Vec<HlRange>) {
71 let mut start = self.hl_range.range.start();
72 let mut nested = self.nested.iter();
73 loop {
74 let next = nested.next();
75 let end = next.map_or(self.hl_range.range.end(), |it| it.hl_range.range.start());
76 if start < end {
77 acc.push(HlRange {
78 range: TextRange::new(start, end),
79 highlight: self.hl_range.highlight,
80 binding_hash: self.hl_range.binding_hash,
81 });
82 }
83 start = match next {
84 Some(child) => {
85 child.flatten(acc);
86 child.hl_range.range.end()
87 }
88 None => break,
89 }
90 }
91 }
92}
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs
index 99ba3a59d..0ee7bc96e 100644
--- a/crates/ide/src/syntax_highlighting/html.rs
+++ b/crates/ide/src/syntax_highlighting/html.rs
@@ -3,7 +3,7 @@
3use ide_db::base_db::SourceDatabase; 3use ide_db::base_db::SourceDatabase;
4use oorandom::Rand32; 4use oorandom::Rand32;
5use stdx::format_to; 5use stdx::format_to;
6use syntax::{AstNode, TextRange, TextSize}; 6use syntax::AstNode;
7 7
8use crate::{syntax_highlighting::highlight, FileId, RootDatabase}; 8use crate::{syntax_highlighting::highlight, FileId, RootDatabase};
9 9
@@ -20,35 +20,27 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
20 ) 20 )
21 } 21 }
22 22
23 let ranges = highlight(db, file_id, None, false); 23 let hl_ranges = highlight(db, file_id, None, false);
24 let text = parse.tree().syntax().to_string(); 24 let text = parse.tree().syntax().to_string();
25 let mut prev_pos = TextSize::from(0);
26 let mut buf = String::new(); 25 let mut buf = String::new();
27 buf.push_str(&STYLE); 26 buf.push_str(&STYLE);
28 buf.push_str("<pre><code>"); 27 buf.push_str("<pre><code>");
29 for range in &ranges { 28 for r in &hl_ranges {
30 if range.range.start() > prev_pos { 29 let chunk = html_escape(&text[r.range]);
31 let curr = &text[TextRange::new(prev_pos, range.range.start())]; 30 if r.highlight.is_empty() {
32 let text = html_escape(curr); 31 format_to!(buf, "{}", chunk);
33 buf.push_str(&text); 32 continue;
34 } 33 }
35 let curr = &text[TextRange::new(range.range.start(), range.range.end())];
36 34
37 let class = range.highlight.to_string().replace('.', " "); 35 let class = r.highlight.to_string().replace('.', " ");
38 let color = match (rainbow, range.binding_hash) { 36 let color = match (rainbow, r.binding_hash) {
39 (true, Some(hash)) => { 37 (true, Some(hash)) => {
40 format!(" data-binding-hash=\"{}\" style=\"color: {};\"", hash, rainbowify(hash)) 38 format!(" data-binding-hash=\"{}\" style=\"color: {};\"", hash, rainbowify(hash))
41 } 39 }
42 _ => "".into(), 40 _ => "".into(),
43 }; 41 };
44 format_to!(buf, "<span class=\"{}\"{}>{}</span>", class, color, html_escape(curr)); 42 format_to!(buf, "<span class=\"{}\"{}>{}</span>", class, color, chunk);
45
46 prev_pos = range.range.end();
47 } 43 }
48 // Add the remaining (non-highlighted) text
49 let curr = &text[TextRange::new(prev_pos, TextSize::of(&text))];
50 let text = html_escape(curr);
51 buf.push_str(&text);
52 buf.push_str("</code></pre>"); 44 buf.push_str("</code></pre>");
53 buf 45 buf
54} 46}
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
new file mode 100644
index 000000000..281461493
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -0,0 +1,158 @@
1//! "Recursive" Syntax highlighting for code in doctests and fixtures.
2
3use hir::Semantics;
4use ide_db::call_info::ActiveParameter;
5use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
6
7use crate::{Analysis, HlMod, HlRange, HlTag, RootDatabase};
8
9use super::{highlights::Highlights, injector::Injector};
10
11pub(super) fn ra_fixture(
12 hl: &mut Highlights,
13 sema: &Semantics<RootDatabase>,
14 literal: ast::String,
15 expanded: SyntaxToken,
16) -> Option<()> {
17 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
18 if !active_parameter.name.starts_with("ra_fixture") {
19 return None;
20 }
21 let value = literal.value()?;
22
23 if let Some(range) = literal.open_quote_text_range() {
24 hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None })
25 }
26
27 let mut inj = Injector::default();
28
29 let mut text = &*value;
30 let mut offset: TextSize = 0.into();
31
32 while !text.is_empty() {
33 let marker = "$0";
34 let idx = text.find(marker).unwrap_or(text.len());
35 let (chunk, next) = text.split_at(idx);
36 inj.add(chunk, TextRange::at(offset, TextSize::of(chunk)));
37
38 text = next;
39 offset += TextSize::of(chunk);
40
41 if let Some(next) = text.strip_prefix(marker) {
42 if let Some(range) = literal.map_range_up(TextRange::at(offset, TextSize::of(marker))) {
43 hl.add(HlRange { range, highlight: HlTag::Keyword.into(), binding_hash: None });
44 }
45
46 text = next;
47
48 let marker_len = TextSize::of(marker);
49 offset += marker_len;
50 }
51 }
52
53 let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string());
54
55 for mut hl_range in analysis.highlight(tmp_file_id).unwrap() {
56 for range in inj.map_range_up(hl_range.range) {
57 if let Some(range) = literal.map_range_up(range) {
58 hl_range.range = range;
59 hl.add(hl_range.clone());
60 }
61 }
62 }
63
64 if let Some(range) = literal.close_quote_text_range() {
65 hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None })
66 }
67
68 Some(())
69}
70
71const RUSTDOC_FENCE: &'static str = "```";
72const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[
73 "",
74 "rust",
75 "should_panic",
76 "ignore",
77 "no_run",
78 "compile_fail",
79 "edition2015",
80 "edition2018",
81 "edition2021",
82];
83
84/// Injection of syntax highlighting of doctests.
85pub(super) fn doc_comment(hl: &mut Highlights, node: &SyntaxNode) {
86 let doc_comments = node
87 .children_with_tokens()
88 .filter_map(|it| it.into_token().and_then(ast::Comment::cast))
89 .filter(|it| it.kind().doc.is_some());
90
91 if !doc_comments.clone().any(|it| it.text().contains(RUSTDOC_FENCE)) {
92 return;
93 }
94
95 let mut inj = Injector::default();
96 inj.add_unmapped("fn doctest() {\n");
97
98 let mut is_codeblock = false;
99 let mut is_doctest = false;
100
101 // Replace the original, line-spanning comment ranges by new, only comment-prefix
102 // spanning comment ranges.
103 let mut new_comments = Vec::new();
104 for comment in doc_comments {
105 match comment.text().find(RUSTDOC_FENCE) {
106 Some(idx) => {
107 is_codeblock = !is_codeblock;
108 // Check whether code is rust by inspecting fence guards
109 let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..];
110 let is_rust =
111 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
112 is_doctest = is_codeblock && is_rust;
113 continue;
114 }
115 None if !is_doctest => continue,
116 None => (),
117 }
118
119 let line: &str = comment.text().as_str();
120 let range = comment.syntax().text_range();
121
122 let mut pos = TextSize::of(comment.prefix());
123 // whitespace after comment is ignored
124 if let Some(ws) = line[pos.into()..].chars().next().filter(|c| c.is_whitespace()) {
125 pos += TextSize::of(ws);
126 }
127 // lines marked with `#` should be ignored in output, we skip the `#` char
128 if let Some(ws) = line[pos.into()..].chars().next().filter(|&c| c == '#') {
129 pos += TextSize::of(ws);
130 }
131
132 new_comments.push(TextRange::at(range.start(), pos));
133
134 inj.add(&line[pos.into()..], TextRange::new(range.start() + pos, range.end()));
135 inj.add_unmapped("\n");
136 }
137 inj.add_unmapped("\n}");
138
139 let (analysis, tmp_file_id) = Analysis::from_single_file(inj.text().to_string());
140
141 for h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() {
142 for r in inj.map_range_up(h.range) {
143 hl.add(HlRange {
144 range: r,
145 highlight: h.highlight | HlMod::Injected,
146 binding_hash: h.binding_hash,
147 });
148 }
149 }
150
151 for range in new_comments {
152 hl.add(HlRange {
153 range,
154 highlight: HlTag::Comment | HlMod::Documentation,
155 binding_hash: None,
156 });
157 }
158}
diff --git a/crates/ide/src/syntax_highlighting/injection.rs b/crates/ide/src/syntax_highlighting/injection.rs
deleted file mode 100644
index 9eb184c74..000000000
--- a/crates/ide/src/syntax_highlighting/injection.rs
+++ /dev/null
@@ -1,183 +0,0 @@
1//! Syntax highlighting injections such as highlighting of documentation tests.
2
3use std::{collections::BTreeMap, convert::TryFrom};
4
5use hir::Semantics;
6use ide_db::call_info::ActiveParameter;
7use itertools::Itertools;
8use syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
9
10use crate::{Analysis, Highlight, HighlightModifier, HighlightTag, HighlightedRange, RootDatabase};
11
12use super::HighlightedRangeStack;
13
14pub(super) fn highlight_injection(
15 acc: &mut HighlightedRangeStack,
16 sema: &Semantics<RootDatabase>,
17 literal: ast::String,
18 expanded: SyntaxToken,
19) -> Option<()> {
20 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
21 if !active_parameter.name.starts_with("ra_fixture") {
22 return None;
23 }
24 let value = literal.value()?;
25 let (analysis, tmp_file_id) = Analysis::from_single_file(value.into_owned());
26
27 if let Some(range) = literal.open_quote_text_range() {
28 acc.add(HighlightedRange {
29 range,
30 highlight: HighlightTag::StringLiteral.into(),
31 binding_hash: None,
32 })
33 }
34
35 for mut h in analysis.highlight(tmp_file_id).unwrap() {
36 if let Some(r) = literal.map_range_up(h.range) {
37 h.range = r;
38 acc.add(h)
39 }
40 }
41
42 if let Some(range) = literal.close_quote_text_range() {
43 acc.add(HighlightedRange {
44 range,
45 highlight: HighlightTag::StringLiteral.into(),
46 binding_hash: None,
47 })
48 }
49
50 Some(())
51}
52
53/// Mapping from extracted documentation code to original code
54type RangesMap = BTreeMap<TextSize, TextSize>;
55
56const RUSTDOC_FENCE: &'static str = "```";
57const RUSTDOC_FENCE_TOKENS: &[&'static str] =
58 &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"];
59
60/// Extracts Rust code from documentation comments as well as a mapping from
61/// the extracted source code back to the original source ranges.
62/// Lastly, a vector of new comment highlight ranges (spanning only the
63/// comment prefix) is returned which is used in the syntax highlighting
64/// injection to replace the previous (line-spanning) comment ranges.
65pub(super) fn extract_doc_comments(
66 node: &SyntaxNode,
67) -> Option<(String, RangesMap, Vec<HighlightedRange>)> {
68 // wrap the doctest into function body to get correct syntax highlighting
69 let prefix = "fn doctest() {\n";
70 let suffix = "}\n";
71 // Mapping from extracted documentation code to original code
72 let mut range_mapping: RangesMap = BTreeMap::new();
73 let mut line_start = TextSize::try_from(prefix.len()).unwrap();
74 let mut is_codeblock = false;
75 let mut is_doctest = false;
76 // Replace the original, line-spanning comment ranges by new, only comment-prefix
77 // spanning comment ranges.
78 let mut new_comments = Vec::new();
79 let doctest = node
80 .children_with_tokens()
81 .filter_map(|el| el.into_token().and_then(ast::Comment::cast))
82 .filter(|comment| comment.kind().doc.is_some())
83 .filter(|comment| {
84 if let Some(idx) = comment.text().find(RUSTDOC_FENCE) {
85 is_codeblock = !is_codeblock;
86 // Check whether code is rust by inspecting fence guards
87 let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..];
88 let is_rust =
89 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
90 is_doctest = is_codeblock && is_rust;
91 false
92 } else {
93 is_doctest
94 }
95 })
96 .map(|comment| {
97 let prefix_len = comment.prefix().len();
98 let line: &str = comment.text().as_str();
99 let range = comment.syntax().text_range();
100
101 // whitespace after comment is ignored
102 let pos = if let Some(ws) = line.chars().nth(prefix_len).filter(|c| c.is_whitespace()) {
103 prefix_len + ws.len_utf8()
104 } else {
105 prefix_len
106 };
107
108 // lines marked with `#` should be ignored in output, we skip the `#` char
109 let pos = if let Some(ws) = line.chars().nth(pos).filter(|&c| c == '#') {
110 pos + ws.len_utf8()
111 } else {
112 pos
113 };
114
115 range_mapping.insert(line_start, range.start() + TextSize::try_from(pos).unwrap());
116 new_comments.push(HighlightedRange {
117 range: TextRange::new(
118 range.start(),
119 range.start() + TextSize::try_from(pos).unwrap(),
120 ),
121 highlight: HighlightTag::Comment | HighlightModifier::Documentation,
122 binding_hash: None,
123 });
124 line_start += range.len() - TextSize::try_from(pos).unwrap();
125 line_start += TextSize::try_from('\n'.len_utf8()).unwrap();
126
127 line[pos..].to_owned()
128 })
129 .join("\n");
130
131 if doctest.is_empty() {
132 return None;
133 }
134
135 let doctest = format!("{}{}{}", prefix, doctest, suffix);
136 Some((doctest, range_mapping, new_comments))
137}
138
139/// Injection of syntax highlighting of doctests.
140pub(super) fn highlight_doc_comment(
141 text: String,
142 range_mapping: RangesMap,
143 new_comments: Vec<HighlightedRange>,
144 stack: &mut HighlightedRangeStack,
145) {
146 let (analysis, tmp_file_id) = Analysis::from_single_file(text);
147
148 stack.push();
149 for mut h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() {
150 // Determine start offset and end offset in case of multi-line ranges
151 let mut start_offset = None;
152 let mut end_offset = None;
153 for (line_start, orig_line_start) in range_mapping.range(..h.range.end()).rev() {
154 // It's possible for orig_line_start - line_start to be negative. Add h.range.start()
155 // here and remove it from the end range after the loop below so that the values are
156 // always non-negative.
157 let offset = h.range.start() + orig_line_start - line_start;
158 if line_start <= &h.range.start() {
159 start_offset.get_or_insert(offset);
160 break;
161 } else {
162 end_offset.get_or_insert(offset);
163 }
164 }
165 if let Some(start_offset) = start_offset {
166 h.range = TextRange::new(
167 start_offset,
168 h.range.end() + end_offset.unwrap_or(start_offset) - h.range.start(),
169 );
170
171 h.highlight |= HighlightModifier::Injected;
172 stack.add(h);
173 }
174 }
175
176 // Inject the comment prefix highlight ranges
177 stack.push();
178 for comment in new_comments {
179 stack.add(comment);
180 }
181 stack.pop_and_inject(None);
182 stack.pop_and_inject(Some(Highlight::from(HighlightTag::Dummy) | HighlightModifier::Injected));
183}
diff --git a/crates/ide/src/syntax_highlighting/injector.rs b/crates/ide/src/syntax_highlighting/injector.rs
new file mode 100644
index 000000000..24ff473ec
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/injector.rs
@@ -0,0 +1,78 @@
1//! Extracts a subsequence of a text document, remembering the mapping of ranges
2//! between original and extracted texts.
3use std::ops::{self, Sub};
4
5use stdx::equal_range_by;
6use syntax::{TextRange, TextSize};
7
8#[derive(Default)]
9pub(super) struct Injector {
10 buf: String,
11 ranges: Vec<(TextRange, Option<Delta<TextSize>>)>,
12}
13
14impl Injector {
15 pub(super) fn add(&mut self, text: &str, source_range: TextRange) {
16 let len = TextSize::of(text);
17 assert_eq!(len, source_range.len());
18 self.add_impl(text, Some(source_range.start()));
19 }
20 pub(super) fn add_unmapped(&mut self, text: &str) {
21 self.add_impl(text, None);
22 }
23 fn add_impl(&mut self, text: &str, source: Option<TextSize>) {
24 let len = TextSize::of(text);
25 let target_range = TextRange::at(TextSize::of(&self.buf), len);
26 self.ranges.push((target_range, source.map(|it| Delta::new(target_range.start(), it))));
27 self.buf.push_str(text);
28 }
29
30 pub(super) fn text(&self) -> &str {
31 &self.buf
32 }
33 pub(super) fn map_range_up(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
34 equal_range_by(&self.ranges, |&(r, _)| TextRange::ordering(r, range)).filter_map(move |i| {
35 let (target_range, delta) = self.ranges[i];
36 let intersection = target_range.intersect(range).unwrap();
37 Some(intersection + delta?)
38 })
39 }
40}
41
42#[derive(Clone, Copy)]
43enum Delta<T> {
44 Add(T),
45 Sub(T),
46}
47
48impl<T> Delta<T> {
49 fn new(from: T, to: T) -> Delta<T>
50 where
51 T: Ord + Sub<Output = T>,
52 {
53 if to >= from {
54 Delta::Add(to - from)
55 } else {
56 Delta::Sub(from - to)
57 }
58 }
59}
60
61impl ops::Add<Delta<TextSize>> for TextSize {
62 type Output = TextSize;
63
64 fn add(self, rhs: Delta<TextSize>) -> TextSize {
65 match rhs {
66 Delta::Add(it) => self + it,
67 Delta::Sub(it) => self - it,
68 }
69 }
70}
71
72impl ops::Add<Delta<TextSize>> for TextRange {
73 type Output = TextRange;
74
75 fn add(self, rhs: Delta<TextSize>) -> TextRange {
76 TextRange::at(self.start() + rhs, self.len())
77 }
78}
diff --git a/crates/ide/src/syntax_highlighting/macro_rules.rs b/crates/ide/src/syntax_highlighting/macro_rules.rs
index 4462af47e..44620e912 100644
--- a/crates/ide/src/syntax_highlighting/macro_rules.rs
+++ b/crates/ide/src/syntax_highlighting/macro_rules.rs
@@ -1,7 +1,7 @@
1//! Syntax highlighting for macro_rules!. 1//! Syntax highlighting for macro_rules!.
2use syntax::{SyntaxElement, SyntaxKind, SyntaxToken, TextRange, T}; 2use syntax::{SyntaxElement, SyntaxKind, SyntaxToken, TextRange, T};
3 3
4use crate::{HighlightTag, HighlightedRange}; 4use crate::{HlRange, HlTag};
5 5
6#[derive(Default)] 6#[derive(Default)]
7pub(super) struct MacroRulesHighlighter { 7pub(super) struct MacroRulesHighlighter {
@@ -19,13 +19,13 @@ impl MacroRulesHighlighter {
19 } 19 }
20 } 20 }
21 21
22 pub(super) fn highlight(&self, element: SyntaxElement) -> Option<HighlightedRange> { 22 pub(super) fn highlight(&self, element: SyntaxElement) -> Option<HlRange> {
23 if let Some(state) = self.state.as_ref() { 23 if let Some(state) = self.state.as_ref() {
24 if matches!(state.rule_state, RuleState::Matcher | RuleState::Expander) { 24 if matches!(state.rule_state, RuleState::Matcher | RuleState::Expander) {
25 if let Some(range) = is_metavariable(element) { 25 if let Some(range) = is_metavariable(element) {
26 return Some(HighlightedRange { 26 return Some(HlRange {
27 range, 27 range,
28 highlight: HighlightTag::UnresolvedReference.into(), 28 highlight: HlTag::UnresolvedReference.into(),
29 binding_hash: None, 29 binding_hash: None,
30 }); 30 });
31 } 31 }
@@ -119,7 +119,7 @@ fn is_metavariable(element: SyntaxElement) -> Option<TextRange> {
119 let tok = element.as_token()?; 119 let tok = element.as_token()?;
120 match tok.kind() { 120 match tok.kind() {
121 kind if kind == SyntaxKind::IDENT || kind.is_keyword() => { 121 kind if kind == SyntaxKind::IDENT || kind.is_keyword() => {
122 if let Some(_dollar) = tok.prev_token().filter(|t| t.kind() == SyntaxKind::DOLLAR) { 122 if let Some(_dollar) = tok.prev_token().filter(|t| t.kind() == T![$]) {
123 return Some(tok.text_range()); 123 return Some(tok.text_range());
124 } 124 }
125 } 125 }
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 2a6cc0cab..8dd05ac52 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -7,15 +7,15 @@ use crate::SymbolKind;
7 7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 8#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
9pub struct Highlight { 9pub struct Highlight {
10 pub tag: HighlightTag, 10 pub tag: HlTag,
11 pub modifiers: HighlightModifiers, 11 pub mods: HlMods,
12} 12}
13 13
14#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 14#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
15pub struct HighlightModifiers(u32); 15pub struct HlMods(u32);
16 16
17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 17#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
18pub enum HighlightTag { 18pub enum HlTag {
19 Symbol(SymbolKind), 19 Symbol(SymbolKind),
20 20
21 BoolLiteral, 21 BoolLiteral,
@@ -29,17 +29,17 @@ pub enum HighlightTag {
29 EscapeSequence, 29 EscapeSequence,
30 FormatSpecifier, 30 FormatSpecifier,
31 Keyword, 31 Keyword,
32 Punctuation, 32 Punctuation(HlPunct),
33 Operator, 33 Operator,
34 UnresolvedReference, 34 UnresolvedReference,
35 35
36 // For things which don't have proper Tag, but want to use modifiers. 36 // For things which don't have a specific highlight.
37 Dummy, 37 None,
38} 38}
39 39
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
41#[repr(u8)] 41#[repr(u8)]
42pub enum HighlightModifier { 42pub enum HlMod {
43 /// Used to differentiate individual elements within attributes. 43 /// Used to differentiate individual elements within attributes.
44 Attribute = 0, 44 Attribute = 0,
45 /// Used with keywords like `if` and `break`. 45 /// Used with keywords like `if` and `break`.
@@ -61,10 +61,32 @@ pub enum HighlightModifier {
61 Unsafe, 61 Unsafe,
62} 62}
63 63
64impl HighlightTag { 64#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
65pub enum HlPunct {
66 /// []
67 Bracket,
68 /// {}
69 Brace,
70 /// ()
71 Parenthesis,
72 /// <>
73 Angle,
74 /// ,
75 Comma,
76 /// .
77 Dot,
78 /// :
79 Colon,
80 /// ;
81 Semi,
82 ///
83 Other,
84}
85
86impl HlTag {
65 fn as_str(self) -> &'static str { 87 fn as_str(self) -> &'static str {
66 match self { 88 match self {
67 HighlightTag::Symbol(symbol) => match symbol { 89 HlTag::Symbol(symbol) => match symbol {
68 SymbolKind::Const => "constant", 90 SymbolKind::Const => "constant",
69 SymbolKind::Static => "static", 91 SymbolKind::Static => "static",
70 SymbolKind::Enum => "enum", 92 SymbolKind::Enum => "enum",
@@ -77,6 +99,7 @@ impl HighlightTag {
77 SymbolKind::Function => "function", 99 SymbolKind::Function => "function",
78 SymbolKind::TypeAlias => "type_alias", 100 SymbolKind::TypeAlias => "type_alias",
79 SymbolKind::TypeParam => "type_param", 101 SymbolKind::TypeParam => "type_param",
102 SymbolKind::ConstParam => "const_param",
80 SymbolKind::LifetimeParam => "lifetime", 103 SymbolKind::LifetimeParam => "lifetime",
81 SymbolKind::Macro => "macro", 104 SymbolKind::Macro => "macro",
82 SymbolKind::Local => "variable", 105 SymbolKind::Local => "variable",
@@ -85,59 +108,69 @@ impl HighlightTag {
85 SymbolKind::SelfParam => "self_keyword", 108 SymbolKind::SelfParam => "self_keyword",
86 SymbolKind::Impl => "self_type", 109 SymbolKind::Impl => "self_type",
87 }, 110 },
88 HighlightTag::Attribute => "attribute", 111 HlTag::Attribute => "attribute",
89 HighlightTag::BoolLiteral => "bool_literal", 112 HlTag::BoolLiteral => "bool_literal",
90 HighlightTag::BuiltinType => "builtin_type", 113 HlTag::BuiltinType => "builtin_type",
91 HighlightTag::ByteLiteral => "byte_literal", 114 HlTag::ByteLiteral => "byte_literal",
92 HighlightTag::CharLiteral => "char_literal", 115 HlTag::CharLiteral => "char_literal",
93 HighlightTag::Comment => "comment", 116 HlTag::Comment => "comment",
94 HighlightTag::EscapeSequence => "escape_sequence", 117 HlTag::EscapeSequence => "escape_sequence",
95 HighlightTag::FormatSpecifier => "format_specifier", 118 HlTag::FormatSpecifier => "format_specifier",
96 HighlightTag::Dummy => "dummy", 119 HlTag::Keyword => "keyword",
97 HighlightTag::Keyword => "keyword", 120 HlTag::Punctuation(punct) => match punct {
98 HighlightTag::Punctuation => "punctuation", 121 HlPunct::Bracket => "bracket",
99 HighlightTag::NumericLiteral => "numeric_literal", 122 HlPunct::Brace => "brace",
100 HighlightTag::Operator => "operator", 123 HlPunct::Parenthesis => "parenthesis",
101 HighlightTag::StringLiteral => "string_literal", 124 HlPunct::Angle => "angle",
102 HighlightTag::UnresolvedReference => "unresolved_reference", 125 HlPunct::Comma => "comma",
126 HlPunct::Dot => "dot",
127 HlPunct::Colon => "colon",
128 HlPunct::Semi => "semicolon",
129 HlPunct::Other => "punctuation",
130 },
131 HlTag::NumericLiteral => "numeric_literal",
132 HlTag::Operator => "operator",
133 HlTag::StringLiteral => "string_literal",
134 HlTag::UnresolvedReference => "unresolved_reference",
135 HlTag::None => "none",
103 } 136 }
104 } 137 }
105} 138}
106 139
107impl fmt::Display for HighlightTag { 140impl fmt::Display for HlTag {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 fmt::Display::fmt(self.as_str(), f) 142 fmt::Display::fmt(self.as_str(), f)
110 } 143 }
111} 144}
112 145
113impl HighlightModifier { 146impl HlMod {
114 const ALL: &'static [HighlightModifier; HighlightModifier::Unsafe as u8 as usize + 1] = &[ 147 const ALL: &'static [HlMod; HlMod::Unsafe as u8 as usize + 1] = &[
115 HighlightModifier::Attribute, 148 HlMod::Attribute,
116 HighlightModifier::ControlFlow, 149 HlMod::ControlFlow,
117 HighlightModifier::Definition, 150 HlMod::Definition,
118 HighlightModifier::Documentation, 151 HlMod::Documentation,
119 HighlightModifier::Injected, 152 HlMod::Injected,
120 HighlightModifier::Mutable, 153 HlMod::Mutable,
121 HighlightModifier::Consuming, 154 HlMod::Consuming,
122 HighlightModifier::Callable, 155 HlMod::Callable,
123 HighlightModifier::Static, 156 HlMod::Static,
124 HighlightModifier::Associated, 157 HlMod::Associated,
125 HighlightModifier::Unsafe, 158 HlMod::Unsafe,
126 ]; 159 ];
127 160
128 fn as_str(self) -> &'static str { 161 fn as_str(self) -> &'static str {
129 match self { 162 match self {
130 HighlightModifier::Attribute => "attribute", 163 HlMod::Attribute => "attribute",
131 HighlightModifier::ControlFlow => "control", 164 HlMod::ControlFlow => "control",
132 HighlightModifier::Definition => "declaration", 165 HlMod::Definition => "declaration",
133 HighlightModifier::Documentation => "documentation", 166 HlMod::Documentation => "documentation",
134 HighlightModifier::Injected => "injected", 167 HlMod::Injected => "injected",
135 HighlightModifier::Mutable => "mutable", 168 HlMod::Mutable => "mutable",
136 HighlightModifier::Consuming => "consuming", 169 HlMod::Consuming => "consuming",
137 HighlightModifier::Unsafe => "unsafe", 170 HlMod::Unsafe => "unsafe",
138 HighlightModifier::Callable => "callable", 171 HlMod::Callable => "callable",
139 HighlightModifier::Static => "static", 172 HlMod::Static => "static",
140 HighlightModifier::Associated => "associated", 173 HlMod::Associated => "associated",
141 } 174 }
142 } 175 }
143 176
@@ -146,7 +179,7 @@ impl HighlightModifier {
146 } 179 }
147} 180}
148 181
149impl fmt::Display for HighlightModifier { 182impl fmt::Display for HlMod {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 fmt::Display::fmt(self.as_str(), f) 184 fmt::Display::fmt(self.as_str(), f)
152 } 185 }
@@ -155,60 +188,63 @@ impl fmt::Display for HighlightModifier {
155impl fmt::Display for Highlight { 188impl fmt::Display for Highlight {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 write!(f, "{}", self.tag)?; 190 write!(f, "{}", self.tag)?;
158 for modifier in self.modifiers.iter() { 191 for modifier in self.mods.iter() {
159 write!(f, ".{}", modifier)? 192 write!(f, ".{}", modifier)?
160 } 193 }
161 Ok(()) 194 Ok(())
162 } 195 }
163} 196}
164 197
165impl From<HighlightTag> for Highlight { 198impl From<HlTag> for Highlight {
166 fn from(tag: HighlightTag) -> Highlight { 199 fn from(tag: HlTag) -> Highlight {
167 Highlight::new(tag) 200 Highlight::new(tag)
168 } 201 }
169} 202}
170 203
171impl Highlight { 204impl Highlight {
172 pub(crate) fn new(tag: HighlightTag) -> Highlight { 205 pub(crate) fn new(tag: HlTag) -> Highlight {
173 Highlight { tag, modifiers: HighlightModifiers::default() } 206 Highlight { tag, mods: HlMods::default() }
207 }
208 pub fn is_empty(&self) -> bool {
209 self.tag == HlTag::None && self.mods == HlMods::default()
174 } 210 }
175} 211}
176 212
177impl ops::BitOr<HighlightModifier> for HighlightTag { 213impl ops::BitOr<HlMod> for HlTag {
178 type Output = Highlight; 214 type Output = Highlight;
179 215
180 fn bitor(self, rhs: HighlightModifier) -> Highlight { 216 fn bitor(self, rhs: HlMod) -> Highlight {
181 Highlight::new(self) | rhs 217 Highlight::new(self) | rhs
182 } 218 }
183} 219}
184 220
185impl ops::BitOrAssign<HighlightModifier> for HighlightModifiers { 221impl ops::BitOrAssign<HlMod> for HlMods {
186 fn bitor_assign(&mut self, rhs: HighlightModifier) { 222 fn bitor_assign(&mut self, rhs: HlMod) {
187 self.0 |= rhs.mask(); 223 self.0 |= rhs.mask();
188 } 224 }
189} 225}
190 226
191impl ops::BitOrAssign<HighlightModifier> for Highlight { 227impl ops::BitOrAssign<HlMod> for Highlight {
192 fn bitor_assign(&mut self, rhs: HighlightModifier) { 228 fn bitor_assign(&mut self, rhs: HlMod) {
193 self.modifiers |= rhs; 229 self.mods |= rhs;
194 } 230 }
195} 231}
196 232
197impl ops::BitOr<HighlightModifier> for Highlight { 233impl ops::BitOr<HlMod> for Highlight {
198 type Output = Highlight; 234 type Output = Highlight;
199 235
200 fn bitor(mut self, rhs: HighlightModifier) -> Highlight { 236 fn bitor(mut self, rhs: HlMod) -> Highlight {
201 self |= rhs; 237 self |= rhs;
202 self 238 self
203 } 239 }
204} 240}
205 241
206impl HighlightModifiers { 242impl HlMods {
207 pub fn contains(self, m: HighlightModifier) -> bool { 243 pub fn contains(self, m: HlMod) -> bool {
208 self.0 & m.mask() == m.mask() 244 self.0 & m.mask() == m.mask()
209 } 245 }
210 246
211 pub fn iter(self) -> impl Iterator<Item = HighlightModifier> { 247 pub fn iter(self) -> impl Iterator<Item = HlMod> {
212 HighlightModifier::ALL.iter().copied().filter(move |it| self.0 & it.mask() == it.mask()) 248 HlMod::ALL.iter().copied().filter(move |it| self.0 & it.mask() == it.mask())
213 } 249 }
214} 250}
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index 506ebe60e..e36e6fc3f 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -36,22 +36,22 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">fn</span> <span class="function declaration">not_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 39<pre><code><span class="keyword">fn</span> <span class="function declaration">not_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
40 40
41<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="punctuation">{</span><span class="punctuation">}</span> 41<span class="keyword">struct</span> <span class="struct declaration">foo</span> <span class="brace">{</span><span class="brace">}</span>
42 42
43<span class="keyword">impl</span> <span class="struct">foo</span> <span class="punctuation">{</span> 43<span class="keyword">impl</span> <span class="struct">foo</span> <span class="brace">{</span>
44 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 44 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
45 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 45 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
46<span class="punctuation">}</span> 46<span class="brace">}</span>
47 47
48<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="punctuation">{</span> 48<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span>
49 <span class="keyword">fn</span> <span class="function declaration static associated">t_is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 49 <span class="keyword">fn</span> <span class="function declaration static associated">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
50 <span class="keyword">fn</span> <span class="function declaration associated">t_is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 50 <span class="keyword">fn</span> <span class="function declaration associated">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
51<span class="punctuation">}</span> 51<span class="brace">}</span>
52 52
53<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="punctuation">{</span> 53<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span>
54 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 54 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration static associated">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
55 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 55 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
56<span class="punctuation">}</span> 56<span class="brace">}</span>
57 </code></pre> \ No newline at end of file 57 </code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 4dd7413ba..6dadda1c1 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -37,67 +37,72 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="comment documentation">/// ```</span> 39<pre><code><span class="comment documentation">/// ```</span>
40<span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="punctuation injected">_</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="punctuation injected">;</span><span class="punctuation injected"> 40<span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"early doctests should not go boom"</span><span class="semicolon injected">;</span>
41</span><span class="comment documentation">/// ```</span> 41<span class="comment documentation">/// ```</span>
42<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span> 42<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span>
43 <span class="field declaration">bar</span><span class="punctuation">:</span> <span class="builtin_type">bool</span><span class="punctuation">,</span> 43 <span class="field declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span><span class="comma">,</span>
44<span class="punctuation">}</span> 44<span class="brace">}</span>
45 45
46<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 46<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span>
47 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration associated">bar</span><span class="punctuation">:</span> <span class="builtin_type">bool</span> <span class="operator">=</span> <span class="bool_literal">true</span><span class="punctuation">;</span> 47 <span class="comment documentation">/// ```</span>
48 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Call me</span>
49 <span class="comment">// KILLER WHALE</span>
50 <span class="comment documentation">/// </span><span class="string_literal injected"> Ishmael."</span><span class="semicolon injected">;</span>
51 <span class="comment documentation">/// ```</span>
52 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration associated">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span> <span class="operator">=</span> <span class="bool_literal">true</span><span class="semicolon">;</span>
48 53
49 <span class="comment documentation">/// Constructs a new `Foo`.</span> 54 <span class="comment documentation">/// Constructs a new `Foo`.</span>
50 <span class="comment documentation">///</span> 55 <span class="comment documentation">///</span>
51 <span class="comment documentation">/// # Examples</span> 56 <span class="comment documentation">/// # Examples</span>
52 <span class="comment documentation">///</span> 57 <span class="comment documentation">///</span>
53 <span class="comment documentation">/// ```</span> 58 <span class="comment documentation">/// ```</span>
54 <span class="comment documentation">/// #</span><span class="dummy injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="punctuation attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="punctuation attribute injected">)</span><span class="attribute attribute injected">]</span> 59 <span class="comment documentation">/// #</span><span class="none injected"> </span><span class="attribute attribute injected">#</span><span class="attribute attribute injected">!</span><span class="attribute attribute injected">[</span><span class="function attribute injected">allow</span><span class="parenthesis attribute injected">(</span><span class="attribute attribute injected">unused_mut</span><span class="parenthesis attribute injected">)</span><span class="attribute attribute injected">]</span>
55 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="keyword injected">mut</span><span class="dummy injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> 60 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="keyword injected">mut</span><span class="none injected"> </span><span class="variable declaration injected mutable">foo</span><span class="colon injected">:</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
56</span> <span class="comment documentation">/// ```</span> 61 <span class="comment documentation">/// ```</span>
57 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static associated">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 62 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration static associated">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="struct">Foo</span> <span class="brace">{</span>
58 <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">bar</span><span class="punctuation">:</span> <span class="bool_literal">true</span> <span class="punctuation">}</span> 63 <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">bar</span><span class="colon">:</span> <span class="bool_literal">true</span> <span class="brace">}</span>
59 <span class="punctuation">}</span> 64 <span class="brace">}</span>
60 65
61 <span class="comment documentation">/// `bar` method on `Foo`.</span> 66 <span class="comment documentation">/// `bar` method on `Foo`.</span>
62 <span class="comment documentation">///</span> 67 <span class="comment documentation">///</span>
63 <span class="comment documentation">/// # Examples</span> 68 <span class="comment documentation">/// # Examples</span>
64 <span class="comment documentation">///</span> 69 <span class="comment documentation">///</span>
65 <span class="comment documentation">/// ```</span> 70 <span class="comment documentation">/// ```</span>
66 <span class="comment documentation">/// </span><span class="keyword injected">use</span><span class="dummy injected"> </span><span class="module injected">x</span><span class="operator injected">::</span><span class="module injected">y</span><span class="punctuation injected">;</span> 71 <span class="comment documentation">/// </span><span class="keyword injected">use</span><span class="none injected"> </span><span class="module injected">x</span><span class="operator injected">::</span><span class="module injected">y</span><span class="semicolon injected">;</span>
67 <span class="comment documentation">///</span> 72 <span class="comment documentation">///</span>
68 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="variable declaration injected">foo</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span> 73 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
69 <span class="comment documentation">///</span> 74 <span class="comment documentation">///</span>
70 <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span> 75 <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span>
71 <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="punctuation injected">(</span><span class="dummy injected">foo</span><span class="operator injected">.</span><span class="dummy injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span> 76 <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
72 <span class="comment documentation">///</span> 77 <span class="comment documentation">///</span>
73 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="variable declaration injected">bar</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="dummy injected"> </span><span class="operator injected">||</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="punctuation injected">;</span> 78 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="operator injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span>
74 <span class="comment documentation">///</span> 79 <span class="comment documentation">///</span>
75 <span class="comment documentation">/// </span><span class="comment injected">/* multi-line 80 <span class="comment documentation">/// </span><span class="comment injected">/* multi-line</span>
76 </span><span class="comment documentation">/// </span><span class="comment injected"> comment */</span> 81 <span class="comment documentation">/// </span><span class="comment injected"> comment */</span>
77 <span class="comment documentation">///</span> 82 <span class="comment documentation">///</span>
78 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="string_literal injected">"Foo 83 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="string_literal injected">"Foo</span>
79 </span><span class="comment documentation">/// </span><span class="string_literal injected"> bar 84 <span class="comment documentation">/// </span><span class="string_literal injected"> bar</span>
80 </span><span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="punctuation injected">;</span> 85 <span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="semicolon injected">;</span>
81 <span class="comment documentation">///</span> 86 <span class="comment documentation">///</span>
82 <span class="comment documentation">/// ```</span> 87 <span class="comment documentation">/// ```</span>
83 <span class="comment documentation">///</span> 88 <span class="comment documentation">///</span>
84 <span class="comment documentation">/// ```rust,no_run</span> 89 <span class="comment documentation">/// ```rust,no_run</span>
85 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="dummy injected"> </span><span class="variable declaration injected">foobar</span><span class="dummy injected"> </span><span class="operator injected">=</span><span class="dummy injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="operator injected">.</span><span class="function injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> 90 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">foobar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="operator injected">.</span><span class="function injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
86</span> <span class="comment documentation">/// ```</span> 91 <span class="comment documentation">/// ```</span>
87 <span class="comment documentation">///</span> 92 <span class="comment documentation">///</span>
88 <span class="comment documentation">/// ```sh</span> 93 <span class="comment documentation">/// ```sh</span>
89 <span class="comment documentation">/// echo 1</span> 94 <span class="comment documentation">/// echo 1</span>
90 <span class="comment documentation">/// ```</span> 95 <span class="comment documentation">/// ```</span>
91 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">foo</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="punctuation">{</span> 96 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration associated">foo</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="brace">{</span>
92 <span class="bool_literal">true</span> 97 <span class="bool_literal">true</span>
93 <span class="punctuation">}</span> 98 <span class="brace">}</span>
94<span class="punctuation">}</span> 99<span class="brace">}</span>
95 100
96<span class="comment documentation">/// ```</span> 101<span class="comment documentation">/// ```</span>
97<span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="punctuation injected">(</span><span class="numeric_literal injected">1</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected"> 102<span class="comment documentation">/// </span><span class="macro injected">noop!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
98</span><span class="comment documentation">/// ```</span> 103<span class="comment documentation">/// ```</span>
99<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="punctuation">{</span> 104<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
100 <span class="punctuation">(</span><span class="punctuation">$</span>expr<span class="punctuation">:</span>expr<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 105 <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
101 <span class="punctuation">$</span>expr 106 <span class="punctuation">$</span>expr
102 <span class="punctuation">}</span> 107 <span class="brace">}</span>
103<span class="punctuation">}</span></code></pre> \ No newline at end of file 108<span class="brace">}</span></code></pre> \ No newline at end of file
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 ed452586a..6f7a7ffff 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
@@ -36,6 +36,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">std</span><span class="punctuation">;</span> 39<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">std</span><span class="semicolon">;</span>
40<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">alloc</span> <span class="keyword">as</span> <span class="module">abc</span><span class="punctuation">;</span> 40<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module">alloc</span> <span class="keyword">as</span> <span class="module">abc</span><span class="semicolon">;</span>
41</code></pre> \ No newline at end of file 41</code></pre> \ No newline at end of file
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 92e7dc3e4..753b535b5 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -36,14 +36,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="punctuation">(</span><span class="value_param declaration">ra_fixture</span><span class="punctuation">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 39<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="value_param declaration">ra_fixture</span><span class="colon">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
40 40
41<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 41<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
42 <span class="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span> 42 <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
43 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span> 43 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span>
44 <span class="keyword">fn</span> <span class="function declaration static associated">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 44 <span class="keyword">fn</span> <span class="function declaration static associated">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
45 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span> 45 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span>
46 <span class="punctuation">}</span> 46 <span class="brace">}</span>
47 <span class="punctuation">}</span><span class="string_literal">"#</span> 47 <span class="brace">}</span><span class="string_literal">"#</span>
48 <span class="punctuation">)</span><span class="punctuation">;</span> 48 <span class="parenthesis">)</span><span class="semicolon">;</span>
49<span class="punctuation">}</span></code></pre> \ No newline at end of file 49<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 31dad5d42..66d80c4b6 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -36,64 +36,64 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">println</span> <span class="punctuation">{</span> 39<pre><code><span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">println</span> <span class="brace">{</span>
40 <span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">(</span><span class="punctuation">{</span> 40 <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="brace">{</span>
41 <span class="punctuation">$</span>crate<span class="punctuation">:</span><span class="punctuation">:</span>io<span class="punctuation">:</span><span class="punctuation">:</span>_print<span class="punctuation">(</span><span class="punctuation">$</span>crate<span class="punctuation">:</span><span class="punctuation">:</span>format_args_nl<span class="punctuation">!</span><span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>arg<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span> 41 <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><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</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>
42 <span class="punctuation">}</span><span class="punctuation">)</span> 42 <span class="brace">}</span><span class="parenthesis">)</span>
43<span class="punctuation">}</span> 43<span class="brace">}</span>
44<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span> 44<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span>
45<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">format_args_nl</span> <span class="punctuation">{</span> 45<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">format_args_nl</span> <span class="brace">{</span>
46 <span class="punctuation">(</span><span class="punctuation">$</span>fmt<span class="punctuation">:</span>expr<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">{</span> <span class="comment">/* compiler built-in */</span> <span class="punctuation">}</span><span class="punctuation">}</span><span class="punctuation">;</span> 46 <span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">{</span> <span class="comment">/* compiler built-in */</span> <span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span>
47 <span class="punctuation">(</span><span class="punctuation">$</span>fmt<span class="punctuation">:</span>expr<span class="punctuation">,</span> <span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>args<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">{</span> <span class="comment">/* compiler built-in */</span> <span class="punctuation">}</span><span class="punctuation">}</span><span class="punctuation">;</span> 47 <span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>args<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">{</span> <span class="comment">/* compiler built-in */</span> <span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span>
48<span class="punctuation">}</span> 48<span class="brace">}</span>
49 49
50<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 50<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
51 <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span> 51 <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
52 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello"</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "Hello"</span> 52 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello"</span>
53 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"world"</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "Hello, world!"</span> 53 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello, world!"</span>
54 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">1</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "The number is 1"</span> 54 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "The number is 1"</span>
55 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</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">"</span><span class="punctuation">,</span> <span class="punctuation">(</span><span class="numeric_literal">3</span><span class="punctuation">,</span> <span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "(3, 4)"</span> 55 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</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">"</span><span class="comma">,</span> <span class="parenthesis">(</span><span class="numeric_literal">3</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "(3, 4)"</span>
56 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> value<span class="operator">=</span><span class="numeric_literal">4</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "4"</span> 56 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> value<span class="operator">=</span><span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "4"</span>
57 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">1</span><span class="punctuation">,</span> <span class="numeric_literal">2</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "1 2"</span> 57 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "1 2"</span>
58 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</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">"</span><span class="punctuation">,</span> <span class="numeric_literal">42</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span> 58 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</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">"</span><span class="comma">,</span> <span class="numeric_literal">42</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span>
59 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">1</span><span class="punctuation">,</span> <span class="numeric_literal">2</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "2 1 1 2"</span> 59 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1 1 2"</span>
60 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> argument <span class="operator">=</span> <span class="string_literal">"test"</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "test"</span> 60 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> argument <span class="operator">=</span> <span class="string_literal">"test"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "test"</span>
61 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> <span class="numeric_literal">1</span><span class="punctuation">,</span> name <span class="operator">=</span> <span class="numeric_literal">2</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "2 1"</span> 61 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> name <span class="operator">=</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1"</span>
62 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> a<span class="operator">=</span><span class="string_literal">"a"</span><span class="punctuation">,</span> b<span class="operator">=</span><span class="char_literal">'b'</span><span class="punctuation">,</span> c<span class="operator">=</span><span class="numeric_literal">3</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "a 3 b"</span> 62 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> a<span class="operator">=</span><span class="string_literal">"a"</span><span class="comma">,</span> b<span class="operator">=</span><span class="char_literal">'b'</span><span class="comma">,</span> c<span class="operator">=</span><span class="numeric_literal">3</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "a 3 b"</span>
63 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">}}"</span><span class="punctuation">,</span> <span class="numeric_literal">2</span><span class="punctuation">)</span><span class="punctuation">;</span> <span class="comment">// =&gt; "{2}"</span> 63 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">}}"</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "{2}"</span>
64 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> 64 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
65 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> 65 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
66 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> 66 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
67 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> width <span class="operator">=</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> 67 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> width <span class="operator">=</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
68 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> 68 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
69 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> 69 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
70 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> 70 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
71 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">)</span><span class="punctuation">;</span> 71 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
72 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> 72 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
73 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</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">!"</span><span class="punctuation">,</span> <span class="numeric_literal">27</span><span class="punctuation">)</span><span class="punctuation">;</span> 73 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</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">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
74 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> 74 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
75 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"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">!"</span><span class="punctuation">,</span> <span class="punctuation">-</span><span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</span> 75 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"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">!"</span><span class="comma">,</span> <span class="punctuation">-</span><span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
76 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</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">!"</span><span class="punctuation">,</span> <span class="numeric_literal">27</span><span class="punctuation">)</span><span class="punctuation">;</span> 76 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</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">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
77 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> 77 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
78 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> 78 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
79 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> 79 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
80 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> 80 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
81 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> 81 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
82 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="punctuation">,</span> <span class="string_literal">"x"</span><span class="punctuation">,</span> prec <span class="operator">=</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> number <span class="operator">=</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span> 82 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> 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">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> prec <span class="operator">=</span> <span class="numeric_literal">5</span><span class="comma">,</span> number <span class="operator">=</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
83 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</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">` has 3 fractional digits"</span><span class="punctuation">,</span> <span class="string_literal">"Hello"</span><span class="punctuation">,</span> <span class="numeric_literal">3</span><span class="punctuation">,</span> name<span class="operator">=</span><span class="numeric_literal">1234.56</span><span class="punctuation">)</span><span class="punctuation">;</span> 83 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</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">` has 3 fractional digits"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="numeric_literal">1234.56</span><span class="parenthesis">)</span><span class="semicolon">;</span>
84 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</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">` has 3 characters"</span><span class="punctuation">,</span> <span class="string_literal">"Hello"</span><span class="punctuation">,</span> <span class="numeric_literal">3</span><span class="punctuation">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="punctuation">)</span><span class="punctuation">;</span> 84 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</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">` has 3 characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
85 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</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">` has 3 right-aligned characters"</span><span class="punctuation">,</span> <span class="string_literal">"Hello"</span><span class="punctuation">,</span> <span class="numeric_literal">3</span><span class="punctuation">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="punctuation">)</span><span class="punctuation">;</span> 85 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</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">` has 3 right-aligned characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
86 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello {{}}"</span><span class="punctuation">)</span><span class="punctuation">;</span> 86 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello {{}}"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
87 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"{{ Hello"</span><span class="punctuation">)</span><span class="punctuation">;</span> 87 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"{{ Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
88 88
89 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="punctuation">,</span> <span class="string_literal">"world"</span><span class="punctuation">)</span><span class="punctuation">;</span> 89 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
90 90
91 <span class="comment">// escape sequences</span> 91 <span class="comment">// escape sequences</span>
92 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal">World"</span><span class="punctuation">)</span><span class="punctuation">;</span> 92 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal">World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
93 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</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"> World"</span><span class="punctuation">)</span><span class="punctuation">;</span> 93 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</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"> World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
94 94
95 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> A <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> 95 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> A <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
96 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="punctuation">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> 96 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
97 97
98 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"</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"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> "</span><span class="punctuation">,</span> thingy<span class="punctuation">,</span> n2<span class="punctuation">)</span><span class="punctuation">;</span> 98 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</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"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> "</span><span class="comma">,</span> thingy<span class="comma">,</span> n2<span class="parenthesis">)</span><span class="semicolon">;</span>
99<span class="punctuation">}</span></code></pre> \ No newline at end of file 99<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 e3a0aa317..9d4d6d4a0 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -36,65 +36,65 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 39<pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
40 40
41<span class="keyword">union</span> <span class="union declaration">Union</span> <span class="punctuation">{</span> 41<span class="keyword">union</span> <span class="union declaration">Union</span> <span class="brace">{</span>
42 <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> 42 <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
43 <span class="field declaration">b</span><span class="punctuation">:</span> <span class="builtin_type">f32</span><span class="punctuation">,</span> 43 <span class="field declaration">b</span><span class="colon">:</span> <span class="builtin_type">f32</span><span class="comma">,</span>
44<span class="punctuation">}</span> 44<span class="brace">}</span>
45 45
46<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="punctuation">;</span> 46<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="semicolon">;</span>
47 47
48<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="punctuation">{</span> 48<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="brace">{</span>
49 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration associated unsafe">unsafe_method</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 49 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration associated unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
50<span class="punctuation">}</span> 50<span class="brace">}</span>
51 51
52<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="punctuation">{</span> 52<span class="keyword">struct</span> <span class="struct declaration">TypeForStaticMut</span> <span class="brace">{</span>
53 <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u8</span> 53 <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u8</span>
54<span class="punctuation">}</span> 54<span class="brace">}</span>
55 55
56<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="punctuation">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> 56<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">global_mut</span><span class="colon">:</span> <span class="struct">TypeForStaticMut</span> <span class="operator">=</span> <span class="struct">TypeForStaticMut</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
57 57
58<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">repr</span><span class="punctuation attribute">(</span><span class="attribute attribute">packed</span><span class="punctuation attribute">)</span><span class="attribute attribute">]</span> 58<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">repr</span><span class="parenthesis attribute">(</span><span class="attribute attribute">packed</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
59<span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="punctuation">{</span> 59<span class="keyword">struct</span> <span class="struct declaration">Packed</span> <span class="brace">{</span>
60 <span class="field declaration">a</span><span class="punctuation">:</span> <span class="builtin_type">u16</span><span class="punctuation">,</span> 60 <span class="field declaration">a</span><span class="colon">:</span> <span class="builtin_type">u16</span><span class="comma">,</span>
61<span class="punctuation">}</span> 61<span class="brace">}</span>
62 62
63<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="punctuation">{</span> 63<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span>
64 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span><span class="punctuation">;</span> 64 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
65<span class="punctuation">}</span> 65<span class="brace">}</span>
66 66
67<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="punctuation">{</span> 67<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span>
68 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span> 68 <span class="keyword">fn</span> <span class="function declaration associated">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
69<span class="punctuation">}</span> 69<span class="brace">}</span>
70 70
71<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 71<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
72 <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="punctuation">;</span> 72 <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>
73 <span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="punctuation">{</span> <span class="field">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> 73 <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>
74 <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> 74 <span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
75 <span class="comment">// unsafe fn and method calls</span> 75 <span class="comment">// unsafe fn and method calls</span>
76 <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 76 <span class="function unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
77 <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">b</span><span class="punctuation">;</span> 77 <span class="keyword">let</span> <span class="variable declaration">b</span> <span class="operator">=</span> <span class="variable">u</span><span class="operator">.</span><span class="field unsafe">b</span><span class="semicolon">;</span>
78 <span class="keyword control">match</span> <span class="variable">u</span> <span class="punctuation">{</span> 78 <span class="keyword control">match</span> <span class="variable">u</span> <span class="brace">{</span>
79 <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">b</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> 79 <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span> <span class="operator">=&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
80 <span class="union">Union</span> <span class="punctuation">{</span> <span class="field unsafe">a</span> <span class="punctuation">}</span> <span class="operator">=&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> 80 <span class="union">Union</span> <span class="brace">{</span> <span class="field unsafe">a</span> <span class="brace">}</span> <span class="operator">=&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
81 <span class="punctuation">}</span> 81 <span class="brace">}</span>
82 <span class="struct">HasUnsafeFn</span><span class="operator">.</span><span class="function associated unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 82 <span class="struct">HasUnsafeFn</span><span class="operator">.</span><span class="function associated unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
83 83
84 <span class="comment">// unsafe deref</span> 84 <span class="comment">// unsafe deref</span>
85 <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="punctuation">;</span> 85 <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="operator">=</span> <span class="operator unsafe">*</span><span class="variable">x</span><span class="semicolon">;</span>
86 86
87 <span class="comment">// unsafe access to a static mut</span> 87 <span class="comment">// unsafe access to a static mut</span>
88 <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span> 88 <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="static mutable unsafe">global_mut</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span>
89 89
90 <span class="comment">// unsafe ref of packed fields</span> 90 <span class="comment">// unsafe ref of packed fields</span>
91 <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="numeric_literal">0</span> <span class="punctuation">}</span><span class="punctuation">;</span> 91 <span class="keyword">let</span> <span class="variable declaration">packed</span> <span class="operator">=</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
92 <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span> 92 <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="operator unsafe">&</span><span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span>
93 <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="punctuation">;</span> 93 <span class="keyword">let</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="semicolon">;</span>
94 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span> 94 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="keyword unsafe">ref</span> <span class="field">a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span>
95 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="punctuation">{</span> <span class="field">a</span><span class="punctuation">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="punctuation">;</span> 95 <span class="keyword">let</span> <span class="struct">Packed</span> <span class="brace">{</span> <span class="field">a</span><span class="colon">:</span> <span class="keyword unsafe">ref</span> <span class="variable declaration">_a</span> <span class="brace">}</span> <span class="operator">=</span> <span class="variable">packed</span><span class="semicolon">;</span>
96 96
97 <span class="comment">// unsafe auto ref of packed field</span> 97 <span class="comment">// unsafe auto ref of packed field</span>
98 <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function associated unsafe">calls_autoref</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 98 <span class="variable">packed</span><span class="operator">.</span><span class="field">a</span><span class="operator">.</span><span class="function associated unsafe">calls_autoref</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
99 <span class="punctuation">}</span> 99 <span class="brace">}</span>
100<span class="punctuation">}</span></code></pre> \ No newline at end of file 100<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 72ff9dd40..6b7447c46 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -36,183 +36,187 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="punctuation">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="punctuation">}</span><span class="punctuation">;</span> 39<pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="brace">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="brace">}</span><span class="semicolon">;</span>
40<span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="punctuation">{</span><span class="punctuation">}</span> 40<span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span><span class="brace">}</span>
41 41
42<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span> 42<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span>
43<span class="keyword">macro</span> <span class="unresolved_reference declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> 43<span class="keyword">macro</span> <span class="unresolved_reference declaration">Copy</span> <span class="brace">{</span><span class="brace">}</span>
44 44
45<span class="comment">// Needed for function consuming vs normal</span> 45<span class="comment">// Needed for function consuming vs normal</span>
46<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="punctuation">{</span> 46<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="brace">{</span>
47 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"copy"</span><span class="attribute attribute">]</span> 47 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"copy"</span><span class="attribute attribute">]</span>
48 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> 48 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Copy</span> <span class="brace">{</span><span class="brace">}</span>
49<span class="punctuation">}</span> 49<span class="brace">}</span>
50 50
51<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">ops</span> <span class="punctuation">{</span> 51<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">ops</span> <span class="brace">{</span>
52 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span> 52 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_once"</span><span class="attribute attribute">]</span>
53 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnOnce</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span> 53 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnOnce</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
54 54
55 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span> 55 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn_mut"</span><span class="attribute attribute">]</span>
56 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnMut</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span><span class="punctuation">:</span> <span class="trait">FnOnce</span><span class="punctuation">&lt;</span><span class="type_param">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span> 56 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">FnMut</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait">FnOnce</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
57 57
58 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span> 58 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">lang</span><span class="attribute attribute"> </span><span class="operator attribute">=</span><span class="attribute attribute"> </span><span class="string_literal attribute">"fn"</span><span class="attribute attribute">]</span>
59 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Fn</span><span class="punctuation">&lt;</span><span class="type_param declaration">Args</span><span class="punctuation">&gt;</span><span class="punctuation">:</span> <span class="trait">FnMut</span><span class="punctuation">&lt;</span><span class="type_param">Args</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">}</span> 59 <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration">Fn</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait">FnMut</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
60<span class="punctuation">}</span> 60<span class="brace">}</span>
61 61
62 62
63<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span> 63<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span>
64 <span class="keyword">pub</span> <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> 64 <span class="keyword">pub</span> <span class="field declaration">x</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span>
65 <span class="keyword">pub</span> <span class="field declaration">y</span><span class="punctuation">:</span> <span class="builtin_type">i32</span><span class="punctuation">,</span> 65 <span class="keyword">pub</span> <span class="field declaration">y</span><span class="colon">:</span> <span class="builtin_type">i32</span><span class="comma">,</span>
66<span class="punctuation">}</span> 66<span class="brace">}</span>
67 67
68<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="punctuation">{</span> 68<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span>
69 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="punctuation">;</span> 69 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span>
70<span class="punctuation">}</span> 70<span class="brace">}</span>
71 71
72<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 72<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span>
73 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> 73 <span class="keyword">fn</span> <span class="function declaration associated">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
74 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 74 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
75 <span class="punctuation">}</span> 75 <span class="brace">}</span>
76<span class="punctuation">}</span> 76<span class="brace">}</span>
77 77
78<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span> 78<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="brace">{</span>
79 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">Foo</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> 79 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">Foo</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
80 <span class="value_param">f</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="punctuation">(</span><span class="self_keyword mutable consuming">self</span><span class="punctuation">)</span> 80 <span class="value_param">f</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="parenthesis">(</span><span class="self_keyword mutable consuming">self</span><span class="parenthesis">)</span>
81 <span class="punctuation">}</span> 81 <span class="brace">}</span>
82 82
83 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> 83 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
84 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> 84 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
85 <span class="punctuation">}</span> 85 <span class="brace">}</span>
86 86
87 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span> 87 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
88 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 88 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
89 <span class="punctuation">}</span> 89 <span class="brace">}</span>
90<span class="punctuation">}</span> 90<span class="brace">}</span>
91 91
92<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">derive</span><span class="punctuation attribute">(</span><span class="attribute attribute">Copy</span><span class="punctuation attribute">)</span><span class="attribute attribute">]</span> 92<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">derive</span><span class="parenthesis attribute">(</span><span class="attribute attribute">Copy</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
93<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="punctuation">{</span> 93<span class="keyword">struct</span> <span class="struct declaration">FooCopy</span> <span class="brace">{</span>
94 <span class="field declaration">x</span><span class="punctuation">:</span> <span class="builtin_type">u32</span><span class="punctuation">,</span> 94 <span class="field declaration">x</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
95<span class="punctuation">}</span> 95<span class="brace">}</span>
96 96
97<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> 97<span class="keyword">impl</span> <span class="struct">FooCopy</span> <span class="brace">{</span>
98 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">f</span><span class="punctuation">:</span> <span class="struct">FooCopy</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> 98 <span class="keyword">fn</span> <span class="function declaration associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="comma">,</span> <span class="value_param declaration">f</span><span class="colon">:</span> <span class="struct">FooCopy</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
99 <span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">)</span> 99 <span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="parenthesis">)</span>
100 <span class="punctuation">}</span> 100 <span class="brace">}</span>
101 101
102 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="punctuation">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span> 102 <span class="keyword">fn</span> <span class="function declaration associated">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
103 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> 103 <span class="self_keyword mutable">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
104 <span class="punctuation">}</span> 104 <span class="brace">}</span>
105 105
106 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="punctuation">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> 106 <span class="keyword">fn</span> <span class="function declaration associated">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
107 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span> 107 <span class="self_keyword">self</span><span class="operator">.</span><span class="field">x</span>
108 <span class="punctuation">}</span> 108 <span class="brace">}</span>
109<span class="punctuation">}</span> 109<span class="brace">}</span>
110 110
111<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">STATIC_MUT</span><span class="punctuation">:</span> <span class="builtin_type">i32</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span> 111<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">STATIC_MUT</span><span class="colon">:</span> <span class="builtin_type">i32</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
112 112
113<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">&lt;</span><span class="lifetime declaration">'a</span><span class="punctuation">,</span> <span class="type_param declaration">T</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="type_param">T</span> <span class="punctuation">{</span> 113<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="angle">&lt;</span><span class="lifetime declaration">'a</span><span class="comma">,</span> <span class="type_param declaration">T</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="type_param">T</span> <span class="brace">{</span>
114 <span class="function">foo</span><span class="operator">::</span><span class="punctuation">&lt;</span><span class="lifetime">'a</span><span class="punctuation">,</span> <span class="builtin_type">i32</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span> 114 <span class="function">foo</span><span class="operator">::</span><span class="angle">&lt;</span><span class="lifetime">'a</span><span class="comma">,</span> <span class="builtin_type">i32</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span>
115<span class="punctuation">}</span> 115<span class="brace">}</span>
116 116
117<span class="keyword">fn</span> <span class="function declaration">never</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">!</span> <span class="punctuation">{</span> 117<span class="keyword">fn</span> <span class="function declaration">never</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">!</span> <span class="brace">{</span>
118 <span class="keyword control">loop</span> <span class="punctuation">{</span><span class="punctuation">}</span> 118 <span class="keyword control">loop</span> <span class="brace">{</span><span class="brace">}</span>
119<span class="punctuation">}</span> 119<span class="brace">}</span>
120 120
121<span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="punctuation">;</span> 121<span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="angle">&lt;</span><span class="keyword">const</span> <span class="const_param declaration">FOO</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">usize</span> <span class="brace">{</span>
122<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">&lt;</span><span class="type_param declaration">F</span><span class="punctuation">:</span> <span class="trait">Fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="value_param declaration callable">f</span><span class="punctuation">:</span> <span class="type_param">F</span><span class="punctuation">)</span> <span class="punctuation">{</span> 122 <span class="const_param">FOO</span>
123 <span class="value_param callable">f</span><span class="punctuation">(</span><span class="punctuation">)</span> 123<span class="brace">}</span>
124<span class="punctuation">}</span>
125 124
126<span class="keyword">fn</span> <span class="function declaration">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="keyword">impl</span> <span class="macro">Copy</span> <span class="punctuation">{</span><span class="punctuation">}</span> 125<span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="semicolon">;</span>
126<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle">&lt;</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="value_param declaration callable">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span>
127 <span class="value_param callable">f</span><span class="parenthesis">(</span><span class="parenthesis">)</span>
128<span class="brace">}</span>
127 129
128<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 130<span class="keyword">fn</span> <span class="function declaration">foobar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="keyword">impl</span> <span class="macro">Copy</span> <span class="brace">{</span><span class="brace">}</span>
129 <span class="keyword">let</span> <span class="variable declaration">bar</span> <span class="operator">=</span> <span class="function">foobar</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
130<span class="punctuation">}</span>
131 131
132<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">def_fn</span> <span class="punctuation">{</span> 132<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
133 <span class="punctuation">(</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>tt<span class="punctuation">:</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span><span class="punctuation">$</span><span class="punctuation">(</span><span class="punctuation">$</span>tt<span class="punctuation">)</span><span class="punctuation">*</span><span class="punctuation">}</span> 133 <span class="keyword">let</span> <span class="variable declaration">bar</span> <span class="operator">=</span> <span class="function">foobar</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
134<span class="punctuation">}</span> 134<span class="brace">}</span>
135 135
136<span class="macro">def_fn!</span> <span class="punctuation">{</span> 136<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">def_fn</span> <span class="brace">{</span>
137 <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-</span><span class="operator">&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span> 137 <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="brace">}</span>
138<span class="brace">}</span>
139
140<span class="macro">def_fn!</span> <span class="brace">{</span>
141 <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-</span><span class="operator">&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
138 <span class="numeric_literal">100</span> 142 <span class="numeric_literal">100</span>
139 <span class="punctuation">}</span> 143 <span class="brace">}</span>
140<span class="punctuation">}</span> 144<span class="brace">}</span>
141 145
142<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="punctuation">{</span> 146<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
143 <span class="punctuation">(</span><span class="punctuation">$</span>expr<span class="punctuation">:</span>expr<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 147 <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
144 <span class="punctuation">$</span>expr 148 <span class="punctuation">$</span>expr
145 <span class="punctuation">}</span> 149 <span class="brace">}</span>
146<span class="punctuation">}</span> 150<span class="brace">}</span>
147 151
148<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">keyword_frag</span> <span class="punctuation">{</span> 152<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">keyword_frag</span> <span class="brace">{</span>
149 <span class="punctuation">(</span><span class="punctuation">$</span>type<span class="punctuation">:</span>ty<span class="punctuation">)</span> <span class="operator">=</span><span class="punctuation">&gt;</span> <span class="punctuation">(</span><span class="punctuation">$</span>type<span class="punctuation">)</span> 153 <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="colon">:</span>ty<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="parenthesis">)</span>
150<span class="punctuation">}</span> 154<span class="brace">}</span>
151 155
152<span class="comment">// comment</span> 156<span class="comment">// comment</span>
153<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 157<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
154 <span class="macro">println!</span><span class="punctuation">(</span><span class="string_literal">"Hello, {}!"</span><span class="punctuation">,</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span> 158 <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
155 159
156 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 160 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="operator">=</span> <span class="unresolved_reference">Vec</span><span class="operator">::</span><span class="unresolved_reference">new</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
157 <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="punctuation">{</span> 161 <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="brace">{</span>
158 <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="numeric_literal">92</span><span class="punctuation">;</span> 162 <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="numeric_literal">92</span><span class="semicolon">;</span>
159 <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">push</span><span class="punctuation">(</span><span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="numeric_literal">1</span> <span class="punctuation">}</span><span class="punctuation">)</span><span class="punctuation">;</span> 163 <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">push</span><span class="parenthesis">(</span><span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="numeric_literal">1</span> <span class="brace">}</span><span class="parenthesis">)</span><span class="semicolon">;</span>
160 <span class="punctuation">}</span> 164 <span class="brace">}</span>
161 <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span> 165 <span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
162 <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">set_len</span><span class="punctuation">(</span><span class="numeric_literal">0</span><span class="punctuation">)</span><span class="punctuation">;</span> 166 <span class="variable mutable">vec</span><span class="operator">.</span><span class="unresolved_reference">set_len</span><span class="parenthesis">(</span><span class="numeric_literal">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
163 <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span> 167 <span class="static mutable unsafe">STATIC_MUT</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="semicolon">;</span>
164 <span class="punctuation">}</span> 168 <span class="brace">}</span>
165 169
166 <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> <span class="punctuation">{</span> 170 <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> <span class="brace">{</span>
167 <span class="comment">// Do nothing</span> 171 <span class="comment">// Do nothing</span>
168 <span class="punctuation">}</span> 172 <span class="brace">}</span>
169 173
170 <span class="macro">noop!</span><span class="punctuation">(</span><span class="macro">noop</span><span class="macro">!</span><span class="punctuation">(</span><span class="numeric_literal">1</span><span class="punctuation">)</span><span class="punctuation">)</span><span class="punctuation">;</span> 174 <span class="macro">noop!</span><span class="parenthesis">(</span><span class="macro">noop</span><span class="macro">!</span><span class="parenthesis">(</span><span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
171 175
172 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> <span class="operator">=</span> <span class="numeric_literal">42</span><span class="punctuation">;</span> 176 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> <span class="operator">=</span> <span class="numeric_literal">42</span><span class="semicolon">;</span>
173 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">mut</span> <span class="variable mutable">x</span><span class="punctuation">;</span> 177 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> <span class="operator">=</span> <span class="operator">&</span><span class="keyword">mut</span> <span class="variable mutable">x</span><span class="semicolon">;</span>
174 <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="operator">&</span><span class="variable mutable">y</span><span class="punctuation">;</span> 178 <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="operator">=</span> <span class="operator">&</span><span class="variable mutable">y</span><span class="semicolon">;</span>
175 179
176 <span class="keyword">let</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">:</span> <span class="variable declaration">z</span><span class="punctuation">,</span> <span class="field">y</span> <span class="punctuation">}</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">:</span> <span class="variable">z</span><span class="punctuation">,</span> <span class="field">y</span> <span class="punctuation">}</span><span class="punctuation">;</span> 180 <span class="keyword">let</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable declaration">z</span><span class="comma">,</span> <span class="field">y</span> <span class="brace">}</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="colon">:</span> <span class="variable">z</span><span class="comma">,</span> <span class="field">y</span> <span class="brace">}</span><span class="semicolon">;</span>
177 181
178 <span class="variable">y</span><span class="punctuation">;</span> 182 <span class="variable">y</span><span class="semicolon">;</span>
179 183
180 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> 184 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span>
181 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="punctuation">{</span> <span class="field">x</span><span class="punctuation">,</span> <span class="field">y</span><span class="punctuation">:</span> <span class="variable mutable">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> 185 <span class="keyword">let</span> <span class="variable declaration">foo2</span> <span class="operator">=</span> <span class="struct">Foo</span> <span class="brace">{</span> <span class="field">x</span><span class="comma">,</span> <span class="field">y</span><span class="colon">:</span> <span class="variable mutable">x</span> <span class="brace">}</span><span class="semicolon">;</span>
182 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 186 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
183 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 187 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
184 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="punctuation">(</span><span class="variable consuming">foo2</span><span class="punctuation">)</span><span class="punctuation">;</span> 188 <span class="variable mutable">foo</span><span class="operator">.</span><span class="function consuming associated">baz</span><span class="parenthesis">(</span><span class="variable consuming">foo2</span><span class="parenthesis">)</span><span class="semicolon">;</span>
185 189
186 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="punctuation">{</span> <span class="field">x</span> <span class="punctuation">}</span><span class="punctuation">;</span> 190 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">copy</span> <span class="operator">=</span> <span class="struct">FooCopy</span> <span class="brace">{</span> <span class="field">x</span> <span class="brace">}</span><span class="semicolon">;</span>
187 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">quop</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 191 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">quop</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
188 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 192 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function mutable associated">qux</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
189 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">baz</span><span class="punctuation">(</span><span class="variable mutable">copy</span><span class="punctuation">)</span><span class="punctuation">;</span> 193 <span class="variable mutable">copy</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="variable mutable">copy</span><span class="parenthesis">)</span><span class="semicolon">;</span>
190 194
191 <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="punctuation">;</span> 195 <span class="keyword">let</span> <span class="variable declaration callable">a</span> <span class="operator">=</span> <span class="punctuation">|</span><span class="value_param declaration">x</span><span class="punctuation">|</span> <span class="value_param">x</span><span class="semicolon">;</span>
192 <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="punctuation">;</span> 196 <span class="keyword">let</span> <span class="variable declaration callable">bar</span> <span class="operator">=</span> <span class="struct">Foo</span><span class="operator">::</span><span class="function associated">baz</span><span class="semicolon">;</span>
193 197
194 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="punctuation">;</span> 198 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span>
195 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span> 199 <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span>
196 200
197 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="punctuation">;</span> 201 <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="semicolon">;</span>
198 202
199 <span class="label declaration">'foo</span><span class="punctuation">:</span> <span class="keyword control">loop</span> <span class="punctuation">{</span> 203 <span class="label declaration">'foo</span><span class="colon">:</span> <span class="keyword control">loop</span> <span class="brace">{</span>
200 <span class="keyword control">break</span> <span class="label">'foo</span><span class="punctuation">;</span> 204 <span class="keyword control">break</span> <span class="label">'foo</span><span class="semicolon">;</span>
201 <span class="keyword control">continue</span> <span class="label">'foo</span><span class="punctuation">;</span> 205 <span class="keyword control">continue</span> <span class="label">'foo</span><span class="semicolon">;</span>
202 <span class="punctuation">}</span> 206 <span class="brace">}</span>
203<span class="punctuation">}</span> 207<span class="brace">}</span>
204 208
205<span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 209<span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="brace">{</span>
206 <span class="enum_variant declaration">Some</span><span class="punctuation">(</span><span class="type_param">T</span><span class="punctuation">)</span><span class="punctuation">,</span> 210 <span class="enum_variant declaration">Some</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="parenthesis">)</span><span class="comma">,</span>
207 <span class="enum_variant declaration">None</span><span class="punctuation">,</span> 211 <span class="enum_variant declaration">None</span><span class="comma">,</span>
208<span class="punctuation">}</span> 212<span class="brace">}</span>
209<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="punctuation">;</span> 213<span class="keyword">use</span> <span class="enum">Option</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span>
210 214
211<span class="keyword">impl</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="enum">Option</span><span class="punctuation">&lt;</span><span class="type_param">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 215<span class="keyword">impl</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">T</span><span class="angle">&gt;</span> <span class="brace">{</span>
212 <span class="keyword">fn</span> <span class="function declaration associated">and</span><span class="punctuation">&lt;</span><span class="type_param declaration">U</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="self_keyword">self</span><span class="punctuation">,</span> <span class="value_param declaration">other</span><span class="punctuation">:</span> <span class="enum">Option</span><span class="punctuation">&lt;</span><span class="type_param">U</span><span class="punctuation">&gt;</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="punctuation">&lt;</span><span class="punctuation">(</span><span class="type_param">T</span><span class="punctuation">,</span> <span class="type_param">U</span><span class="punctuation">)</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span> 216 <span class="keyword">fn</span> <span class="function declaration associated">and</span><span class="angle">&lt;</span><span class="type_param declaration">U</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="comma">,</span> <span class="value_param declaration">other</span><span class="colon">:</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">U</span><span class="angle">&gt;</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="comma">,</span> <span class="type_param">U</span><span class="parenthesis">)</span><span class="angle">&gt;</span> <span class="brace">{</span>
213 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="punctuation">{</span> 217 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="brace">{</span>
214 <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="macro">unimplemented!</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span> 218 <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="macro">unimplemented!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
215 <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="punctuation">,</span> 219 <span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="comma">,</span>
216 <span class="punctuation">}</span> 220 <span class="brace">}</span>
217 <span class="punctuation">}</span> 221 <span class="brace">}</span>
218<span class="punctuation">}</span></code></pre> \ No newline at end of file 222<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/injection.html b/crates/ide/src/syntax_highlighting/test_data/injection.html
new file mode 100644
index 000000000..78dfec951
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/test_data/injection.html
@@ -0,0 +1,48 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.label { color: #DFAF8F; font-style: italic; }
8.comment { color: #7F9F7F; }
9.documentation { color: #629755; }
10.injected { opacity: 0.65 ; }
11.struct, .enum { color: #7CB8BB; }
12.enum_variant { color: #BDE0F3; }
13.string_literal { color: #CC9393; }
14.field { color: #94BFF3; }
15.function { color: #93E0E3; }
16.function.unsafe { color: #BC8383; }
17.operator.unsafe { color: #BC8383; }
18.parameter { color: #94BFF3; }
19.text { color: #DCDCCC; }
20.type { color: #7CB8BB; }
21.builtin_type { color: #8CD0D3; }
22.type_param { color: #DFAF8F; }
23.attribute { color: #94BFF3; }
24.numeric_literal { color: #BFEBBF; }
25.bool_literal { color: #BFE6EB; }
26.macro { color: #94BFF3; }
27.module { color: #AFD8AF; }
28.value_param { color: #DCDCCC; }
29.variable { color: #DCDCCC; }
30.format_specifier { color: #CC696B; }
31.mutable { text-decoration: underline; }
32.escape_sequence { color: #94BFF3; }
33.keyword { color: #F0DFAF; font-weight: bold; }
34.keyword.unsafe { color: #BC8383; font-weight: bold; }
35.control { font-style: italic; }
36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style>
39<pre><code><span class="keyword">fn</span> <span class="function declaration">f</span><span class="parenthesis">(</span><span class="value_param declaration">ra_fixture</span><span class="colon">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
40<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
41 <span class="function">f</span><span class="parenthesis">(</span><span class="string_literal">r"</span>
42<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
43 <span class="function">foo</span><span class="parenthesis">(</span><span class="keyword">$0</span><span class="brace">{</span>
44 <span class="numeric_literal">92</span>
45 <span class="brace">}</span><span class="keyword">$0</span><span class="parenthesis">)</span>
46<span class="brace">}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
47<span class="brace">}</span>
48 </code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
index 8b3dfa69f..e64f2e5e9 100644
--- a/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/rainbow_highlighting.html
@@ -36,15 +36,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
36 36
37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } 37.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
38</style> 38</style>
39<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 39<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>
40 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span> 40 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span>
41 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 41 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(76,47%,83%);">x</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
42 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 42 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(15,86%,51%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
43 43
44 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="punctuation">;</span> 44 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="semicolon">;</span>
45 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span> 45 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(90,74%,79%);">y</span> <span class="operator">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(127,71%,87%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
46<span class="punctuation">}</span> 46<span class="brace">}</span>
47 47
48<span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span> 48<span class="keyword">fn</span> <span class="function declaration">bar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
49 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span> 49 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable" data-binding-hash="8121853618659664005" style="color: hsl(273,88%,88%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span>
50<span class="punctuation">}</span></code></pre> \ No newline at end of file 50<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index e0df0d2b5..a62704c39 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -91,6 +91,10 @@ fn never() -> ! {
91 loop {} 91 loop {}
92} 92}
93 93
94fn const_param<const FOO: usize>() -> usize {
95 FOO
96}
97
94use ops::Fn; 98use ops::Fn;
95fn baz<F: Fn() -> ()>(f: F) { 99fn baz<F: Fn() -> ()>(f: F) {
96 f() 100 f()
@@ -442,6 +446,11 @@ struct Foo {
442} 446}
443 447
444impl Foo { 448impl Foo {
449 /// ```
450 /// let _ = "Call me
451 // KILLER WHALE
452 /// Ishmael.";
453 /// ```
445 pub const bar: bool = true; 454 pub const bar: bool = true;
446 455
447 /// Constructs a new `Foo`. 456 /// Constructs a new `Foo`.
@@ -551,6 +560,25 @@ impl t for foo {
551 ) 560 )
552} 561}
553 562
563#[test]
564fn test_injection() {
565 check_highlighting(
566 r##"
567fn f(ra_fixture: &str) {}
568fn main() {
569 f(r"
570fn foo() {
571 foo(\$0{
572 92
573 }\$0)
574}");
575}
576 "##,
577 expect_file!["./test_data/injection.html"],
578 false,
579 );
580}
581
554/// Highlights the code given by the `ra_fixture` argument, renders the 582/// Highlights the code given by the `ra_fixture` argument, renders the
555/// result as HTML, and compares it with the HTML file given as `snapshot`. 583/// result as HTML, and compares it with the HTML file given as `snapshot`.
556/// Note that the `snapshot` file is overwritten by the rendered HTML. 584/// Note that the `snapshot` file is overwritten by the rendered HTML.
diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs
index 6dd05c05d..1f26f8043 100644
--- a/crates/ide/src/syntax_tree.rs
+++ b/crates/ide/src/syntax_tree.rs
@@ -85,7 +85,7 @@ fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<St
85 .trim_end_matches('"') 85 .trim_end_matches('"')
86 .trim() 86 .trim()
87 // Remove custom markers 87 // Remove custom markers
88 .replace("<|>", ""); 88 .replace("$0", "");
89 89
90 let parsed = SourceFile::parse(&text); 90 let parsed = SourceFile::parse(&text);
91 91
@@ -182,7 +182,7 @@ [email protected]
182 182
183 #[test] 183 #[test]
184 fn test_syntax_tree_with_range() { 184 fn test_syntax_tree_with_range() {
185 let (analysis, range) = fixture::range(r#"<|>fn foo() {}<|>"#.trim()); 185 let (analysis, range) = fixture::range(r#"$0fn foo() {}$0"#.trim());
186 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 186 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
187 187
188 assert_eq_text!( 188 assert_eq_text!(
@@ -206,10 +206,10 @@ [email protected]
206 206
207 let (analysis, range) = fixture::range( 207 let (analysis, range) = fixture::range(
208 r#"fn test() { 208 r#"fn test() {
209 <|>assert!(" 209 $0assert!("
210 fn foo() { 210 fn foo() {
211 } 211 }
212 ", "");<|> 212 ", "");$0
213}"# 213}"#
214 .trim(), 214 .trim(),
215 ); 215 );
@@ -243,8 +243,8 @@ [email protected]
243 let (analysis, range) = fixture::range( 243 let (analysis, range) = fixture::range(
244 r#"fn test() { 244 r#"fn test() {
245 assert!(" 245 assert!("
246<|>fn foo() { 246$0fn foo() {
247}<|> 247}$0
248fn bar() { 248fn bar() {
249} 249}
250 ", ""); 250 ", "");
@@ -277,8 +277,8 @@ [email protected]
277 let (analysis, range) = fixture::range( 277 let (analysis, range) = fixture::range(
278 r###"fn test() { 278 r###"fn test() {
279 assert!(r#" 279 assert!(r#"
280<|>fn foo() { 280$0fn foo() {
281}<|> 281}$0
282fn bar() { 282fn bar() {
283} 283}
284 "#, ""); 284 "#, "");
@@ -310,11 +310,11 @@ [email protected]
310 // With a raw string 310 // With a raw string
311 let (analysis, range) = fixture::range( 311 let (analysis, range) = fixture::range(
312 r###"fn test() { 312 r###"fn test() {
313 assert!(r<|>#" 313 assert!(r$0#"
314fn foo() { 314fn foo() {
315} 315}
316fn bar() { 316fn bar() {
317}"<|>#, ""); 317}"$0#, "");
318}"### 318}"###
319 .trim(), 319 .trim(),
320 ); 320 );
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index 43458a3a2..88c905003 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -170,7 +170,7 @@ mod tests {
170 fn test_on_eq_typed() { 170 fn test_on_eq_typed() {
171 // do_check(r" 171 // do_check(r"
172 // fn foo() { 172 // fn foo() {
173 // let foo =<|> 173 // let foo =$0
174 // } 174 // }
175 // ", r" 175 // ", r"
176 // fn foo() { 176 // fn foo() {
@@ -181,7 +181,7 @@ mod tests {
181 '=', 181 '=',
182 r" 182 r"
183fn foo() { 183fn foo() {
184 let foo <|> 1 + 1 184 let foo $0 1 + 1
185} 185}
186", 186",
187 r" 187 r"
@@ -192,7 +192,7 @@ fn foo() {
192 ); 192 );
193 // do_check(r" 193 // do_check(r"
194 // fn foo() { 194 // fn foo() {
195 // let foo =<|> 195 // let foo =$0
196 // let bar = 1; 196 // let bar = 1;
197 // } 197 // }
198 // ", r" 198 // ", r"
@@ -210,7 +210,7 @@ fn foo() {
210 r" 210 r"
211 fn main() { 211 fn main() {
212 xs.foo() 212 xs.foo()
213 <|> 213 $0
214 } 214 }
215 ", 215 ",
216 r" 216 r"
@@ -225,7 +225,7 @@ fn foo() {
225 r" 225 r"
226 fn main() { 226 fn main() {
227 xs.foo() 227 xs.foo()
228 <|> 228 $0
229 } 229 }
230 ", 230 ",
231 ) 231 )
@@ -238,7 +238,7 @@ fn foo() {
238 r" 238 r"
239 fn main() { 239 fn main() {
240 xs.foo() 240 xs.foo()
241 <|>; 241 $0;
242 } 242 }
243 ", 243 ",
244 r" 244 r"
@@ -253,7 +253,7 @@ fn foo() {
253 r" 253 r"
254 fn main() { 254 fn main() {
255 xs.foo() 255 xs.foo()
256 <|>; 256 $0;
257 } 257 }
258 ", 258 ",
259 ) 259 )
@@ -266,7 +266,7 @@ fn foo() {
266 r#" 266 r#"
267fn main() { 267fn main() {
268 let _ = foo 268 let _ = foo
269 <|> 269 $0
270 bar() 270 bar()
271} 271}
272"#, 272"#,
@@ -288,7 +288,7 @@ fn main() {
288 fn main() { 288 fn main() {
289 xs.foo() 289 xs.foo()
290 .first() 290 .first()
291 <|> 291 $0
292 } 292 }
293 ", 293 ",
294 r" 294 r"
@@ -305,7 +305,7 @@ fn main() {
305 fn main() { 305 fn main() {
306 xs.foo() 306 xs.foo()
307 .first() 307 .first()
308 <|> 308 $0
309 } 309 }
310 ", 310 ",
311 ); 311 );
@@ -318,7 +318,7 @@ fn main() {
318 r" 318 r"
319 fn source_impl() { 319 fn source_impl() {
320 let var = enum_defvariant_list().unwrap() 320 let var = enum_defvariant_list().unwrap()
321 <|> 321 $0
322 .nth(92) 322 .nth(92)
323 .unwrap(); 323 .unwrap();
324 } 324 }
@@ -337,7 +337,7 @@ fn main() {
337 r" 337 r"
338 fn source_impl() { 338 fn source_impl() {
339 let var = enum_defvariant_list().unwrap() 339 let var = enum_defvariant_list().unwrap()
340 <|> 340 $0
341 .nth(92) 341 .nth(92)
342 .unwrap(); 342 .unwrap();
343 } 343 }
@@ -351,7 +351,7 @@ fn main() {
351 '.', 351 '.',
352 r" 352 r"
353 fn main() { 353 fn main() {
354 <|> 354 $0
355 } 355 }
356 ", 356 ",
357 ); 357 );
@@ -359,7 +359,7 @@ fn main() {
359 '.', 359 '.',
360 r" 360 r"
361 fn main() { 361 fn main() {
362 <|> 362 $0
363 } 363 }
364 ", 364 ",
365 ); 365 );
@@ -367,6 +367,6 @@ fn main() {
367 367
368 #[test] 368 #[test]
369 fn adds_space_after_return_type() { 369 fn adds_space_after_return_type() {
370 type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -> { 92 }") 370 type_char('>', "fn foo() -$0{ 92 }", "fn foo() -> { 92 }")
371 } 371 }
372} 372}
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index f4ea30352..63cd51b69 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -136,7 +136,7 @@ mod tests {
136 fn continues_doc_comment() { 136 fn continues_doc_comment() {
137 do_check( 137 do_check(
138 r" 138 r"
139/// Some docs<|> 139/// Some docs$0
140fn foo() { 140fn foo() {
141} 141}
142", 142",
@@ -151,7 +151,7 @@ fn foo() {
151 do_check( 151 do_check(
152 r" 152 r"
153impl S { 153impl S {
154 /// Some<|> docs. 154 /// Some$0 docs.
155 fn foo() {} 155 fn foo() {}
156} 156}
157", 157",
@@ -166,7 +166,7 @@ impl S {
166 166
167 do_check( 167 do_check(
168 r" 168 r"
169///<|> Some docs 169///$0 Some docs
170fn foo() { 170fn foo() {
171} 171}
172", 172",
@@ -181,7 +181,7 @@ fn foo() {
181 181
182 #[test] 182 #[test]
183 fn does_not_continue_before_doc_comment() { 183 fn does_not_continue_before_doc_comment() {
184 do_check_noop(r"<|>//! docz"); 184 do_check_noop(r"$0//! docz");
185 } 185 }
186 186
187 #[test] 187 #[test]
@@ -189,7 +189,7 @@ fn foo() {
189 do_check( 189 do_check(
190 r" 190 r"
191fn main() { 191fn main() {
192 // Fix<|> me 192 // Fix$0 me
193 let x = 1 + 1; 193 let x = 1 + 1;
194} 194}
195", 195",
@@ -208,7 +208,7 @@ fn main() {
208 do_check( 208 do_check(
209 r" 209 r"
210fn main() { 210fn main() {
211 // Fix<|> 211 // Fix$0
212 // me 212 // me
213 let x = 1 + 1; 213 let x = 1 + 1;
214} 214}
@@ -229,7 +229,7 @@ fn main() {
229 do_check_noop( 229 do_check_noop(
230 r" 230 r"
231fn main() { 231fn main() {
232 // Fix me<|> 232 // Fix me$0
233 let x = 1 + 1; 233 let x = 1 + 1;
234} 234}
235", 235",
@@ -242,7 +242,7 @@ fn main() {
242 do_check( 242 do_check(
243 r#" 243 r#"
244fn main() { 244fn main() {
245 // Fix me <|> 245 // Fix me $0
246 let x = 1 + 1; 246 let x = 1 + 1;
247} 247}
248"#, 248"#,
@@ -261,7 +261,7 @@ fn main() {
261 do_check( 261 do_check(
262 " 262 "
263fn main() { 263fn main() {
264 // Fix me \t\t <|> 264 // Fix me \t\t $0
265 let x = 1 + 1; 265 let x = 1 + 1;
266} 266}
267", 267",
diff --git a/crates/ide/src/view_hir.rs b/crates/ide/src/view_hir.rs
new file mode 100644
index 000000000..cfcfb7cfb
--- /dev/null
+++ b/crates/ide/src/view_hir.rs
@@ -0,0 +1,25 @@
1use hir::{Function, Semantics};
2use ide_db::base_db::FilePosition;
3use ide_db::RootDatabase;
4use syntax::{algo::find_node_at_offset, ast, AstNode};
5
6// Feature: View Hir
7//
8// |===
9// | Editor | Action Name
10//
11// | VS Code | **Rust Analyzer: View Hir**
12// |===
13pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String {
14 body_hir(db, position).unwrap_or("Not inside a function body".to_string())
15}
16
17fn body_hir(db: &RootDatabase, position: FilePosition) -> Option<String> {
18 let sema = Semantics::new(db);
19 let source_file = sema.parse(position.file_id);
20
21 let function = find_node_at_offset::<ast::Fn>(source_file.syntax(), position.offset)?;
22
23 let function: Function = sema.to_def(&function)?;
24 Some(function.debug_hir(db))
25}
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml
index 0ad6e1000..d3d3dc688 100644
--- a/crates/ide_db/Cargo.toml
+++ b/crates/ide_db/Cargo.toml
@@ -19,7 +19,7 @@ fst = { version = "0.4", default-features = false }
19rustc-hash = "1.1.0" 19rustc-hash = "1.1.0"
20once_cell = "1.3.1" 20once_cell = "1.3.1"
21either = "1.6.1" 21either = "1.6.1"
22itertools = "0.9.0" 22itertools = "0.10.0"
23 23
24stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
25syntax = { path = "../syntax", version = "0.0.0" } 25syntax = { path = "../syntax", version = "0.0.0" }
@@ -32,4 +32,4 @@ test_utils = { path = "../test_utils", version = "0.0.0" }
32hir = { path = "../hir", version = "0.0.0" } 32hir = { path = "../hir", version = "0.0.0" }
33 33
34[dev-dependencies] 34[dev-dependencies]
35expect-test = "1.0" 35expect-test = "1.1"
diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs
index 71c19ed13..c770a236b 100644
--- a/crates/ide_db/src/apply_change.rs
+++ b/crates/ide_db/src/apply_change.rs
@@ -145,6 +145,7 @@ impl RootDatabase {
145 hir::db::MacroDefQuery 145 hir::db::MacroDefQuery
146 hir::db::ParseMacroExpansionQuery 146 hir::db::ParseMacroExpansionQuery
147 hir::db::MacroExpandQuery 147 hir::db::MacroExpandQuery
148 hir::db::HygieneFrameQuery
148 149
149 // DefDatabase 150 // DefDatabase
150 hir::db::ItemTreeQuery 151 hir::db::ItemTreeQuery
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
index 9335aeaa5..c714cf280 100644
--- a/crates/ide_db/src/call_info/tests.rs
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -3,12 +3,12 @@ use base_db::{fixture::ChangeFixture, FilePosition};
3use expect_test::{expect, Expect}; 3use expect_test::{expect, Expect};
4use test_utils::{mark, RangeOrOffset}; 4use test_utils::{mark, RangeOrOffset};
5 5
6/// Creates analysis from a multi-file fixture, returns positions marked with <|>. 6/// Creates analysis from a multi-file fixture, returns positions marked with $0.
7pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { 7pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
8 let change_fixture = ChangeFixture::parse(ra_fixture); 8 let change_fixture = ChangeFixture::parse(ra_fixture);
9 let mut database = RootDatabase::default(); 9 let mut database = RootDatabase::default();
10 database.apply_change(change_fixture.change); 10 database.apply_change(change_fixture.change);
11 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); 11 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
12 let offset = match range_or_offset { 12 let offset = match range_or_offset {
13 RangeOrOffset::Range(_) => panic!(), 13 RangeOrOffset::Range(_) => panic!(),
14 RangeOrOffset::Offset(it) => it, 14 RangeOrOffset::Offset(it) => it,
@@ -49,7 +49,7 @@ fn test_fn_signature_two_args() {
49 check( 49 check(
50 r#" 50 r#"
51fn foo(x: u32, y: u32) -> u32 {x + y} 51fn foo(x: u32, y: u32) -> u32 {x + y}
52fn bar() { foo(<|>3, ); } 52fn bar() { foo($03, ); }
53"#, 53"#,
54 expect![[r#" 54 expect![[r#"
55 fn foo(x: u32, y: u32) -> u32 55 fn foo(x: u32, y: u32) -> u32
@@ -59,7 +59,7 @@ fn bar() { foo(<|>3, ); }
59 check( 59 check(
60 r#" 60 r#"
61fn foo(x: u32, y: u32) -> u32 {x + y} 61fn foo(x: u32, y: u32) -> u32 {x + y}
62fn bar() { foo(3<|>, ); } 62fn bar() { foo(3$0, ); }
63"#, 63"#,
64 expect![[r#" 64 expect![[r#"
65 fn foo(x: u32, y: u32) -> u32 65 fn foo(x: u32, y: u32) -> u32
@@ -69,7 +69,7 @@ fn bar() { foo(3<|>, ); }
69 check( 69 check(
70 r#" 70 r#"
71fn foo(x: u32, y: u32) -> u32 {x + y} 71fn foo(x: u32, y: u32) -> u32 {x + y}
72fn bar() { foo(3,<|> ); } 72fn bar() { foo(3,$0 ); }
73"#, 73"#,
74 expect![[r#" 74 expect![[r#"
75 fn foo(x: u32, y: u32) -> u32 75 fn foo(x: u32, y: u32) -> u32
@@ -79,7 +79,7 @@ fn bar() { foo(3,<|> ); }
79 check( 79 check(
80 r#" 80 r#"
81fn foo(x: u32, y: u32) -> u32 {x + y} 81fn foo(x: u32, y: u32) -> u32 {x + y}
82fn bar() { foo(3, <|>); } 82fn bar() { foo(3, $0); }
83"#, 83"#,
84 expect![[r#" 84 expect![[r#"
85 fn foo(x: u32, y: u32) -> u32 85 fn foo(x: u32, y: u32) -> u32
@@ -93,7 +93,7 @@ fn test_fn_signature_two_args_empty() {
93 check( 93 check(
94 r#" 94 r#"
95fn foo(x: u32, y: u32) -> u32 {x + y} 95fn foo(x: u32, y: u32) -> u32 {x + y}
96fn bar() { foo(<|>); } 96fn bar() { foo($0); }
97"#, 97"#,
98 expect![[r#" 98 expect![[r#"
99 fn foo(x: u32, y: u32) -> u32 99 fn foo(x: u32, y: u32) -> u32
@@ -110,7 +110,7 @@ fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
110 where T: Copy + Display, U: Debug 110 where T: Copy + Display, U: Debug
111{ x + y } 111{ x + y }
112 112
113fn bar() { foo(<|>3, ); } 113fn bar() { foo($03, ); }
114"#, 114"#,
115 expect![[r#" 115 expect![[r#"
116 fn foo(x: i32, y: {unknown}) -> u32 116 fn foo(x: i32, y: {unknown}) -> u32
@@ -124,7 +124,7 @@ fn test_fn_signature_no_params() {
124 check( 124 check(
125 r#" 125 r#"
126fn foo<T>() -> T where T: Copy + Display {} 126fn foo<T>() -> T where T: Copy + Display {}
127fn bar() { foo(<|>); } 127fn bar() { foo($0); }
128"#, 128"#,
129 expect![[r#" 129 expect![[r#"
130 fn foo() -> {unknown} 130 fn foo() -> {unknown}
@@ -140,7 +140,7 @@ fn test_fn_signature_for_impl() {
140struct F; 140struct F;
141impl F { pub fn new() { } } 141impl F { pub fn new() { } }
142fn bar() { 142fn bar() {
143 let _ : F = F::new(<|>); 143 let _ : F = F::new($0);
144} 144}
145"#, 145"#,
146 expect![[r#" 146 expect![[r#"
@@ -159,7 +159,7 @@ impl S { pub fn do_it(&self) {} }
159 159
160fn bar() { 160fn bar() {
161 let s: S = S; 161 let s: S = S;
162 s.do_it(<|>); 162 s.do_it($0);
163} 163}
164"#, 164"#,
165 expect![[r#" 165 expect![[r#"
@@ -178,7 +178,7 @@ impl S {
178 fn foo(&self, x: i32) {} 178 fn foo(&self, x: i32) {}
179} 179}
180 180
181fn main() { S.foo(<|>); } 181fn main() { S.foo($0); }
182"#, 182"#,
183 expect![[r#" 183 expect![[r#"
184 fn foo(&self, x: i32) 184 fn foo(&self, x: i32)
@@ -196,7 +196,7 @@ impl S {
196 fn foo(&self, x: i32) {} 196 fn foo(&self, x: i32) {}
197} 197}
198 198
199fn main() { S::foo(<|>); } 199fn main() { S::foo($0); }
200"#, 200"#,
201 expect![[r#" 201 expect![[r#"
202 fn foo(self: &S, x: i32) 202 fn foo(self: &S, x: i32)
@@ -216,7 +216,7 @@ fn foo(j: u32) -> u32 {
216} 216}
217 217
218fn bar() { 218fn bar() {
219 let _ = foo(<|>); 219 let _ = foo($0);
220} 220}
221"#, 221"#,
222 expect![[r#" 222 expect![[r#"
@@ -246,7 +246,7 @@ pub fn add_one(x: i32) -> i32 {
246} 246}
247 247
248pub fn do() { 248pub fn do() {
249 add_one(<|> 249 add_one($0
250}"#, 250}"#,
251 expect![[r##" 251 expect![[r##"
252 Adds one to the number given. 252 Adds one to the number given.
@@ -287,7 +287,7 @@ impl addr {
287 287
288pub fn do_it() { 288pub fn do_it() {
289 addr {}; 289 addr {};
290 addr::add_one(<|>); 290 addr::add_one($0);
291} 291}
292"#, 292"#,
293 expect![[r##" 293 expect![[r##"
@@ -331,7 +331,7 @@ impl<E> WriteHandler<E> {
331} 331}
332 332
333pub fn foo(mut r: WriteHandler<()>) { 333pub fn foo(mut r: WriteHandler<()>) {
334 r.finished(<|>); 334 r.finished($0);
335} 335}
336"#, 336"#,
337 expect![[r#" 337 expect![[r#"
@@ -351,7 +351,7 @@ fn call_info_bad_offset() {
351 check( 351 check(
352 r#" 352 r#"
353fn foo(x: u32, y: u32) -> u32 {x + y} 353fn foo(x: u32, y: u32) -> u32 {x + y}
354fn bar() { foo <|> (3, ); } 354fn bar() { foo $0 (3, ); }
355"#, 355"#,
356 expect![[""]], 356 expect![[""]],
357 ); 357 );
@@ -368,7 +368,7 @@ fn bar(_: u32) { }
368 368
369fn main() { 369fn main() {
370 let foo = Foo; 370 let foo = Foo;
371 std::thread::spawn(move || foo.bar(<|>)); 371 std::thread::spawn(move || foo.bar($0));
372} 372}
373"#, 373"#,
374 expect![[r#" 374 expect![[r#"
@@ -385,7 +385,7 @@ fn works_for_tuple_structs() {
385/// A cool tuple struct 385/// A cool tuple struct
386struct S(u32, i32); 386struct S(u32, i32);
387fn main() { 387fn main() {
388 let s = S(0, <|>); 388 let s = S(0, $0);
389} 389}
390"#, 390"#,
391 expect![[r#" 391 expect![[r#"
@@ -403,7 +403,7 @@ fn generic_struct() {
403 r#" 403 r#"
404struct S<T>(T); 404struct S<T>(T);
405fn main() { 405fn main() {
406 let s = S(<|>); 406 let s = S($0);
407} 407}
408"#, 408"#,
409 expect![[r#" 409 expect![[r#"
@@ -427,7 +427,7 @@ enum E {
427} 427}
428 428
429fn main() { 429fn main() {
430 let a = E::A(<|>); 430 let a = E::A($0);
431} 431}
432"#, 432"#,
433 expect![[r#" 433 expect![[r#"
@@ -445,7 +445,7 @@ fn cant_call_struct_record() {
445 r#" 445 r#"
446struct S { x: u32, y: i32 } 446struct S { x: u32, y: i32 }
447fn main() { 447fn main() {
448 let s = S(<|>); 448 let s = S($0);
449} 449}
450"#, 450"#,
451 expect![[""]], 451 expect![[""]],
@@ -466,7 +466,7 @@ enum E {
466} 466}
467 467
468fn main() { 468fn main() {
469 let a = E::C(<|>); 469 let a = E::C($0);
470} 470}
471"#, 471"#,
472 expect![[""]], 472 expect![[""]],
@@ -480,7 +480,7 @@ fn fn_signature_for_call_in_macro() {
480macro_rules! id { ($($tt:tt)*) => { $($tt)* } } 480macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
481fn foo() { } 481fn foo() { }
482id! { 482id! {
483 fn bar() { foo(<|>); } 483 fn bar() { foo($0); }
484} 484}
485"#, 485"#,
486 expect![[r#" 486 expect![[r#"
@@ -497,7 +497,7 @@ fn call_info_for_lambdas() {
497struct S; 497struct S;
498fn foo(s: S) -> i32 { 92 } 498fn foo(s: S) -> i32 { 92 }
499fn main() { 499fn main() {
500 (|s| foo(s))(<|>) 500 (|s| foo(s))($0)
501} 501}
502 "#, 502 "#,
503 expect![[r#" 503 expect![[r#"
@@ -512,7 +512,7 @@ fn call_info_for_fn_ptr() {
512 check( 512 check(
513 r#" 513 r#"
514fn main(f: fn(i32, f64) -> char) { 514fn main(f: fn(i32, f64) -> char) {
515 f(0, <|>) 515 f(0, $0)
516} 516}
517 "#, 517 "#,
518 expect![[r#" 518 expect![[r#"
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index d33a6cb86..d68fe42b0 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -6,8 +6,8 @@
6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). 6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
7 7
8use hir::{ 8use hir::{
9 db::HirDatabase, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, 9 db::HirDatabase, Crate, Field, GenericParam, HasVisibility, Impl, Label, Local, MacroDef,
10 Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility, 10 Module, ModuleDef, Name, PathResolution, Semantics, Visibility,
11}; 11};
12use syntax::{ 12use syntax::{
13 ast::{self, AstNode}, 13 ast::{self, AstNode},
@@ -24,8 +24,7 @@ pub enum Definition {
24 ModuleDef(ModuleDef), 24 ModuleDef(ModuleDef),
25 SelfType(Impl), 25 SelfType(Impl),
26 Local(Local), 26 Local(Local),
27 TypeParam(TypeParam), 27 GenericParam(GenericParam),
28 LifetimeParam(LifetimeParam),
29 Label(Label), 28 Label(Label),
30} 29}
31 30
@@ -37,8 +36,7 @@ impl Definition {
37 Definition::ModuleDef(it) => it.module(db), 36 Definition::ModuleDef(it) => it.module(db),
38 Definition::SelfType(it) => Some(it.module(db)), 37 Definition::SelfType(it) => Some(it.module(db)),
39 Definition::Local(it) => Some(it.module(db)), 38 Definition::Local(it) => Some(it.module(db)),
40 Definition::TypeParam(it) => Some(it.module(db)), 39 Definition::GenericParam(it) => Some(it.module(db)),
41 Definition::LifetimeParam(it) => Some(it.module(db)),
42 Definition::Label(it) => Some(it.module(db)), 40 Definition::Label(it) => Some(it.module(db)),
43 } 41 }
44 } 42 }
@@ -50,8 +48,7 @@ impl Definition {
50 Definition::ModuleDef(def) => def.definition_visibility(db), 48 Definition::ModuleDef(def) => def.definition_visibility(db),
51 Definition::SelfType(_) => None, 49 Definition::SelfType(_) => None,
52 Definition::Local(_) => None, 50 Definition::Local(_) => None,
53 Definition::TypeParam(_) => None, 51 Definition::GenericParam(_) => None,
54 Definition::LifetimeParam(_) => None,
55 Definition::Label(_) => None, 52 Definition::Label(_) => None,
56 } 53 }
57 } 54 }
@@ -77,8 +74,7 @@ impl Definition {
77 }, 74 },
78 Definition::SelfType(_) => return None, 75 Definition::SelfType(_) => return None,
79 Definition::Local(it) => it.name(db)?, 76 Definition::Local(it) => it.name(db)?,
80 Definition::TypeParam(it) => it.name(db), 77 Definition::GenericParam(it) => it.name(db),
81 Definition::LifetimeParam(it) => it.name(db),
82 Definition::Label(it) => it.name(db), 78 Definition::Label(it) => it.name(db),
83 }; 79 };
84 Some(name) 80 Some(name)
@@ -231,7 +227,11 @@ impl NameClass {
231 }, 227 },
232 ast::TypeParam(it) => { 228 ast::TypeParam(it) => {
233 let def = sema.to_def(&it)?; 229 let def = sema.to_def(&it)?;
234 Some(NameClass::Definition(Definition::TypeParam(def))) 230 Some(NameClass::Definition(Definition::GenericParam(def.into())))
231 },
232 ast::ConstParam(it) => {
233 let def = sema.to_def(&it)?;
234 Some(NameClass::Definition(Definition::GenericParam(def.into())))
235 }, 235 },
236 _ => None, 236 _ => None,
237 } 237 }
@@ -249,7 +249,7 @@ impl NameClass {
249 match parent { 249 match parent {
250 ast::LifetimeParam(it) => { 250 ast::LifetimeParam(it) => {
251 let def = sema.to_def(&it)?; 251 let def = sema.to_def(&it)?;
252 Some(NameClass::Definition(Definition::LifetimeParam(def))) 252 Some(NameClass::Definition(Definition::GenericParam(def.into())))
253 }, 253 },
254 ast::Label(it) => { 254 ast::Label(it) => {
255 let def = sema.to_def(&it)?; 255 let def = sema.to_def(&it)?;
@@ -350,7 +350,7 @@ impl NameRefClass {
350 if let Some(path) = macro_call.path() { 350 if let Some(path) = macro_call.path() {
351 if path.qualifier().is_none() { 351 if path.qualifier().is_none() {
352 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment 352 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
353 // paths are handled below (allowing `log<|>::info!` to resolve to the log crate). 353 // paths are handled below (allowing `log$0::info!` to resolve to the log crate).
354 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) { 354 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
355 return Some(NameRefClass::Definition(Definition::Macro(macro_def))); 355 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
356 } 356 }
@@ -385,7 +385,8 @@ impl NameRefClass {
385 | SyntaxKind::WHERE_PRED 385 | SyntaxKind::WHERE_PRED
386 | SyntaxKind::REF_TYPE => sema 386 | SyntaxKind::REF_TYPE => sema
387 .resolve_lifetime_param(lifetime) 387 .resolve_lifetime_param(lifetime)
388 .map(Definition::LifetimeParam) 388 .map(GenericParam::LifetimeParam)
389 .map(Definition::GenericParam)
389 .map(NameRefClass::Definition), 390 .map(NameRefClass::Definition),
390 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check 391 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
391 // if our lifetime is in a LifetimeParam without being the constrained lifetime 392 // if our lifetime is in a LifetimeParam without being the constrained lifetime
@@ -393,7 +394,8 @@ impl NameRefClass {
393 != Some(lifetime) => 394 != Some(lifetime) =>
394 { 395 {
395 sema.resolve_lifetime_param(lifetime) 396 sema.resolve_lifetime_param(lifetime)
396 .map(Definition::LifetimeParam) 397 .map(GenericParam::LifetimeParam)
398 .map(Definition::GenericParam)
397 .map(NameRefClass::Definition) 399 .map(NameRefClass::Definition)
398 } 400 }
399 _ => None, 401 _ => None,
@@ -414,9 +416,10 @@ impl From<PathResolution> for Definition {
414 Definition::ModuleDef(def) 416 Definition::ModuleDef(def)
415 } 417 }
416 PathResolution::Local(local) => Definition::Local(local), 418 PathResolution::Local(local) => Definition::Local(local),
417 PathResolution::TypeParam(par) => Definition::TypeParam(par), 419 PathResolution::TypeParam(par) => Definition::GenericParam(par.into()),
418 PathResolution::Macro(def) => Definition::Macro(def), 420 PathResolution::Macro(def) => Definition::Macro(def),
419 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), 421 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
422 PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),
420 } 423 }
421 } 424 }
422} 425}
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index d988588ff..c6763ae36 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -1,9 +1,10 @@
1//! A module with ide helpers for high-level ide features. 1//! A module with ide helpers for high-level ide features.
2use crate::RootDatabase; 2pub mod insert_use;
3
3use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; 4use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait};
4use syntax::ast::{self, make}; 5use syntax::ast::{self, make};
5 6
6pub mod insert_use; 7use crate::RootDatabase;
7 8
8/// Converts the mod path struct into its ast representation. 9/// Converts the mod path struct into its ast representation.
9pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { 10pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
@@ -37,94 +38,7 @@ pub struct FamousDefs<'a, 'b>(pub &'a Semantics<'b, RootDatabase>, pub Option<Cr
37 38
38#[allow(non_snake_case)] 39#[allow(non_snake_case)]
39impl FamousDefs<'_, '_> { 40impl FamousDefs<'_, '_> {
40 pub const FIXTURE: &'static str = r#"//- /libcore.rs crate:core 41 pub const FIXTURE: &'static str = include_str!("helpers/famous_defs_fixture.rs");
41pub mod convert {
42 pub trait From<T> {
43 fn from(t: T) -> Self;
44 }
45}
46
47pub mod default {
48 pub trait Default {
49 fn default() -> Self;
50 }
51}
52
53pub mod iter {
54 pub use self::traits::{collect::IntoIterator, iterator::Iterator};
55 mod traits {
56 pub(crate) mod iterator {
57 use crate::option::Option;
58 pub trait Iterator {
59 type Item;
60 fn next(&mut self) -> Option<Self::Item>;
61 fn by_ref(&mut self) -> &mut Self {
62 self
63 }
64 fn take(self, n: usize) -> crate::iter::Take<Self> {
65 crate::iter::Take { inner: self }
66 }
67 }
68
69 impl<I: Iterator> Iterator for &mut I {
70 type Item = I::Item;
71 fn next(&mut self) -> Option<I::Item> {
72 (**self).next()
73 }
74 }
75 }
76 pub(crate) mod collect {
77 pub trait IntoIterator {
78 type Item;
79 }
80 }
81 }
82
83 pub use self::sources::*;
84 pub(crate) mod sources {
85 use super::Iterator;
86 use crate::option::Option::{self, *};
87 pub struct Repeat<A> {
88 element: A,
89 }
90
91 pub fn repeat<T>(elt: T) -> Repeat<T> {
92 Repeat { element: elt }
93 }
94
95 impl<A> Iterator for Repeat<A> {
96 type Item = A;
97
98 fn next(&mut self) -> Option<A> {
99 None
100 }
101 }
102 }
103
104 pub use self::adapters::*;
105 pub(crate) mod adapters {
106 use super::Iterator;
107 use crate::option::Option::{self, *};
108 pub struct Take<I> { pub(crate) inner: I }
109 impl<I> Iterator for Take<I> where I: Iterator {
110 type Item = <I as Iterator>::Item;
111 fn next(&mut self) -> Option<<I as Iterator>::Item> {
112 None
113 }
114 }
115 }
116}
117
118pub mod option {
119 pub enum Option<T> { None, Some(T)}
120}
121
122pub mod prelude {
123 pub use crate::{convert::From, iter::{IntoIterator, Iterator}, option::Option::{self, *}, default::Default};
124}
125#[prelude_import]
126pub use prelude::*;
127"#;
128 42
129 pub fn core(&self) -> Option<Crate> { 43 pub fn core(&self) -> Option<Crate> {
130 self.find_crate("core") 44 self.find_crate("core")
@@ -201,3 +115,18 @@ pub use prelude::*;
201 Some(def) 115 Some(def)
202 } 116 }
203} 117}
118
119#[derive(Clone, Copy, Debug, PartialEq, Eq)]
120pub struct SnippetCap {
121 _private: (),
122}
123
124impl SnippetCap {
125 pub const fn new(allow_snippets: bool) -> Option<SnippetCap> {
126 if allow_snippets {
127 Some(SnippetCap { _private: () })
128 } else {
129 None
130 }
131 }
132}
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs
new file mode 100644
index 000000000..5e88de64d
--- /dev/null
+++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs
@@ -0,0 +1,120 @@
1//- /libcore.rs crate:core
2//! Signatures of traits, types and functions from the core lib for use in tests.
3pub mod convert {
4 pub trait From<T> {
5 fn from(t: T) -> Self;
6 }
7}
8
9pub mod default {
10 pub trait Default {
11 fn default() -> Self;
12 }
13}
14
15pub mod iter {
16 pub use self::traits::{collect::IntoIterator, iterator::Iterator};
17 mod traits {
18 pub(crate) mod iterator {
19 use crate::option::Option;
20 pub trait Iterator {
21 type Item;
22 fn next(&mut self) -> Option<Self::Item>;
23 fn by_ref(&mut self) -> &mut Self {
24 self
25 }
26 fn take(self, n: usize) -> crate::iter::Take<Self> {
27 crate::iter::Take { inner: self }
28 }
29 }
30
31 impl<I: Iterator> Iterator for &mut I {
32 type Item = I::Item;
33 fn next(&mut self) -> Option<I::Item> {
34 (**self).next()
35 }
36 }
37 }
38 pub(crate) mod collect {
39 pub trait IntoIterator {
40 type Item;
41 }
42 }
43 }
44
45 pub use self::sources::*;
46 pub(crate) mod sources {
47 use super::Iterator;
48 use crate::option::Option::{self, *};
49 pub struct Repeat<A> {
50 element: A,
51 }
52
53 pub fn repeat<T>(elt: T) -> Repeat<T> {
54 Repeat { element: elt }
55 }
56
57 impl<A> Iterator for Repeat<A> {
58 type Item = A;
59
60 fn next(&mut self) -> Option<A> {
61 None
62 }
63 }
64 }
65
66 pub use self::adapters::*;
67 pub(crate) mod adapters {
68 use super::Iterator;
69 use crate::option::Option::{self, *};
70 pub struct Take<I> {
71 pub(crate) inner: I,
72 }
73 impl<I> Iterator for Take<I>
74 where
75 I: Iterator,
76 {
77 type Item = <I as Iterator>::Item;
78 fn next(&mut self) -> Option<<I as Iterator>::Item> {
79 None
80 }
81 }
82 }
83}
84
85pub mod ops {
86 #[lang = "fn"]
87 pub trait Fn<Args>: FnMut<Args> {
88 extern "rust-call" fn call(&self, args: Args) -> Self::Output;
89 }
90
91 #[lang = "fn_mut"]
92 pub trait FnMut<Args>: FnOnce<Args> {
93 extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
94 }
95 #[lang = "fn_once"]
96 pub trait FnOnce<Args> {
97 #[lang = "fn_once_output"]
98 type Output;
99 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
100 }
101}
102
103pub mod option {
104 pub enum Option<T> {
105 None,
106 Some(T),
107 }
108}
109
110pub mod prelude {
111 pub use crate::{
112 convert::From,
113 default::Default,
114 iter::{IntoIterator, Iterator},
115 ops::{Fn, FnMut, FnOnce},
116 option::Option::{self, *},
117 };
118}
119#[prelude_import]
120pub use prelude::*;
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index 0f4c2ca47..e9f23adf8 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -1,7 +1,7 @@
1//! This module contains an import search funcionality that is provided to the assists module. 1//! This module contains an import search functionality that is provided to the assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the assists module.
3 3
4use hir::{import_map, Crate, MacroDef, ModuleDef, Semantics}; 4use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics};
5use syntax::{ast, AstNode, SyntaxKind::NAME}; 5use syntax::{ast, AstNode, SyntaxKind::NAME};
6 6
7use crate::{ 7use crate::{
@@ -40,8 +40,9 @@ pub fn find_similar_imports<'a>(
40 krate: Crate, 40 krate: Crate,
41 limit: Option<usize>, 41 limit: Option<usize>,
42 fuzzy_search_string: String, 42 fuzzy_search_string: String,
43 ignore_assoc_items: bool,
43 name_only: bool, 44 name_only: bool,
44) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 45) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a {
45 let _p = profile::span("find_similar_imports"); 46 let _p = profile::span("find_similar_imports");
46 47
47 let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) 48 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
@@ -57,7 +58,21 @@ pub fn find_similar_imports<'a>(
57 external_query = external_query.limit(limit); 58 external_query = external_query.limit(limit);
58 } 59 }
59 60
60 find_imports(sema, krate, local_query, external_query) 61 let db = sema.db;
62 find_imports(sema, krate, local_query, external_query).filter(move |import_candidate| {
63 if ignore_assoc_items {
64 match import_candidate {
65 Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_none(),
66 Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_none(),
67 Either::Left(ModuleDef::TypeAlias(type_alias)) => {
68 type_alias.as_assoc_item(db).is_none()
69 }
70 _ => true,
71 }
72 } else {
73 true
74 }
75 })
61} 76}
62 77
63fn find_imports<'a>( 78fn find_imports<'a>(
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index ff10f71c3..b5fa46642 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -18,9 +18,43 @@ use crate::{
18 RootDatabase, 18 RootDatabase,
19}; 19};
20 20
21#[derive(Debug, Default, Clone)]
22pub struct UsageSearchResult {
23 pub references: FxHashMap<FileId, Vec<FileReference>>,
24}
25
26impl UsageSearchResult {
27 pub fn is_empty(&self) -> bool {
28 self.references.is_empty()
29 }
30
31 pub fn len(&self) -> usize {
32 self.references.len()
33 }
34
35 pub fn iter(&self) -> impl Iterator<Item = (&FileId, &Vec<FileReference>)> + '_ {
36 self.references.iter()
37 }
38
39 pub fn file_ranges(&self) -> impl Iterator<Item = FileRange> + '_ {
40 self.references.iter().flat_map(|(&file_id, refs)| {
41 refs.iter().map(move |&FileReference { range, .. }| FileRange { file_id, range })
42 })
43 }
44}
45
46impl IntoIterator for UsageSearchResult {
47 type Item = (FileId, Vec<FileReference>);
48 type IntoIter = <FxHashMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter;
49
50 fn into_iter(self) -> Self::IntoIter {
51 self.references.into_iter()
52 }
53}
54
21#[derive(Debug, Clone)] 55#[derive(Debug, Clone)]
22pub struct Reference { 56pub struct FileReference {
23 pub file_range: FileRange, 57 pub range: TextRange,
24 pub kind: ReferenceKind, 58 pub kind: ReferenceKind,
25 pub access: Option<ReferenceAccess>, 59 pub access: Option<ReferenceAccess>,
26} 60}
@@ -121,31 +155,55 @@ impl Definition {
121 155
122 if let Definition::Local(var) = self { 156 if let Definition::Local(var) = self {
123 let range = match var.parent(db) { 157 let range = match var.parent(db) {
124 DefWithBody::Function(f) => f.source(db).value.syntax().text_range(), 158 DefWithBody::Function(f) => {
125 DefWithBody::Const(c) => c.source(db).value.syntax().text_range(), 159 f.source(db).and_then(|src| Some(src.value.syntax().text_range()))
126 DefWithBody::Static(s) => s.source(db).value.syntax().text_range(), 160 }
161 DefWithBody::Const(c) => {
162 c.source(db).and_then(|src| Some(src.value.syntax().text_range()))
163 }
164 DefWithBody::Static(s) => {
165 s.source(db).and_then(|src| Some(src.value.syntax().text_range()))
166 }
127 }; 167 };
128 let mut res = FxHashMap::default(); 168 let mut res = FxHashMap::default();
129 res.insert(file_id, Some(range)); 169 res.insert(file_id, range);
130 return SearchScope::new(res); 170 return SearchScope::new(res);
131 } 171 }
132 172
133 if let Definition::LifetimeParam(param) = self { 173 if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self {
134 let range = match param.parent(db) { 174 let range = match param.parent(db) {
135 hir::GenericDef::Function(it) => it.source(db).value.syntax().text_range(), 175 hir::GenericDef::Function(it) => {
176 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
177 }
136 hir::GenericDef::Adt(it) => match it { 178 hir::GenericDef::Adt(it) => match it {
137 hir::Adt::Struct(it) => it.source(db).value.syntax().text_range(), 179 hir::Adt::Struct(it) => {
138 hir::Adt::Union(it) => it.source(db).value.syntax().text_range(), 180 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
139 hir::Adt::Enum(it) => it.source(db).value.syntax().text_range(), 181 }
182 hir::Adt::Union(it) => {
183 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
184 }
185 hir::Adt::Enum(it) => {
186 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
187 }
140 }, 188 },
141 hir::GenericDef::Trait(it) => it.source(db).value.syntax().text_range(), 189 hir::GenericDef::Trait(it) => {
142 hir::GenericDef::TypeAlias(it) => it.source(db).value.syntax().text_range(), 190 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
143 hir::GenericDef::Impl(it) => it.source(db).value.syntax().text_range(), 191 }
144 hir::GenericDef::Variant(it) => it.source(db).value.syntax().text_range(), 192 hir::GenericDef::TypeAlias(it) => {
145 hir::GenericDef::Const(it) => it.source(db).value.syntax().text_range(), 193 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
194 }
195 hir::GenericDef::Impl(it) => {
196 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
197 }
198 hir::GenericDef::Variant(it) => {
199 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
200 }
201 hir::GenericDef::Const(it) => {
202 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
203 }
146 }; 204 };
147 let mut res = FxHashMap::default(); 205 let mut res = FxHashMap::default();
148 res.insert(file_id, Some(range)); 206 res.insert(file_id, range);
149 return SearchScope::new(res); 207 return SearchScope::new(res);
150 } 208 }
151 209
@@ -228,23 +286,23 @@ impl<'a> FindUsages<'a> {
228 286
229 pub fn at_least_one(self) -> bool { 287 pub fn at_least_one(self) -> bool {
230 let mut found = false; 288 let mut found = false;
231 self.search(&mut |_reference| { 289 self.search(&mut |_, _| {
232 found = true; 290 found = true;
233 true 291 true
234 }); 292 });
235 found 293 found
236 } 294 }
237 295
238 pub fn all(self) -> Vec<Reference> { 296 pub fn all(self) -> UsageSearchResult {
239 let mut res = Vec::new(); 297 let mut res = UsageSearchResult::default();
240 self.search(&mut |reference| { 298 self.search(&mut |file_id, reference| {
241 res.push(reference); 299 res.references.entry(file_id).or_default().push(reference);
242 false 300 false
243 }); 301 });
244 res 302 res
245 } 303 }
246 304
247 fn search(self, sink: &mut dyn FnMut(Reference) -> bool) { 305 fn search(self, sink: &mut dyn FnMut(FileId, FileReference) -> bool) {
248 let _p = profile::span("FindUsages:search"); 306 let _p = profile::span("FindUsages:search");
249 let sema = self.sema; 307 let sema = self.sema;
250 308
@@ -296,16 +354,14 @@ impl<'a> FindUsages<'a> {
296 fn found_lifetime( 354 fn found_lifetime(
297 &self, 355 &self,
298 lifetime: &ast::Lifetime, 356 lifetime: &ast::Lifetime,
299 sink: &mut dyn FnMut(Reference) -> bool, 357 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
300 ) -> bool { 358 ) -> bool {
301 match NameRefClass::classify_lifetime(self.sema, lifetime) { 359 match NameRefClass::classify_lifetime(self.sema, lifetime) {
302 Some(NameRefClass::Definition(def)) if &def == self.def => { 360 Some(NameRefClass::Definition(def)) if &def == self.def => {
303 let reference = Reference { 361 let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
304 file_range: self.sema.original_range(lifetime.syntax()), 362 let reference =
305 kind: ReferenceKind::Lifetime, 363 FileReference { range, kind: ReferenceKind::Lifetime, access: None };
306 access: None, 364 sink(file_id, reference)
307 };
308 sink(reference)
309 } 365 }
310 _ => false, // not a usage 366 _ => false, // not a usage
311 } 367 }
@@ -314,7 +370,7 @@ impl<'a> FindUsages<'a> {
314 fn found_name_ref( 370 fn found_name_ref(
315 &self, 371 &self,
316 name_ref: &ast::NameRef, 372 name_ref: &ast::NameRef,
317 sink: &mut dyn FnMut(Reference) -> bool, 373 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
318 ) -> bool { 374 ) -> bool {
319 match NameRefClass::classify(self.sema, &name_ref) { 375 match NameRefClass::classify(self.sema, &name_ref) {
320 Some(NameRefClass::Definition(def)) if &def == self.def => { 376 Some(NameRefClass::Definition(def)) if &def == self.def => {
@@ -328,46 +384,50 @@ impl<'a> FindUsages<'a> {
328 ReferenceKind::Other 384 ReferenceKind::Other
329 }; 385 };
330 386
331 let reference = Reference { 387 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
332 file_range: self.sema.original_range(name_ref.syntax()), 388 let reference =
333 kind, 389 FileReference { range, kind, access: reference_access(&def, &name_ref) };
334 access: reference_access(&def, &name_ref), 390 sink(file_id, reference)
335 };
336 sink(reference)
337 } 391 }
338 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { 392 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
393 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
339 let reference = match self.def { 394 let reference = match self.def {
340 Definition::Field(_) if &field == self.def => Reference { 395 Definition::Field(_) if &field == self.def => FileReference {
341 file_range: self.sema.original_range(name_ref.syntax()), 396 range,
342 kind: ReferenceKind::FieldShorthandForField, 397 kind: ReferenceKind::FieldShorthandForField,
343 access: reference_access(&field, &name_ref), 398 access: reference_access(&field, &name_ref),
344 }, 399 },
345 Definition::Local(l) if &local == l => Reference { 400 Definition::Local(l) if &local == l => FileReference {
346 file_range: self.sema.original_range(name_ref.syntax()), 401 range,
347 kind: ReferenceKind::FieldShorthandForLocal, 402 kind: ReferenceKind::FieldShorthandForLocal,
348 access: reference_access(&Definition::Local(local), &name_ref), 403 access: reference_access(&Definition::Local(local), &name_ref),
349 }, 404 },
350 _ => return false, // not a usage 405 _ => return false, // not a usage
351 }; 406 };
352 sink(reference) 407 sink(file_id, reference)
353 } 408 }
354 _ => false, // not a usage 409 _ => false, // not a usage
355 } 410 }
356 } 411 }
357 412
358 fn found_name(&self, name: &ast::Name, sink: &mut dyn FnMut(Reference) -> bool) -> bool { 413 fn found_name(
414 &self,
415 name: &ast::Name,
416 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
417 ) -> bool {
359 match NameClass::classify(self.sema, name) { 418 match NameClass::classify(self.sema, name) {
360 Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => { 419 Some(NameClass::PatFieldShorthand { local_def: _, field_ref }) => {
361 let reference = match self.def { 420 if !matches!(self.def, Definition::Field(_) if &field_ref == self.def) {
362 Definition::Field(_) if &field_ref == self.def => Reference { 421 return false;
363 file_range: self.sema.original_range(name.syntax()), 422 }
364 kind: ReferenceKind::FieldShorthandForField, 423 let FileRange { file_id, range } = self.sema.original_range(name.syntax());
365 // FIXME: mutable patterns should have `Write` access 424 let reference = FileReference {
366 access: Some(ReferenceAccess::Read), 425 range,
367 }, 426 kind: ReferenceKind::FieldShorthandForField,
368 _ => return false, // not a usage 427 // FIXME: mutable patterns should have `Write` access
428 access: Some(ReferenceAccess::Read),
369 }; 429 };
370 sink(reference) 430 sink(file_id, reference)
371 } 431 }
372 _ => false, // not a usage 432 _ => false, // not a usage
373 } 433 }
diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs
index 09c7ac3ec..84bb25505 100644
--- a/crates/ide_db/src/traits/tests.rs
+++ b/crates/ide_db/src/traits/tests.rs
@@ -5,12 +5,12 @@ use hir::Semantics;
5use syntax::ast::{self, AstNode}; 5use syntax::ast::{self, AstNode};
6use test_utils::RangeOrOffset; 6use test_utils::RangeOrOffset;
7 7
8/// Creates analysis from a multi-file fixture, returns positions marked with <|>. 8/// Creates analysis from a multi-file fixture, returns positions marked with $0.
9pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { 9pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
10 let change_fixture = ChangeFixture::parse(ra_fixture); 10 let change_fixture = ChangeFixture::parse(ra_fixture);
11 let mut database = RootDatabase::default(); 11 let mut database = RootDatabase::default();
12 database.apply_change(change_fixture.change); 12 database.apply_change(change_fixture.change);
13 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); 13 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
14 let offset = match range_or_offset { 14 let offset = match range_or_offset {
15 RangeOrOffset::Range(_) => panic!(), 15 RangeOrOffset::Range(_) => panic!(),
16 RangeOrOffset::Offset(it) => it, 16 RangeOrOffset::Offset(it) => it,
@@ -55,7 +55,7 @@ pub trait Foo {
55 fn bar(); 55 fn bar();
56} 56}
57impl Foo for u8 { 57impl Foo for u8 {
58 <|> 58 $0
59} 59}
60 "#, 60 "#,
61 expect![["Foo"]], 61 expect![["Foo"]],
@@ -68,7 +68,7 @@ pub trait Foo {
68impl Foo for u8 { 68impl Foo for u8 {
69 fn bar() { 69 fn bar() {
70 fn baz() { 70 fn baz() {
71 <|> 71 $0
72 } 72 }
73 baz(); 73 baz();
74 } 74 }
@@ -83,7 +83,7 @@ pub trait Foo {
83} 83}
84pub struct Bar; 84pub struct Bar;
85impl Bar { 85impl Bar {
86 <|> 86 $0
87} 87}
88 "#, 88 "#,
89 expect![[""]], 89 expect![[""]],
@@ -99,7 +99,7 @@ pub trait Foo {
99 fn bar(); 99 fn bar();
100} 100}
101impl Foo for u8 { 101impl Foo for u8 {
102 <|> 102 $0
103}"#, 103}"#,
104 expect![[r#" 104 expect![[r#"
105 FOO 105 FOO
@@ -114,7 +114,7 @@ pub trait Foo {
114} 114}
115impl Foo for u8 { 115impl Foo for u8 {
116 const FOO: u8 = 10; 116 const FOO: u8 = 10;
117 <|> 117 $0
118}"#, 118}"#,
119 expect![[r#" 119 expect![[r#"
120 bar"#]], 120 bar"#]],
@@ -128,7 +128,7 @@ pub trait Foo {
128} 128}
129impl Foo for u8 { 129impl Foo for u8 {
130 const FOO: u8 = 10; 130 const FOO: u8 = 10;
131 fn bar() {<|>} 131 fn bar() {$0}
132}"#, 132}"#,
133 expect![[r#""#]], 133 expect![[r#""#]],
134 ); 134 );
@@ -137,7 +137,7 @@ impl Foo for u8 {
137 r#" 137 r#"
138pub struct Foo; 138pub struct Foo;
139impl Foo { 139impl Foo {
140 fn bar() {<|>} 140 fn bar() {$0}
141}"#, 141}"#,
142 expect![[r#""#]], 142 expect![[r#""#]],
143 ); 143 );
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 7878faaa4..19543d777 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -24,7 +24,7 @@ use crate::{
24#[derive(Debug, PartialEq, Eq)] 24#[derive(Debug, PartialEq, Eq)]
25pub enum ParseError { 25pub enum ParseError {
26 Expected(String), 26 Expected(String),
27 RepetitionEmtpyTokenTree, 27 RepetitionEmptyTokenTree,
28} 28}
29 29
30#[derive(Debug, PartialEq, Eq, Clone)] 30#[derive(Debug, PartialEq, Eq, Clone)]
@@ -217,18 +217,14 @@ impl MacroRules {
217 217
218impl Rule { 218impl Rule {
219 fn parse(src: &mut TtIter) -> Result<Rule, ParseError> { 219 fn parse(src: &mut TtIter) -> Result<Rule, ParseError> {
220 let mut lhs = src 220 let lhs = src
221 .expect_subtree() 221 .expect_subtree()
222 .map_err(|()| ParseError::Expected("expected subtree".to_string()))? 222 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?;
223 .clone();
224 lhs.delimiter = None;
225 src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?; 223 src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?;
226 src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?; 224 src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?;
227 let mut rhs = src 225 let rhs = src
228 .expect_subtree() 226 .expect_subtree()
229 .map_err(|()| ParseError::Expected("expected subtree".to_string()))? 227 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?;
230 .clone();
231 rhs.delimiter = None;
232 228
233 let lhs = MetaTemplate { tokens: parse_pattern(&lhs), delimiter: None }; 229 let lhs = MetaTemplate { tokens: parse_pattern(&lhs), delimiter: None };
234 let rhs = MetaTemplate { tokens: parse_template(&rhs), delimiter: None }; 230 let rhs = MetaTemplate { tokens: parse_template(&rhs), delimiter: None };
@@ -274,7 +270,7 @@ fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> {
274 } 270 }
275 false 271 false
276 }) { 272 }) {
277 return Err(ParseError::RepetitionEmtpyTokenTree); 273 return Err(ParseError::RepetitionEmptyTokenTree);
278 } 274 }
279 } 275 }
280 validate(subtree)? 276 validate(subtree)?
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs
index ab5f87c48..d32e60521 100644
--- a/crates/mbe/src/mbe_expander/matcher.rs
+++ b/crates/mbe/src/mbe_expander/matcher.rs
@@ -150,7 +150,7 @@ fn match_subtree(
150 res.add_err(err!("leftover tokens")); 150 res.add_err(err!("leftover tokens"));
151 } 151 }
152 } 152 }
153 Op::Var { name, kind } => { 153 Op::Var { name, kind, .. } => {
154 let kind = match kind { 154 let kind = match kind {
155 Some(k) => k, 155 Some(k) => k,
156 None => { 156 None => {
@@ -309,7 +309,7 @@ impl<'a> TtIter<'a> {
309 } 309 }
310 } 310 }
311 311
312 let buffer = TokenBuffer::new(&self.inner.as_slice()); 312 let buffer = TokenBuffer::from_tokens(&self.inner.as_slice());
313 let mut src = SubtreeTokenSource::new(&buffer); 313 let mut src = SubtreeTokenSource::new(&buffer);
314 let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false }; 314 let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false };
315 315
@@ -336,11 +336,11 @@ impl<'a> TtIter<'a> {
336 err = Some(err!("no tokens consumed")); 336 err = Some(err!("no tokens consumed"));
337 } 337 }
338 let res = match res.len() { 338 let res = match res.len() {
339 1 => Some(res[0].clone()), 339 1 => Some(res[0].cloned()),
340 0 => None, 340 0 => None,
341 _ => Some(tt::TokenTree::Subtree(tt::Subtree { 341 _ => Some(tt::TokenTree::Subtree(tt::Subtree {
342 delimiter: None, 342 delimiter: None,
343 token_trees: res.into_iter().cloned().collect(), 343 token_trees: res.into_iter().map(|it| it.cloned()).collect(),
344 })), 344 })),
345 }; 345 };
346 ExpandResult { value: res, err } 346 ExpandResult { value: res, err }
@@ -378,7 +378,7 @@ pub(super) fn match_repeat(
378 src: &mut TtIter, 378 src: &mut TtIter,
379) -> Result<(), ExpandError> { 379) -> Result<(), ExpandError> {
380 // Dirty hack to make macro-expansion terminate. 380 // Dirty hack to make macro-expansion terminate.
381 // This should be replaced by a propper macro-by-example implementation 381 // This should be replaced by a proper macro-by-example implementation
382 let mut limit = 65536; 382 let mut limit = 65536;
383 let mut counter = 0; 383 let mut counter = 0;
384 384
diff --git a/crates/mbe/src/mbe_expander/transcriber.rs b/crates/mbe/src/mbe_expander/transcriber.rs
index 720531237..59a3c80a8 100644
--- a/crates/mbe/src/mbe_expander/transcriber.rs
+++ b/crates/mbe/src/mbe_expander/transcriber.rs
@@ -67,7 +67,7 @@ struct NestingState {
67 /// because there is no variable in use by the current repetition 67 /// because there is no variable in use by the current repetition
68 hit: bool, 68 hit: bool,
69 /// `at_end` is currently necessary to tell `expand_repeat` if it should stop 69 /// `at_end` is currently necessary to tell `expand_repeat` if it should stop
70 /// because there is no more value avaible for the current repetition 70 /// because there is no more value available for the current repetition
71 at_end: bool, 71 at_end: bool,
72} 72}
73 73
@@ -100,8 +100,8 @@ fn expand_subtree(
100 err = err.or(e); 100 err = err.or(e);
101 arena.push(tt.into()); 101 arena.push(tt.into());
102 } 102 }
103 Op::Var { name, .. } => { 103 Op::Var { name, id, .. } => {
104 let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name); 104 let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name, *id);
105 err = err.or(e); 105 err = err.or(e);
106 push_fragment(arena, fragment); 106 push_fragment(arena, fragment);
107 } 107 }
@@ -118,14 +118,11 @@ fn expand_subtree(
118 ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err } 118 ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err }
119} 119}
120 120
121fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { 121fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult<Fragment> {
122 if v == "crate" { 122 // We already handle $crate case in mbe parser
123 // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. 123 debug_assert!(v != "crate");
124 let tt = 124
125 tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() }) 125 if !ctx.bindings.contains(v) {
126 .into();
127 ExpandResult::ok(Fragment::Tokens(tt))
128 } else if !ctx.bindings.contains(v) {
129 // Note that it is possible to have a `$var` inside a macro which is not bound. 126 // Note that it is possible to have a `$var` inside a macro which is not bound.
130 // For example: 127 // For example:
131 // ``` 128 // ```
@@ -142,14 +139,8 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
142 let tt = tt::Subtree { 139 let tt = tt::Subtree {
143 delimiter: None, 140 delimiter: None,
144 token_trees: vec![ 141 token_trees: vec![
145 tt::Leaf::from(tt::Punct { 142 tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, id }).into(),
146 char: '$', 143 tt::Leaf::from(tt::Ident { text: v.clone(), id }).into(),
147 spacing: tt::Spacing::Alone,
148 id: tt::TokenId::unspecified(),
149 })
150 .into(),
151 tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() })
152 .into(),
153 ], 144 ],
154 } 145 }
155 .into(); 146 .into();
@@ -188,11 +179,7 @@ fn expand_repeat(
188 179
189 counter += 1; 180 counter += 1;
190 if counter == limit { 181 if counter == limit {
191 log::warn!( 182 log::warn!("expand_tt in repeat pattern exceed limit => {:#?}\n{:#?}", template, ctx);
192 "expand_tt excced in repeat pattern exceed limit => {:#?}\n{:#?}",
193 template,
194 ctx
195 );
196 break; 183 break;
197 } 184 }
198 185
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 2f3ebc831..f3047972d 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -8,7 +8,7 @@ use crate::{tt_iter::TtIter, ExpandError, MetaTemplate};
8 8
9#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub(crate) enum Op { 10pub(crate) enum Op {
11 Var { name: SmolStr, kind: Option<SmolStr> }, 11 Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId },
12 Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> }, 12 Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
13 Leaf(tt::Leaf), 13 Leaf(tt::Leaf),
14 Subtree(MetaTemplate), 14 Subtree(MetaTemplate),
@@ -106,18 +106,25 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
106 } 106 }
107 let name = UNDERSCORE.clone(); 107 let name = UNDERSCORE.clone();
108 let kind = eat_fragment_kind(src, mode)?; 108 let kind = eat_fragment_kind(src, mode)?;
109 Op::Var { name, kind } 109 let id = punct.id;
110 Op::Var { name, kind, id }
111 }
112 tt::Leaf::Ident(ident) if ident.text == "crate" => {
113 // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
114 Op::Leaf(tt::Leaf::from(tt::Ident { text: "$crate".into(), id: ident.id }))
110 } 115 }
111 tt::Leaf::Ident(ident) => { 116 tt::Leaf::Ident(ident) => {
112 let name = ident.text.clone(); 117 let name = ident.text.clone();
113 let kind = eat_fragment_kind(src, mode)?; 118 let kind = eat_fragment_kind(src, mode)?;
114 Op::Var { name, kind } 119 let id = ident.id;
120 Op::Var { name, kind, id }
115 } 121 }
116 tt::Leaf::Literal(lit) => { 122 tt::Leaf::Literal(lit) => {
117 if is_boolean_literal(&lit) { 123 if is_boolean_literal(&lit) {
118 let name = lit.text.clone(); 124 let name = lit.text.clone();
119 let kind = eat_fragment_kind(src, mode)?; 125 let kind = eat_fragment_kind(src, mode)?;
120 Op::Var { name, kind } 126 let id = lit.id;
127 Op::Var { name, kind, id }
121 } else { 128 } else {
122 bail!("bad var 2"); 129 bail!("bad var 2");
123 } 130 }
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs
index d10d4b70e..d7433bd35 100644
--- a/crates/mbe/src/subtree_source.rs
+++ b/crates/mbe/src/subtree_source.rs
@@ -1,129 +1,104 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use parser::{Token, TokenSource}; 3use parser::{Token, TokenSource};
4use std::cell::{Cell, Ref, RefCell};
5use syntax::{lex_single_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T}; 4use syntax::{lex_single_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T};
6use tt::buffer::{Cursor, TokenBuffer}; 5use tt::buffer::TokenBuffer;
7 6
8#[derive(Debug, Clone, Eq, PartialEq)] 7#[derive(Debug, Clone, Eq, PartialEq)]
9struct TtToken { 8struct TtToken {
10 kind: SyntaxKind, 9 tt: Token,
11 is_joint_to_next: bool,
12 text: SmolStr, 10 text: SmolStr,
13} 11}
14 12
15pub(crate) struct SubtreeTokenSource<'a> { 13pub(crate) struct SubtreeTokenSource {
16 cached_cursor: Cell<Cursor<'a>>, 14 cached: Vec<TtToken>,
17 cached: RefCell<Vec<Option<TtToken>>>,
18 curr: (Token, usize), 15 curr: (Token, usize),
19} 16}
20 17
21impl<'a> SubtreeTokenSource<'a> { 18impl<'a> SubtreeTokenSource {
22 // Helper function used in test 19 // Helper function used in test
23 #[cfg(test)] 20 #[cfg(test)]
24 pub(crate) fn text(&self) -> SmolStr { 21 pub(crate) fn text(&self) -> SmolStr {
25 match *self.get(self.curr.1) { 22 match self.cached.get(self.curr.1) {
26 Some(ref tt) => tt.text.clone(), 23 Some(ref tt) => tt.text.clone(),
27 _ => SmolStr::new(""), 24 _ => SmolStr::new(""),
28 } 25 }
29 } 26 }
30} 27}
31 28
32impl<'a> SubtreeTokenSource<'a> { 29impl<'a> SubtreeTokenSource {
33 pub(crate) fn new(buffer: &'a TokenBuffer) -> SubtreeTokenSource<'a> { 30 pub(crate) fn new(buffer: &TokenBuffer) -> SubtreeTokenSource {
34 let cursor = buffer.begin(); 31 let mut current = buffer.begin();
32 let mut cached = Vec::with_capacity(100);
35 33
36 let mut res = SubtreeTokenSource { 34 while !current.eof() {
37 curr: (Token { kind: EOF, is_jointed_to_next: false }, 0), 35 let cursor = current;
38 cached_cursor: Cell::new(cursor), 36 let tt = cursor.token_tree();
39 cached: RefCell::new(Vec::with_capacity(10)),
40 };
41 res.curr = (res.mk_token(0), 0);
42 res
43 }
44 37
45 fn mk_token(&self, pos: usize) -> Token { 38 // Check if it is lifetime
46 match *self.get(pos) { 39 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = tt {
47 Some(ref tt) => Token { kind: tt.kind, is_jointed_to_next: tt.is_joint_to_next },
48 None => Token { kind: EOF, is_jointed_to_next: false },
49 }
50 }
51
52 fn get(&self, pos: usize) -> Ref<Option<TtToken>> {
53 fn is_lifetime(c: Cursor) -> Option<(Cursor, SmolStr)> {
54 let tkn = c.token_tree();
55
56 if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = tkn {
57 if punct.char == '\'' { 40 if punct.char == '\'' {
58 let next = c.bump(); 41 let next = cursor.bump();
59 if let Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) = next.token_tree() { 42 if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) =
60 let res_cursor = next.bump(); 43 next.token_tree()
61 let text = SmolStr::new("'".to_string() + &ident.to_string()); 44 {
62 45 let text = SmolStr::new("'".to_string() + &ident.text);
63 return Some((res_cursor, text)); 46 cached.push(TtToken {
47 tt: Token { kind: LIFETIME_IDENT, is_jointed_to_next: false },
48 text,
49 });
50 current = next.bump();
51 continue;
64 } else { 52 } else {
65 panic!("Next token must be ident : {:#?}", next.token_tree()); 53 panic!("Next token must be ident : {:#?}", next.token_tree());
66 } 54 }
67 } 55 }
68 } 56 }
69 57
70 None 58 current = match tt {
71 } 59 Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => {
72 60 cached.push(convert_leaf(&leaf));
73 if pos < self.cached.borrow().len() { 61 cursor.bump()
74 return Ref::map(self.cached.borrow(), |c| &c[pos]);
75 }
76
77 {
78 let mut cached = self.cached.borrow_mut();
79 while pos >= cached.len() {
80 let cursor = self.cached_cursor.get();
81 if cursor.eof() {
82 cached.push(None);
83 continue;
84 } 62 }
85 63 Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => {
86 if let Some((curr, text)) = is_lifetime(cursor) { 64 cached.push(convert_delim(subtree.delimiter_kind(), false));
87 cached.push(Some(TtToken { 65 cursor.subtree().unwrap()
88 kind: LIFETIME_IDENT,
89 is_joint_to_next: false,
90 text,
91 }));
92 self.cached_cursor.set(curr);
93 continue;
94 } 66 }
95 67 None => {
96 match cursor.token_tree() { 68 if let Some(subtree) = cursor.end() {
97 Some(tt::TokenTree::Leaf(leaf)) => { 69 cached.push(convert_delim(subtree.delimiter_kind(), true));
98 cached.push(Some(convert_leaf(&leaf))); 70 cursor.bump()
99 self.cached_cursor.set(cursor.bump()); 71 } else {
100 } 72 continue;
101 Some(tt::TokenTree::Subtree(subtree)) => {
102 self.cached_cursor.set(cursor.subtree().unwrap());
103 cached.push(Some(convert_delim(subtree.delimiter_kind(), false)));
104 }
105 None => {
106 if let Some(subtree) = cursor.end() {
107 cached.push(Some(convert_delim(subtree.delimiter_kind(), true)));
108 self.cached_cursor.set(cursor.bump());
109 }
110 } 73 }
111 } 74 }
112 } 75 };
113 } 76 }
114 77
115 Ref::map(self.cached.borrow(), |c| &c[pos]) 78 let mut res = SubtreeTokenSource {
79 curr: (Token { kind: EOF, is_jointed_to_next: false }, 0),
80 cached,
81 };
82 res.curr = (res.token(0), 0);
83 res
84 }
85
86 fn token(&self, pos: usize) -> Token {
87 match self.cached.get(pos) {
88 Some(it) => it.tt,
89 None => Token { kind: EOF, is_jointed_to_next: false },
90 }
116 } 91 }
117} 92}
118 93
119impl<'a> TokenSource for SubtreeTokenSource<'a> { 94impl<'a> TokenSource for SubtreeTokenSource {
120 fn current(&self) -> Token { 95 fn current(&self) -> Token {
121 self.curr.0 96 self.curr.0
122 } 97 }
123 98
124 /// Lookahead n token 99 /// Lookahead n token
125 fn lookahead_nth(&self, n: usize) -> Token { 100 fn lookahead_nth(&self, n: usize) -> Token {
126 self.mk_token(self.curr.1 + n) 101 self.token(self.curr.1 + n)
127 } 102 }
128 103
129 /// bump cursor to next token 104 /// bump cursor to next token
@@ -131,13 +106,12 @@ impl<'a> TokenSource for SubtreeTokenSource<'a> {
131 if self.current().kind == EOF { 106 if self.current().kind == EOF {
132 return; 107 return;
133 } 108 }
134 109 self.curr = (self.token(self.curr.1 + 1), self.curr.1 + 1);
135 self.curr = (self.mk_token(self.curr.1 + 1), self.curr.1 + 1);
136 } 110 }
137 111
138 /// Is the current token a specified keyword? 112 /// Is the current token a specified keyword?
139 fn is_keyword(&self, kw: &str) -> bool { 113 fn is_keyword(&self, kw: &str) -> bool {
140 match *self.get(self.curr.1) { 114 match self.cached.get(self.curr.1) {
141 Some(ref t) => t.text == *kw, 115 Some(ref t) => t.text == *kw,
142 _ => false, 116 _ => false,
143 } 117 }
@@ -155,7 +129,7 @@ fn convert_delim(d: Option<tt::DelimiterKind>, closing: bool) -> TtToken {
155 let idx = closing as usize; 129 let idx = closing as usize;
156 let kind = kinds[idx]; 130 let kind = kinds[idx];
157 let text = if !texts.is_empty() { &texts[idx..texts.len() - (1 - idx)] } else { "" }; 131 let text = if !texts.is_empty() { &texts[idx..texts.len() - (1 - idx)] } else { "" };
158 TtToken { kind, is_joint_to_next: false, text: SmolStr::new(text) } 132 TtToken { tt: Token { kind, is_jointed_to_next: false }, text: SmolStr::new(text) }
159} 133}
160 134
161fn convert_literal(l: &tt::Literal) -> TtToken { 135fn convert_literal(l: &tt::Literal) -> TtToken {
@@ -169,7 +143,7 @@ fn convert_literal(l: &tt::Literal) -> TtToken {
169 }) 143 })
170 .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l)); 144 .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l));
171 145
172 TtToken { kind, is_joint_to_next: false, text: l.text.clone() } 146 TtToken { tt: Token { kind, is_jointed_to_next: false }, text: l.text.clone() }
173} 147}
174 148
175fn convert_ident(ident: &tt::Ident) -> TtToken { 149fn convert_ident(ident: &tt::Ident) -> TtToken {
@@ -180,7 +154,7 @@ fn convert_ident(ident: &tt::Ident) -> TtToken {
180 _ => SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT), 154 _ => SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT),
181 }; 155 };
182 156
183 TtToken { kind, is_joint_to_next: false, text: ident.text.clone() } 157 TtToken { tt: Token { kind, is_jointed_to_next: false }, text: ident.text.clone() }
184} 158}
185 159
186fn convert_punct(p: tt::Punct) -> TtToken { 160fn convert_punct(p: tt::Punct) -> TtToken {
@@ -194,7 +168,7 @@ fn convert_punct(p: tt::Punct) -> TtToken {
194 let s: &str = p.char.encode_utf8(&mut buf); 168 let s: &str = p.char.encode_utf8(&mut buf);
195 SmolStr::new(s) 169 SmolStr::new(s)
196 }; 170 };
197 TtToken { kind, is_joint_to_next: p.spacing == tt::Spacing::Joint, text } 171 TtToken { tt: Token { kind, is_jointed_to_next: p.spacing == tt::Spacing::Joint }, text }
198} 172}
199 173
200fn convert_leaf(leaf: &tt::Leaf) -> TtToken { 174fn convert_leaf(leaf: &tt::Leaf) -> TtToken {
@@ -208,6 +182,7 @@ fn convert_leaf(leaf: &tt::Leaf) -> TtToken {
208#[cfg(test)] 182#[cfg(test)]
209mod tests { 183mod tests {
210 use super::{convert_literal, TtToken}; 184 use super::{convert_literal, TtToken};
185 use parser::Token;
211 use syntax::{SmolStr, SyntaxKind}; 186 use syntax::{SmolStr, SyntaxKind};
212 187
213 #[test] 188 #[test]
@@ -218,8 +193,7 @@ mod tests {
218 text: SmolStr::new("-42.0") 193 text: SmolStr::new("-42.0")
219 }), 194 }),
220 TtToken { 195 TtToken {
221 kind: SyntaxKind::FLOAT_NUMBER, 196 tt: Token { kind: SyntaxKind::FLOAT_NUMBER, is_jointed_to_next: false },
222 is_joint_to_next: false,
223 text: SmolStr::new("-42.0") 197 text: SmolStr::new("-42.0")
224 } 198 }
225 ); 199 );
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 265c0d63d..e648519f9 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -70,15 +70,12 @@ pub fn token_tree_to_syntax_node(
70 tt: &tt::Subtree, 70 tt: &tt::Subtree,
71 fragment_kind: FragmentKind, 71 fragment_kind: FragmentKind,
72) -> Result<(Parse<SyntaxNode>, TokenMap), ExpandError> { 72) -> Result<(Parse<SyntaxNode>, TokenMap), ExpandError> {
73 let tmp; 73 let buffer = match tt {
74 let tokens = match tt { 74 tt::Subtree { delimiter: None, token_trees } => {
75 tt::Subtree { delimiter: None, token_trees } => token_trees.as_slice(), 75 TokenBuffer::from_tokens(token_trees.as_slice())
76 _ => {
77 tmp = [tt.clone().into()];
78 &tmp[..]
79 } 76 }
77 _ => TokenBuffer::from_subtree(tt),
80 }; 78 };
81 let buffer = TokenBuffer::new(&tokens);
82 let mut token_source = SubtreeTokenSource::new(&buffer); 79 let mut token_source = SubtreeTokenSource::new(&buffer);
83 let mut tree_sink = TtTreeSink::new(buffer.begin()); 80 let mut tree_sink = TtTreeSink::new(buffer.begin());
84 parser::parse_fragment(&mut token_source, &mut tree_sink, fragment_kind); 81 parser::parse_fragment(&mut token_source, &mut tree_sink, fragment_kind);
@@ -152,7 +149,7 @@ impl TokenMap {
152 } 149 }
153 150
154 fn remove_delim(&mut self, idx: usize) { 151 fn remove_delim(&mut self, idx: usize) {
155 // FIXME: This could be accidently quadratic 152 // FIXME: This could be accidentally quadratic
156 self.entries.remove(idx); 153 self.entries.remove(idx);
157 } 154 }
158} 155}
@@ -414,7 +411,7 @@ trait TokenConvertor {
414 fn id_alloc(&mut self) -> &mut TokenIdAlloc; 411 fn id_alloc(&mut self) -> &mut TokenIdAlloc;
415} 412}
416 413
417impl<'a> SrcToken for (RawToken, &'a str) { 414impl<'a> SrcToken for (&'a RawToken, &'a str) {
418 fn kind(&self) -> SyntaxKind { 415 fn kind(&self) -> SyntaxKind {
419 self.0.kind 416 self.0.kind
420 } 417 }
@@ -431,7 +428,7 @@ impl<'a> SrcToken for (RawToken, &'a str) {
431impl RawConvertor<'_> {} 428impl RawConvertor<'_> {}
432 429
433impl<'a> TokenConvertor for RawConvertor<'a> { 430impl<'a> TokenConvertor for RawConvertor<'a> {
434 type Token = (RawToken, &'a str); 431 type Token = (&'a RawToken, &'a str);
435 432
436 fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> { 433 fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> {
437 convert_doc_comment(&doc_comment(token.1)) 434 convert_doc_comment(&doc_comment(token.1))
@@ -442,11 +439,11 @@ impl<'a> TokenConvertor for RawConvertor<'a> {
442 let range = TextRange::at(self.offset, token.len); 439 let range = TextRange::at(self.offset, token.len);
443 self.offset += token.len; 440 self.offset += token.len;
444 441
445 Some(((*token, &self.text[range]), range)) 442 Some(((token, &self.text[range]), range))
446 } 443 }
447 444
448 fn peek(&self) -> Option<Self::Token> { 445 fn peek(&self) -> Option<Self::Token> {
449 let token = self.inner.as_slice().get(0).cloned(); 446 let token = self.inner.as_slice().get(0);
450 447
451 token.map(|it| { 448 token.map(|it| {
452 let range = TextRange::at(self.offset, it.len); 449 let range = TextRange::at(self.offset, it.len);
@@ -479,14 +476,14 @@ impl Convertor {
479 476
480#[derive(Debug)] 477#[derive(Debug)]
481enum SynToken { 478enum SynToken {
482 Ordiniary(SyntaxToken), 479 Ordinary(SyntaxToken),
483 Punch(SyntaxToken, TextSize), 480 Punch(SyntaxToken, TextSize),
484} 481}
485 482
486impl SynToken { 483impl SynToken {
487 fn token(&self) -> &SyntaxToken { 484 fn token(&self) -> &SyntaxToken {
488 match self { 485 match self {
489 SynToken::Ordiniary(it) => it, 486 SynToken::Ordinary(it) => it,
490 SynToken::Punch(it, _) => it, 487 SynToken::Punch(it, _) => it,
491 } 488 }
492 } 489 }
@@ -498,7 +495,7 @@ impl SrcToken for SynToken {
498 } 495 }
499 fn to_char(&self) -> Option<char> { 496 fn to_char(&self) -> Option<char> {
500 match self { 497 match self {
501 SynToken::Ordiniary(_) => None, 498 SynToken::Ordinary(_) => None,
502 SynToken::Punch(it, i) => it.text().chars().nth((*i).into()), 499 SynToken::Punch(it, i) => it.text().chars().nth((*i).into()),
503 } 500 }
504 } 501 }
@@ -538,7 +535,7 @@ impl TokenConvertor for Convertor {
538 } else { 535 } else {
539 self.punct_offset = None; 536 self.punct_offset = None;
540 let range = curr.text_range(); 537 let range = curr.text_range();
541 (SynToken::Ordiniary(curr), range) 538 (SynToken::Ordinary(curr), range)
542 }; 539 };
543 540
544 Some(token) 541 Some(token)
@@ -560,7 +557,7 @@ impl TokenConvertor for Convertor {
560 let token = if curr.kind().is_punct() { 557 let token = if curr.kind().is_punct() {
561 SynToken::Punch(curr, 0.into()) 558 SynToken::Punch(curr, 0.into())
562 } else { 559 } else {
563 SynToken::Ordiniary(curr) 560 SynToken::Ordinary(curr)
564 }; 561 };
565 Some(token) 562 Some(token)
566 } 563 }
@@ -601,17 +598,16 @@ impl<'a> TtTreeSink<'a> {
601 } 598 }
602} 599}
603 600
604fn delim_to_str(d: Option<tt::DelimiterKind>, closing: bool) -> SmolStr { 601fn delim_to_str(d: Option<tt::DelimiterKind>, closing: bool) -> &'static str {
605 let texts = match d { 602 let texts = match d {
606 Some(tt::DelimiterKind::Parenthesis) => "()", 603 Some(tt::DelimiterKind::Parenthesis) => "()",
607 Some(tt::DelimiterKind::Brace) => "{}", 604 Some(tt::DelimiterKind::Brace) => "{}",
608 Some(tt::DelimiterKind::Bracket) => "[]", 605 Some(tt::DelimiterKind::Bracket) => "[]",
609 None => return "".into(), 606 None => return "",
610 }; 607 };
611 608
612 let idx = closing as usize; 609 let idx = closing as usize;
613 let text = &texts[idx..texts.len() - (1 - idx)]; 610 &texts[idx..texts.len() - (1 - idx)]
614 text.into()
615} 611}
616 612
617impl<'a> TreeSink for TtTreeSink<'a> { 613impl<'a> TreeSink for TtTreeSink<'a> {
@@ -626,29 +622,32 @@ impl<'a> TreeSink for TtTreeSink<'a> {
626 622
627 let mut last = self.cursor; 623 let mut last = self.cursor;
628 for _ in 0..n_tokens { 624 for _ in 0..n_tokens {
625 let tmp_str: SmolStr;
629 if self.cursor.eof() { 626 if self.cursor.eof() {
630 break; 627 break;
631 } 628 }
632 last = self.cursor; 629 last = self.cursor;
633 let text: SmolStr = match self.cursor.token_tree() { 630 let text: &str = match self.cursor.token_tree() {
634 Some(tt::TokenTree::Leaf(leaf)) => { 631 Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => {
635 // Mark the range if needed 632 // Mark the range if needed
636 let (text, id) = match leaf { 633 let (text, id) = match leaf {
637 tt::Leaf::Ident(ident) => (ident.text.clone(), ident.id), 634 tt::Leaf::Ident(ident) => (&ident.text, ident.id),
638 tt::Leaf::Punct(punct) => { 635 tt::Leaf::Punct(punct) => {
639 assert!(punct.char.is_ascii()); 636 assert!(punct.char.is_ascii());
640 let char = &(punct.char as u8); 637 let char = &(punct.char as u8);
641 let text = std::str::from_utf8(std::slice::from_ref(char)).unwrap(); 638 tmp_str = SmolStr::new_inline(
642 (SmolStr::new_inline(text), punct.id) 639 std::str::from_utf8(std::slice::from_ref(char)).unwrap(),
640 );
641 (&tmp_str, punct.id)
643 } 642 }
644 tt::Leaf::Literal(lit) => (lit.text.clone(), lit.id), 643 tt::Leaf::Literal(lit) => (&lit.text, lit.id),
645 }; 644 };
646 let range = TextRange::at(self.text_pos, TextSize::of(text.as_str())); 645 let range = TextRange::at(self.text_pos, TextSize::of(text.as_str()));
647 self.token_map.insert(id, range); 646 self.token_map.insert(id, range);
648 self.cursor = self.cursor.bump(); 647 self.cursor = self.cursor.bump();
649 text 648 text
650 } 649 }
651 Some(tt::TokenTree::Subtree(subtree)) => { 650 Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => {
652 self.cursor = self.cursor.subtree().unwrap(); 651 self.cursor = self.cursor.subtree().unwrap();
653 if let Some(id) = subtree.delimiter.map(|it| it.id) { 652 if let Some(id) = subtree.delimiter.map(|it| it.id) {
654 self.open_delims.insert(id, self.text_pos); 653 self.open_delims.insert(id, self.text_pos);
@@ -672,7 +671,7 @@ impl<'a> TreeSink for TtTreeSink<'a> {
672 } 671 }
673 }; 672 };
674 self.buf += &text; 673 self.buf += &text;
675 self.text_pos += TextSize::of(text.as_str()); 674 self.text_pos += TextSize::of(text);
676 } 675 }
677 676
678 let text = SmolStr::new(self.buf.as_str()); 677 let text = SmolStr::new(self.buf.as_str());
@@ -682,8 +681,8 @@ impl<'a> TreeSink for TtTreeSink<'a> {
682 // Add whitespace between adjoint puncts 681 // Add whitespace between adjoint puncts
683 let next = last.bump(); 682 let next = last.bump();
684 if let ( 683 if let (
685 Some(tt::TokenTree::Leaf(tt::Leaf::Punct(curr))), 684 Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(curr), _)),
686 Some(tt::TokenTree::Leaf(tt::Leaf::Punct(_))), 685 Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(_), _)),
687 ) = (last.token_tree(), next.token_tree()) 686 ) = (last.token_tree(), next.token_tree())
688 { 687 {
689 // Note: We always assume the semi-colon would be the last token in 688 // Note: We always assume the semi-colon would be the last token in
@@ -742,7 +741,7 @@ mod tests {
742 ) 741 )
743 .expand_tt("literals!(foo);"); 742 .expand_tt("literals!(foo);");
744 let tts = &[expansion.into()]; 743 let tts = &[expansion.into()];
745 let buffer = tt::buffer::TokenBuffer::new(tts); 744 let buffer = tt::buffer::TokenBuffer::from_tokens(tts);
746 let mut tt_src = SubtreeTokenSource::new(&buffer); 745 let mut tt_src = SubtreeTokenSource::new(&buffer);
747 let mut tokens = vec![]; 746 let mut tokens = vec![];
748 while tt_src.current().kind != EOF { 747 while tt_src.current().kind != EOF {
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 1d9afb4fb..ecea15c11 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -1080,6 +1080,19 @@ fn test_vertical_bar_with_pat() {
1080} 1080}
1081 1081
1082#[test] 1082#[test]
1083fn test_dollar_crate_lhs_is_not_meta() {
1084 parse_macro(
1085 r#"
1086macro_rules! foo {
1087 ($crate) => {};
1088 () => {0};
1089}
1090 "#,
1091 )
1092 .assert_expand_items(r#"foo!{}"#, r#"0"#);
1093}
1094
1095#[test]
1083fn test_lifetime() { 1096fn test_lifetime() {
1084 parse_macro( 1097 parse_macro(
1085 r#" 1098 r#"
@@ -1954,7 +1967,7 @@ fn test_no_space_after_semi_colon() {
1954#[test] 1967#[test]
1955fn test_rustc_issue_57597() { 1968fn test_rustc_issue_57597() {
1956 fn test_error(fixture: &str) { 1969 fn test_error(fixture: &str) {
1957 assert_eq!(parse_macro_error(fixture), ParseError::RepetitionEmtpyTokenTree); 1970 assert_eq!(parse_macro_error(fixture), ParseError::RepetitionEmptyTokenTree);
1958 } 1971 }
1959 1972
1960 test_error("macro_rules! foo { ($($($i:ident)?)+) => {}; }"); 1973 test_error("macro_rules! foo { ($($($i:ident)?)+) => {}; }");
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 63cc90027..bb9ffea8b 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -66,6 +66,10 @@ pub(crate) mod fragments {
66 expressions::stmt(p, expressions::StmtWithSemi::No) 66 expressions::stmt(p, expressions::StmtWithSemi::No)
67 } 67 }
68 68
69 pub(crate) fn stmt_optional_semi(p: &mut Parser) {
70 expressions::stmt(p, expressions::StmtWithSemi::Optional)
71 }
72
69 pub(crate) fn opt_visibility(p: &mut Parser) { 73 pub(crate) fn opt_visibility(p: &mut Parser) {
70 let _ = super::opt_visibility(p); 74 let _ = super::opt_visibility(p);
71 } 75 }
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index c7a3556a7..d61950b96 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -15,8 +15,16 @@ use super::*;
15// let _ = b"e"; 15// let _ = b"e";
16// let _ = br"f"; 16// let _ = br"f";
17// } 17// }
18pub(crate) const LITERAL_FIRST: TokenSet = 18pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[
19 TokenSet::new(&[TRUE_KW, FALSE_KW, INT_NUMBER, FLOAT_NUMBER, BYTE, CHAR, STRING, BYTE_STRING]); 19 T![true],
20 T![false],
21 INT_NUMBER,
22 FLOAT_NUMBER,
23 BYTE,
24 CHAR,
25 STRING,
26 BYTE_STRING,
27]);
20 28
21pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> { 29pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> {
22 if !p.at_ts(LITERAL_FIRST) { 30 if !p.at_ts(LITERAL_FIRST) {
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index cf4168d32..2070ce163 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -27,19 +27,19 @@ pub(super) fn mod_contents(p: &mut Parser, stop_on_r_curly: bool) {
27} 27}
28 28
29pub(super) const ITEM_RECOVERY_SET: TokenSet = TokenSet::new(&[ 29pub(super) const ITEM_RECOVERY_SET: TokenSet = TokenSet::new(&[
30 FN_KW, 30 T![fn],
31 STRUCT_KW, 31 T![struct],
32 ENUM_KW, 32 T![enum],
33 IMPL_KW, 33 T![impl],
34 TRAIT_KW, 34 T![trait],
35 CONST_KW, 35 T![const],
36 STATIC_KW, 36 T![static],
37 LET_KW, 37 T![let],
38 MOD_KW, 38 T![mod],
39 PUB_KW, 39 T![pub],
40 CRATE_KW, 40 T![crate],
41 USE_KW, 41 T![use],
42 MACRO_KW, 42 T![macro],
43 T![;], 43 T![;],
44]); 44]);
45 45
diff --git a/crates/parser/src/grammar/items/traits.rs b/crates/parser/src/grammar/items/traits.rs
index ab9a12b4d..d076974ed 100644
--- a/crates/parser/src/grammar/items/traits.rs
+++ b/crates/parser/src/grammar/items/traits.rs
@@ -110,7 +110,7 @@ fn choose_type_params_over_qpath(p: &Parser) -> bool {
110 if !p.at(T![<]) { 110 if !p.at(T![<]) {
111 return false; 111 return false;
112 } 112 }
113 if p.nth(1) == T![#] || p.nth(1) == T![>] || p.nth(1) == CONST_KW { 113 if p.nth(1) == T![#] || p.nth(1) == T![>] || p.nth(1) == T![const] {
114 return true; 114 return true;
115 } 115 }
116 (p.nth(1) == LIFETIME_IDENT || p.nth(1) == IDENT) 116 (p.nth(1) == LIFETIME_IDENT || p.nth(1) == IDENT)
diff --git a/crates/parser/src/grammar/items/use_item.rs b/crates/parser/src/grammar/items/use_item.rs
index 20e6a13cf..5cb8b08e7 100644
--- a/crates/parser/src/grammar/items/use_item.rs
+++ b/crates/parser/src/grammar/items/use_item.rs
@@ -46,7 +46,7 @@ fn use_tree(p: &mut Parser, top_level: bool) {
46 // test use_tree_list 46 // test use_tree_list
47 // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) 47 // use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`)
48 // use {path::from::root}; // Rust 2015 48 // use {path::from::root}; // Rust 2015
49 // use ::{some::arbritrary::path}; // Rust 2015 49 // use ::{some::arbitrary::path}; // Rust 2015
50 // use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting 50 // use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting
51 T!['{'] => { 51 T!['{'] => {
52 use_tree_list(p); 52 use_tree_list(p);
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index b53d5749f..da71498a8 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -83,7 +83,7 @@ fn pattern_single_r(p: &mut Parser, recovery_set: TokenSet) {
83} 83}
84 84
85const PAT_RECOVERY_SET: TokenSet = 85const PAT_RECOVERY_SET: TokenSet =
86 TokenSet::new(&[LET_KW, IF_KW, WHILE_KW, LOOP_KW, MATCH_KW, R_PAREN, COMMA]); 86 TokenSet::new(&[T![let], T![if], T![while], T![loop], T![match], T![')'], T![,]]);
87 87
88fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> { 88fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
89 let m = match p.nth(0) { 89 let m = match p.nth(0) {
diff --git a/crates/parser/src/grammar/type_args.rs b/crates/parser/src/grammar/type_args.rs
index a013c49b9..42cd426bd 100644
--- a/crates/parser/src/grammar/type_args.rs
+++ b/crates/parser/src/grammar/type_args.rs
@@ -26,7 +26,7 @@ pub(super) fn opt_generic_arg_list(p: &mut Parser, colon_colon_required: bool) {
26} 26}
27 27
28// test type_arg 28// test type_arg
29// type A = B<'static, i32, 1, { 2 }, Item=u64>; 29// type A = B<'static, i32, 1, { 2 }, Item=u64, true, false>;
30fn generic_arg(p: &mut Parser) { 30fn generic_arg(p: &mut Parser) {
31 let m = p.start(); 31 let m = p.start();
32 match p.current() { 32 match p.current() {
@@ -55,6 +55,10 @@ fn generic_arg(p: &mut Parser) {
55 expressions::literal(p); 55 expressions::literal(p);
56 m.complete(p, CONST_ARG); 56 m.complete(p, CONST_ARG);
57 } 57 }
58 T![true] | T![false] => {
59 expressions::literal(p);
60 m.complete(p, CONST_ARG);
61 }
58 _ => { 62 _ => {
59 types::type_(p); 63 types::type_(p);
60 m.complete(p, TYPE_ARG); 64 m.complete(p, TYPE_ARG);
diff --git a/crates/parser/src/grammar/type_params.rs b/crates/parser/src/grammar/type_params.rs
index 4aeccd193..3de5248da 100644
--- a/crates/parser/src/grammar/type_params.rs
+++ b/crates/parser/src/grammar/type_params.rs
@@ -25,7 +25,7 @@ fn generic_param_list(p: &mut Parser) {
25 match p.current() { 25 match p.current() {
26 LIFETIME_IDENT => lifetime_param(p, m), 26 LIFETIME_IDENT => lifetime_param(p, m),
27 IDENT => type_param(p, m), 27 IDENT => type_param(p, m),
28 CONST_KW => const_param(p, m), 28 T![const] => const_param(p, m),
29 _ => { 29 _ => {
30 m.abandon(p); 30 m.abandon(p);
31 p.err_and_bump("expected type parameter") 31 p.err_and_bump("expected type parameter")
@@ -66,7 +66,7 @@ fn type_param(p: &mut Parser, m: Marker) {
66// test const_param 66// test const_param
67// struct S<const N: u32>; 67// struct S<const N: u32>;
68fn const_param(p: &mut Parser, m: Marker) { 68fn const_param(p: &mut Parser, m: Marker) {
69 assert!(p.at(CONST_KW)); 69 assert!(p.at(T![const]));
70 p.bump(T![const]); 70 p.bump(T![const]);
71 name(p); 71 name(p);
72 types::ascription(p); 72 types::ascription(p);
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 811e740f9..9dfe63028 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -88,6 +88,7 @@ pub enum FragmentKind {
88 Path, 88 Path,
89 Expr, 89 Expr,
90 Statement, 90 Statement,
91 StatementOptionalSemi,
91 Type, 92 Type,
92 Pattern, 93 Pattern,
93 Item, 94 Item,
@@ -118,6 +119,7 @@ pub fn parse_fragment(
118 FragmentKind::Visibility => grammar::fragments::opt_visibility, 119 FragmentKind::Visibility => grammar::fragments::opt_visibility,
119 FragmentKind::MetaItem => grammar::fragments::meta_item, 120 FragmentKind::MetaItem => grammar::fragments::meta_item,
120 FragmentKind::Statement => grammar::fragments::stmt, 121 FragmentKind::Statement => grammar::fragments::stmt,
122 FragmentKind::StatementOptionalSemi => grammar::fragments::stmt_optional_semi,
121 FragmentKind::Items => grammar::fragments::macro_items, 123 FragmentKind::Items => grammar::fragments::macro_items,
122 FragmentKind::Statements => grammar::fragments::macro_stmts, 124 FragmentKind::Statements => grammar::fragments::macro_stmts,
123 FragmentKind::Attr => grammar::fragments::attr, 125 FragmentKind::Attr => grammar::fragments::attr,
diff --git a/crates/proc_macro_api/src/msg.rs b/crates/proc_macro_api/src/msg.rs
index f84ebdbc5..970f165ed 100644
--- a/crates/proc_macro_api/src/msg.rs
+++ b/crates/proc_macro_api/src/msg.rs
@@ -58,7 +58,13 @@ pub trait Message: Serialize + DeserializeOwned {
58 fn read(inp: &mut impl BufRead) -> io::Result<Option<Self>> { 58 fn read(inp: &mut impl BufRead) -> io::Result<Option<Self>> {
59 Ok(match read_json(inp)? { 59 Ok(match read_json(inp)? {
60 None => None, 60 None => None,
61 Some(text) => Some(serde_json::from_str(&text)?), 61 Some(text) => {
62 let mut deserializer = serde_json::Deserializer::from_str(&text);
63 // Note that some proc-macro generate very deep syntax tree
64 // We have to disable the current limit of serde here
65 deserializer.disable_recursion_limit();
66 Some(Self::deserialize(&mut deserializer)?)
67 }
62 }) 68 })
63 } 69 }
64 fn write(self, out: &mut impl Write) -> io::Result<()> { 70 fn write(self, out: &mut impl Write) -> io::Result<()> {
@@ -73,7 +79,7 @@ impl Message for Response {}
73fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> { 79fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> {
74 let mut buf = String::new(); 80 let mut buf = String::new();
75 inp.read_line(&mut buf)?; 81 inp.read_line(&mut buf)?;
76 buf.pop(); // Remove traling '\n' 82 buf.pop(); // Remove trailing '\n'
77 Ok(match buf.len() { 83 Ok(match buf.len() {
78 0 => None, 84 0 => None,
79 _ => Some(buf), 85 _ => Some(buf),
diff --git a/crates/proc_macro_api/src/process.rs b/crates/proc_macro_api/src/process.rs
index d68723ada..6d6ab8888 100644
--- a/crates/proc_macro_api/src/process.rs
+++ b/crates/proc_macro_api/src/process.rs
@@ -92,10 +92,11 @@ fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
92 for Task { req, result_tx } in task_rx { 92 for Task { req, result_tx } in task_rx {
93 match send_request(&mut stdin, &mut stdout, req) { 93 match send_request(&mut stdin, &mut stdout, req) {
94 Ok(res) => result_tx.send(res).unwrap(), 94 Ok(res) => result_tx.send(res).unwrap(),
95 Err(_err) => { 95 Err(err) => {
96 log::error!( 96 log::error!(
97 "proc macro server crashed, server process state: {:?}", 97 "proc macro server crashed, server process state: {:?}, server request error: {:?}",
98 process.child.try_wait() 98 process.child.try_wait(),
99 err
99 ); 100 );
100 let res = Response::Error(ResponseError { 101 let res = Response::Error(ResponseError {
101 code: ErrorCode::ServerErrorEnd, 102 code: ErrorCode::ServerErrorEnd,
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml
index df9a55c10..83f9ead17 100644
--- a/crates/proc_macro_srv/Cargo.toml
+++ b/crates/proc_macro_srv/Cargo.toml
@@ -20,8 +20,7 @@ proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" }
20test_utils = { path = "../test_utils", version = "0.0.0" } 20test_utils = { path = "../test_utils", version = "0.0.0" }
21 21
22[dev-dependencies] 22[dev-dependencies]
23cargo_metadata = "=0.12.0" 23cargo_metadata = "0.12.2"
24difference = "2.0.0"
25 24
26# used as proc macro test targets 25# used as proc macro test targets
27serde_derive = "1.0.106" 26serde_derive = "1.0.106"
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs b/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs
index 3528d5c99..bd1e7c2fc 100644
--- a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs
+++ b/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs
@@ -251,7 +251,7 @@ impl<S> DecodeMut<'_, '_, S> for String {
251 } 251 }
252} 252}
253 253
254/// Simplied version of panic payloads, ignoring 254/// Simplified version of panic payloads, ignoring
255/// types other than `&'static str` and `String`. 255/// types other than `&'static str` and `String`.
256#[derive(Debug)] 256#[derive(Debug)]
257pub enum PanicMessage { 257pub enum PanicMessage {
diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/rustc_server.rs
index 503f4c101..e6006a3c8 100644
--- a/crates/proc_macro_srv/src/rustc_server.rs
+++ b/crates/proc_macro_srv/src/rustc_server.rs
@@ -4,7 +4,7 @@
4//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that 4//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that
5//! we could provide any TokenStream implementation. 5//! we could provide any TokenStream implementation.
6//! The original idea from fedochet is using proc-macro2 as backend, 6//! The original idea from fedochet is using proc-macro2 as backend,
7//! we use tt instead for better intergation with RA. 7//! we use tt instead for better integration with RA.
8//! 8//!
9//! FIXME: No span and source file information is implemented yet 9//! FIXME: No span and source file information is implemented yet
10 10
@@ -204,17 +204,18 @@ pub mod token_stream {
204 let content = subtree 204 let content = subtree
205 .token_trees 205 .token_trees
206 .iter() 206 .iter()
207 .map(|tkn| { 207 .fold((String::new(), true), |(last, last_to_joint), tkn| {
208 let s = to_text(tkn); 208 let s = [last, to_text(tkn)].join(if last_to_joint { "" } else { " " });
209 let mut is_joint = false;
209 if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn { 210 if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn {
210 if punct.spacing == tt::Spacing::Alone { 211 if punct.spacing == tt::Spacing::Joint {
211 return s + " "; 212 is_joint = true;
212 } 213 }
213 } 214 }
214 s 215 (s, is_joint)
215 }) 216 })
216 .collect::<Vec<_>>() 217 .0;
217 .concat(); 218
218 let (open, close) = match subtree.delimiter.map(|it| it.kind) { 219 let (open, close) = match subtree.delimiter.map(|it| it.kind) {
219 None => ("", ""), 220 None => ("", ""),
220 Some(tt::DelimiterKind::Brace) => ("{", "}"), 221 Some(tt::DelimiterKind::Brace) => ("{", "}"),
@@ -710,4 +711,32 @@ mod tests {
710 assert_eq!(srv.character('c').text, "'c'"); 711 assert_eq!(srv.character('c').text, "'c'");
711 assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\""); 712 assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\"");
712 } 713 }
714
715 #[test]
716 fn test_rustc_server_to_string() {
717 let s = TokenStream {
718 subtree: tt::Subtree {
719 delimiter: None,
720 token_trees: vec![
721 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
722 text: "struct".into(),
723 id: tt::TokenId::unspecified(),
724 })),
725 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
726 text: "T".into(),
727 id: tt::TokenId::unspecified(),
728 })),
729 tt::TokenTree::Subtree(tt::Subtree {
730 delimiter: Some(tt::Delimiter {
731 id: tt::TokenId::unspecified(),
732 kind: tt::DelimiterKind::Brace,
733 }),
734 token_trees: vec![],
735 }),
736 ],
737 },
738 };
739
740 assert_eq!(s.to_string(), "struct T {}");
741 }
713} 742}
diff --git a/crates/profile/src/stop_watch.rs b/crates/profile/src/stop_watch.rs
index 5e276190e..cb6915d45 100644
--- a/crates/profile/src/stop_watch.rs
+++ b/crates/profile/src/stop_watch.rs
@@ -76,7 +76,11 @@ impl fmt::Display for StopWatchSpan {
76 instructions /= 1000; 76 instructions /= 1000;
77 prefix = "m" 77 prefix = "m"
78 } 78 }
79 write!(f, ", {}{}i", instructions, prefix)?; 79 if instructions > 10000 {
80 instructions /= 1000;
81 prefix = "g"
82 }
83 write!(f, ", {}{}instr", instructions, prefix)?;
80 } 84 }
81 if let Some(memory) = self.memory { 85 if let Some(memory) = self.memory {
82 write!(f, ", {}", memory)?; 86 write!(f, ", {}", memory)?;
diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml
index c55e85709..855fb83ea 100644
--- a/crates/project_model/Cargo.toml
+++ b/crates/project_model/Cargo.toml
@@ -12,11 +12,11 @@ doctest = false
12[dependencies] 12[dependencies]
13log = "0.4.8" 13log = "0.4.8"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15cargo_metadata = "=0.12.0" 15cargo_metadata = "0.12.2"
16serde = { version = "1.0.106", features = ["derive"] } 16serde = { version = "1.0.106", features = ["derive"] }
17serde_json = "1.0.48" 17serde_json = "1.0.48"
18anyhow = "1.0.26" 18anyhow = "1.0.26"
19itertools = "0.9.0" 19itertools = "0.10.0"
20 20
21arena = { path = "../arena", version = "0.0.0" } 21arena = { path = "../arena", version = "0.0.0" }
22cfg = { path = "../cfg", version = "0.0.0" } 22cfg = { path = "../cfg", version = "0.0.0" }
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index bb3b6f2ef..2ee4e88b2 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -1,10 +1,12 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{ 3use std::{
4 convert::TryInto,
4 ffi::OsStr, 5 ffi::OsStr,
6 io::BufReader,
5 ops, 7 ops,
6 path::{Path, PathBuf}, 8 path::{Path, PathBuf},
7 process::Command, 9 process::{Command, Stdio},
8}; 10};
9 11
10use anyhow::{Context, Result}; 12use anyhow::{Context, Result};
@@ -14,6 +16,7 @@ use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}
14use itertools::Itertools; 16use itertools::Itertools;
15use paths::{AbsPath, AbsPathBuf}; 17use paths::{AbsPath, AbsPathBuf};
16use rustc_hash::FxHashMap; 18use rustc_hash::FxHashMap;
19use stdx::JodChild;
17 20
18use crate::cfg_flag::CfgFlag; 21use crate::cfg_flag::CfgFlag;
19use crate::utf8_stdout; 22use crate::utf8_stdout;
@@ -79,19 +82,35 @@ pub type Package = Idx<PackageData>;
79 82
80pub type Target = Idx<TargetData>; 83pub type Target = Idx<TargetData>;
81 84
85/// Information associated with a cargo crate
82#[derive(Debug, Clone, Eq, PartialEq)] 86#[derive(Debug, Clone, Eq, PartialEq)]
83pub struct PackageData { 87pub struct PackageData {
88 /// Version given in the `Cargo.toml`
84 pub version: String, 89 pub version: String,
90 /// Name as given in the `Cargo.toml`
85 pub name: String, 91 pub name: String,
92 /// Path containing the `Cargo.toml`
86 pub manifest: AbsPathBuf, 93 pub manifest: AbsPathBuf,
94 /// Targets provided by the crate (lib, bin, example, test, ...)
87 pub targets: Vec<Target>, 95 pub targets: Vec<Target>,
96 /// Is this package a member of the current workspace
88 pub is_member: bool, 97 pub is_member: bool,
98 /// List of packages this package depends on
89 pub dependencies: Vec<PackageDependency>, 99 pub dependencies: Vec<PackageDependency>,
100 /// Rust edition for this package
90 pub edition: Edition, 101 pub edition: Edition,
102 /// List of features to activate
91 pub features: Vec<String>, 103 pub features: Vec<String>,
104 /// List of config flags defined by this package's build script
92 pub cfgs: Vec<CfgFlag>, 105 pub cfgs: Vec<CfgFlag>,
106 /// List of cargo-related environment variables with their value
107 ///
108 /// If the package has a build script which defines environment variables,
109 /// they can also be found here.
93 pub envs: Vec<(String, String)>, 110 pub envs: Vec<(String, String)>,
111 /// Directory where a build script might place its output
94 pub out_dir: Option<AbsPathBuf>, 112 pub out_dir: Option<AbsPathBuf>,
113 /// Path to the proc-macro library file if this package exposes proc-macros
95 pub proc_macro_dylib_path: Option<AbsPathBuf>, 114 pub proc_macro_dylib_path: Option<AbsPathBuf>,
96} 115}
97 116
@@ -101,12 +120,18 @@ pub struct PackageDependency {
101 pub name: String, 120 pub name: String,
102} 121}
103 122
123/// Information associated with a package's target
104#[derive(Debug, Clone, Eq, PartialEq)] 124#[derive(Debug, Clone, Eq, PartialEq)]
105pub struct TargetData { 125pub struct TargetData {
126 /// Package that provided this target
106 pub package: Package, 127 pub package: Package,
128 /// Name as given in the `Cargo.toml` or generated from the file name
107 pub name: String, 129 pub name: String,
130 /// Path to the main source file of the target
108 pub root: AbsPathBuf, 131 pub root: AbsPathBuf,
132 /// Kind of target
109 pub kind: TargetKind, 133 pub kind: TargetKind,
134 /// Is this target a proc-macro
110 pub is_proc_macro: bool, 135 pub is_proc_macro: bool,
111} 136}
112 137
@@ -148,6 +173,7 @@ impl CargoWorkspace {
148 pub fn from_cargo_metadata( 173 pub fn from_cargo_metadata(
149 cargo_toml: &AbsPath, 174 cargo_toml: &AbsPath,
150 config: &CargoConfig, 175 config: &CargoConfig,
176 progress: &dyn Fn(String),
151 ) -> Result<CargoWorkspace> { 177 ) -> Result<CargoWorkspace> {
152 let mut meta = MetadataCommand::new(); 178 let mut meta = MetadataCommand::new();
153 meta.cargo_path(toolchain::cargo()); 179 meta.cargo_path(toolchain::cargo());
@@ -196,8 +222,26 @@ impl CargoWorkspace {
196 if let Some(target) = target { 222 if let Some(target) = target {
197 meta.other_options(vec![String::from("--filter-platform"), target]); 223 meta.other_options(vec![String::from("--filter-platform"), target]);
198 } 224 }
225
226 // FIXME: Currently MetadataCommand is not based on parse_stream,
227 // So we just report it as a whole
228 progress("metadata".to_string());
199 let mut meta = meta.exec().with_context(|| { 229 let mut meta = meta.exec().with_context(|| {
200 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) 230 let cwd: Option<AbsPathBuf> =
231 std::env::current_dir().ok().and_then(|p| p.try_into().ok());
232
233 let workdir = cargo_toml
234 .parent()
235 .map(|p| p.to_path_buf())
236 .or(cwd)
237 .map(|dir| dir.to_string_lossy().to_string())
238 .unwrap_or_else(|| "<failed to get path>".into());
239
240 format!(
241 "Failed to run `cargo metadata --manifest-path {}` in `{}`",
242 cargo_toml.display(),
243 workdir
244 )
201 })?; 245 })?;
202 246
203 let mut out_dir_by_id = FxHashMap::default(); 247 let mut out_dir_by_id = FxHashMap::default();
@@ -205,7 +249,7 @@ impl CargoWorkspace {
205 let mut envs = FxHashMap::default(); 249 let mut envs = FxHashMap::default();
206 let mut proc_macro_dylib_paths = FxHashMap::default(); 250 let mut proc_macro_dylib_paths = FxHashMap::default();
207 if config.load_out_dirs_from_check { 251 if config.load_out_dirs_from_check {
208 let resources = load_extern_resources(cargo_toml, config)?; 252 let resources = load_extern_resources(cargo_toml, config, progress)?;
209 out_dir_by_id = resources.out_dirs; 253 out_dir_by_id = resources.out_dirs;
210 cfgs = resources.cfgs; 254 cfgs = resources.cfgs;
211 envs = resources.env; 255 envs = resources.env;
@@ -330,10 +374,16 @@ pub(crate) struct ExternResources {
330pub(crate) fn load_extern_resources( 374pub(crate) fn load_extern_resources(
331 cargo_toml: &Path, 375 cargo_toml: &Path,
332 cargo_features: &CargoConfig, 376 cargo_features: &CargoConfig,
377 progress: &dyn Fn(String),
333) -> Result<ExternResources> { 378) -> Result<ExternResources> {
334 let mut cmd = Command::new(toolchain::cargo()); 379 let mut cmd = Command::new(toolchain::cargo());
335 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); 380 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
336 381
382 // --all-targets includes tests, benches and examples in addition to the
383 // default lib and bins. This is an independent concept from the --targets
384 // flag below.
385 cmd.arg("--all-targets");
386
337 if let Some(target) = &cargo_features.target { 387 if let Some(target) = &cargo_features.target {
338 cmd.args(&["--target", target]); 388 cmd.args(&["--target", target]);
339 } 389 }
@@ -352,11 +402,14 @@ pub(crate) fn load_extern_resources(
352 } 402 }
353 } 403 }
354 404
355 let output = cmd.output()?; 405 cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
356 406
357 let mut res = ExternResources::default(); 407 let mut child = cmd.spawn().map(JodChild)?;
408 let child_stdout = child.stdout.take().unwrap();
409 let stdout = BufReader::new(child_stdout);
358 410
359 for message in cargo_metadata::Message::parse_stream(output.stdout.as_slice()) { 411 let mut res = ExternResources::default();
412 for message in cargo_metadata::Message::parse_stream(stdout) {
360 if let Ok(message) = message { 413 if let Ok(message) = message {
361 match message { 414 match message {
362 Message::BuildScriptExecuted(BuildScript { 415 Message::BuildScriptExecuted(BuildScript {
@@ -389,6 +442,8 @@ pub(crate) fn load_extern_resources(
389 res.env.insert(package_id, env); 442 res.env.insert(package_id, env);
390 } 443 }
391 Message::CompilerArtifact(message) => { 444 Message::CompilerArtifact(message) => {
445 progress(format!("metadata {}", message.target.name));
446
392 if message.target.kind.contains(&"proc-macro".to_string()) { 447 if message.target.kind.contains(&"proc-macro".to_string()) {
393 let package_id = message.package_id; 448 let package_id = message.package_id;
394 // Skip rmeta file 449 // Skip rmeta file
@@ -399,7 +454,9 @@ pub(crate) fn load_extern_resources(
399 } 454 }
400 } 455 }
401 } 456 }
402 Message::CompilerMessage(_) => (), 457 Message::CompilerMessage(message) => {
458 progress(message.target.name.clone());
459 }
403 Message::Unknown => (), 460 Message::Unknown => (),
404 Message::BuildFinished(_) => {} 461 Message::BuildFinished(_) => {}
405 Message::TextLine(_) => {} 462 Message::TextLine(_) => {}
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index 24aa9b8fa..aabb7a47d 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -1,9 +1,9 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3mod cargo_workspace; 3mod cargo_workspace;
4mod cfg_flag;
4mod project_json; 5mod project_json;
5mod sysroot; 6mod sysroot;
6mod cfg_flag;
7mod workspace; 7mod workspace;
8 8
9use std::{ 9use std::{
@@ -17,7 +17,10 @@ use paths::{AbsPath, AbsPathBuf};
17use rustc_hash::FxHashSet; 17use rustc_hash::FxHashSet;
18 18
19pub use crate::{ 19pub use crate::{
20 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, 20 cargo_workspace::{
21 CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, Target, TargetData,
22 TargetKind,
23 },
21 project_json::{ProjectJson, ProjectJsonData}, 24 project_json::{ProjectJson, ProjectJsonData},
22 sysroot::Sysroot, 25 sysroot::Sysroot,
23 workspace::{PackageRoot, ProjectWorkspace}, 26 workspace::{PackageRoot, ProjectWorkspace},
diff --git a/crates/project_model/src/project_json.rs b/crates/project_model/src/project_json.rs
index aab279223..41a2ac03e 100644
--- a/crates/project_model/src/project_json.rs
+++ b/crates/project_model/src/project_json.rs
@@ -110,13 +110,13 @@ impl ProjectJson {
110 } 110 }
111} 111}
112 112
113#[derive(Deserialize)] 113#[derive(Deserialize, Debug, Clone)]
114pub struct ProjectJsonData { 114pub struct ProjectJsonData {
115 sysroot_src: Option<PathBuf>, 115 sysroot_src: Option<PathBuf>,
116 crates: Vec<CrateData>, 116 crates: Vec<CrateData>,
117} 117}
118 118
119#[derive(Deserialize)] 119#[derive(Deserialize, Debug, Clone)]
120struct CrateData { 120struct CrateData {
121 display_name: Option<String>, 121 display_name: Option<String>,
122 root_module: PathBuf, 122 root_module: PathBuf,
@@ -132,13 +132,15 @@ struct CrateData {
132 source: Option<CrateSource>, 132 source: Option<CrateSource>,
133} 133}
134 134
135#[derive(Deserialize)] 135#[derive(Deserialize, Debug, Clone)]
136#[serde(rename = "edition")] 136#[serde(rename = "edition")]
137enum EditionData { 137enum EditionData {
138 #[serde(rename = "2015")] 138 #[serde(rename = "2015")]
139 Edition2015, 139 Edition2015,
140 #[serde(rename = "2018")] 140 #[serde(rename = "2018")]
141 Edition2018, 141 Edition2018,
142 #[serde(rename = "2021")]
143 Edition2021,
142} 144}
143 145
144impl From<EditionData> for Edition { 146impl From<EditionData> for Edition {
@@ -146,11 +148,12 @@ impl From<EditionData> for Edition {
146 match data { 148 match data {
147 EditionData::Edition2015 => Edition::Edition2015, 149 EditionData::Edition2015 => Edition::Edition2015,
148 EditionData::Edition2018 => Edition::Edition2018, 150 EditionData::Edition2018 => Edition::Edition2018,
151 EditionData::Edition2021 => Edition::Edition2021,
149 } 152 }
150 } 153 }
151} 154}
152 155
153#[derive(Deserialize)] 156#[derive(Deserialize, Debug, Clone)]
154struct DepData { 157struct DepData {
155 /// Identifies a crate by position in the crates array. 158 /// Identifies a crate by position in the crates array.
156 #[serde(rename = "crate")] 159 #[serde(rename = "crate")]
@@ -159,7 +162,7 @@ struct DepData {
159 name: CrateName, 162 name: CrateName,
160} 163}
161 164
162#[derive(Deserialize)] 165#[derive(Deserialize, Debug, Clone)]
163struct CrateSource { 166struct CrateSource {
164 include_dirs: Vec<PathBuf>, 167 include_dirs: Vec<PathBuf>,
165 exclude_dirs: Vec<PathBuf>, 168 exclude_dirs: Vec<PathBuf>,
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 68a235ce3..06a0be284 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -64,7 +64,11 @@ impl fmt::Debug for ProjectWorkspace {
64} 64}
65 65
66impl ProjectWorkspace { 66impl ProjectWorkspace {
67 pub fn load(manifest: ProjectManifest, config: &CargoConfig) -> Result<ProjectWorkspace> { 67 pub fn load(
68 manifest: ProjectManifest,
69 config: &CargoConfig,
70 progress: &dyn Fn(String),
71 ) -> Result<ProjectWorkspace> {
68 let res = match manifest { 72 let res = match manifest {
69 ProjectManifest::ProjectJson(project_json) => { 73 ProjectManifest::ProjectJson(project_json) => {
70 let file = fs::read_to_string(&project_json).with_context(|| { 74 let file = fs::read_to_string(&project_json).with_context(|| {
@@ -84,15 +88,14 @@ impl ProjectWorkspace {
84 cmd 88 cmd
85 })?; 89 })?;
86 90
87 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, config).with_context( 91 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, config, progress)
88 || { 92 .with_context(|| {
89 format!( 93 format!(
90 "Failed to read Cargo metadata from Cargo.toml file {}, {}", 94 "Failed to read Cargo metadata from Cargo.toml file {}, {}",
91 cargo_toml.display(), 95 cargo_toml.display(),
92 cargo_version 96 cargo_version
93 ) 97 )
94 }, 98 })?;
95 )?;
96 let sysroot = if config.no_sysroot { 99 let sysroot = if config.no_sysroot {
97 Sysroot::default() 100 Sysroot::default()
98 } else { 101 } else {
@@ -105,9 +108,12 @@ impl ProjectWorkspace {
105 }; 108 };
106 109
107 let rustc = if let Some(rustc_dir) = &config.rustc_source { 110 let rustc = if let Some(rustc_dir) = &config.rustc_source {
108 Some(CargoWorkspace::from_cargo_metadata(&rustc_dir, config).with_context( 111 Some(
109 || format!("Failed to read Cargo metadata for Rust sources"), 112 CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress)
110 )?) 113 .with_context(|| {
114 format!("Failed to read Cargo metadata for Rust sources")
115 })?,
116 )
111 } else { 117 } else {
112 None 118 None
113 }; 119 };
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 53e70eaf7..af7b86ead 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -17,8 +17,9 @@ path = "src/bin/main.rs"
17[dependencies] 17[dependencies]
18anyhow = "1.0.26" 18anyhow = "1.0.26"
19crossbeam-channel = "0.5.0" 19crossbeam-channel = "0.5.0"
20dissimilar = "1.0.2"
20env_logger = { version = "0.8.1", default-features = false } 21env_logger = { version = "0.8.1", default-features = false }
21itertools = "0.9.0" 22itertools = "0.10.0"
22jod-thread = "0.1.0" 23jod-thread = "0.1.0"
23log = "0.4.8" 24log = "0.4.8"
24lsp-types = { version = "0.86.0", features = ["proposed"] } 25lsp-types = { version = "0.86.0", features = ["proposed"] }
@@ -61,7 +62,7 @@ proc_macro_srv = { path = "../proc_macro_srv", version = "0.0.0" }
61winapi = "0.3.8" 62winapi = "0.3.8"
62 63
63[dev-dependencies] 64[dev-dependencies]
64expect-test = "1.0" 65expect-test = "1.1"
65test_utils = { path = "../test_utils" } 66test_utils = { path = "../test_utils" }
66mbe = { path = "../mbe" } 67mbe = { path = "../mbe" }
67tt = { path = "../tt" } 68tt = { path = "../tt" }
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index defdcbd74..3af3c59d8 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -8,11 +8,7 @@ use std::{convert::TryFrom, env, fs, path::PathBuf, process};
8 8
9use lsp_server::Connection; 9use lsp_server::Connection;
10use project_model::ProjectManifest; 10use project_model::ProjectManifest;
11use rust_analyzer::{ 11use rust_analyzer::{cli, config::Config, from_json, Result};
12 cli,
13 config::{Config, LinkedProject},
14 from_json, Result,
15};
16use vfs::AbsPathBuf; 12use vfs::AbsPathBuf;
17 13
18#[cfg(all(feature = "mimalloc"))] 14#[cfg(all(feature = "mimalloc"))]
@@ -138,13 +134,12 @@ fn run_server() -> Result<()> {
138 } 134 }
139 }; 135 };
140 136
141 let mut config = Config::new(root_path); 137 let mut config = Config::new(root_path, initialize_params.capabilities);
142 if let Some(json) = initialize_params.initialization_options { 138 if let Some(json) = initialize_params.initialization_options {
143 config.update(json); 139 config.update(json);
144 } 140 }
145 config.update_caps(&initialize_params.capabilities);
146 141
147 if config.linked_projects.is_empty() { 142 if config.linked_projects().is_empty() {
148 let workspace_roots = initialize_params 143 let workspace_roots = initialize_params
149 .workspace_folders 144 .workspace_folders
150 .map(|workspaces| { 145 .map(|workspaces| {
@@ -163,7 +158,7 @@ fn run_server() -> Result<()> {
163 log::error!("failed to find any projects in {:?}", workspace_roots); 158 log::error!("failed to find any projects in {:?}", workspace_roots);
164 } 159 }
165 160
166 config.linked_projects = discovered.into_iter().map(LinkedProject::from).collect(); 161 config.discovered_projects = Some(discovered);
167 } 162 }
168 163
169 config 164 config
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 80e46bf7f..f148521a2 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -1,7 +1,6 @@
1//! Advertizes the capabilities of the LSP Server. 1//! Advertizes the capabilities of the LSP Server.
2use std::env; 2use std::env;
3 3
4use ide::CompletionResolveCapability;
5use lsp_types::{ 4use lsp_types::{
6 CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions, 5 CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
7 CodeActionProviderCapability, CodeLensOptions, CompletionOptions, 6 CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
@@ -14,7 +13,6 @@ use lsp_types::{
14 WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities, 13 WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities,
15 WorkspaceServerCapabilities, 14 WorkspaceServerCapabilities,
16}; 15};
17use rustc_hash::FxHashSet;
18use serde_json::json; 16use serde_json::json;
19 17
20use crate::semantic_tokens; 18use crate::semantic_tokens;
@@ -77,14 +75,24 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
77 will_create: None, 75 will_create: None,
78 did_rename: None, 76 did_rename: None,
79 will_rename: Some(FileOperationRegistrationOptions { 77 will_rename: Some(FileOperationRegistrationOptions {
80 filters: vec![FileOperationFilter { 78 filters: vec![
81 scheme: Some(String::from("file")), 79 FileOperationFilter {
82 pattern: FileOperationPattern { 80 scheme: Some(String::from("file")),
83 glob: String::from("**/*.rs"), 81 pattern: FileOperationPattern {
84 matches: Some(FileOperationPatternKind::File), 82 glob: String::from("**/*.rs"),
85 options: None, 83 matches: Some(FileOperationPatternKind::File),
84 options: None,
85 },
86 }, 86 },
87 }], 87 FileOperationFilter {
88 scheme: Some(String::from("file")),
89 pattern: FileOperationPattern {
90 glob: String::from("**"),
91 matches: Some(FileOperationPatternKind::Folder),
92 options: None,
93 },
94 },
95 ],
88 }), 96 }),
89 did_delete: None, 97 did_delete: None,
90 will_delete: None, 98 will_delete: None,
@@ -118,37 +126,31 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
118} 126}
119 127
120fn completions_resolve_provider(client_caps: &ClientCapabilities) -> Option<bool> { 128fn completions_resolve_provider(client_caps: &ClientCapabilities) -> Option<bool> {
121 if enabled_completions_resolve_capabilities(client_caps)?.is_empty() { 129 if completion_item_edit_resolve(client_caps) {
130 Some(true)
131 } else {
122 log::info!("No `additionalTextEdits` completion resolve capability was found in the client capabilities, autoimport completion is disabled"); 132 log::info!("No `additionalTextEdits` completion resolve capability was found in the client capabilities, autoimport completion is disabled");
123 None 133 None
124 } else {
125 Some(true)
126 } 134 }
127} 135}
128 136
129/// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports. 137/// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports.
130pub(crate) fn enabled_completions_resolve_capabilities( 138pub(crate) fn completion_item_edit_resolve(caps: &ClientCapabilities) -> bool {
131 caps: &ClientCapabilities, 139 (|| {
132) -> Option<FxHashSet<CompletionResolveCapability>> { 140 Some(
133 Some( 141 caps.text_document
134 caps.text_document 142 .as_ref()?
135 .as_ref()? 143 .completion
136 .completion 144 .as_ref()?
137 .as_ref()? 145 .completion_item
138 .completion_item 146 .as_ref()?
139 .as_ref()? 147 .resolve_support
140 .resolve_support 148 .as_ref()?
141 .as_ref()? 149 .properties
142 .properties 150 .iter()
143 .iter() 151 .any(|cap_string| cap_string.as_str() == "additionalTextEdits"),
144 .filter_map(|cap_string| match cap_string.as_str() { 152 )
145 "additionalTextEdits" => Some(CompletionResolveCapability::AdditionalTextEdits), 153 })() == Some(true)
146 "detail" => Some(CompletionResolveCapability::Detail),
147 "documentation" => Some(CompletionResolveCapability::Documentation),
148 _unsupported => None,
149 })
150 .collect(),
151 )
152} 154}
153 155
154fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability { 156fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability {
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 8a8b4a32c..5af0802a2 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -84,14 +84,15 @@ impl CargoTargetSpec {
84 } 84 }
85 } 85 }
86 86
87 if snap.config.cargo.all_features { 87 let cargo_config = snap.config.cargo();
88 if cargo_config.all_features {
88 args.push("--all-features".to_string()); 89 args.push("--all-features".to_string());
89 } else { 90 } else {
90 let mut features = Vec::new(); 91 let mut features = Vec::new();
91 if let Some(cfg) = cfg.as_ref() { 92 if let Some(cfg) = cfg.as_ref() {
92 required_features(cfg, &mut features); 93 required_features(cfg, &mut features);
93 } 94 }
94 for feature in &snap.config.cargo.features { 95 for feature in cargo_config.features {
95 features.push(feature.clone()); 96 features.push(feature.clone());
96 } 97 }
97 features.dedup(); 98 features.dedup();
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 5a8484c62..7d3fda7a8 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -6,9 +6,12 @@ use anyhow::{bail, format_err, Result};
6use ide::{ 6use ide::{
7 Analysis, AnalysisHost, Change, CompletionConfig, DiagnosticsConfig, FilePosition, LineCol, 7 Analysis, AnalysisHost, Change, CompletionConfig, DiagnosticsConfig, FilePosition, LineCol,
8}; 8};
9use ide_db::base_db::{ 9use ide_db::{
10 salsa::{Database, Durability}, 10 base_db::{
11 FileId, 11 salsa::{Database, Durability},
12 FileId,
13 },
14 helpers::SnippetCap,
12}; 15};
13use vfs::AbsPathBuf; 16use vfs::AbsPathBuf;
14 17
@@ -87,7 +90,14 @@ impl BenchCmd {
87 let file_position = FilePosition { file_id, offset }; 90 let file_position = FilePosition { file_id, offset };
88 91
89 if is_completion { 92 if is_completion {
90 let options = CompletionConfig::default(); 93 let options = CompletionConfig {
94 enable_postfix_completions: true,
95 enable_autoimport_completions: true,
96 add_call_parenthesis: true,
97 add_call_argument_snippets: true,
98 snippet_cap: SnippetCap::new(true),
99 merge: None,
100 };
91 let res = do_work(&mut host, file_id, |analysis| { 101 let res = do_work(&mut host, file_id, |analysis| {
92 analysis.completions(&options, file_position) 102 analysis.completions(&options, file_position)
93 }); 103 });
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index a23fb7a33..fd1407e60 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -58,7 +58,7 @@ impl AnalysisStatsCmd {
58 let mut db_load_sw = self.stop_watch(); 58 let mut db_load_sw = self.stop_watch();
59 let (host, vfs) = load_cargo(&self.path, self.load_output_dirs, self.with_proc_macro)?; 59 let (host, vfs) = load_cargo(&self.path, self.load_output_dirs, self.with_proc_macro)?;
60 let db = host.raw_database(); 60 let db = host.raw_database();
61 eprintln!("Database loaded {}", db_load_sw.elapsed()); 61 eprintln!("{:<20} {}", "Database loaded:", db_load_sw.elapsed());
62 62
63 let mut analysis_sw = self.stop_watch(); 63 let mut analysis_sw = self.stop_watch();
64 let mut num_crates = 0; 64 let mut num_crates = 0;
@@ -85,7 +85,7 @@ impl AnalysisStatsCmd {
85 shuffle(&mut rng, &mut visit_queue); 85 shuffle(&mut rng, &mut visit_queue);
86 } 86 }
87 87
88 eprintln!("Crates in this dir: {}", num_crates); 88 eprint!(" crates: {}", num_crates);
89 let mut num_decls = 0; 89 let mut num_decls = 0;
90 let mut funcs = Vec::new(); 90 let mut funcs = Vec::new();
91 while let Some(module) = visit_queue.pop() { 91 while let Some(module) = visit_queue.pop() {
@@ -109,10 +109,8 @@ impl AnalysisStatsCmd {
109 } 109 }
110 } 110 }
111 } 111 }
112 eprintln!("Total modules found: {}", visited_modules.len()); 112 eprintln!(", mods: {}, decls: {}, fns: {}", visited_modules.len(), num_decls, funcs.len());
113 eprintln!("Total declarations: {}", num_decls); 113 eprintln!("{:<20} {}", "Item Collection:", analysis_sw.elapsed());
114 eprintln!("Total functions: {}", funcs.len());
115 eprintln!("Item Collection: {}", analysis_sw.elapsed());
116 114
117 if self.randomize { 115 if self.randomize {
118 shuffle(&mut rng, &mut funcs); 116 shuffle(&mut rng, &mut funcs);
@@ -135,7 +133,7 @@ impl AnalysisStatsCmd {
135 snap.0.infer(f_id.into()); 133 snap.0.infer(f_id.into());
136 }) 134 })
137 .count(); 135 .count();
138 eprintln!("Parallel Inference: {}", inference_sw.elapsed()); 136 eprintln!("{:<20} {}", "Parallel Inference:", inference_sw.elapsed());
139 } 137 }
140 138
141 let mut inference_sw = self.stop_watch(); 139 let mut inference_sw = self.stop_watch();
@@ -161,11 +159,12 @@ impl AnalysisStatsCmd {
161 } 159 }
162 let mut msg = format!("processing: {}", full_name); 160 let mut msg = format!("processing: {}", full_name);
163 if verbosity.is_verbose() { 161 if verbosity.is_verbose() {
164 let src = f.source(db); 162 if let Some(src) = f.source(db) {
165 let original_file = src.file_id.original_file(db); 163 let original_file = src.file_id.original_file(db);
166 let path = vfs.file_path(original_file); 164 let path = vfs.file_path(original_file);
167 let syntax_range = src.value.syntax().text_range(); 165 let syntax_range = src.value.syntax().text_range();
168 format_to!(msg, " ({} {:?})", path, syntax_range); 166 format_to!(msg, " ({} {:?})", path, syntax_range);
167 }
169 } 168 }
170 if verbosity.is_spammy() { 169 if verbosity.is_spammy() {
171 bar.println(msg.to_string()); 170 bar.println(msg.to_string());
@@ -272,27 +271,22 @@ impl AnalysisStatsCmd {
272 bar.inc(1); 271 bar.inc(1);
273 } 272 }
274 bar.finish_and_clear(); 273 bar.finish_and_clear();
275 eprintln!("Total expressions: {}", num_exprs);
276 eprintln!( 274 eprintln!(
277 "Expressions of unknown type: {} ({}%)", 275 " exprs: {}, ??ty: {} ({}%), ?ty: {} ({}%), !ty: {}",
276 num_exprs,
278 num_exprs_unknown, 277 num_exprs_unknown,
279 if num_exprs > 0 { num_exprs_unknown * 100 / num_exprs } else { 100 } 278 percentage(num_exprs_unknown, num_exprs),
280 );
281 report_metric("unknown type", num_exprs_unknown, "#");
282
283 eprintln!(
284 "Expressions of partially unknown type: {} ({}%)",
285 num_exprs_partially_unknown, 279 num_exprs_partially_unknown,
286 if num_exprs > 0 { num_exprs_partially_unknown * 100 / num_exprs } else { 100 } 280 percentage(num_exprs_partially_unknown, num_exprs),
281 num_type_mismatches
287 ); 282 );
288 283 report_metric("unknown type", num_exprs_unknown, "#");
289 eprintln!("Type mismatches: {}", num_type_mismatches);
290 report_metric("type mismatches", num_type_mismatches, "#"); 284 report_metric("type mismatches", num_type_mismatches, "#");
291 285
292 eprintln!("Inference: {}", inference_sw.elapsed()); 286 eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed());
293 287
294 let total_span = analysis_sw.elapsed(); 288 let total_span = analysis_sw.elapsed();
295 eprintln!("Total: {}", total_span); 289 eprintln!("{:<20} {}", "Total:", total_span);
296 report_metric("total time", total_span.time.as_millis() as u64, "ms"); 290 report_metric("total time", total_span.time.as_millis() as u64, "ms");
297 if let Some(instructions) = total_span.instructions { 291 if let Some(instructions) = total_span.instructions {
298 report_metric("total instructions", instructions, "#instr"); 292 report_metric("total instructions", instructions, "#instr");
@@ -301,7 +295,7 @@ impl AnalysisStatsCmd {
301 report_metric("total memory", memory.allocated.megabytes() as u64, "MB"); 295 report_metric("total memory", memory.allocated.megabytes() as u64, "MB");
302 } 296 }
303 297
304 if self.memory_usage { 298 if self.memory_usage && verbosity.is_verbose() {
305 print_memory_usage(host, vfs); 299 print_memory_usage(host, vfs);
306 } 300 }
307 301
@@ -324,3 +318,7 @@ fn shuffle<T>(rng: &mut Rand32, slice: &mut [T]) {
324 slice.swap(0, idx); 318 slice.swap(0, idx);
325 } 319 }
326} 320}
321
322fn percentage(n: u64, total: u64) -> u64 {
323 (n * 100).checked_div(total).unwrap_or(100)
324}
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index e5ab6c73b..31a16ca46 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -21,6 +21,7 @@ pub fn load_cargo(
21 let ws = ProjectWorkspace::load( 21 let ws = ProjectWorkspace::load(
22 root, 22 root,
23 &CargoConfig { load_out_dirs_from_check, ..Default::default() }, 23 &CargoConfig { load_out_dirs_from_check, ..Default::default() },
24 &|_| {},
24 )?; 25 )?;
25 26
26 let (sender, receiver) = unbounded(); 27 let (sender, receiver) = unbounded();
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 1db5b4e7d..27b92a5a9 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -7,12 +7,15 @@
7//! configure the server itself, feature flags are passed into analysis, and 7//! configure the server itself, feature flags are passed into analysis, and
8//! tweak things like automatic insertion of `()` in completions. 8//! tweak things like automatic insertion of `()` in completions.
9 9
10use std::{convert::TryFrom, ffi::OsString, path::PathBuf}; 10use std::{convert::TryFrom, ffi::OsString, iter, path::PathBuf};
11 11
12use flycheck::FlycheckConfig; 12use flycheck::FlycheckConfig;
13use hir::PrefixKind; 13use hir::PrefixKind;
14use ide::{AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig}; 14use ide::{
15use ide_db::helpers::insert_use::MergeBehavior; 15 AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig,
16 InsertUseConfig,
17};
18use ide_db::helpers::{insert_use::MergeBehavior, SnippetCap};
16use itertools::Itertools; 19use itertools::Itertools;
17use lsp_types::{ClientCapabilities, MarkupKind}; 20use lsp_types::{ClientCapabilities, MarkupKind};
18use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; 21use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest};
@@ -20,12 +23,13 @@ use rustc_hash::FxHashSet;
20use serde::{de::DeserializeOwned, Deserialize}; 23use serde::{de::DeserializeOwned, Deserialize};
21use vfs::AbsPathBuf; 24use vfs::AbsPathBuf;
22 25
23use crate::{caps::enabled_completions_resolve_capabilities, diagnostics::DiagnosticsMapConfig}; 26use crate::{caps::completion_item_edit_resolve, diagnostics::DiagnosticsMapConfig};
24 27
25config_data! { 28config_data! {
26 struct ConfigData { 29 struct ConfigData {
27 /// The strategy to use when inserting new imports or merging imports. 30 /// The strategy to use when inserting new imports or merging imports.
28 assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"", 31 assist_importMergeBehavior |
32 assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"",
29 /// The path structure for newly inserted paths to use. 33 /// The path structure for newly inserted paths to use.
30 assist_importPrefix: ImportPrefixDef = "\"plain\"", 34 assist_importPrefix: ImportPrefixDef = "\"plain\"",
31 35
@@ -148,13 +152,19 @@ config_data! {
148 /// of projects.\n\nElements must be paths pointing to `Cargo.toml`, 152 /// of projects.\n\nElements must be paths pointing to `Cargo.toml`,
149 /// `rust-project.json`, or JSON objects in `rust-project.json` format. 153 /// `rust-project.json`, or JSON objects in `rust-project.json` format.
150 linkedProjects: Vec<ManifestOrProjectJson> = "[]", 154 linkedProjects: Vec<ManifestOrProjectJson> = "[]",
155
151 /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. 156 /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
152 lruCapacity: Option<usize> = "null", 157 lruCapacity: Option<usize> = "null",
158
153 /// Whether to show `can't find Cargo.toml` error message. 159 /// Whether to show `can't find Cargo.toml` error message.
154 notifications_cargoTomlNotFound: bool = "true", 160 notifications_cargoTomlNotFound: bool = "true",
161
155 /// Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be 162 /// Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be
156 /// enabled. 163 /// enabled.
157 procMacro_enable: bool = "false", 164 procMacro_enable: bool = "false",
165 /// Internal config, path to proc-macro server executable (typically,
166 /// this is rust-analyzer itself, but we override this in tests).
167 procMacro_server: Option<PathBuf> = "null",
158 168
159 /// Command to be executed instead of 'cargo' for runnables. 169 /// Command to be executed instead of 'cargo' for runnables.
160 runnables_overrideCargo: Option<String> = "null", 170 runnables_overrideCargo: Option<String> = "null",
@@ -163,7 +173,7 @@ config_data! {
163 runnables_cargoExtraArgs: Vec<String> = "[]", 173 runnables_cargoExtraArgs: Vec<String> = "[]",
164 174
165 /// Path to the rust compiler sources, for usage in rustc_private projects. 175 /// Path to the rust compiler sources, for usage in rustc_private projects.
166 rustcSource : Option<String> = "null", 176 rustcSource : Option<PathBuf> = "null",
167 177
168 /// Additional arguments to `rustfmt`. 178 /// Additional arguments to `rustfmt`.
169 rustfmt_extraArgs: Vec<String> = "[]", 179 rustfmt_extraArgs: Vec<String> = "[]",
@@ -173,34 +183,17 @@ config_data! {
173 } 183 }
174} 184}
175 185
186impl Default for ConfigData {
187 fn default() -> Self {
188 ConfigData::from_json(serde_json::Value::Null)
189 }
190}
191
176#[derive(Debug, Clone)] 192#[derive(Debug, Clone)]
177pub struct Config { 193pub struct Config {
178 pub client_caps: ClientCapsConfig, 194 caps: lsp_types::ClientCapabilities,
179 195 data: ConfigData,
180 pub publish_diagnostics: bool, 196 pub discovered_projects: Option<Vec<ProjectManifest>>,
181 pub diagnostics: DiagnosticsConfig,
182 pub diagnostics_map: DiagnosticsMapConfig,
183 pub lru_capacity: Option<usize>,
184 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
185 pub files: FilesConfig,
186 pub notifications: NotificationsConfig,
187
188 pub cargo_autoreload: bool,
189 pub cargo: CargoConfig,
190 pub rustfmt: RustfmtConfig,
191 pub flycheck: Option<FlycheckConfig>,
192 pub runnables: RunnablesConfig,
193
194 pub inlay_hints: InlayHintsConfig,
195 pub completion: CompletionConfig,
196 pub assist: AssistConfig,
197 pub call_info_full: bool,
198 pub lens: LensConfig,
199 pub hover: HoverConfig,
200 pub semantic_tokens_refresh: bool,
201 pub code_lens_refresh: bool,
202
203 pub linked_projects: Vec<LinkedProject>,
204 pub root_path: AbsPathBuf, 197 pub root_path: AbsPathBuf,
205} 198}
206 199
@@ -230,12 +223,6 @@ pub struct LensConfig {
230 pub method_refs: bool, 223 pub method_refs: bool,
231} 224}
232 225
233impl Default for LensConfig {
234 fn default() -> Self {
235 Self { run: true, debug: true, implementations: true, method_refs: false }
236 }
237}
238
239impl LensConfig { 226impl LensConfig {
240 pub fn any(&self) -> bool { 227 pub fn any(&self) -> bool {
241 self.implementations || self.runnable() || self.references() 228 self.implementations || self.runnable() || self.references()
@@ -278,7 +265,7 @@ pub enum RustfmtConfig {
278} 265}
279 266
280/// Configuration for runnable items, such as `main` function or tests. 267/// Configuration for runnable items, such as `main` function or tests.
281#[derive(Debug, Clone, Default)] 268#[derive(Debug, Clone)]
282pub struct RunnablesConfig { 269pub struct RunnablesConfig {
283 /// Custom command to be executed instead of `cargo` for runnables. 270 /// Custom command to be executed instead of `cargo` for runnables.
284 pub override_cargo: Option<String>, 271 pub override_cargo: Option<String>,
@@ -286,325 +273,366 @@ pub struct RunnablesConfig {
286 pub cargo_extra_args: Vec<String>, 273 pub cargo_extra_args: Vec<String>,
287} 274}
288 275
289#[derive(Debug, Clone, Default)]
290pub struct ClientCapsConfig {
291 pub location_link: bool,
292 pub line_folding_only: bool,
293 pub hierarchical_symbols: bool,
294 pub code_action_literals: bool,
295 pub work_done_progress: bool,
296 pub code_action_group: bool,
297 pub code_action_resolve: bool,
298 pub hover_actions: bool,
299 pub status_notification: bool,
300 pub signature_help_label_offsets: bool,
301}
302
303impl Config { 276impl Config {
304 pub fn new(root_path: AbsPathBuf) -> Self { 277 pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self {
305 // Defaults here don't matter, we'll immediately re-write them with 278 Config { caps, data: ConfigData::default(), discovered_projects: None, root_path }
306 // ConfigData.
307 let mut res = Config {
308 client_caps: ClientCapsConfig::default(),
309
310 publish_diagnostics: false,
311 diagnostics: DiagnosticsConfig::default(),
312 diagnostics_map: DiagnosticsMapConfig::default(),
313 lru_capacity: None,
314 proc_macro_srv: None,
315 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
316 notifications: NotificationsConfig { cargo_toml_not_found: false },
317
318 cargo_autoreload: false,
319 cargo: CargoConfig::default(),
320 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
321 flycheck: Some(FlycheckConfig::CargoCommand {
322 command: String::new(),
323 target_triple: None,
324 no_default_features: false,
325 all_targets: false,
326 all_features: false,
327 extra_args: Vec::new(),
328 features: Vec::new(),
329 }),
330 runnables: RunnablesConfig::default(),
331
332 inlay_hints: InlayHintsConfig {
333 type_hints: false,
334 parameter_hints: false,
335 chaining_hints: false,
336 max_length: None,
337 },
338 completion: CompletionConfig::default(),
339 assist: AssistConfig::default(),
340 call_info_full: false,
341 lens: LensConfig::default(),
342 hover: HoverConfig::default(),
343 semantic_tokens_refresh: false,
344 code_lens_refresh: false,
345 linked_projects: Vec::new(),
346 root_path,
347 };
348 res.do_update(serde_json::json!({}));
349 res
350 } 279 }
351 pub fn update(&mut self, json: serde_json::Value) { 280 pub fn update(&mut self, json: serde_json::Value) {
352 log::info!("updating config from JSON: {:#}", json); 281 log::info!("updating config from JSON: {:#}", json);
353 if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) { 282 if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
354 return; 283 return;
355 } 284 }
356 self.do_update(json); 285 self.data = ConfigData::from_json(json);
357 log::info!("updated config: {:#?}", self);
358 } 286 }
359 fn do_update(&mut self, json: serde_json::Value) {
360 let data = ConfigData::from_json(json);
361
362 self.publish_diagnostics = data.diagnostics_enable;
363 self.diagnostics = DiagnosticsConfig {
364 disable_experimental: !data.diagnostics_enableExperimental,
365 disabled: data.diagnostics_disabled,
366 };
367 self.diagnostics_map = DiagnosticsMapConfig {
368 warnings_as_info: data.diagnostics_warningsAsInfo,
369 warnings_as_hint: data.diagnostics_warningsAsHint,
370 };
371 self.lru_capacity = data.lruCapacity;
372 self.files.watcher = match data.files_watcher.as_str() {
373 "notify" => FilesWatcher::Notify,
374 "client" | _ => FilesWatcher::Client,
375 };
376 self.notifications =
377 NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound };
378 self.cargo_autoreload = data.cargo_autoreload;
379
380 let rustc_source = if let Some(rustc_source) = data.rustcSource {
381 let rustpath: PathBuf = rustc_source.into();
382 AbsPathBuf::try_from(rustpath)
383 .map_err(|_| {
384 log::error!("rustc source directory must be an absolute path");
385 })
386 .ok()
387 } else {
388 None
389 };
390
391 self.cargo = CargoConfig {
392 no_default_features: data.cargo_noDefaultFeatures,
393 all_features: data.cargo_allFeatures,
394 features: data.cargo_features.clone(),
395 load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck,
396 target: data.cargo_target.clone(),
397 rustc_source: rustc_source,
398 no_sysroot: data.cargo_noSysroot,
399 };
400 self.runnables = RunnablesConfig {
401 override_cargo: data.runnables_overrideCargo,
402 cargo_extra_args: data.runnables_cargoExtraArgs,
403 };
404 287
405 self.proc_macro_srv = if data.procMacro_enable { 288 pub fn json_schema() -> serde_json::Value {
406 std::env::current_exe().ok().map(|path| (path, vec!["proc-macro".into()])) 289 ConfigData::json_schema()
407 } else { 290 }
408 None 291}
409 };
410 292
411 self.rustfmt = match data.rustfmt_overrideCommand { 293macro_rules! try_ {
412 Some(mut args) if !args.is_empty() => { 294 ($expr:expr) => {
413 let command = args.remove(0); 295 || -> _ { Some($expr) }()
414 RustfmtConfig::CustomCommand { command, args } 296 };
415 } 297}
416 Some(_) | None => RustfmtConfig::Rustfmt { extra_args: data.rustfmt_extraArgs }, 298macro_rules! try_or {
417 }; 299 ($expr:expr, $or:expr) => {
300 try_!($expr).unwrap_or($or)
301 };
302}
418 303
419 self.flycheck = if data.checkOnSave_enable { 304impl Config {
420 let flycheck_config = match data.checkOnSave_overrideCommand { 305 pub fn linked_projects(&self) -> Vec<LinkedProject> {
421 Some(mut args) if !args.is_empty() => { 306 if self.data.linkedProjects.is_empty() {
422 let command = args.remove(0); 307 self.discovered_projects
423 FlycheckConfig::CustomCommand { command, args } 308 .as_ref()
424 } 309 .into_iter()
425 Some(_) | None => FlycheckConfig::CargoCommand { 310 .flatten()
426 command: data.checkOnSave_command, 311 .cloned()
427 target_triple: data.checkOnSave_target.or(data.cargo_target), 312 .map(LinkedProject::from)
428 all_targets: data.checkOnSave_allTargets, 313 .collect()
429 no_default_features: data
430 .checkOnSave_noDefaultFeatures
431 .unwrap_or(data.cargo_noDefaultFeatures),
432 all_features: data.checkOnSave_allFeatures.unwrap_or(data.cargo_allFeatures),
433 features: data.checkOnSave_features.unwrap_or(data.cargo_features),
434 extra_args: data.checkOnSave_extraArgs,
435 },
436 };
437 Some(flycheck_config)
438 } else { 314 } else {
439 None 315 self.data
440 }; 316 .linkedProjects
441 317 .iter()
442 self.inlay_hints = InlayHintsConfig { 318 .filter_map(|linked_project| {
443 type_hints: data.inlayHints_typeHints, 319 let res = match linked_project {
444 parameter_hints: data.inlayHints_parameterHints, 320 ManifestOrProjectJson::Manifest(it) => {
445 chaining_hints: data.inlayHints_chainingHints, 321 let path = self.root_path.join(it);
446 max_length: data.inlayHints_maxLength, 322 ProjectManifest::from_manifest_file(path)
447 }; 323 .map_err(|e| log::error!("failed to load linked project: {}", e))
448 324 .ok()?
449 self.assist.insert_use.merge = match data.assist_importMergeBehaviour { 325 .into()
450 MergeBehaviorDef::None => None,
451 MergeBehaviorDef::Full => Some(MergeBehavior::Full),
452 MergeBehaviorDef::Last => Some(MergeBehavior::Last),
453 };
454 self.assist.insert_use.prefix_kind = match data.assist_importPrefix {
455 ImportPrefixDef::Plain => PrefixKind::Plain,
456 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
457 ImportPrefixDef::BySelf => PrefixKind::BySelf,
458 };
459
460 self.completion.enable_postfix_completions = data.completion_postfix_enable;
461 self.completion.enable_autoimport_completions = data.completion_autoimport_enable;
462 self.completion.add_call_parenthesis = data.completion_addCallParenthesis;
463 self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets;
464 self.completion.merge = self.assist.insert_use.merge;
465
466 self.call_info_full = data.callInfo_full;
467
468 self.lens = LensConfig {
469 run: data.lens_enable && data.lens_run,
470 debug: data.lens_enable && data.lens_debug,
471 implementations: data.lens_enable && data.lens_implementations,
472 method_refs: data.lens_enable && data.lens_methodReferences,
473 };
474
475 if !data.linkedProjects.is_empty() {
476 self.linked_projects.clear();
477 for linked_project in data.linkedProjects {
478 let linked_project = match linked_project {
479 ManifestOrProjectJson::Manifest(it) => {
480 let path = self.root_path.join(it);
481 match ProjectManifest::from_manifest_file(path) {
482 Ok(it) => it.into(),
483 Err(e) => {
484 log::error!("failed to load linked project: {}", e);
485 continue;
486 }
487 } 326 }
488 } 327 ManifestOrProjectJson::ProjectJson(it) => {
489 ManifestOrProjectJson::ProjectJson(it) => { 328 ProjectJson::new(&self.root_path, it.clone()).into()
490 ProjectJson::new(&self.root_path, it).into() 329 }
491 } 330 };
492 }; 331 Some(res)
493 self.linked_projects.push(linked_project); 332 })
494 } 333 .collect()
495 } 334 }
335 }
496 336
497 self.hover = HoverConfig { 337 pub fn did_save_text_document_dynamic_registration(&self) -> bool {
498 implementations: data.hoverActions_enable && data.hoverActions_implementations, 338 let caps =
499 run: data.hoverActions_enable && data.hoverActions_run, 339 try_or!(self.caps.text_document.as_ref()?.synchronization.clone()?, Default::default());
500 debug: data.hoverActions_enable && data.hoverActions_debug, 340 caps.did_save == Some(true) && caps.dynamic_registration == Some(true)
501 goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef, 341 }
502 links_in_hover: data.hoverActions_linksInHover, 342 pub fn did_change_watched_files_dynamic_registration(&self) -> bool {
503 markdown: true, 343 try_or!(
504 }; 344 self.caps.workspace.as_ref()?.did_change_watched_files.as_ref()?.dynamic_registration?,
345 false
346 )
505 } 347 }
506 348
507 pub fn update_caps(&mut self, caps: &ClientCapabilities) { 349 pub fn location_link(&self) -> bool {
508 if let Some(doc_caps) = caps.text_document.as_ref() { 350 try_or!(self.caps.text_document.as_ref()?.definition?.link_support?, false)
509 if let Some(value) = doc_caps.hover.as_ref().and_then(|it| it.content_format.as_ref()) { 351 }
510 self.hover.markdown = value.contains(&MarkupKind::Markdown) 352 pub fn line_folding_only(&self) -> bool {
511 } 353 try_or!(self.caps.text_document.as_ref()?.folding_range.as_ref()?.line_folding_only?, false)
512 if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) { 354 }
513 self.client_caps.location_link = value; 355 pub fn hierarchical_symbols(&self) -> bool {
514 } 356 try_or!(
515 if let Some(value) = doc_caps.folding_range.as_ref().and_then(|it| it.line_folding_only) 357 self.caps
516 { 358 .text_document
517 self.client_caps.line_folding_only = value 359 .as_ref()?
518 }
519 if let Some(value) = doc_caps
520 .document_symbol 360 .document_symbol
521 .as_ref() 361 .as_ref()?
522 .and_then(|it| it.hierarchical_document_symbol_support) 362 .hierarchical_document_symbol_support?,
523 { 363 false
524 self.client_caps.hierarchical_symbols = value 364 )
525 } 365 }
526 if let Some(value) = 366 pub fn code_action_literals(&self) -> bool {
527 doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some()) 367 try_!(self
528 { 368 .caps
529 self.client_caps.code_action_literals = value; 369 .text_document
530 } 370 .as_ref()?
531 if let Some(value) = doc_caps 371 .code_action
372 .as_ref()?
373 .code_action_literal_support
374 .as_ref()?)
375 .is_some()
376 }
377 pub fn work_done_progress(&self) -> bool {
378 try_or!(self.caps.window.as_ref()?.work_done_progress?, false)
379 }
380 pub fn code_action_resolve(&self) -> bool {
381 try_or!(
382 self.caps
383 .text_document
384 .as_ref()?
385 .code_action
386 .as_ref()?
387 .resolve_support
388 .as_ref()?
389 .properties
390 .as_slice(),
391 &[]
392 )
393 .iter()
394 .any(|it| it == "edit")
395 }
396 pub fn signature_help_label_offsets(&self) -> bool {
397 try_or!(
398 self.caps
399 .text_document
400 .as_ref()?
532 .signature_help 401 .signature_help
533 .as_ref() 402 .as_ref()?
534 .and_then(|it| it.signature_information.as_ref()) 403 .signature_information
535 .and_then(|it| it.parameter_information.as_ref()) 404 .as_ref()?
536 .and_then(|it| it.label_offset_support) 405 .parameter_information
537 { 406 .as_ref()?
538 self.client_caps.signature_help_label_offsets = value; 407 .label_offset_support?,
539 } 408 false
409 )
410 }
540 411
541 self.completion.allow_snippets(false); 412 fn experimental(&self, index: &'static str) -> bool {
542 self.completion.active_resolve_capabilities = 413 try_or!(self.caps.experimental.as_ref()?.get(index)?.as_bool()?, false)
543 enabled_completions_resolve_capabilities(caps).unwrap_or_default(); 414 }
544 if let Some(completion) = &doc_caps.completion { 415 pub fn code_action_group(&self) -> bool {
545 if let Some(completion_item) = &completion.completion_item { 416 self.experimental("codeActionGroup")
546 if let Some(value) = completion_item.snippet_support { 417 }
547 self.completion.allow_snippets(value); 418 pub fn hover_actions(&self) -> bool {
548 } 419 self.experimental("hoverActions")
549 } 420 }
550 } 421 pub fn status_notification(&self) -> bool {
422 self.experimental("statusNotification")
423 }
551 424
552 if let Some(code_action) = &doc_caps.code_action { 425 pub fn publish_diagnostics(&self) -> bool {
553 if let Some(resolve_support) = &code_action.resolve_support { 426 self.data.diagnostics_enable
554 if resolve_support.properties.iter().any(|it| it == "edit") { 427 }
555 self.client_caps.code_action_resolve = true; 428 pub fn diagnostics(&self) -> DiagnosticsConfig {
556 } 429 DiagnosticsConfig {
557 } 430 disable_experimental: !self.data.diagnostics_enableExperimental,
558 } 431 disabled: self.data.diagnostics_disabled.clone(),
559 } 432 }
560 433 }
561 if let Some(window_caps) = caps.window.as_ref() { 434 pub fn diagnostics_map(&self) -> DiagnosticsMapConfig {
562 if let Some(value) = window_caps.work_done_progress { 435 DiagnosticsMapConfig {
563 self.client_caps.work_done_progress = value; 436 warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
564 } 437 warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
565 } 438 }
566 439 }
567 self.assist.allow_snippets(false); 440 pub fn lru_capacity(&self) -> Option<usize> {
568 if let Some(experimental) = &caps.experimental { 441 self.data.lruCapacity
569 let get_bool = 442 }
570 |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true); 443 pub fn proc_macro_srv(&self) -> Option<(PathBuf, Vec<OsString>)> {
571 444 if !self.data.procMacro_enable {
572 let snippet_text_edit = get_bool("snippetTextEdit"); 445 return None;
573 self.assist.allow_snippets(snippet_text_edit);
574
575 self.client_caps.code_action_group = get_bool("codeActionGroup");
576 self.client_caps.hover_actions = get_bool("hoverActions");
577 self.client_caps.status_notification = get_bool("statusNotification");
578 } 446 }
579 447
580 if let Some(workspace_caps) = caps.workspace.as_ref() { 448 let path = self.data.procMacro_server.clone().or_else(|| std::env::current_exe().ok())?;
581 if let Some(refresh_support) = 449 Some((path, vec!["proc-macro".into()]))
582 workspace_caps.semantic_tokens.as_ref().and_then(|it| it.refresh_support) 450 }
583 { 451 pub fn files(&self) -> FilesConfig {
584 self.semantic_tokens_refresh = refresh_support; 452 FilesConfig {
453 watcher: match self.data.files_watcher.as_str() {
454 "notify" => FilesWatcher::Notify,
455 "client" | _ => FilesWatcher::Client,
456 },
457 exclude: Vec::new(),
458 }
459 }
460 pub fn notifications(&self) -> NotificationsConfig {
461 NotificationsConfig { cargo_toml_not_found: self.data.notifications_cargoTomlNotFound }
462 }
463 pub fn cargo_autoreload(&self) -> bool {
464 self.data.cargo_autoreload
465 }
466 pub fn cargo(&self) -> CargoConfig {
467 let rustc_source = self.data.rustcSource.clone().and_then(|it| {
468 AbsPathBuf::try_from(it)
469 .map_err(|_| log::error!("rustc source directory must be an absolute path"))
470 .ok()
471 });
472
473 CargoConfig {
474 no_default_features: self.data.cargo_noDefaultFeatures,
475 all_features: self.data.cargo_allFeatures,
476 features: self.data.cargo_features.clone(),
477 load_out_dirs_from_check: self.data.cargo_loadOutDirsFromCheck,
478 target: self.data.cargo_target.clone(),
479 rustc_source,
480 no_sysroot: self.data.cargo_noSysroot,
481 }
482 }
483 pub fn rustfmt(&self) -> RustfmtConfig {
484 match &self.data.rustfmt_overrideCommand {
485 Some(args) if !args.is_empty() => {
486 let mut args = args.clone();
487 let command = args.remove(0);
488 RustfmtConfig::CustomCommand { command, args }
585 } 489 }
586 490 Some(_) | None => {
587 if let Some(refresh_support) = 491 RustfmtConfig::Rustfmt { extra_args: self.data.rustfmt_extraArgs.clone() }
588 workspace_caps.code_lens.as_ref().and_then(|it| it.refresh_support)
589 {
590 self.code_lens_refresh = refresh_support;
591 } 492 }
592 } 493 }
593 } 494 }
594 495 pub fn flycheck(&self) -> Option<FlycheckConfig> {
595 pub fn json_schema() -> serde_json::Value { 496 if !self.data.checkOnSave_enable {
596 ConfigData::json_schema() 497 return None;
498 }
499 let flycheck_config = match &self.data.checkOnSave_overrideCommand {
500 Some(args) if !args.is_empty() => {
501 let mut args = args.clone();
502 let command = args.remove(0);
503 FlycheckConfig::CustomCommand { command, args }
504 }
505 Some(_) | None => FlycheckConfig::CargoCommand {
506 command: self.data.checkOnSave_command.clone(),
507 target_triple: self
508 .data
509 .checkOnSave_target
510 .clone()
511 .or(self.data.cargo_target.clone()),
512 all_targets: self.data.checkOnSave_allTargets,
513 no_default_features: self
514 .data
515 .checkOnSave_noDefaultFeatures
516 .unwrap_or(self.data.cargo_noDefaultFeatures),
517 all_features: self
518 .data
519 .checkOnSave_allFeatures
520 .unwrap_or(self.data.cargo_allFeatures),
521 features: self
522 .data
523 .checkOnSave_features
524 .clone()
525 .unwrap_or(self.data.cargo_features.clone()),
526 extra_args: self.data.checkOnSave_extraArgs.clone(),
527 },
528 };
529 Some(flycheck_config)
530 }
531 pub fn runnables(&self) -> RunnablesConfig {
532 RunnablesConfig {
533 override_cargo: self.data.runnables_overrideCargo.clone(),
534 cargo_extra_args: self.data.runnables_cargoExtraArgs.clone(),
535 }
536 }
537 pub fn inlay_hints(&self) -> InlayHintsConfig {
538 InlayHintsConfig {
539 type_hints: self.data.inlayHints_typeHints,
540 parameter_hints: self.data.inlayHints_parameterHints,
541 chaining_hints: self.data.inlayHints_chainingHints,
542 max_length: self.data.inlayHints_maxLength,
543 }
544 }
545 fn merge_behavior(&self) -> Option<MergeBehavior> {
546 match self.data.assist_importMergeBehavior {
547 MergeBehaviorDef::None => None,
548 MergeBehaviorDef::Full => Some(MergeBehavior::Full),
549 MergeBehaviorDef::Last => Some(MergeBehavior::Last),
550 }
551 }
552 pub fn completion(&self) -> CompletionConfig {
553 CompletionConfig {
554 enable_postfix_completions: self.data.completion_postfix_enable,
555 enable_autoimport_completions: self.data.completion_autoimport_enable
556 && completion_item_edit_resolve(&self.caps),
557 add_call_parenthesis: self.data.completion_addCallParenthesis,
558 add_call_argument_snippets: self.data.completion_addCallArgumentSnippets,
559 merge: self.merge_behavior(),
560 snippet_cap: SnippetCap::new(try_or!(
561 self.caps
562 .text_document
563 .as_ref()?
564 .completion
565 .as_ref()?
566 .completion_item
567 .as_ref()?
568 .snippet_support?,
569 false
570 )),
571 }
572 }
573 pub fn assist(&self) -> AssistConfig {
574 AssistConfig {
575 snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
576 allowed: None,
577 insert_use: InsertUseConfig {
578 merge: self.merge_behavior(),
579 prefix_kind: match self.data.assist_importPrefix {
580 ImportPrefixDef::Plain => PrefixKind::Plain,
581 ImportPrefixDef::ByCrate => PrefixKind::ByCrate,
582 ImportPrefixDef::BySelf => PrefixKind::BySelf,
583 },
584 },
585 }
586 }
587 pub fn call_info_full(&self) -> bool {
588 self.data.callInfo_full
589 }
590 pub fn lens(&self) -> LensConfig {
591 LensConfig {
592 run: self.data.lens_enable && self.data.lens_run,
593 debug: self.data.lens_enable && self.data.lens_debug,
594 implementations: self.data.lens_enable && self.data.lens_implementations,
595 method_refs: self.data.lens_enable && self.data.lens_methodReferences,
596 }
597 }
598 pub fn hover(&self) -> HoverConfig {
599 HoverConfig {
600 implementations: self.data.hoverActions_enable
601 && self.data.hoverActions_implementations,
602 run: self.data.hoverActions_enable && self.data.hoverActions_run,
603 debug: self.data.hoverActions_enable && self.data.hoverActions_debug,
604 goto_type_def: self.data.hoverActions_enable && self.data.hoverActions_gotoTypeDef,
605 links_in_hover: self.data.hoverActions_linksInHover,
606 markdown: try_or!(
607 self.caps
608 .text_document
609 .as_ref()?
610 .hover
611 .as_ref()?
612 .content_format
613 .as_ref()?
614 .as_slice(),
615 &[]
616 )
617 .contains(&MarkupKind::Markdown),
618 }
619 }
620 pub fn semantic_tokens_refresh(&self) -> bool {
621 try_or!(self.caps.workspace.as_ref()?.semantic_tokens.as_ref()?.refresh_support?, false)
622 }
623 pub fn code_lens_refresh(&self) -> bool {
624 try_or!(self.caps.workspace.as_ref()?.code_lens.as_ref()?.refresh_support?, false)
597 } 625 }
598} 626}
599 627
600#[derive(Deserialize)] 628#[derive(Deserialize, Debug, Clone)]
601#[serde(untagged)] 629#[serde(untagged)]
602enum ManifestOrProjectJson { 630enum ManifestOrProjectJson {
603 Manifest(PathBuf), 631 Manifest(PathBuf),
604 ProjectJson(ProjectJsonData), 632 ProjectJson(ProjectJsonData),
605} 633}
606 634
607#[derive(Deserialize)] 635#[derive(Deserialize, Debug, Clone)]
608#[serde(rename_all = "snake_case")] 636#[serde(rename_all = "snake_case")]
609enum MergeBehaviorDef { 637enum MergeBehaviorDef {
610 None, 638 None,
@@ -612,7 +640,7 @@ enum MergeBehaviorDef {
612 Last, 640 Last,
613} 641}
614 642
615#[derive(Deserialize)] 643#[derive(Deserialize, Debug, Clone)]
616#[serde(rename_all = "snake_case")] 644#[serde(rename_all = "snake_case")]
617enum ImportPrefixDef { 645enum ImportPrefixDef {
618 Plain, 646 Plain,
@@ -624,15 +652,21 @@ macro_rules! _config_data {
624 (struct $name:ident { 652 (struct $name:ident {
625 $( 653 $(
626 $(#[doc=$doc:literal])* 654 $(#[doc=$doc:literal])*
627 $field:ident: $ty:ty = $default:expr, 655 $field:ident $(| $alias:ident)?: $ty:ty = $default:expr,
628 )* 656 )*
629 }) => { 657 }) => {
630 #[allow(non_snake_case)] 658 #[allow(non_snake_case)]
659 #[derive(Debug, Clone)]
631 struct $name { $($field: $ty,)* } 660 struct $name { $($field: $ty,)* }
632 impl $name { 661 impl $name {
633 fn from_json(mut json: serde_json::Value) -> $name { 662 fn from_json(mut json: serde_json::Value) -> $name {
634 $name {$( 663 $name {$(
635 $field: get_field(&mut json, stringify!($field), $default), 664 $field: get_field(
665 &mut json,
666 stringify!($field),
667 None$(.or(Some(stringify!($alias))))?,
668 $default,
669 ),
636 )*} 670 )*}
637 } 671 }
638 672
@@ -664,14 +698,21 @@ use _config_data as config_data;
664fn get_field<T: DeserializeOwned>( 698fn get_field<T: DeserializeOwned>(
665 json: &mut serde_json::Value, 699 json: &mut serde_json::Value,
666 field: &'static str, 700 field: &'static str,
701 alias: Option<&'static str>,
667 default: &str, 702 default: &str,
668) -> T { 703) -> T {
669 let default = serde_json::from_str(default).unwrap(); 704 let default = serde_json::from_str(default).unwrap();
670 705
671 let mut pointer = field.replace('_', "/"); 706 // XXX: check alias first, to work-around the VS Code where it pre-fills the
672 pointer.insert(0, '/'); 707 // defaults instead of sending an empty object.
673 json.pointer_mut(&pointer) 708 alias
674 .and_then(|it| serde_json::from_value(it.take()).ok()) 709 .into_iter()
710 .chain(iter::once(field))
711 .find_map(move |field| {
712 let mut pointer = field.replace('_', "/");
713 pointer.insert(0, '/');
714 json.pointer_mut(&pointer).and_then(|it| serde_json::from_value(it.take()).ok())
715 })
675 .unwrap_or(default) 716 .unwrap_or(default)
676} 717}
677 718
@@ -679,7 +720,7 @@ fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json:
679 for ((f1, ..), (f2, ..)) in fields.iter().zip(&fields[1..]) { 720 for ((f1, ..), (f2, ..)) in fields.iter().zip(&fields[1..]) {
680 fn key(f: &str) -> &str { 721 fn key(f: &str) -> &str {
681 f.splitn(2, "_").next().unwrap() 722 f.splitn(2, "_").next().unwrap()
682 }; 723 }
683 assert!(key(f1) <= key(f2), "wrong field order: {:?} {:?}", f1, f2); 724 assert!(key(f1) <= key(f2), "wrong field order: {:?} {:?}", f1, f2);
684 } 725 }
685 726
@@ -733,6 +774,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
733 "Option<String>" => set! { 774 "Option<String>" => set! {
734 "type": ["null", "string"], 775 "type": ["null", "string"],
735 }, 776 },
777 "Option<PathBuf>" => set! {
778 "type": ["null", "string"],
779 },
736 "Option<bool>" => set! { 780 "Option<bool>" => set! {
737 "type": ["null", "boolean"], 781 "type": ["null", "boolean"],
738 }, 782 },
@@ -777,9 +821,8 @@ fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String {
777 fields 821 fields
778 .iter() 822 .iter()
779 .map(|(field, _ty, doc, default)| { 823 .map(|(field, _ty, doc, default)| {
780 let name = field.replace("_", "."); 824 let name = format!("rust-analyzer.{}", field.replace("_", "."));
781 let name = format!("rust-analyzer.{} (default: `{}`)", name, default); 825 format!("[[{}]]{} (default: `{}`)::\n{}\n", name, name, default, doc.join(" "))
782 format!("{}::\n{}\n", name, doc.join(" "))
783 }) 826 })
784 .collect::<String>() 827 .collect::<String>()
785} 828}
diff --git a/crates/rust-analyzer/src/diff.rs b/crates/rust-analyzer/src/diff.rs
new file mode 100644
index 000000000..231be5807
--- /dev/null
+++ b/crates/rust-analyzer/src/diff.rs
@@ -0,0 +1,53 @@
1//! Generate minimal `TextEdit`s from different text versions
2use dissimilar::Chunk;
3use ide::{TextEdit, TextRange, TextSize};
4
5pub(crate) fn diff(left: &str, right: &str) -> TextEdit {
6 let chunks = dissimilar::diff(left, right);
7 textedit_from_chunks(chunks)
8}
9
10fn textedit_from_chunks(chunks: Vec<dissimilar::Chunk>) -> TextEdit {
11 let mut builder = TextEdit::builder();
12 let mut pos = TextSize::default();
13
14 let mut chunks = chunks.into_iter().peekable();
15 while let Some(chunk) = chunks.next() {
16 if let (Chunk::Delete(deleted), Some(&Chunk::Insert(inserted))) = (chunk, chunks.peek()) {
17 chunks.next().unwrap();
18 let deleted_len = TextSize::of(deleted);
19 builder.replace(TextRange::at(pos, deleted_len), inserted.into());
20 pos += deleted_len;
21 continue;
22 }
23
24 match chunk {
25 Chunk::Equal(text) => {
26 pos += TextSize::of(text);
27 }
28 Chunk::Delete(deleted) => {
29 let deleted_len = TextSize::of(deleted);
30 builder.delete(TextRange::at(pos, deleted_len));
31 pos += deleted_len;
32 }
33 Chunk::Insert(inserted) => {
34 builder.insert(pos, inserted.into());
35 }
36 }
37 }
38 builder.finish()
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn diff_applies() {
47 let mut original = String::from("fn foo(a:u32){\n}");
48 let result = "fn foo(a: u32) {}";
49 let edit = diff(&original, result);
50 edit.apply(&mut original);
51 assert_eq!(original, result);
52 }
53}
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 71dc56915..442fbd14c 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -22,6 +22,7 @@ use crate::{
22 from_proto, 22 from_proto,
23 line_endings::LineEndings, 23 line_endings::LineEndings,
24 main_loop::Task, 24 main_loop::Task,
25 op_queue::OpQueue,
25 reload::SourceRootConfig, 26 reload::SourceRootConfig,
26 request_metrics::{LatestRequests, RequestMetrics}, 27 request_metrics::{LatestRequests, RequestMetrics},
27 thread_pool::TaskPool, 28 thread_pool::TaskPool,
@@ -67,7 +68,7 @@ pub(crate) struct GlobalState {
67 pub(crate) flycheck: Vec<FlycheckHandle>, 68 pub(crate) flycheck: Vec<FlycheckHandle>,
68 pub(crate) flycheck_sender: Sender<flycheck::Message>, 69 pub(crate) flycheck_sender: Sender<flycheck::Message>,
69 pub(crate) flycheck_receiver: Receiver<flycheck::Message>, 70 pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
70 pub(crate) config: Config, 71 pub(crate) config: Arc<Config>,
71 pub(crate) analysis_host: AnalysisHost, 72 pub(crate) analysis_host: AnalysisHost,
72 pub(crate) diagnostics: DiagnosticCollection, 73 pub(crate) diagnostics: DiagnosticCollection,
73 pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>, 74 pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>,
@@ -78,12 +79,13 @@ pub(crate) struct GlobalState {
78 pub(crate) source_root_config: SourceRootConfig, 79 pub(crate) source_root_config: SourceRootConfig,
79 pub(crate) proc_macro_client: Option<ProcMacroClient>, 80 pub(crate) proc_macro_client: Option<ProcMacroClient>,
80 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, 81 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
82 pub(crate) fetch_workspaces_queue: OpQueue,
81 latest_requests: Arc<RwLock<LatestRequests>>, 83 latest_requests: Arc<RwLock<LatestRequests>>,
82} 84}
83 85
84/// An immutable snapshot of the world's state at a point in time. 86/// An immutable snapshot of the world's state at a point in time.
85pub(crate) struct GlobalStateSnapshot { 87pub(crate) struct GlobalStateSnapshot {
86 pub(crate) config: Config, 88 pub(crate) config: Arc<Config>,
87 pub(crate) analysis: Analysis, 89 pub(crate) analysis: Analysis,
88 pub(crate) check_fixes: CheckFixes, 90 pub(crate) check_fixes: CheckFixes,
89 pub(crate) latest_requests: Arc<RwLock<LatestRequests>>, 91 pub(crate) latest_requests: Arc<RwLock<LatestRequests>>,
@@ -109,7 +111,7 @@ impl GlobalState {
109 Handle { handle, receiver } 111 Handle { handle, receiver }
110 }; 112 };
111 113
112 let analysis_host = AnalysisHost::new(config.lru_capacity); 114 let analysis_host = AnalysisHost::new(config.lru_capacity());
113 let (flycheck_sender, flycheck_receiver) = unbounded(); 115 let (flycheck_sender, flycheck_receiver) = unbounded();
114 GlobalState { 116 GlobalState {
115 sender, 117 sender,
@@ -119,7 +121,7 @@ impl GlobalState {
119 flycheck: Vec::new(), 121 flycheck: Vec::new(),
120 flycheck_sender, 122 flycheck_sender,
121 flycheck_receiver, 123 flycheck_receiver,
122 config, 124 config: Arc::new(config),
123 analysis_host, 125 analysis_host,
124 diagnostics: Default::default(), 126 diagnostics: Default::default(),
125 mem_docs: FxHashMap::default(), 127 mem_docs: FxHashMap::default(),
@@ -130,6 +132,7 @@ impl GlobalState {
130 source_root_config: SourceRootConfig::default(), 132 source_root_config: SourceRootConfig::default(),
131 proc_macro_client: None, 133 proc_macro_client: None,
132 workspaces: Arc::new(Vec::new()), 134 workspaces: Arc::new(Vec::new()),
135 fetch_workspaces_queue: OpQueue::default(),
133 latest_requests: Default::default(), 136 latest_requests: Default::default(),
134 } 137 }
135 } 138 }
@@ -184,7 +187,7 @@ impl GlobalState {
184 187
185 pub(crate) fn snapshot(&self) -> GlobalStateSnapshot { 188 pub(crate) fn snapshot(&self) -> GlobalStateSnapshot {
186 GlobalStateSnapshot { 189 GlobalStateSnapshot {
187 config: self.config.clone(), 190 config: Arc::clone(&self.config),
188 workspaces: Arc::clone(&self.workspaces), 191 workspaces: Arc::clone(&self.workspaces),
189 analysis: self.analysis_host.analysis(), 192 analysis: self.analysis_host.analysis(),
190 vfs: Arc::clone(&self.vfs), 193 vfs: Arc::clone(&self.vfs),
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 23f323f55..dc81f55d6 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -9,9 +9,8 @@ use std::{
9}; 9};
10 10
11use ide::{ 11use ide::{
12 AssistConfig, CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, 12 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, LineIndex, NavigationTarget,
13 HoverGotoTypeData, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, 13 Query, RangeInfo, Runnable, RunnableKind, SearchScope, SourceChange, SymbolKind, TextEdit,
14 SearchScope, SourceChange, SymbolKind, TextEdit,
15}; 14};
16use itertools::Itertools; 15use itertools::Itertools;
17use lsp_server::ErrorCode; 16use lsp_server::ErrorCode;
@@ -19,8 +18,8 @@ use lsp_types::{
19 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, 18 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
20 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, 19 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
21 CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag, 20 CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag,
22 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, 21 DocumentFormattingParams, DocumentHighlight, FoldingRange, FoldingRangeParams, HoverContents,
23 HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams, 22 Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams,
24 SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams, 23 SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
25 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, 24 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
26 SymbolTag, TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkspaceEdit, 25 SymbolTag, TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkspaceEdit,
@@ -34,8 +33,10 @@ use syntax::{algo, ast, AstNode, TextRange, TextSize};
34use crate::{ 33use crate::{
35 cargo_target_spec::CargoTargetSpec, 34 cargo_target_spec::CargoTargetSpec,
36 config::RustfmtConfig, 35 config::RustfmtConfig,
36 diff::diff,
37 from_json, from_proto, 37 from_json, from_proto,
38 global_state::{GlobalState, GlobalStateSnapshot}, 38 global_state::{GlobalState, GlobalStateSnapshot},
39 line_endings::LineEndings,
39 lsp_ext::{self, InlayHint, InlayHintsParams}, 40 lsp_ext::{self, InlayHint, InlayHintsParams},
40 lsp_utils::all_edits_are_disjoint, 41 lsp_utils::all_edits_are_disjoint,
41 to_proto, LspError, Result, 42 to_proto, LspError, Result,
@@ -104,6 +105,16 @@ pub(crate) fn handle_syntax_tree(
104 Ok(res) 105 Ok(res)
105} 106}
106 107
108pub(crate) fn handle_view_hir(
109 snap: GlobalStateSnapshot,
110 params: lsp_types::TextDocumentPositionParams,
111) -> Result<String> {
112 let _p = profile::span("handle_view_hir");
113 let position = from_proto::file_position(&snap, params)?;
114 let res = snap.analysis.view_hir(position)?;
115 Ok(res)
116}
117
107pub(crate) fn handle_expand_macro( 118pub(crate) fn handle_expand_macro(
108 snap: GlobalStateSnapshot, 119 snap: GlobalStateSnapshot,
109 params: lsp_ext::ExpandMacroParams, 120 params: lsp_ext::ExpandMacroParams,
@@ -269,7 +280,7 @@ pub(crate) fn handle_document_symbol(
269 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?; 280 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
270 let line_index = snap.analysis.file_line_index(file_id)?; 281 let line_index = snap.analysis.file_line_index(file_id)?;
271 282
272 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); 283 let mut parents: Vec<(lsp_types::DocumentSymbol, Option<usize>)> = Vec::new();
273 284
274 for symbol in snap.analysis.file_structure(file_id)? { 285 for symbol in snap.analysis.file_structure(file_id)? {
275 let mut tags = Vec::new(); 286 let mut tags = Vec::new();
@@ -278,7 +289,7 @@ pub(crate) fn handle_document_symbol(
278 }; 289 };
279 290
280 #[allow(deprecated)] 291 #[allow(deprecated)]
281 let doc_symbol = DocumentSymbol { 292 let doc_symbol = lsp_types::DocumentSymbol {
282 name: symbol.label, 293 name: symbol.label,
283 detail: symbol.detail, 294 detail: symbol.detail,
284 kind: to_proto::symbol_kind(symbol.kind), 295 kind: to_proto::symbol_kind(symbol.kind),
@@ -309,7 +320,7 @@ pub(crate) fn handle_document_symbol(
309 acc 320 acc
310 }; 321 };
311 322
312 let res = if snap.config.client_caps.hierarchical_symbols { 323 let res = if snap.config.hierarchical_symbols() {
313 document_symbols.into() 324 document_symbols.into()
314 } else { 325 } else {
315 let url = to_proto::url(&snap, file_id); 326 let url = to_proto::url(&snap, file_id);
@@ -322,7 +333,7 @@ pub(crate) fn handle_document_symbol(
322 return Ok(Some(res)); 333 return Ok(Some(res));
323 334
324 fn flatten_document_symbol( 335 fn flatten_document_symbol(
325 symbol: &DocumentSymbol, 336 symbol: &lsp_types::DocumentSymbol,
326 container_name: Option<String>, 337 container_name: Option<String>,
327 url: &Url, 338 url: &Url,
328 res: &mut Vec<SymbolInformation>, 339 res: &mut Vec<SymbolInformation>,
@@ -421,9 +432,27 @@ pub(crate) fn handle_will_rename_files(
421 // Limit to single-level moves for now. 432 // Limit to single-level moves for now.
422 match (from_path.parent(), to_path.parent()) { 433 match (from_path.parent(), to_path.parent()) {
423 (Some(p1), Some(p2)) if p1 == p2 => { 434 (Some(p1), Some(p2)) if p1 == p2 => {
424 let new_name = to_path.file_stem()?; 435 if from_path.is_dir() {
425 let new_name = new_name.to_str()?; 436 // add '/' to end of url -- from `file://path/to/folder` to `file://path/to/folder/`
426 Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())) 437 let mut old_folder_name = from_path.file_stem()?.to_str()?.to_string();
438 old_folder_name.push('/');
439 let from_with_trailing_slash = from.join(&old_folder_name).ok()?;
440
441 let imitate_from_url = from_with_trailing_slash.join("mod.rs").ok()?;
442 let new_file_name = to_path.file_name()?.to_str()?;
443 Some((
444 snap.url_to_file_id(&imitate_from_url).ok()?,
445 new_file_name.to_string(),
446 ))
447 } else {
448 let old_name = from_path.file_stem()?.to_str()?;
449 let new_name = to_path.file_stem()?.to_str()?;
450 match (old_name, new_name) {
451 ("mod", _) => None,
452 (_, "mod") => None,
453 _ => Some((snap.url_to_file_id(&from).ok()?, new_name.to_string())),
454 }
455 }
427 } 456 }
428 _ => None, 457 _ => None,
429 } 458 }
@@ -536,7 +565,7 @@ pub(crate) fn handle_runnables(
536 } 565 }
537 566
538 // Add `cargo check` and `cargo test` for all targets of the whole package 567 // Add `cargo check` and `cargo test` for all targets of the whole package
539 let config = &snap.config.runnables; 568 let config = snap.config.runnables();
540 match cargo_spec { 569 match cargo_spec {
541 Some(spec) => { 570 Some(spec) => {
542 for &cmd in ["check", "test"].iter() { 571 for &cmd in ["check", "test"].iter() {
@@ -567,9 +596,9 @@ pub(crate) fn handle_runnables(
567 kind: lsp_ext::RunnableKind::Cargo, 596 kind: lsp_ext::RunnableKind::Cargo,
568 args: lsp_ext::CargoRunnable { 597 args: lsp_ext::CargoRunnable {
569 workspace_root: None, 598 workspace_root: None,
570 override_cargo: config.override_cargo.clone(), 599 override_cargo: config.override_cargo,
571 cargo_args: vec!["check".to_string(), "--workspace".to_string()], 600 cargo_args: vec!["check".to_string(), "--workspace".to_string()],
572 cargo_extra_args: config.cargo_extra_args.clone(), 601 cargo_extra_args: config.cargo_extra_args,
573 executable_args: Vec::new(), 602 executable_args: Vec::new(),
574 expect_test: None, 603 expect_test: None,
575 }, 604 },
@@ -608,7 +637,8 @@ pub(crate) fn handle_completion(
608 return Ok(None); 637 return Ok(None);
609 } 638 }
610 639
611 let items = match snap.analysis.completions(&snap.config.completion, position)? { 640 let completion_config = &snap.config.completion();
641 let items = match snap.analysis.completions(completion_config, position)? {
612 None => return Ok(None), 642 None => return Ok(None),
613 Some(items) => items, 643 Some(items) => items,
614 }; 644 };
@@ -621,10 +651,9 @@ pub(crate) fn handle_completion(
621 let mut new_completion_items = 651 let mut new_completion_items =
622 to_proto::completion_item(&line_index, line_endings, item.clone()); 652 to_proto::completion_item(&line_index, line_endings, item.clone());
623 653
624 if snap.config.completion.resolve_additional_edits_lazily() { 654 if completion_config.enable_autoimport_completions {
625 for new_item in &mut new_completion_items { 655 for new_item in &mut new_completion_items {
626 let _ = fill_resolve_data(&mut new_item.data, &item, &text_document_position) 656 fill_resolve_data(&mut new_item.data, &item, &text_document_position);
627 .take();
628 } 657 }
629 } 658 }
630 659
@@ -650,16 +679,6 @@ pub(crate) fn handle_completion_resolve(
650 .into()); 679 .into());
651 } 680 }
652 681
653 // FIXME resolve the other capabilities also?
654 if !snap
655 .config
656 .completion
657 .active_resolve_capabilities
658 .contains(&CompletionResolveCapability::AdditionalTextEdits)
659 {
660 return Ok(original_completion);
661 }
662
663 let resolve_data = match original_completion 682 let resolve_data = match original_completion
664 .data 683 .data
665 .take() 684 .take()
@@ -678,7 +697,7 @@ pub(crate) fn handle_completion_resolve(
678 let additional_edits = snap 697 let additional_edits = snap
679 .analysis 698 .analysis
680 .resolve_completion_edits( 699 .resolve_completion_edits(
681 &snap.config.completion, 700 &snap.config.completion(),
682 FilePosition { file_id, offset }, 701 FilePosition { file_id, offset },
683 &resolve_data.full_import_path, 702 &resolve_data.full_import_path,
684 resolve_data.imported_name, 703 resolve_data.imported_name,
@@ -716,7 +735,7 @@ pub(crate) fn handle_folding_range(
716 let folds = snap.analysis.folding_ranges(file_id)?; 735 let folds = snap.analysis.folding_ranges(file_id)?;
717 let text = snap.analysis.file_text(file_id)?; 736 let text = snap.analysis.file_text(file_id)?;
718 let line_index = snap.analysis.file_line_index(file_id)?; 737 let line_index = snap.analysis.file_line_index(file_id)?;
719 let line_folding_only = snap.config.client_caps.line_folding_only; 738 let line_folding_only = snap.config.line_folding_only();
720 let res = folds 739 let res = folds
721 .into_iter() 740 .into_iter()
722 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) 741 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it))
@@ -734,12 +753,9 @@ pub(crate) fn handle_signature_help(
734 Some(it) => it, 753 Some(it) => it,
735 None => return Ok(None), 754 None => return Ok(None),
736 }; 755 };
737 let concise = !snap.config.call_info_full; 756 let concise = !snap.config.call_info_full();
738 let res = to_proto::signature_help( 757 let res =
739 call_info, 758 to_proto::signature_help(call_info, concise, snap.config.signature_help_label_offsets());
740 concise,
741 snap.config.client_caps.signature_help_label_offsets,
742 );
743 Ok(Some(res)) 759 Ok(Some(res))
744} 760}
745 761
@@ -749,14 +765,12 @@ pub(crate) fn handle_hover(
749) -> Result<Option<lsp_ext::Hover>> { 765) -> Result<Option<lsp_ext::Hover>> {
750 let _p = profile::span("handle_hover"); 766 let _p = profile::span("handle_hover");
751 let position = from_proto::file_position(&snap, params.text_document_position_params)?; 767 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
752 let info = match snap.analysis.hover( 768 let hover_config = snap.config.hover();
753 position, 769 let info =
754 snap.config.hover.links_in_hover, 770 match snap.analysis.hover(position, hover_config.links_in_hover, hover_config.markdown)? {
755 snap.config.hover.markdown, 771 None => return Ok(None),
756 )? { 772 Some(info) => info,
757 None => return Ok(None), 773 };
758 Some(info) => info,
759 };
760 let line_index = snap.analysis.file_line_index(position.file_id)?; 774 let line_index = snap.analysis.file_line_index(position.file_id)?;
761 let range = to_proto::range(&line_index, info.range); 775 let range = to_proto::range(&line_index, info.range);
762 let hover = lsp_ext::Hover { 776 let hover = lsp_ext::Hover {
@@ -777,7 +791,8 @@ pub(crate) fn handle_prepare_rename(
777 let _p = profile::span("handle_prepare_rename"); 791 let _p = profile::span("handle_prepare_rename");
778 let position = from_proto::file_position(&snap, params)?; 792 let position = from_proto::file_position(&snap, params)?;
779 793
780 let change = snap.analysis.prepare_rename(position)??; 794 let change = snap.analysis.prepare_rename(position)?.map_err(to_proto::rename_error)?;
795
781 let line_index = snap.analysis.file_line_index(position.file_id)?; 796 let line_index = snap.analysis.file_line_index(position.file_id)?;
782 let range = to_proto::range(&line_index, change.range); 797 let range = to_proto::range(&line_index, change.range);
783 Ok(Some(PrepareRenameResponse::Range(range))) 798 Ok(Some(PrepareRenameResponse::Range(range)))
@@ -790,15 +805,8 @@ pub(crate) fn handle_rename(
790 let _p = profile::span("handle_rename"); 805 let _p = profile::span("handle_rename");
791 let position = from_proto::file_position(&snap, params.text_document_position)?; 806 let position = from_proto::file_position(&snap, params.text_document_position)?;
792 807
793 if params.new_name.is_empty() { 808 let change =
794 return Err(LspError::new( 809 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?;
795 ErrorCode::InvalidParams as i32,
796 "New Name cannot be empty".into(),
797 )
798 .into());
799 }
800
801 let change = snap.analysis.rename(position, &*params.new_name)??;
802 let workspace_edit = to_proto::workspace_edit(&snap, change.info)?; 810 let workspace_edit = to_proto::workspace_edit(&snap, change.info)?;
803 Ok(Some(workspace_edit)) 811 Ok(Some(workspace_edit))
804} 812}
@@ -816,14 +824,15 @@ pub(crate) fn handle_references(
816 }; 824 };
817 825
818 let locations = if params.context.include_declaration { 826 let locations = if params.context.include_declaration {
819 refs.into_iter() 827 refs.references_with_declaration()
820 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok()) 828 .file_ranges()
829 .filter_map(|frange| to_proto::location(&snap, frange).ok())
821 .collect() 830 .collect()
822 } else { 831 } else {
823 // Only iterate over the references if include_declaration was false 832 // Only iterate over the references if include_declaration was false
824 refs.references() 833 refs.references()
825 .iter() 834 .file_ranges()
826 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok()) 835 .filter_map(|frange| to_proto::location(&snap, frange).ok())
827 .collect() 836 .collect()
828 }; 837 };
829 838
@@ -840,9 +849,9 @@ pub(crate) fn handle_formatting(
840 let crate_ids = snap.analysis.crate_for(file_id)?; 849 let crate_ids = snap.analysis.crate_for(file_id)?;
841 850
842 let file_line_index = snap.analysis.file_line_index(file_id)?; 851 let file_line_index = snap.analysis.file_line_index(file_id)?;
843 let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); 852 let file_line_endings = snap.file_line_endings(file_id);
844 853
845 let mut rustfmt = match &snap.config.rustfmt { 854 let mut rustfmt = match snap.config.rustfmt() {
846 RustfmtConfig::Rustfmt { extra_args } => { 855 RustfmtConfig::Rustfmt { extra_args } => {
847 let mut cmd = process::Command::new(toolchain::rustfmt()); 856 let mut cmd = process::Command::new(toolchain::rustfmt());
848 cmd.args(extra_args); 857 cmd.args(extra_args);
@@ -861,16 +870,18 @@ pub(crate) fn handle_formatting(
861 } 870 }
862 }; 871 };
863 872
864 let mut rustfmt = rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?; 873 let mut rustfmt =
874 rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?;
865 875
866 rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; 876 rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
867 877
868 let output = rustfmt.wait_with_output()?; 878 let output = rustfmt.wait_with_output()?;
869 let captured_stdout = String::from_utf8(output.stdout)?; 879 let captured_stdout = String::from_utf8(output.stdout)?;
880 let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default();
870 881
871 if !output.status.success() { 882 if !output.status.success() {
872 match output.status.code() { 883 match output.status.code() {
873 Some(1) => { 884 Some(1) if !captured_stderr.contains("not installed") => {
874 // While `rustfmt` doesn't have a specific exit code for parse errors this is the 885 // While `rustfmt` doesn't have a specific exit code for parse errors this is the
875 // likely cause exiting with 1. Most Language Servers swallow parse errors on 886 // likely cause exiting with 1. Most Language Servers swallow parse errors on
876 // formatting because otherwise an error is surfaced to the user on top of the 887 // formatting because otherwise an error is surfaced to the user on top of the
@@ -886,8 +897,9 @@ pub(crate) fn handle_formatting(
886 format!( 897 format!(
887 r#"rustfmt exited with: 898 r#"rustfmt exited with:
888 Status: {} 899 Status: {}
889 stdout: {}"#, 900 stdout: {}
890 output.status, captured_stdout, 901 stderr: {}"#,
902 output.status, captured_stdout, captured_stderr,
891 ), 903 ),
892 ) 904 )
893 .into()); 905 .into());
@@ -895,14 +907,26 @@ pub(crate) fn handle_formatting(
895 } 907 }
896 } 908 }
897 909
898 if *file == captured_stdout { 910 let (new_text, new_line_endings) = LineEndings::normalize(captured_stdout);
911
912 if file_line_endings != new_line_endings {
913 // If line endings are different, send the entire file.
914 // Diffing would not work here, as the line endings might be the only
915 // difference.
916 Ok(Some(to_proto::text_edit_vec(
917 &file_line_index,
918 new_line_endings,
919 TextEdit::replace(TextRange::up_to(TextSize::of(&*file)), new_text),
920 )))
921 } else if *file == new_text {
899 // The document is already formatted correctly -- no edits needed. 922 // The document is already formatted correctly -- no edits needed.
900 Ok(None) 923 Ok(None)
901 } else { 924 } else {
902 Ok(Some(vec![lsp_types::TextEdit { 925 Ok(Some(to_proto::text_edit_vec(
903 range: Range::new(Position::new(0, 0), end_position), 926 &file_line_index,
904 new_text: captured_stdout, 927 file_line_endings,
905 }])) 928 diff(&file, &new_text),
929 )))
906 } 930 }
907} 931}
908 932
@@ -914,7 +938,7 @@ pub(crate) fn handle_code_action(
914 // We intentionally don't support command-based actions, as those either 938 // We intentionally don't support command-based actions, as those either
915 // requires custom client-code anyway, or requires server-initiated edits. 939 // requires custom client-code anyway, or requires server-initiated edits.
916 // Server initiated edits break causality, so we avoid those as well. 940 // Server initiated edits break causality, so we avoid those as well.
917 if !snap.config.client_caps.code_action_literals { 941 if !snap.config.code_action_literals() {
918 return Ok(None); 942 return Ok(None);
919 } 943 }
920 944
@@ -923,14 +947,12 @@ pub(crate) fn handle_code_action(
923 let range = from_proto::text_range(&line_index, params.range); 947 let range = from_proto::text_range(&line_index, params.range);
924 let frange = FileRange { file_id, range }; 948 let frange = FileRange { file_id, range };
925 949
926 let assists_config = AssistConfig { 950 let mut assists_config = snap.config.assist();
927 allowed: params 951 assists_config.allowed = params
928 .clone() 952 .clone()
929 .context 953 .context
930 .only 954 .only
931 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()), 955 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
932 ..snap.config.assist
933 };
934 956
935 let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); 957 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
936 958
@@ -944,7 +966,7 @@ pub(crate) fn handle_code_action(
944 add_quick_fixes(&snap, frange, &line_index, &mut res)?; 966 add_quick_fixes(&snap, frange, &line_index, &mut res)?;
945 } 967 }
946 968
947 if snap.config.client_caps.code_action_resolve { 969 if snap.config.code_action_resolve() {
948 for (index, assist) in 970 for (index, assist) in
949 snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate() 971 snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate()
950 { 972 {
@@ -965,7 +987,7 @@ fn add_quick_fixes(
965 line_index: &Arc<LineIndex>, 987 line_index: &Arc<LineIndex>,
966 acc: &mut Vec<lsp_ext::CodeAction>, 988 acc: &mut Vec<lsp_ext::CodeAction>,
967) -> Result<()> { 989) -> Result<()> {
968 let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics, frange.file_id)?; 990 let diagnostics = snap.analysis.diagnostics(&snap.config.diagnostics(), frange.file_id)?;
969 991
970 for fix in diagnostics 992 for fix in diagnostics
971 .into_iter() 993 .into_iter()
@@ -994,7 +1016,7 @@ fn add_quick_fixes(
994} 1016}
995 1017
996pub(crate) fn handle_code_action_resolve( 1018pub(crate) fn handle_code_action_resolve(
997 mut snap: GlobalStateSnapshot, 1019 snap: GlobalStateSnapshot,
998 mut code_action: lsp_ext::CodeAction, 1020 mut code_action: lsp_ext::CodeAction,
999) -> Result<lsp_ext::CodeAction> { 1021) -> Result<lsp_ext::CodeAction> {
1000 let _p = profile::span("handle_code_action_resolve"); 1022 let _p = profile::span("handle_code_action_resolve");
@@ -1008,13 +1030,14 @@ pub(crate) fn handle_code_action_resolve(
1008 let range = from_proto::text_range(&line_index, params.code_action_params.range); 1030 let range = from_proto::text_range(&line_index, params.code_action_params.range);
1009 let frange = FileRange { file_id, range }; 1031 let frange = FileRange { file_id, range };
1010 1032
1011 snap.config.assist.allowed = params 1033 let mut assists_config = snap.config.assist();
1034 assists_config.allowed = params
1012 .code_action_params 1035 .code_action_params
1013 .context 1036 .context
1014 .only 1037 .only
1015 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect()); 1038 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
1016 1039
1017 let assists = snap.analysis.assists(&snap.config.assist, true, frange)?; 1040 let assists = snap.analysis.assists(&assists_config, true, frange)?;
1018 let (id, index) = split_once(&params.id, ':').unwrap(); 1041 let (id, index) = split_once(&params.id, ':').unwrap();
1019 let index = index.parse::<usize>().unwrap(); 1042 let index = index.parse::<usize>().unwrap();
1020 let assist = &assists[index]; 1043 let assist = &assists[index];
@@ -1031,7 +1054,8 @@ pub(crate) fn handle_code_lens(
1031 let _p = profile::span("handle_code_lens"); 1054 let _p = profile::span("handle_code_lens");
1032 let mut lenses: Vec<CodeLens> = Default::default(); 1055 let mut lenses: Vec<CodeLens> = Default::default();
1033 1056
1034 if snap.config.lens.none() { 1057 let lens_config = snap.config.lens();
1058 if lens_config.none() {
1035 // early return before any db query! 1059 // early return before any db query!
1036 return Ok(Some(lenses)); 1060 return Ok(Some(lenses));
1037 } 1061 }
@@ -1040,7 +1064,7 @@ pub(crate) fn handle_code_lens(
1040 let line_index = snap.analysis.file_line_index(file_id)?; 1064 let line_index = snap.analysis.file_line_index(file_id)?;
1041 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; 1065 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
1042 1066
1043 if snap.config.lens.runnable() { 1067 if lens_config.runnable() {
1044 // Gather runnables 1068 // Gather runnables
1045 for runnable in snap.analysis.runnables(file_id)? { 1069 for runnable in snap.analysis.runnables(file_id)? {
1046 if should_skip_target(&runnable, cargo_spec.as_ref()) { 1070 if should_skip_target(&runnable, cargo_spec.as_ref()) {
@@ -1050,7 +1074,7 @@ pub(crate) fn handle_code_lens(
1050 let action = runnable.action(); 1074 let action = runnable.action();
1051 let range = to_proto::range(&line_index, runnable.nav.full_range); 1075 let range = to_proto::range(&line_index, runnable.nav.full_range);
1052 let r = to_proto::runnable(&snap, file_id, runnable)?; 1076 let r = to_proto::runnable(&snap, file_id, runnable)?;
1053 if snap.config.lens.run { 1077 if lens_config.run {
1054 let lens = CodeLens { 1078 let lens = CodeLens {
1055 range, 1079 range,
1056 command: Some(run_single_command(&r, action.run_title)), 1080 command: Some(run_single_command(&r, action.run_title)),
@@ -1059,7 +1083,7 @@ pub(crate) fn handle_code_lens(
1059 lenses.push(lens); 1083 lenses.push(lens);
1060 } 1084 }
1061 1085
1062 if action.debugee && snap.config.lens.debug { 1086 if action.debugee && lens_config.debug {
1063 let debug_lens = 1087 let debug_lens =
1064 CodeLens { range, command: Some(debug_single_command(&r)), data: None }; 1088 CodeLens { range, command: Some(debug_single_command(&r)), data: None };
1065 lenses.push(debug_lens); 1089 lenses.push(debug_lens);
@@ -1067,7 +1091,7 @@ pub(crate) fn handle_code_lens(
1067 } 1091 }
1068 } 1092 }
1069 1093
1070 if snap.config.lens.implementations { 1094 if lens_config.implementations {
1071 // Handle impls 1095 // Handle impls
1072 lenses.extend( 1096 lenses.extend(
1073 snap.analysis 1097 snap.analysis
@@ -1102,7 +1126,7 @@ pub(crate) fn handle_code_lens(
1102 ); 1126 );
1103 } 1127 }
1104 1128
1105 if snap.config.lens.references() { 1129 if lens_config.references() {
1106 lenses.extend(snap.analysis.find_all_methods(file_id)?.into_iter().map(|it| { 1130 lenses.extend(snap.analysis.find_all_methods(file_id)?.into_iter().map(|it| {
1107 let range = to_proto::range(&line_index, it.range); 1131 let range = to_proto::range(&line_index, it.range);
1108 let position = to_proto::position(&line_index, it.range.start()); 1132 let position = to_proto::position(&line_index, it.range.start());
@@ -1164,8 +1188,8 @@ pub(crate) fn handle_code_lens_resolve(
1164 .unwrap_or(None) 1188 .unwrap_or(None)
1165 .map(|r| { 1189 .map(|r| {
1166 r.references() 1190 r.references()
1167 .iter() 1191 .file_ranges()
1168 .filter_map(|it| to_proto::location(&snap, it.file_range).ok()) 1192 .filter_map(|frange| to_proto::location(&snap, frange).ok())
1169 .collect_vec() 1193 .collect_vec()
1170 }) 1194 })
1171 .unwrap_or_default(); 1195 .unwrap_or_default();
@@ -1209,13 +1233,19 @@ pub(crate) fn handle_document_highlight(
1209 }; 1233 };
1210 1234
1211 let res = refs 1235 let res = refs
1212 .into_iter() 1236 .references_with_declaration()
1213 .filter(|reference| reference.file_range.file_id == position.file_id) 1237 .references
1214 .map(|reference| DocumentHighlight { 1238 .get(&position.file_id)
1215 range: to_proto::range(&line_index, reference.file_range.range), 1239 .map(|file_refs| {
1216 kind: reference.access.map(to_proto::document_highlight_kind), 1240 file_refs
1241 .into_iter()
1242 .map(|r| DocumentHighlight {
1243 range: to_proto::range(&line_index, r.range),
1244 kind: r.access.map(to_proto::document_highlight_kind),
1245 })
1246 .collect()
1217 }) 1247 })
1218 .collect(); 1248 .unwrap_or_default();
1219 Ok(Some(res)) 1249 Ok(Some(res))
1220} 1250}
1221 1251
@@ -1248,7 +1278,7 @@ pub(crate) fn publish_diagnostics(
1248 1278
1249 let diagnostics: Vec<Diagnostic> = snap 1279 let diagnostics: Vec<Diagnostic> = snap
1250 .analysis 1280 .analysis
1251 .diagnostics(&snap.config.diagnostics, file_id)? 1281 .diagnostics(&snap.config.diagnostics(), file_id)?
1252 .into_iter() 1282 .into_iter()
1253 .map(|d| Diagnostic { 1283 .map(|d| Diagnostic {
1254 range: to_proto::range(&line_index, d.range), 1284 range: to_proto::range(&line_index, d.range),
@@ -1281,7 +1311,7 @@ pub(crate) fn handle_inlay_hints(
1281 let line_index = snap.analysis.file_line_index(file_id)?; 1311 let line_index = snap.analysis.file_line_index(file_id)?;
1282 Ok(snap 1312 Ok(snap
1283 .analysis 1313 .analysis
1284 .inlay_hints(file_id, &snap.config.inlay_hints)? 1314 .inlay_hints(file_id, &snap.config.inlay_hints())?
1285 .into_iter() 1315 .into_iter()
1286 .map(|it| to_proto::inlay_hint(&line_index, it)) 1316 .map(|it| to_proto::inlay_hint(&line_index, it))
1287 .collect()) 1317 .collect())
@@ -1527,7 +1557,7 @@ fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command {
1527} 1557}
1528 1558
1529fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> { 1559fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> {
1530 let value = if snap.config.client_caps.location_link { 1560 let value = if snap.config.location_link() {
1531 let link = to_proto::location_link(snap, None, nav.clone()).ok()?; 1561 let link = to_proto::location_link(snap, None, nav.clone()).ok()?;
1532 to_value(link).ok()? 1562 to_value(link).ok()?
1533 } else { 1563 } else {
@@ -1551,7 +1581,7 @@ fn show_impl_command_link(
1551 snap: &GlobalStateSnapshot, 1581 snap: &GlobalStateSnapshot,
1552 position: &FilePosition, 1582 position: &FilePosition,
1553) -> Option<lsp_ext::CommandLinkGroup> { 1583) -> Option<lsp_ext::CommandLinkGroup> {
1554 if snap.config.hover.implementations { 1584 if snap.config.hover().implementations {
1555 if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) { 1585 if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) {
1556 let uri = to_proto::url(snap, position.file_id); 1586 let uri = to_proto::url(snap, position.file_id);
1557 let line_index = snap.analysis.file_line_index(position.file_id).ok()?; 1587 let line_index = snap.analysis.file_line_index(position.file_id).ok()?;
@@ -1579,7 +1609,8 @@ fn runnable_action_links(
1579 runnable: Runnable, 1609 runnable: Runnable,
1580) -> Option<lsp_ext::CommandLinkGroup> { 1610) -> Option<lsp_ext::CommandLinkGroup> {
1581 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?; 1611 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?;
1582 if !snap.config.hover.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) { 1612 let hover_config = snap.config.hover();
1613 if !hover_config.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) {
1583 return None; 1614 return None;
1584 } 1615 }
1585 1616
@@ -1587,12 +1618,12 @@ fn runnable_action_links(
1587 to_proto::runnable(snap, file_id, runnable).ok().map(|r| { 1618 to_proto::runnable(snap, file_id, runnable).ok().map(|r| {
1588 let mut group = lsp_ext::CommandLinkGroup::default(); 1619 let mut group = lsp_ext::CommandLinkGroup::default();
1589 1620
1590 if snap.config.hover.run { 1621 if hover_config.run {
1591 let run_command = run_single_command(&r, action.run_title); 1622 let run_command = run_single_command(&r, action.run_title);
1592 group.commands.push(to_command_link(run_command, r.label.clone())); 1623 group.commands.push(to_command_link(run_command, r.label.clone()));
1593 } 1624 }
1594 1625
1595 if snap.config.hover.debug { 1626 if hover_config.debug {
1596 let dbg_command = debug_single_command(&r); 1627 let dbg_command = debug_single_command(&r);
1597 group.commands.push(to_command_link(dbg_command, r.label)); 1628 group.commands.push(to_command_link(dbg_command, r.label));
1598 } 1629 }
@@ -1605,7 +1636,7 @@ fn goto_type_action_links(
1605 snap: &GlobalStateSnapshot, 1636 snap: &GlobalStateSnapshot,
1606 nav_targets: &[HoverGotoTypeData], 1637 nav_targets: &[HoverGotoTypeData],
1607) -> Option<lsp_ext::CommandLinkGroup> { 1638) -> Option<lsp_ext::CommandLinkGroup> {
1608 if !snap.config.hover.goto_type_def || nav_targets.is_empty() { 1639 if !snap.config.hover().goto_type_def || nav_targets.is_empty() {
1609 return None; 1640 return None;
1610 } 1641 }
1611 1642
@@ -1626,14 +1657,14 @@ fn prepare_hover_actions(
1626 file_id: FileId, 1657 file_id: FileId,
1627 actions: &[HoverAction], 1658 actions: &[HoverAction],
1628) -> Vec<lsp_ext::CommandLinkGroup> { 1659) -> Vec<lsp_ext::CommandLinkGroup> {
1629 if snap.config.hover.none() || !snap.config.client_caps.hover_actions { 1660 if snap.config.hover().none() || !snap.config.hover_actions() {
1630 return Vec::new(); 1661 return Vec::new();
1631 } 1662 }
1632 1663
1633 actions 1664 actions
1634 .iter() 1665 .iter()
1635 .filter_map(|it| match it { 1666 .filter_map(|it| match it {
1636 HoverAction::Implementaion(position) => show_impl_command_link(snap, position), 1667 HoverAction::Implementation(position) => show_impl_command_link(snap, position),
1637 HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()), 1668 HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()),
1638 HoverAction::GoToType(targets) => goto_type_action_links(snap, targets), 1669 HoverAction::GoToType(targets) => goto_type_action_links(snap, targets),
1639 }) 1670 })
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index d538ad69a..2207b9a87 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -34,6 +34,8 @@ mod request_metrics;
34mod lsp_utils; 34mod lsp_utils;
35mod thread_pool; 35mod thread_pool;
36mod document; 36mod document;
37mod diff;
38mod op_queue;
37pub mod lsp_ext; 39pub mod lsp_ext;
38pub mod config; 40pub mod config;
39 41
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 93ac45415..a85978737 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -53,6 +53,14 @@ pub struct SyntaxTreeParams {
53 pub range: Option<Range>, 53 pub range: Option<Range>,
54} 54}
55 55
56pub enum ViewHir {}
57
58impl Request for ViewHir {
59 type Params = lsp_types::TextDocumentPositionParams;
60 type Result = String;
61 const METHOD: &'static str = "rust-analyzer/viewHir";
62}
63
56pub enum ExpandMacro {} 64pub enum ExpandMacro {}
57 65
58impl Request for ExpandMacro { 66impl Request for ExpandMacro {
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 60c12e4e2..2d06fe538 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -46,7 +46,7 @@ impl GlobalState {
46 message: Option<String>, 46 message: Option<String>,
47 fraction: Option<f64>, 47 fraction: Option<f64>,
48 ) { 48 ) {
49 if !self.config.client_caps.work_done_progress { 49 if !self.config.work_done_progress() {
50 return; 50 return;
51 } 51 }
52 let percentage = fraction.map(|f| { 52 let percentage = fraction.map(|f| {
@@ -130,7 +130,7 @@ pub(crate) fn apply_document_changes(
130} 130}
131 131
132/// Checks that the edits inside the completion and the additional edits do not overlap. 132/// Checks that the edits inside the completion and the additional edits do not overlap.
133/// LSP explicitly forbits the additional edits to overlap both with the main edit and themselves. 133/// LSP explicitly forbids the additional edits to overlap both with the main edit and themselves.
134pub(crate) fn all_edits_are_disjoint( 134pub(crate) fn all_edits_are_disjoint(
135 completion: &lsp_types::CompletionItem, 135 completion: &lsp_types::CompletionItem,
136 additional_edits: &[lsp_types::TextEdit], 136 additional_edits: &[lsp_types::TextEdit],
@@ -290,7 +290,7 @@ mod tests {
290 Some(vec![disjoint_edit.clone(), joint_edit.clone()]); 290 Some(vec![disjoint_edit.clone(), joint_edit.clone()]);
291 assert!( 291 assert!(
292 !all_edits_are_disjoint(&completion_with_joint_edits, &[]), 292 !all_edits_are_disjoint(&completion_with_joint_edits, &[]),
293 "Completion with disjoint edits fails the validaton even with empty extra edits" 293 "Completion with disjoint edits fails the validation even with empty extra edits"
294 ); 294 );
295 295
296 completion_with_joint_edits.text_edit = 296 completion_with_joint_edits.text_edit =
@@ -298,7 +298,7 @@ mod tests {
298 completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit.clone()]); 298 completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit.clone()]);
299 assert!( 299 assert!(
300 !all_edits_are_disjoint(&completion_with_joint_edits, &[]), 300 !all_edits_are_disjoint(&completion_with_joint_edits, &[]),
301 "Completion with disjoint edits fails the validaton even with empty extra edits" 301 "Completion with disjoint edits fails the validation even with empty extra edits"
302 ); 302 );
303 303
304 completion_with_joint_edits.text_edit = 304 completion_with_joint_edits.text_edit =
@@ -310,7 +310,7 @@ mod tests {
310 completion_with_joint_edits.additional_text_edits = None; 310 completion_with_joint_edits.additional_text_edits = None;
311 assert!( 311 assert!(
312 !all_edits_are_disjoint(&completion_with_joint_edits, &[]), 312 !all_edits_are_disjoint(&completion_with_joint_edits, &[]),
313 "Completion with disjoint edits fails the validaton even with empty extra edits" 313 "Completion with disjoint edits fails the validation even with empty extra edits"
314 ); 314 );
315 315
316 completion_with_joint_edits.text_edit = 316 completion_with_joint_edits.text_edit =
@@ -322,7 +322,7 @@ mod tests {
322 completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit]); 322 completion_with_joint_edits.additional_text_edits = Some(vec![joint_edit]);
323 assert!( 323 assert!(
324 !all_edits_are_disjoint(&completion_with_joint_edits, &[]), 324 !all_edits_are_disjoint(&completion_with_joint_edits, &[]),
325 "Completion with disjoint edits fails the validaton even with empty extra edits" 325 "Completion with disjoint edits fails the validation even with empty extra edits"
326 ); 326 );
327 } 327 }
328 328
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 5d55dc96e..6d2475a59 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -11,7 +11,6 @@ use ide::{Canceled, FileId};
11use ide_db::base_db::VfsPath; 11use ide_db::base_db::VfsPath;
12use lsp_server::{Connection, Notification, Request, Response}; 12use lsp_server::{Connection, Notification, Request, Response};
13use lsp_types::notification::Notification as _; 13use lsp_types::notification::Notification as _;
14use project_model::ProjectWorkspace;
15use vfs::ChangeKind; 14use vfs::ChangeKind;
16 15
17use crate::{ 16use crate::{
@@ -22,6 +21,7 @@ use crate::{
22 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, 21 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status},
23 handlers, lsp_ext, 22 handlers, lsp_ext,
24 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, 23 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
24 reload::ProjectWorkspaceProgress,
25 Result, 25 Result,
26}; 26};
27 27
@@ -61,8 +61,8 @@ enum Event {
61pub(crate) enum Task { 61pub(crate) enum Task {
62 Response(Response), 62 Response(Response),
63 Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>), 63 Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
64 Workspaces(Vec<anyhow::Result<ProjectWorkspace>>),
65 PrimeCaches(PrimeCachesProgress), 64 PrimeCaches(PrimeCachesProgress),
65 FetchWorkspace(ProjectWorkspaceProgress),
66} 66}
67 67
68impl fmt::Debug for Event { 68impl fmt::Debug for Event {
@@ -99,7 +99,8 @@ impl fmt::Debug for Event {
99 99
100impl GlobalState { 100impl GlobalState {
101 fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> { 101 fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
102 if self.config.linked_projects.is_empty() && self.config.notifications.cargo_toml_not_found 102 if self.config.linked_projects().is_empty()
103 && self.config.notifications().cargo_toml_not_found
103 { 104 {
104 self.show_message( 105 self.show_message(
105 lsp_types::MessageType::Error, 106 lsp_types::MessageType::Error,
@@ -107,40 +108,43 @@ impl GlobalState {
107 ); 108 );
108 }; 109 };
109 110
110 let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions { 111 if self.config.did_save_text_document_dynamic_registration() {
111 include_text: Some(false), 112 let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
112 text_document_registration_options: lsp_types::TextDocumentRegistrationOptions { 113 include_text: Some(false),
113 document_selector: Some(vec![ 114 text_document_registration_options: lsp_types::TextDocumentRegistrationOptions {
114 lsp_types::DocumentFilter { 115 document_selector: Some(vec![
115 language: None, 116 lsp_types::DocumentFilter {
116 scheme: None, 117 language: None,
117 pattern: Some("**/*.rs".into()), 118 scheme: None,
118 }, 119 pattern: Some("**/*.rs".into()),
119 lsp_types::DocumentFilter { 120 },
120 language: None, 121 lsp_types::DocumentFilter {
121 scheme: None, 122 language: None,
122 pattern: Some("**/Cargo.toml".into()), 123 scheme: None,
123 }, 124 pattern: Some("**/Cargo.toml".into()),
124 lsp_types::DocumentFilter { 125 },
125 language: None, 126 lsp_types::DocumentFilter {
126 scheme: None, 127 language: None,
127 pattern: Some("**/Cargo.lock".into()), 128 scheme: None,
128 }, 129 pattern: Some("**/Cargo.lock".into()),
129 ]), 130 },
130 }, 131 ]),
131 }; 132 },
132 133 };
133 let registration = lsp_types::Registration { 134
134 id: "textDocument/didSave".to_string(), 135 let registration = lsp_types::Registration {
135 method: "textDocument/didSave".to_string(), 136 id: "textDocument/didSave".to_string(),
136 register_options: Some(serde_json::to_value(save_registration_options).unwrap()), 137 method: "textDocument/didSave".to_string(),
137 }; 138 register_options: Some(serde_json::to_value(save_registration_options).unwrap()),
138 self.send_request::<lsp_types::request::RegisterCapability>( 139 };
139 lsp_types::RegistrationParams { registrations: vec![registration] }, 140 self.send_request::<lsp_types::request::RegisterCapability>(
140 |_, _| (), 141 lsp_types::RegistrationParams { registrations: vec![registration] },
141 ); 142 |_, _| (),
143 );
144 }
142 145
143 self.fetch_workspaces(); 146 self.fetch_workspaces_request();
147 self.fetch_workspaces_if_needed();
144 148
145 while let Some(event) = self.next_event(&inbox) { 149 while let Some(event) = self.next_event(&inbox) {
146 if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { 150 if let Event::Lsp(lsp_server::Message::Notification(not)) = &event {
@@ -201,7 +205,6 @@ impl GlobalState {
201 self.diagnostics.set_native_diagnostics(file_id, diagnostics) 205 self.diagnostics.set_native_diagnostics(file_id, diagnostics)
202 } 206 }
203 } 207 }
204 Task::Workspaces(workspaces) => self.switch_workspaces(workspaces),
205 Task::PrimeCaches(progress) => match progress { 208 Task::PrimeCaches(progress) => match progress {
206 PrimeCachesProgress::Started => prime_caches_progress.push(progress), 209 PrimeCachesProgress::Started => prime_caches_progress.push(progress),
207 PrimeCachesProgress::StartedOnCrate { .. } => { 210 PrimeCachesProgress::StartedOnCrate { .. } => {
@@ -215,6 +218,20 @@ impl GlobalState {
215 } 218 }
216 PrimeCachesProgress::Finished => prime_caches_progress.push(progress), 219 PrimeCachesProgress::Finished => prime_caches_progress.push(progress),
217 }, 220 },
221 Task::FetchWorkspace(progress) => {
222 let (state, msg) = match progress {
223 ProjectWorkspaceProgress::Begin => (Progress::Begin, None),
224 ProjectWorkspaceProgress::Report(msg) => {
225 (Progress::Report, Some(msg))
226 }
227 ProjectWorkspaceProgress::End(workspaces) => {
228 self.fetch_workspaces_completed();
229 self.switch_workspaces(workspaces);
230 (Progress::End, None)
231 }
232 };
233 self.report_progress("fetching", state, msg, None);
234 }
218 } 235 }
219 // Coalesce multiple task events into one loop turn 236 // Coalesce multiple task events into one loop turn
220 task = match self.task_pool.receiver.try_recv() { 237 task = match self.task_pool.receiver.try_recv() {
@@ -296,7 +313,7 @@ impl GlobalState {
296 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { 313 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
297 let diagnostics = 314 let diagnostics =
298 crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( 315 crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
299 &self.config.diagnostics_map, 316 &self.config.diagnostics_map(),
300 &diagnostic, 317 &diagnostic,
301 &workspace_root, 318 &workspace_root,
302 ); 319 );
@@ -365,13 +382,13 @@ impl GlobalState {
365 self.update_file_notifications_on_threadpool(); 382 self.update_file_notifications_on_threadpool();
366 383
367 // Refresh semantic tokens if the client supports it. 384 // Refresh semantic tokens if the client supports it.
368 if self.config.semantic_tokens_refresh { 385 if self.config.semantic_tokens_refresh() {
369 self.semantic_tokens_cache.lock().clear(); 386 self.semantic_tokens_cache.lock().clear();
370 self.send_request::<lsp_types::request::SemanticTokensRefesh>((), |_, _| ()); 387 self.send_request::<lsp_types::request::SemanticTokensRefesh>((), |_, _| ());
371 } 388 }
372 389
373 // Refresh code lens if the client supports it. 390 // Refresh code lens if the client supports it.
374 if self.config.code_lens_refresh { 391 if self.config.code_lens_refresh() {
375 self.send_request::<lsp_types::request::CodeLensRefresh>((), |_, _| ()); 392 self.send_request::<lsp_types::request::CodeLensRefresh>((), |_, _| ());
376 } 393 }
377 } 394 }
@@ -390,6 +407,8 @@ impl GlobalState {
390 } 407 }
391 } 408 }
392 409
410 self.fetch_workspaces_if_needed();
411
393 let loop_duration = loop_start.elapsed(); 412 let loop_duration = loop_start.elapsed();
394 if loop_duration > Duration::from_millis(100) { 413 if loop_duration > Duration::from_millis(100) {
395 log::warn!("overly long loop turn: {:?}", loop_duration); 414 log::warn!("overly long loop turn: {:?}", loop_duration);
@@ -427,7 +446,7 @@ impl GlobalState {
427 } 446 }
428 447
429 RequestDispatcher { req: Some(req), global_state: self } 448 RequestDispatcher { req: Some(req), global_state: self }
430 .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces()))? 449 .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces_request()))?
431 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? 450 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
432 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? 451 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
433 .on_sync::<lsp_types::request::Shutdown>(|s, ()| { 452 .on_sync::<lsp_types::request::Shutdown>(|s, ()| {
@@ -443,6 +462,7 @@ impl GlobalState {
443 .on_sync::<lsp_ext::MemoryUsage>(|s, p| handlers::handle_memory_usage(s, p))? 462 .on_sync::<lsp_ext::MemoryUsage>(|s, p| handlers::handle_memory_usage(s, p))?
444 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status) 463 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
445 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree) 464 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
465 .on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
446 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro) 466 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)
447 .on::<lsp_ext::ParentModule>(handlers::handle_parent_module) 467 .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)
448 .on::<lsp_ext::Runnables>(handlers::handle_runnables) 468 .on::<lsp_ext::Runnables>(handlers::handle_runnables)
@@ -607,7 +627,7 @@ impl GlobalState {
607 if let Some(json) = configs.get_mut(0) { 627 if let Some(json) = configs.get_mut(0) {
608 // Note that json can be null according to the spec if the client can't 628 // Note that json can be null according to the spec if the client can't
609 // provide a configuration. This is handled in Config::update below. 629 // provide a configuration. This is handled in Config::update below.
610 let mut config = this.config.clone(); 630 let mut config = Config::clone(&*this.config);
611 config.update(json.take()); 631 config.update(json.take());
612 this.update_configuration(config); 632 this.update_configuration(config);
613 } 633 }
@@ -657,7 +677,7 @@ impl GlobalState {
657 .collect::<Vec<_>>(); 677 .collect::<Vec<_>>();
658 678
659 log::trace!("updating notifications for {:?}", subscriptions); 679 log::trace!("updating notifications for {:?}", subscriptions);
660 if self.config.publish_diagnostics { 680 if self.config.publish_diagnostics() {
661 let snapshot = self.snapshot(); 681 let snapshot = self.snapshot();
662 self.task_pool.handle.spawn(move || { 682 self.task_pool.handle.spawn(move || {
663 let diagnostics = subscriptions 683 let diagnostics = subscriptions
diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs
index 968ea55f0..865eaae9b 100644
--- a/crates/rust-analyzer/src/markdown.rs
+++ b/crates/rust-analyzer/src/markdown.rs
@@ -1,8 +1,17 @@
1//! Transforms markdown 1//! Transforms markdown
2 2
3const RUSTDOC_FENCE: &str = "```"; 3const RUSTDOC_FENCE: &str = "```";
4const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] = 4const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] = &[
5 &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"]; 5 "",
6 "rust",
7 "should_panic",
8 "ignore",
9 "no_run",
10 "compile_fail",
11 "edition2015",
12 "edition2018",
13 "edition2021",
14];
6 15
7pub(crate) fn format_docs(src: &str) -> String { 16pub(crate) fn format_docs(src: &str) -> String {
8 let mut processed_lines = Vec::new(); 17 let mut processed_lines = Vec::new();
@@ -97,7 +106,7 @@ mod tests {
97 106
98 #[test] 107 #[test]
99 fn test_format_docs_preserves_newlines() { 108 fn test_format_docs_preserves_newlines() {
100 let comment = "this\nis\nultiline"; 109 let comment = "this\nis\nmultiline";
101 assert_eq!(format_docs(comment), comment); 110 assert_eq!(format_docs(comment), comment);
102 } 111 }
103 112
diff --git a/crates/rust-analyzer/src/op_queue.rs b/crates/rust-analyzer/src/op_queue.rs
new file mode 100644
index 000000000..51d66f4b3
--- /dev/null
+++ b/crates/rust-analyzer/src/op_queue.rs
@@ -0,0 +1,25 @@
1//! Bookkeeping to make sure only one long-running operation is executed.
2
3#[derive(Default)]
4pub(crate) struct OpQueue {
5 op_scheduled: bool,
6 op_in_progress: bool,
7}
8
9impl OpQueue {
10 pub(crate) fn request_op(&mut self) {
11 self.op_scheduled = true;
12 }
13 pub(crate) fn should_start_op(&mut self) -> bool {
14 if !self.op_in_progress && self.op_scheduled {
15 self.op_in_progress = true;
16 self.op_scheduled = false;
17 return true;
18 }
19 false
20 }
21 pub(crate) fn op_completed(&mut self) {
22 assert!(self.op_in_progress);
23 self.op_in_progress = false;
24 }
25}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 79e39e3a5..97e20362f 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -15,16 +15,23 @@ use crate::{
15}; 15};
16use lsp_ext::StatusParams; 16use lsp_ext::StatusParams;
17 17
18#[derive(Debug)]
19pub(crate) enum ProjectWorkspaceProgress {
20 Begin,
21 Report(String),
22 End(Vec<anyhow::Result<ProjectWorkspace>>),
23}
24
18impl GlobalState { 25impl GlobalState {
19 pub(crate) fn update_configuration(&mut self, config: Config) { 26 pub(crate) fn update_configuration(&mut self, config: Config) {
20 let _p = profile::span("GlobalState::update_configuration"); 27 let _p = profile::span("GlobalState::update_configuration");
21 let old_config = mem::replace(&mut self.config, config); 28 let old_config = mem::replace(&mut self.config, Arc::new(config));
22 if self.config.lru_capacity != old_config.lru_capacity { 29 if self.config.lru_capacity() != old_config.lru_capacity() {
23 self.analysis_host.update_lru_capacity(old_config.lru_capacity); 30 self.analysis_host.update_lru_capacity(self.config.lru_capacity());
24 } 31 }
25 if self.config.linked_projects != old_config.linked_projects { 32 if self.config.linked_projects() != old_config.linked_projects() {
26 self.fetch_workspaces() 33 self.fetch_workspaces_request()
27 } else if self.config.flycheck != old_config.flycheck { 34 } else if self.config.flycheck() != old_config.flycheck() {
28 self.reload_flycheck(); 35 self.reload_flycheck();
29 } 36 }
30 } 37 }
@@ -36,8 +43,8 @@ impl GlobalState {
36 Status::Loading | Status::NeedsReload => return, 43 Status::Loading | Status::NeedsReload => return,
37 Status::Ready | Status::Invalid => (), 44 Status::Ready | Status::Invalid => (),
38 } 45 }
39 if self.config.cargo_autoreload { 46 if self.config.cargo_autoreload() {
40 self.fetch_workspaces(); 47 self.fetch_workspaces_request();
41 } else { 48 } else {
42 self.transition(Status::NeedsReload); 49 self.transition(Status::NeedsReload);
43 } 50 }
@@ -79,7 +86,7 @@ impl GlobalState {
79 } 86 }
80 pub(crate) fn transition(&mut self, new_status: Status) { 87 pub(crate) fn transition(&mut self, new_status: Status) {
81 self.status = new_status; 88 self.status = new_status;
82 if self.config.client_caps.status_notification { 89 if self.config.status_notification() {
83 let lsp_status = match new_status { 90 let lsp_status = match new_status {
84 Status::Loading => lsp_ext::Status::Loading, 91 Status::Loading => lsp_ext::Status::Loading,
85 Status::Ready => lsp_ext::Status::Ready, 92 Status::Ready => lsp_ext::Status::Ready,
@@ -91,28 +98,59 @@ impl GlobalState {
91 }); 98 });
92 } 99 }
93 } 100 }
94 pub(crate) fn fetch_workspaces(&mut self) { 101
102 pub(crate) fn fetch_workspaces_request(&mut self) {
103 self.fetch_workspaces_queue.request_op()
104 }
105 pub(crate) fn fetch_workspaces_if_needed(&mut self) {
95 log::info!("will fetch workspaces"); 106 log::info!("will fetch workspaces");
96 self.task_pool.handle.spawn({ 107 if !self.fetch_workspaces_queue.should_start_op() {
97 let linked_projects = self.config.linked_projects.clone(); 108 return;
98 let cargo_config = self.config.cargo.clone(); 109 }
99 move || { 110
111 self.task_pool.handle.spawn_with_sender({
112 let linked_projects = self.config.linked_projects();
113 let cargo_config = self.config.cargo();
114
115 move |sender| {
116 let progress = {
117 let sender = sender.clone();
118 move |msg| {
119 sender
120 .send(Task::FetchWorkspace(ProjectWorkspaceProgress::Report(msg)))
121 .unwrap()
122 }
123 };
124
125 sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::Begin)).unwrap();
126
100 let workspaces = linked_projects 127 let workspaces = linked_projects
101 .iter() 128 .iter()
102 .map(|project| match project { 129 .map(|project| match project {
103 LinkedProject::ProjectManifest(manifest) => { 130 LinkedProject::ProjectManifest(manifest) => {
104 project_model::ProjectWorkspace::load(manifest.clone(), &cargo_config) 131 project_model::ProjectWorkspace::load(
132 manifest.clone(),
133 &cargo_config,
134 &progress,
135 )
105 } 136 }
106 LinkedProject::InlineJsonProject(it) => { 137 LinkedProject::InlineJsonProject(it) => {
107 project_model::ProjectWorkspace::load_inline(it.clone()) 138 project_model::ProjectWorkspace::load_inline(it.clone())
108 } 139 }
109 }) 140 })
110 .collect::<Vec<_>>(); 141 .collect::<Vec<_>>();
142
111 log::info!("did fetch workspaces {:?}", workspaces); 143 log::info!("did fetch workspaces {:?}", workspaces);
112 Task::Workspaces(workspaces) 144 sender
145 .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(workspaces)))
146 .unwrap();
113 } 147 }
114 }); 148 });
115 } 149 }
150 pub(crate) fn fetch_workspaces_completed(&mut self) {
151 self.fetch_workspaces_queue.op_completed()
152 }
153
116 pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) { 154 pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) {
117 let _p = profile::span("GlobalState::switch_workspaces"); 155 let _p = profile::span("GlobalState::switch_workspaces");
118 log::info!("will switch workspaces: {:?}", workspaces); 156 log::info!("will switch workspaces: {:?}", workspaces);
@@ -143,36 +181,41 @@ impl GlobalState {
143 return; 181 return;
144 } 182 }
145 183
146 if let FilesWatcher::Client = self.config.files.watcher { 184 if let FilesWatcher::Client = self.config.files().watcher {
147 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { 185 if self.config.did_change_watched_files_dynamic_registration() {
148 watchers: workspaces 186 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
149 .iter() 187 watchers: workspaces
150 .flat_map(ProjectWorkspace::to_roots) 188 .iter()
151 .filter(|it| it.is_member) 189 .flat_map(ProjectWorkspace::to_roots)
152 .flat_map(|root| { 190 .filter(|it| it.is_member)
153 root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display())) 191 .flat_map(|root| {
154 }) 192 root.include.into_iter().map(|it| format!("{}/**/*.rs", it.display()))
155 .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None }) 193 })
156 .collect(), 194 .map(|glob_pattern| lsp_types::FileSystemWatcher {
157 }; 195 glob_pattern,
158 let registration = lsp_types::Registration { 196 kind: None,
159 id: "workspace/didChangeWatchedFiles".to_string(), 197 })
160 method: "workspace/didChangeWatchedFiles".to_string(), 198 .collect(),
161 register_options: Some(serde_json::to_value(registration_options).unwrap()), 199 };
162 }; 200 let registration = lsp_types::Registration {
163 self.send_request::<lsp_types::request::RegisterCapability>( 201 id: "workspace/didChangeWatchedFiles".to_string(),
164 lsp_types::RegistrationParams { registrations: vec![registration] }, 202 method: "workspace/didChangeWatchedFiles".to_string(),
165 |_, _| (), 203 register_options: Some(serde_json::to_value(registration_options).unwrap()),
166 ); 204 };
205 self.send_request::<lsp_types::request::RegisterCapability>(
206 lsp_types::RegistrationParams { registrations: vec![registration] },
207 |_, _| (),
208 );
209 }
167 } 210 }
168 211
169 let mut change = Change::new(); 212 let mut change = Change::new();
170 213
171 let project_folders = ProjectFolders::new(&workspaces); 214 let project_folders = ProjectFolders::new(&workspaces);
172 215
173 self.proc_macro_client = match &self.config.proc_macro_srv { 216 self.proc_macro_client = match self.config.proc_macro_srv() {
174 None => None, 217 None => None,
175 Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) { 218 Some((path, args)) => match ProcMacroClient::extern_process(path.clone(), args) {
176 Ok(it) => Some(it), 219 Ok(it) => Some(it),
177 Err(err) => { 220 Err(err) => {
178 log::error!( 221 log::error!(
@@ -185,7 +228,7 @@ impl GlobalState {
185 }, 228 },
186 }; 229 };
187 230
188 let watch = match self.config.files.watcher { 231 let watch = match self.config.files().watcher {
189 FilesWatcher::Client => vec![], 232 FilesWatcher::Client => vec![],
190 FilesWatcher::Notify => project_folders.watch, 233 FilesWatcher::Notify => project_folders.watch,
191 }; 234 };
@@ -211,7 +254,7 @@ impl GlobalState {
211 }; 254 };
212 for ws in workspaces.iter() { 255 for ws in workspaces.iter() {
213 crate_graph.extend(ws.to_crate_graph( 256 crate_graph.extend(ws.to_crate_graph(
214 self.config.cargo.target.as_deref(), 257 self.config.cargo().target.as_deref(),
215 self.proc_macro_client.as_ref(), 258 self.proc_macro_client.as_ref(),
216 &mut load, 259 &mut load,
217 )); 260 ));
@@ -231,7 +274,7 @@ impl GlobalState {
231 } 274 }
232 275
233 fn reload_flycheck(&mut self) { 276 fn reload_flycheck(&mut self) {
234 let config = match self.config.flycheck.clone() { 277 let config = match self.config.flycheck() {
235 Some(it) => it, 278 Some(it) => it,
236 None => { 279 None => {
237 self.flycheck = Vec::new(); 280 self.flycheck = Vec::new();
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index c2f6a655d..7ce9a4ab6 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -38,16 +38,25 @@ macro_rules! define_semantic_token_types {
38} 38}
39 39
40define_semantic_token_types![ 40define_semantic_token_types![
41 (ANGLE, "angle"),
41 (ATTRIBUTE, "attribute"), 42 (ATTRIBUTE, "attribute"),
42 (BOOLEAN, "boolean"), 43 (BOOLEAN, "boolean"),
44 (BRACE, "brace"),
45 (BRACKET, "bracket"),
43 (BUILTIN_TYPE, "builtinType"), 46 (BUILTIN_TYPE, "builtinType"),
47 (COMMA, "comma"),
48 (COLON, "colon"),
49 (DOT, "dot"),
44 (ESCAPE_SEQUENCE, "escapeSequence"), 50 (ESCAPE_SEQUENCE, "escapeSequence"),
45 (FORMAT_SPECIFIER, "formatSpecifier"), 51 (FORMAT_SPECIFIER, "formatSpecifier"),
46 (GENERIC, "generic"), 52 (GENERIC, "generic"),
53 (CONST_PARAMETER, "constParameter"),
47 (LIFETIME, "lifetime"), 54 (LIFETIME, "lifetime"),
48 (LABEL, "label"), 55 (LABEL, "label"),
56 (PARENTHESIS, "parenthesis"),
49 (PUNCTUATION, "punctuation"), 57 (PUNCTUATION, "punctuation"),
50 (SELF_KEYWORD, "selfKeyword"), 58 (SELF_KEYWORD, "selfKeyword"),
59 (SEMICOLON, "semicolon"),
51 (TYPE_ALIAS, "typeAlias"), 60 (TYPE_ALIAS, "typeAlias"),
52 (UNION, "union"), 61 (UNION, "union"),
53 (UNRESOLVED_REFERENCE, "unresolvedReference"), 62 (UNRESOLVED_REFERENCE, "unresolvedReference"),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 1a38e79f0..a7ff8975a 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -6,10 +6,10 @@ use std::{
6 6
7use ide::{ 7use ide::{
8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId, 8 Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation, FileId,
9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, 9 FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, HlRange, HlTag, Indel,
10 HighlightedRange, Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, 10 InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget, ReferenceAccess,
11 NavigationTarget, ReferenceAccess, Runnable, Severity, SourceChange, SourceFileEdit, 11 RenameError, Runnable, Severity, SourceChange, SourceFileEdit, SymbolKind, TextEdit, TextRange,
12 SymbolKind, TextEdit, TextRange, TextSize, 12 TextSize,
13}; 13};
14use itertools::Itertools; 14use itertools::Itertools;
15 15
@@ -42,6 +42,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
42 SymbolKind::Field => lsp_types::SymbolKind::Field, 42 SymbolKind::Field => lsp_types::SymbolKind::Field,
43 SymbolKind::Static => lsp_types::SymbolKind::Constant, 43 SymbolKind::Static => lsp_types::SymbolKind::Constant,
44 SymbolKind::Const => lsp_types::SymbolKind::Constant, 44 SymbolKind::Const => lsp_types::SymbolKind::Constant,
45 SymbolKind::ConstParam => lsp_types::SymbolKind::Constant,
45 SymbolKind::Impl => lsp_types::SymbolKind::Object, 46 SymbolKind::Impl => lsp_types::SymbolKind::Object,
46 SymbolKind::Local 47 SymbolKind::Local
47 | SymbolKind::SelfParam 48 | SymbolKind::SelfParam
@@ -336,12 +337,15 @@ static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);
336pub(crate) fn semantic_tokens( 337pub(crate) fn semantic_tokens(
337 text: &str, 338 text: &str,
338 line_index: &LineIndex, 339 line_index: &LineIndex,
339 highlights: Vec<HighlightedRange>, 340 highlights: Vec<HlRange>,
340) -> lsp_types::SemanticTokens { 341) -> lsp_types::SemanticTokens {
341 let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string(); 342 let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string();
342 let mut builder = semantic_tokens::SemanticTokensBuilder::new(id); 343 let mut builder = semantic_tokens::SemanticTokensBuilder::new(id);
343 344
344 for highlight_range in highlights { 345 for highlight_range in highlights {
346 if highlight_range.highlight.is_empty() {
347 continue;
348 }
345 let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight); 349 let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
346 let token_index = semantic_tokens::type_index(type_); 350 let token_index = semantic_tokens::type_index(type_);
347 let modifier_bitset = mods.0; 351 let modifier_bitset = mods.0;
@@ -373,18 +377,19 @@ fn semantic_token_type_and_modifiers(
373) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { 377) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) {
374 let mut mods = semantic_tokens::ModifierSet::default(); 378 let mut mods = semantic_tokens::ModifierSet::default();
375 let type_ = match highlight.tag { 379 let type_ = match highlight.tag {
376 HighlightTag::Symbol(symbol) => match symbol { 380 HlTag::Symbol(symbol) => match symbol {
377 SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE, 381 SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE,
378 SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE, 382 SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE,
379 SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, 383 SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY,
380 SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, 384 SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER,
385 SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER,
381 SymbolKind::LifetimeParam => semantic_tokens::LIFETIME, 386 SymbolKind::LifetimeParam => semantic_tokens::LIFETIME,
382 SymbolKind::Label => semantic_tokens::LABEL, 387 SymbolKind::Label => semantic_tokens::LABEL,
383 SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER, 388 SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER,
384 SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD, 389 SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD,
385 SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE, 390 SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE,
386 SymbolKind::Function => { 391 SymbolKind::Function => {
387 if highlight.modifiers.contains(HighlightModifier::Associated) { 392 if highlight.mods.contains(HlMod::Associated) {
388 lsp_types::SemanticTokenType::METHOD 393 lsp_types::SemanticTokenType::METHOD
389 } else { 394 } else {
390 lsp_types::SemanticTokenType::FUNCTION 395 lsp_types::SemanticTokenType::FUNCTION
@@ -407,38 +412,44 @@ fn semantic_token_type_and_modifiers(
407 SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE, 412 SymbolKind::Trait => lsp_types::SemanticTokenType::INTERFACE,
408 SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO, 413 SymbolKind::Macro => lsp_types::SemanticTokenType::MACRO,
409 }, 414 },
410 HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE, 415 HlTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
411 HighlightTag::Dummy => semantic_tokens::GENERIC, 416 HlTag::None => semantic_tokens::GENERIC,
412 HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => { 417 HlTag::ByteLiteral | HlTag::NumericLiteral => lsp_types::SemanticTokenType::NUMBER,
413 lsp_types::SemanticTokenType::NUMBER 418 HlTag::BoolLiteral => semantic_tokens::BOOLEAN,
414 } 419 HlTag::CharLiteral | HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING,
415 HighlightTag::BoolLiteral => semantic_tokens::BOOLEAN, 420 HlTag::Comment => lsp_types::SemanticTokenType::COMMENT,
416 HighlightTag::CharLiteral | HighlightTag::StringLiteral => { 421 HlTag::Attribute => semantic_tokens::ATTRIBUTE,
417 lsp_types::SemanticTokenType::STRING 422 HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD,
418 } 423 HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
419 HighlightTag::Comment => lsp_types::SemanticTokenType::COMMENT, 424 HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
420 HighlightTag::Attribute => semantic_tokens::ATTRIBUTE, 425 HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR,
421 HighlightTag::Keyword => lsp_types::SemanticTokenType::KEYWORD, 426 HlTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
422 HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, 427 HlTag::Punctuation(punct) => match punct {
423 HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, 428 HlPunct::Bracket => semantic_tokens::BRACKET,
424 HighlightTag::Operator => lsp_types::SemanticTokenType::OPERATOR, 429 HlPunct::Brace => semantic_tokens::BRACE,
425 HighlightTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE, 430 HlPunct::Parenthesis => semantic_tokens::PARENTHESIS,
426 HighlightTag::Punctuation => semantic_tokens::PUNCTUATION, 431 HlPunct::Angle => semantic_tokens::ANGLE,
432 HlPunct::Comma => semantic_tokens::COMMA,
433 HlPunct::Dot => semantic_tokens::DOT,
434 HlPunct::Colon => semantic_tokens::COLON,
435 HlPunct::Semi => semantic_tokens::SEMICOLON,
436 HlPunct::Other => semantic_tokens::PUNCTUATION,
437 },
427 }; 438 };
428 439
429 for modifier in highlight.modifiers.iter() { 440 for modifier in highlight.mods.iter() {
430 let modifier = match modifier { 441 let modifier = match modifier {
431 HighlightModifier::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER, 442 HlMod::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER,
432 HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION, 443 HlMod::Definition => lsp_types::SemanticTokenModifier::DECLARATION,
433 HighlightModifier::Documentation => lsp_types::SemanticTokenModifier::DOCUMENTATION, 444 HlMod::Documentation => lsp_types::SemanticTokenModifier::DOCUMENTATION,
434 HighlightModifier::Injected => semantic_tokens::INJECTED, 445 HlMod::Injected => semantic_tokens::INJECTED,
435 HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, 446 HlMod::ControlFlow => semantic_tokens::CONTROL_FLOW,
436 HighlightModifier::Mutable => semantic_tokens::MUTABLE, 447 HlMod::Mutable => semantic_tokens::MUTABLE,
437 HighlightModifier::Consuming => semantic_tokens::CONSUMING, 448 HlMod::Consuming => semantic_tokens::CONSUMING,
438 HighlightModifier::Unsafe => semantic_tokens::UNSAFE, 449 HlMod::Unsafe => semantic_tokens::UNSAFE,
439 HighlightModifier::Callable => semantic_tokens::CALLABLE, 450 HlMod::Callable => semantic_tokens::CALLABLE,
440 HighlightModifier::Static => lsp_types::SemanticTokenModifier::STATIC, 451 HlMod::Static => lsp_types::SemanticTokenModifier::STATIC,
441 HighlightModifier::Associated => continue, 452 HlMod::Associated => continue,
442 }; 453 };
443 mods |= modifier; 454 mods |= modifier;
444 } 455 }
@@ -603,7 +614,7 @@ pub(crate) fn goto_definition_response(
603 src: Option<FileRange>, 614 src: Option<FileRange>,
604 targets: Vec<NavigationTarget>, 615 targets: Vec<NavigationTarget>,
605) -> Result<lsp_types::GotoDefinitionResponse> { 616) -> Result<lsp_types::GotoDefinitionResponse> {
606 if snap.config.client_caps.location_link { 617 if snap.config.location_link() {
607 let links = targets 618 let links = targets
608 .into_iter() 619 .into_iter()
609 .map(|nav| location_link(snap, src, nav)) 620 .map(|nav| location_link(snap, src, nav))
@@ -783,7 +794,7 @@ pub(crate) fn unresolved_code_action(
783 assert!(assist.source_change.is_none()); 794 assert!(assist.source_change.is_none());
784 let res = lsp_ext::CodeAction { 795 let res = lsp_ext::CodeAction {
785 title: assist.label.to_string(), 796 title: assist.label.to_string(),
786 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), 797 group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0),
787 kind: Some(code_action_kind(assist.id.1)), 798 kind: Some(code_action_kind(assist.id.1)),
788 edit: None, 799 edit: None,
789 is_preferred: None, 800 is_preferred: None,
@@ -803,7 +814,7 @@ pub(crate) fn resolved_code_action(
803 let res = lsp_ext::CodeAction { 814 let res = lsp_ext::CodeAction {
804 edit: Some(snippet_workspace_edit(snap, change)?), 815 edit: Some(snippet_workspace_edit(snap, change)?),
805 title: assist.label.to_string(), 816 title: assist.label.to_string(),
806 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), 817 group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0),
807 kind: Some(code_action_kind(assist.id.1)), 818 kind: Some(code_action_kind(assist.id.1)),
808 is_preferred: None, 819 is_preferred: None,
809 data: None, 820 data: None,
@@ -816,7 +827,7 @@ pub(crate) fn runnable(
816 file_id: FileId, 827 file_id: FileId,
817 runnable: Runnable, 828 runnable: Runnable,
818) -> Result<lsp_ext::Runnable> { 829) -> Result<lsp_ext::Runnable> {
819 let config = &snap.config.runnables; 830 let config = snap.config.runnables();
820 let spec = CargoTargetSpec::for_file(snap, file_id)?; 831 let spec = CargoTargetSpec::for_file(snap, file_id)?;
821 let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone()); 832 let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone());
822 let target = spec.as_ref().map(|s| s.target.clone()); 833 let target = spec.as_ref().map(|s| s.target.clone());
@@ -831,9 +842,9 @@ pub(crate) fn runnable(
831 kind: lsp_ext::RunnableKind::Cargo, 842 kind: lsp_ext::RunnableKind::Cargo,
832 args: lsp_ext::CargoRunnable { 843 args: lsp_ext::CargoRunnable {
833 workspace_root: workspace_root.map(|it| it.into()), 844 workspace_root: workspace_root.map(|it| it.into()),
834 override_cargo: config.override_cargo.clone(), 845 override_cargo: config.override_cargo,
835 cargo_args, 846 cargo_args,
836 cargo_extra_args: config.cargo_extra_args.clone(), 847 cargo_extra_args: config.cargo_extra_args,
837 executable_args, 848 executable_args,
838 expect_test: None, 849 expect_test: None,
839 }, 850 },
@@ -845,9 +856,14 @@ pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent {
845 lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value } 856 lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value }
846} 857}
847 858
859pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
860 crate::LspError { code: lsp_server::ErrorCode::InvalidParams as i32, message: err.to_string() }
861}
862
848#[cfg(test)] 863#[cfg(test)]
849mod tests { 864mod tests {
850 use ide::Analysis; 865 use ide::Analysis;
866 use ide_db::helpers::SnippetCap;
851 867
852 use super::*; 868 use super::*;
853 869
@@ -858,7 +874,7 @@ mod tests {
858 fn foo(arg: &Foo) {} 874 fn foo(arg: &Foo) {}
859 fn main() { 875 fn main() {
860 let arg = Foo; 876 let arg = Foo;
861 foo(<|>) 877 foo($0)
862 }"#; 878 }"#;
863 879
864 let (offset, text) = test_utils::extract_offset(fixture); 880 let (offset, text) = test_utils::extract_offset(fixture);
@@ -866,7 +882,14 @@ mod tests {
866 let (analysis, file_id) = Analysis::from_single_file(text); 882 let (analysis, file_id) = Analysis::from_single_file(text);
867 let completions: Vec<(String, Option<String>)> = analysis 883 let completions: Vec<(String, Option<String>)> = analysis
868 .completions( 884 .completions(
869 &ide::CompletionConfig::default(), 885 &ide::CompletionConfig {
886 enable_postfix_completions: true,
887 enable_autoimport_completions: true,
888 add_call_parenthesis: true,
889 add_call_argument_snippets: true,
890 snippet_cap: SnippetCap::new(true),
891 merge: None,
892 },
870 ide_db::base_db::FilePosition { file_id, offset }, 893 ide_db::base_db::FilePosition { file_id, offset },
871 ) 894 )
872 .unwrap() 895 .unwrap()
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs
index e51eb2626..80bde29b9 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/main.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs
@@ -13,13 +13,17 @@ mod support;
13 13
14use std::{collections::HashMap, path::PathBuf, time::Instant}; 14use std::{collections::HashMap, path::PathBuf, time::Instant};
15 15
16use expect_test::expect;
16use lsp_types::{ 17use lsp_types::{
17 notification::DidOpenTextDocument, 18 notification::DidOpenTextDocument,
18 request::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest}, 19 request::{
20 CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest,
21 WillRenameFiles,
22 },
19 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, 23 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
20 DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams, 24 DocumentFormattingParams, FileRename, FormattingOptions, GotoDefinitionParams, HoverParams,
21 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, 25 PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem,
22 WorkDoneProgressParams, 26 TextDocumentPositionParams, WorkDoneProgressParams,
23}; 27};
24use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams}; 28use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams};
25use serde_json::json; 29use serde_json::json;
@@ -190,15 +194,10 @@ pub use std::collections::HashMap;
190 }, 194 },
191 json!([ 195 json!([
192 { 196 {
193 "newText": r#"mod bar; 197 "newText": "",
194
195fn main() {}
196
197pub use std::collections::HashMap;
198"#,
199 "range": { 198 "range": {
200 "end": { "character": 0, "line": 6 }, 199 "end": { "character": 0, "line": 3 },
201 "start": { "character": 0, "line": 0 } 200 "start": { "character": 11, "line": 2 }
202 } 201 }
203 } 202 }
204 ]), 203 ]),
@@ -248,17 +247,17 @@ pub use std::collections::HashMap;
248 }, 247 },
249 json!([ 248 json!([
250 { 249 {
251 "newText": r#"mod bar; 250 "newText": "",
252 251 "range": {
253async fn test() {} 252 "end": { "character": 0, "line": 3 },
254 253 "start": { "character": 17, "line": 2 }
255fn main() {} 254 }
256 255 },
257pub use std::collections::HashMap; 256 {
258"#, 257 "newText": "",
259 "range": { 258 "range": {
260 "end": { "character": 0, "line": 9 }, 259 "end": { "character": 0, "line": 6 },
261 "start": { "character": 0, "line": 0 } 260 "start": { "character": 11, "line": 5 }
262 } 261 }
263 } 262 }
264 ]), 263 ]),
@@ -574,9 +573,9 @@ fn main() {
574} 573}
575"###, 574"###,
576 ) 575 )
577 .with_config(|config| { 576 .with_config(serde_json::json!({
578 config.cargo.load_out_dirs_from_check = true; 577 "cargo": { "loadOutDirsFromCheck": true }
579 }) 578 }))
580 .server() 579 .server()
581 .wait_until_workspace_is_loaded(); 580 .wait_until_workspace_is_loaded();
582 581
@@ -717,12 +716,13 @@ pub fn foo(_input: TokenStream) -> TokenStream {
717 716
718"###, 717"###,
719 ) 718 )
720 .with_config(|config| { 719 .with_config(serde_json::json!({
721 let macro_srv_path = PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer")); 720 "cargo": { "loadOutDirsFromCheck": true },
722 721 "procMacro": {
723 config.cargo.load_out_dirs_from_check = true; 722 "enable": true,
724 config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".into()])); 723 "server": PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer")),
725 }) 724 }
725 }))
726 .root("foo") 726 .root("foo")
727 .root("bar") 727 .root("bar")
728 .server() 728 .server()
@@ -735,6 +735,149 @@ pub fn foo(_input: TokenStream) -> TokenStream {
735 ), 735 ),
736 work_done_progress_params: Default::default(), 736 work_done_progress_params: Default::default(),
737 }); 737 });
738 let value = res.get("contents").unwrap().get("value").unwrap().to_string(); 738 let value = res.get("contents").unwrap().get("value").unwrap().as_str().unwrap();
739 assert_eq!(value, r#""\n```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#) 739
740 expect![[r#"
741
742 ```rust
743 foo::Bar
744 ```
745
746 ```rust
747 fn bar()
748 ```"#]]
749 .assert_eq(&value);
750}
751
752#[test]
753fn test_will_rename_files_same_level() {
754 if skip_slow_tests() {
755 return;
756 }
757
758 let tmp_dir = TestDir::new();
759 let tmp_dir_path = tmp_dir.path().to_owned();
760 let tmp_dir_str = tmp_dir_path.to_str().unwrap();
761 let base_path = PathBuf::from(format!("file://{}", tmp_dir_str));
762
763 let code = r#"
764//- /Cargo.toml
765[package]
766name = "foo"
767version = "0.0.0"
768
769//- /src/lib.rs
770mod old_file;
771mod from_mod;
772mod to_mod;
773mod old_folder;
774fn main() {}
775
776//- /src/old_file.rs
777
778//- /src/old_folder/mod.rs
779
780//- /src/from_mod/mod.rs
781
782//- /src/to_mod/foo.rs
783
784"#;
785 let server =
786 Project::with_fixture(&code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded();
787
788 //rename same level file
789 server.request::<WillRenameFiles>(
790 RenameFilesParams {
791 files: vec![FileRename {
792 old_uri: base_path.join("src/old_file.rs").to_str().unwrap().to_string(),
793 new_uri: base_path.join("src/new_file.rs").to_str().unwrap().to_string(),
794 }],
795 },
796 json!({
797 "documentChanges": [
798 {
799 "textDocument": {
800 "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace("\\", "/")),
801 "version": null
802 },
803 "edits": [
804 {
805 "range": {
806 "start": {
807 "line": 0,
808 "character": 4
809 },
810 "end": {
811 "line": 0,
812 "character": 12
813 }
814 },
815 "newText": "new_file"
816 }
817 ]
818 }
819 ]
820 }),
821 );
822
823 //rename file from mod.rs to foo.rs
824 server.request::<WillRenameFiles>(
825 RenameFilesParams {
826 files: vec![FileRename {
827 old_uri: base_path.join("src/from_mod/mod.rs").to_str().unwrap().to_string(),
828 new_uri: base_path.join("src/from_mod/foo.rs").to_str().unwrap().to_string(),
829 }],
830 },
831 json!({
832 "documentChanges": []
833 }),
834 );
835
836 //rename file from foo.rs to mod.rs
837 server.request::<WillRenameFiles>(
838 RenameFilesParams {
839 files: vec![FileRename {
840 old_uri: base_path.join("src/to_mod/foo.rs").to_str().unwrap().to_string(),
841 new_uri: base_path.join("src/to_mod/mod.rs").to_str().unwrap().to_string(),
842 }],
843 },
844 json!({
845 "documentChanges": []
846 }),
847 );
848
849 //rename same level file
850 server.request::<WillRenameFiles>(
851 RenameFilesParams {
852 files: vec![FileRename {
853 old_uri: base_path.join("src/old_folder").to_str().unwrap().to_string(),
854 new_uri: base_path.join("src/new_folder").to_str().unwrap().to_string(),
855 }],
856 },
857 json!({
858 "documentChanges": [
859 {
860 "textDocument": {
861 "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace("\\", "/")),
862 "version": null
863 },
864 "edits": [
865 {
866 "range": {
867 "start": {
868 "line": 3,
869 "character": 4
870 },
871 "end": {
872 "line": 3,
873 "character": 14
874 }
875 },
876 "newText": "new_folder"
877 }
878 ]
879 }
880 ]
881 }),
882 );
740} 883}
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs
index 456125789..2658ee185 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/support.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs
@@ -12,11 +12,8 @@ use lsp_types::{
12 notification::Exit, request::Shutdown, TextDocumentIdentifier, Url, WorkDoneProgress, 12 notification::Exit, request::Shutdown, TextDocumentIdentifier, Url, WorkDoneProgress,
13}; 13};
14use lsp_types::{ProgressParams, ProgressParamsValue}; 14use lsp_types::{ProgressParams, ProgressParamsValue};
15use project_model::{CargoConfig, ProjectManifest}; 15use project_model::ProjectManifest;
16use rust_analyzer::{ 16use rust_analyzer::{config::Config, main_loop};
17 config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject},
18 main_loop,
19};
20use serde::Serialize; 17use serde::Serialize;
21use serde_json::{to_string_pretty, Value}; 18use serde_json::{to_string_pretty, Value};
22use test_utils::{find_mismatch, Fixture}; 19use test_utils::{find_mismatch, Fixture};
@@ -29,12 +26,18 @@ pub(crate) struct Project<'a> {
29 with_sysroot: bool, 26 with_sysroot: bool,
30 tmp_dir: Option<TestDir>, 27 tmp_dir: Option<TestDir>,
31 roots: Vec<PathBuf>, 28 roots: Vec<PathBuf>,
32 config: Option<Box<dyn Fn(&mut Config)>>, 29 config: serde_json::Value,
33} 30}
34 31
35impl<'a> Project<'a> { 32impl<'a> Project<'a> {
36 pub(crate) fn with_fixture(fixture: &str) -> Project { 33 pub(crate) fn with_fixture(fixture: &str) -> Project {
37 Project { fixture, tmp_dir: None, roots: vec![], with_sysroot: false, config: None } 34 Project {
35 fixture,
36 tmp_dir: None,
37 roots: vec![],
38 with_sysroot: false,
39 config: serde_json::Value::Null,
40 }
38 } 41 }
39 42
40 pub(crate) fn tmp_dir(mut self, tmp_dir: TestDir) -> Project<'a> { 43 pub(crate) fn tmp_dir(mut self, tmp_dir: TestDir) -> Project<'a> {
@@ -52,8 +55,8 @@ impl<'a> Project<'a> {
52 self 55 self
53 } 56 }
54 57
55 pub(crate) fn with_config(mut self, config: impl Fn(&mut Config) + 'static) -> Project<'a> { 58 pub(crate) fn with_config(mut self, config: serde_json::Value) -> Project<'a> {
56 self.config = Some(Box::new(config)); 59 self.config = config;
57 self 60 self
58 } 61 }
59 62
@@ -77,27 +80,40 @@ impl<'a> Project<'a> {
77 if roots.is_empty() { 80 if roots.is_empty() {
78 roots.push(tmp_dir_path.clone()); 81 roots.push(tmp_dir_path.clone());
79 } 82 }
80 let linked_projects = roots 83 let discovered_projects = roots
81 .into_iter() 84 .into_iter()
82 .map(|it| ProjectManifest::discover_single(&it).unwrap()) 85 .map(|it| ProjectManifest::discover_single(&it).unwrap())
83 .map(LinkedProject::from)
84 .collect::<Vec<_>>(); 86 .collect::<Vec<_>>();
85 87
86 let mut config = Config { 88 let mut config = Config::new(
87 client_caps: ClientCapsConfig { 89 tmp_dir_path,
88 location_link: true, 90 lsp_types::ClientCapabilities {
89 code_action_literals: true, 91 text_document: Some(lsp_types::TextDocumentClientCapabilities {
90 work_done_progress: true, 92 definition: Some(lsp_types::GotoCapability {
93 link_support: Some(true),
94 ..Default::default()
95 }),
96 code_action: Some(lsp_types::CodeActionClientCapabilities {
97 code_action_literal_support: Some(
98 lsp_types::CodeActionLiteralSupport::default(),
99 ),
100 ..Default::default()
101 }),
102 hover: Some(lsp_types::HoverClientCapabilities {
103 content_format: Some(vec![lsp_types::MarkupKind::Markdown]),
104 ..Default::default()
105 }),
106 ..Default::default()
107 }),
108 window: Some(lsp_types::WindowClientCapabilities {
109 work_done_progress: Some(true),
110 ..Default::default()
111 }),
91 ..Default::default() 112 ..Default::default()
92 }, 113 },
93 cargo: CargoConfig { no_sysroot: !self.with_sysroot, ..Default::default() }, 114 );
94 linked_projects, 115 config.discovered_projects = Some(discovered_projects);
95 files: FilesConfig { watcher: FilesWatcher::Client, exclude: Vec::new() }, 116 config.update(self.config);
96 ..Config::new(tmp_dir_path)
97 };
98 if let Some(f) = &self.config {
99 f(&mut config)
100 }
101 117
102 Server::new(tmp_dir, config) 118 Server::new(tmp_dir, config)
103 } 119 }
diff --git a/crates/ssr/Cargo.toml b/crates/ssr/Cargo.toml
index 98ed25fb6..cc8136d22 100644
--- a/crates/ssr/Cargo.toml
+++ b/crates/ssr/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12 12
13[dependencies] 13[dependencies]
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15itertools = "0.9.0" 15itertools = "0.10.0"
16 16
17text_edit = { path = "../text_edit", version = "0.0.0" } 17text_edit = { path = "../text_edit", version = "0.0.0" }
18syntax = { path = "../syntax", version = "0.0.0" } 18syntax = { path = "../syntax", version = "0.0.0" }
@@ -21,4 +21,4 @@ hir = { path = "../hir", version = "0.0.0" }
21test_utils = { path = "../test_utils", version = "0.0.0" } 21test_utils = { path = "../test_utils", version = "0.0.0" }
22 22
23[dev-dependencies] 23[dev-dependencies]
24expect-test = "1.0" 24expect-test = "1.1"
diff --git a/crates/ssr/src/matching.rs b/crates/ssr/src/matching.rs
index 99b187311..6cf831431 100644
--- a/crates/ssr/src/matching.rs
+++ b/crates/ssr/src/matching.rs
@@ -473,7 +473,9 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
473 } 473 }
474 SyntaxElement::Node(n) => { 474 SyntaxElement::Node(n) => {
475 if let Some(first_token) = n.first_token() { 475 if let Some(first_token) = n.first_token() {
476 if Some(first_token.to_string()) == next_pattern_token { 476 if Some(first_token.text().as_str())
477 == next_pattern_token.as_deref()
478 {
477 if let Some(SyntaxElement::Node(p)) = pattern.next() { 479 if let Some(SyntaxElement::Node(p)) = pattern.next() {
478 // We have a subtree that starts with the next token in our pattern. 480 // We have a subtree that starts with the next token in our pattern.
479 self.attempt_match_token_tree(phase, &p, &n)?; 481 self.attempt_match_token_tree(phase, &p, &n)?;
diff --git a/crates/ssr/src/parsing.rs b/crates/ssr/src/parsing.rs
index f3b084baf..3d5e4feb7 100644
--- a/crates/ssr/src/parsing.rs
+++ b/crates/ssr/src/parsing.rs
@@ -73,11 +73,18 @@ impl ParsedRule {
73 placeholders_by_stand_in: pattern.placeholders_by_stand_in(), 73 placeholders_by_stand_in: pattern.placeholders_by_stand_in(),
74 rules: Vec::new(), 74 rules: Vec::new(),
75 }; 75 };
76 builder.try_add(ast::Expr::parse(&raw_pattern), raw_template.map(ast::Expr::parse)); 76
77 let raw_template_stmt = raw_template.map(ast::Stmt::parse);
78 if let raw_template_expr @ Some(Ok(_)) = raw_template.map(ast::Expr::parse) {
79 builder.try_add(ast::Expr::parse(&raw_pattern), raw_template_expr);
80 } else {
81 builder.try_add(ast::Expr::parse(&raw_pattern), raw_template_stmt.clone());
82 }
77 builder.try_add(ast::Type::parse(&raw_pattern), raw_template.map(ast::Type::parse)); 83 builder.try_add(ast::Type::parse(&raw_pattern), raw_template.map(ast::Type::parse));
78 builder.try_add(ast::Item::parse(&raw_pattern), raw_template.map(ast::Item::parse)); 84 builder.try_add(ast::Item::parse(&raw_pattern), raw_template.map(ast::Item::parse));
79 builder.try_add(ast::Path::parse(&raw_pattern), raw_template.map(ast::Path::parse)); 85 builder.try_add(ast::Path::parse(&raw_pattern), raw_template.map(ast::Path::parse));
80 builder.try_add(ast::Pat::parse(&raw_pattern), raw_template.map(ast::Pat::parse)); 86 builder.try_add(ast::Pat::parse(&raw_pattern), raw_template.map(ast::Pat::parse));
87 builder.try_add(ast::Stmt::parse(&raw_pattern), raw_template_stmt);
81 builder.build() 88 builder.build()
82 } 89 }
83} 90}
@@ -88,7 +95,11 @@ struct RuleBuilder {
88} 95}
89 96
90impl RuleBuilder { 97impl RuleBuilder {
91 fn try_add<T: AstNode>(&mut self, pattern: Result<T, ()>, template: Option<Result<T, ()>>) { 98 fn try_add<T: AstNode, T2: AstNode>(
99 &mut self,
100 pattern: Result<T, ()>,
101 template: Option<Result<T2, ()>>,
102 ) {
92 match (pattern, template) { 103 match (pattern, template) {
93 (Ok(pattern), Some(Ok(template))) => self.rules.push(ParsedRule { 104 (Ok(pattern), Some(Ok(template))) => self.rules.push(ParsedRule {
94 placeholders_by_stand_in: self.placeholders_by_stand_in.clone(), 105 placeholders_by_stand_in: self.placeholders_by_stand_in.clone(),
diff --git a/crates/ssr/src/search.rs b/crates/ssr/src/search.rs
index 44b5db029..836eb94b2 100644
--- a/crates/ssr/src/search.rs
+++ b/crates/ssr/src/search.rs
@@ -5,10 +5,10 @@ use crate::{
5 resolving::{ResolvedPath, ResolvedPattern, ResolvedRule}, 5 resolving::{ResolvedPath, ResolvedPattern, ResolvedRule},
6 Match, MatchFinder, 6 Match, MatchFinder,
7}; 7};
8use ide_db::base_db::{FileId, FileRange};
9use ide_db::{ 8use ide_db::{
9 base_db::{FileId, FileRange},
10 defs::Definition, 10 defs::Definition,
11 search::{Reference, SearchScope}, 11 search::{SearchScope, UsageSearchResult},
12}; 12};
13use rustc_hash::FxHashSet; 13use rustc_hash::FxHashSet;
14use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; 14use syntax::{ast, AstNode, SyntaxKind, SyntaxNode};
@@ -20,7 +20,7 @@ use test_utils::mark;
20/// them more than once. 20/// them more than once.
21#[derive(Default)] 21#[derive(Default)]
22pub(crate) struct UsageCache { 22pub(crate) struct UsageCache {
23 usages: Vec<(Definition, Vec<Reference>)>, 23 usages: Vec<(Definition, UsageSearchResult)>,
24} 24}
25 25
26impl<'db> MatchFinder<'db> { 26impl<'db> MatchFinder<'db> {
@@ -58,8 +58,8 @@ impl<'db> MatchFinder<'db> {
58 ) { 58 ) {
59 if let Some(resolved_path) = pick_path_for_usages(pattern) { 59 if let Some(resolved_path) = pick_path_for_usages(pattern) {
60 let definition: Definition = resolved_path.resolution.clone().into(); 60 let definition: Definition = resolved_path.resolution.clone().into();
61 for reference in self.find_usages(usage_cache, definition) { 61 for file_range in self.find_usages(usage_cache, definition).file_ranges() {
62 if let Some(node_to_match) = self.find_node_to_match(resolved_path, reference) { 62 if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) {
63 if !is_search_permitted_ancestors(&node_to_match) { 63 if !is_search_permitted_ancestors(&node_to_match) {
64 mark::hit!(use_declaration_with_braces); 64 mark::hit!(use_declaration_with_braces);
65 continue; 65 continue;
@@ -73,11 +73,11 @@ impl<'db> MatchFinder<'db> {
73 fn find_node_to_match( 73 fn find_node_to_match(
74 &self, 74 &self,
75 resolved_path: &ResolvedPath, 75 resolved_path: &ResolvedPath,
76 reference: &Reference, 76 file_range: FileRange,
77 ) -> Option<SyntaxNode> { 77 ) -> Option<SyntaxNode> {
78 let file = self.sema.parse(reference.file_range.file_id); 78 let file = self.sema.parse(file_range.file_id);
79 let depth = resolved_path.depth as usize; 79 let depth = resolved_path.depth as usize;
80 let offset = reference.file_range.range.start(); 80 let offset = file_range.range.start();
81 if let Some(path) = 81 if let Some(path) =
82 self.sema.find_node_at_offset_with_descend::<ast::Path>(file.syntax(), offset) 82 self.sema.find_node_at_offset_with_descend::<ast::Path>(file.syntax(), offset)
83 { 83 {
@@ -108,7 +108,7 @@ impl<'db> MatchFinder<'db> {
108 &self, 108 &self,
109 usage_cache: &'a mut UsageCache, 109 usage_cache: &'a mut UsageCache,
110 definition: Definition, 110 definition: Definition,
111 ) -> &'a [Reference] { 111 ) -> &'a UsageSearchResult {
112 // Logically if a lookup succeeds we should just return it. Unfortunately returning it would 112 // Logically if a lookup succeeds we should just return it. Unfortunately returning it would
113 // extend the lifetime of the borrow, then we wouldn't be able to do the insertion on a 113 // extend the lifetime of the borrow, then we wouldn't be able to do the insertion on a
114 // cache miss. This is a limitation of NLL and is fixed with Polonius. For now we do two 114 // cache miss. This is a limitation of NLL and is fixed with Polonius. For now we do two
@@ -250,7 +250,7 @@ fn is_search_permitted(node: &SyntaxNode) -> bool {
250} 250}
251 251
252impl UsageCache { 252impl UsageCache {
253 fn find(&mut self, definition: &Definition) -> Option<&[Reference]> { 253 fn find(&mut self, definition: &Definition) -> Option<&UsageSearchResult> {
254 // We expect a very small number of cache entries (generally 1), so a linear scan should be 254 // We expect a very small number of cache entries (generally 1), so a linear scan should be
255 // fast enough and avoids the need to implement Hash for Definition. 255 // fast enough and avoids the need to implement Hash for Definition.
256 for (d, refs) in &self.usages { 256 for (d, refs) in &self.usages {
diff --git a/crates/ssr/src/tests.rs b/crates/ssr/src/tests.rs
index 63131f6ca..d6918c22d 100644
--- a/crates/ssr/src/tests.rs
+++ b/crates/ssr/src/tests.rs
@@ -59,7 +59,7 @@ fn parser_undefined_placeholder_in_replacement() {
59 ); 59 );
60} 60}
61 61
62/// `code` may optionally contain a cursor marker `<|>`. If it doesn't, then the position will be 62/// `code` may optionally contain a cursor marker `$0`. If it doesn't, then the position will be
63/// the start of the file. If there's a second cursor marker, then we'll return a single range. 63/// the start of the file. If there's a second cursor marker, then we'll return a single range.
64pub(crate) fn single_file(code: &str) -> (ide_db::RootDatabase, FilePosition, Vec<FileRange>) { 64pub(crate) fn single_file(code: &str) -> (ide_db::RootDatabase, FilePosition, Vec<FileRange>) {
65 use ide_db::base_db::fixture::WithFixture; 65 use ide_db::base_db::fixture::WithFixture;
@@ -160,6 +160,97 @@ fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expecte
160} 160}
161 161
162#[test] 162#[test]
163fn ssr_let_stmt_in_macro_match() {
164 assert_matches(
165 "let a = 0",
166 r#"
167 macro_rules! m1 { ($a:stmt) => {$a}; }
168 fn f() {m1!{ let a = 0 };}"#,
169 // FIXME: Whitespace is not part of the matched block
170 &["leta=0"],
171 );
172}
173
174#[test]
175fn ssr_let_stmt_in_fn_match() {
176 assert_matches("let $a = 10;", "fn main() { let x = 10; x }", &["let x = 10;"]);
177 assert_matches("let $a = $b;", "fn main() { let x = 10; x }", &["let x = 10;"]);
178}
179
180#[test]
181fn ssr_block_expr_match() {
182 assert_matches("{ let $a = $b; }", "fn main() { let x = 10; }", &["{ let x = 10; }"]);
183 assert_matches("{ let $a = $b; $c }", "fn main() { let x = 10; x }", &["{ let x = 10; x }"]);
184}
185
186#[test]
187fn ssr_let_stmt_replace() {
188 // Pattern and template with trailing semicolon
189 assert_ssr_transform(
190 "let $a = $b; ==>> let $a = 11;",
191 "fn main() { let x = 10; x }",
192 expect![["fn main() { let x = 11; x }"]],
193 );
194}
195
196#[test]
197fn ssr_let_stmt_replace_expr() {
198 // Trailing semicolon should be dropped from the new expression
199 assert_ssr_transform(
200 "let $a = $b; ==>> $b",
201 "fn main() { let x = 10; }",
202 expect![["fn main() { 10 }"]],
203 );
204}
205
206#[test]
207fn ssr_blockexpr_replace_stmt_with_stmt() {
208 assert_ssr_transform(
209 "if $a() {$b;} ==>> $b;",
210 "{
211 if foo() {
212 bar();
213 }
214 Ok(())
215}",
216 expect![[r#"{
217 bar();
218 Ok(())
219}"#]],
220 );
221}
222
223#[test]
224fn ssr_blockexpr_match_trailing_expr() {
225 assert_matches(
226 "if $a() {$b;}",
227 "{
228 if foo() {
229 bar();
230 }
231}",
232 &["if foo() {
233 bar();
234 }"],
235 );
236}
237
238#[test]
239fn ssr_blockexpr_replace_trailing_expr_with_stmt() {
240 assert_ssr_transform(
241 "if $a() {$b;} ==>> $b;",
242 "{
243 if foo() {
244 bar();
245 }
246}",
247 expect![["{
248 bar();
249}"]],
250 );
251}
252
253#[test]
163fn ssr_function_to_method() { 254fn ssr_function_to_method() {
164 assert_ssr_transform( 255 assert_ssr_transform(
165 "my_function($a, $b) ==>> ($a).my_method($b)", 256 "my_function($a, $b) ==>> ($a).my_method($b)",
@@ -505,7 +596,7 @@ fn replace_function_call() {
505 // This test also makes sure that we ignore empty-ranges. 596 // This test also makes sure that we ignore empty-ranges.
506 assert_ssr_transform( 597 assert_ssr_transform(
507 "foo() ==>> bar()", 598 "foo() ==>> bar()",
508 "fn foo() {<|><|>} fn bar() {} fn f1() {foo(); foo();}", 599 "fn foo() {$0$0} fn bar() {} fn f1() {foo(); foo();}",
509 expect![["fn foo() {} fn bar() {} fn f1() {bar(); bar();}"]], 600 expect![["fn foo() {} fn bar() {} fn f1() {bar(); bar();}"]],
510 ); 601 );
511} 602}
@@ -615,7 +706,7 @@ fn replace_associated_trait_constant() {
615 706
616#[test] 707#[test]
617fn replace_path_in_different_contexts() { 708fn replace_path_in_different_contexts() {
618 // Note the <|> inside module a::b which marks the point where the rule is interpreted. We 709 // Note the $0 inside module a::b which marks the point where the rule is interpreted. We
619 // replace foo with bar, but both need different path qualifiers in different contexts. In f4, 710 // replace foo with bar, but both need different path qualifiers in different contexts. In f4,
620 // foo is unqualified because of a use statement, however the replacement needs to be fully 711 // foo is unqualified because of a use statement, however the replacement needs to be fully
621 // qualified. 712 // qualified.
@@ -623,7 +714,7 @@ fn replace_path_in_different_contexts() {
623 "c::foo() ==>> c::bar()", 714 "c::foo() ==>> c::bar()",
624 r#" 715 r#"
625 mod a { 716 mod a {
626 pub mod b {<|> 717 pub mod b {$0
627 pub mod c { 718 pub mod c {
628 pub fn foo() {} 719 pub fn foo() {}
629 pub fn bar() {} 720 pub fn bar() {}
@@ -1005,7 +1096,7 @@ fn pattern_is_a_single_segment_path() {
1005 fn f1() -> i32 { 1096 fn f1() -> i32 {
1006 let foo = 1; 1097 let foo = 1;
1007 let bar = 2; 1098 let bar = 2;
1008 foo<|> 1099 foo$0
1009 } 1100 }
1010 "#, 1101 "#,
1011 expect![[r#" 1102 expect![[r#"
@@ -1037,7 +1128,7 @@ fn replace_local_variable_reference() {
1037 let foo = 5; 1128 let foo = 5;
1038 res += foo + 1; 1129 res += foo + 1;
1039 let foo = 10; 1130 let foo = 10;
1040 res += foo + 2;<|> 1131 res += foo + 2;$0
1041 res += foo + 3; 1132 res += foo + 3;
1042 let foo = 15; 1133 let foo = 15;
1043 res += foo + 4; 1134 res += foo + 4;
@@ -1069,9 +1160,9 @@ fn replace_path_within_selection() {
1069 let foo = 41; 1160 let foo = 41;
1070 let bar = 42; 1161 let bar = 42;
1071 do_stuff(foo); 1162 do_stuff(foo);
1072 do_stuff(foo);<|> 1163 do_stuff(foo);$0
1073 do_stuff(foo); 1164 do_stuff(foo);
1074 do_stuff(foo);<|> 1165 do_stuff(foo);$0
1075 do_stuff(foo); 1166 do_stuff(foo);
1076 }"#, 1167 }"#,
1077 expect![[r#" 1168 expect![[r#"
@@ -1094,9 +1185,9 @@ fn replace_nonpath_within_selection() {
1094 "$a + $b ==>> $b * $a", 1185 "$a + $b ==>> $b * $a",
1095 r#" 1186 r#"
1096 fn main() { 1187 fn main() {
1097 let v = 1 + 2;<|> 1188 let v = 1 + 2;$0
1098 let v2 = 3 + 3; 1189 let v2 = 3 + 3;
1099 let v3 = 4 + 5;<|> 1190 let v3 = 4 + 5;$0
1100 let v4 = 6 + 7; 1191 let v4 = 6 + 7;
1101 }"#, 1192 }"#,
1102 expect![[r#" 1193 expect![[r#"
@@ -1121,7 +1212,7 @@ fn replace_self() {
1121 fn bar(_: &S1) {} 1212 fn bar(_: &S1) {}
1122 impl S1 { 1213 impl S1 {
1123 fn f1(&self) { 1214 fn f1(&self) {
1124 foo(self)<|> 1215 foo(self)$0
1125 } 1216 }
1126 fn f2(&self) { 1217 fn f2(&self) {
1127 foo(self) 1218 foo(self)
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 374ed5910..13aab1451 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -1,5 +1,5 @@
1//! Missing batteries for standard libraries. 1//! Missing batteries for standard libraries.
2use std::time::Instant; 2use std::{cmp::Ordering, ops, process, time::Instant};
3 3
4mod macros; 4mod macros;
5pub mod panic_context; 5pub mod panic_context;
@@ -117,7 +117,12 @@ impl<'a> Iterator for LinesWithEnds<'a> {
117 } 117 }
118} 118}
119 119
120// https://github.com/rust-lang/rust/issues/73831 120/// Returns `idx` such that:
121///
122/// ∀ x in slice[..idx]: pred(x)
123/// && ∀ x in slice[idx..]: !pred(x)
124///
125/// https://github.com/rust-lang/rust/issues/73831
121pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize 126pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize
122where 127where
123 P: FnMut(&T) -> bool, 128 P: FnMut(&T) -> bool,
@@ -147,6 +152,36 @@ where
147 left 152 left
148} 153}
149 154
155pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
156where
157 F: FnMut(&T) -> Ordering,
158{
159 let start = partition_point(slice, |it| key(it) == Ordering::Less);
160 let len = partition_point(&slice[start..], |it| key(it) == Ordering::Equal);
161 start..start + len
162}
163
164pub struct JodChild(pub process::Child);
165
166impl ops::Deref for JodChild {
167 type Target = process::Child;
168 fn deref(&self) -> &process::Child {
169 &self.0
170 }
171}
172
173impl ops::DerefMut for JodChild {
174 fn deref_mut(&mut self) -> &mut process::Child {
175 &mut self.0
176 }
177}
178
179impl Drop for JodChild {
180 fn drop(&mut self) {
181 let _ = self.0.kill();
182 }
183}
184
150#[cfg(test)] 185#[cfg(test)]
151mod tests { 186mod tests {
152 use super::*; 187 use super::*;
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 21015591c..cfeaed9e6 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -11,9 +11,9 @@ edition = "2018"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14itertools = "0.9.0" 14itertools = "0.10.0"
15rowan = "0.10.0" 15rowan = "0.10.0"
16rustc_lexer = { version = "695.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "697.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
19once_cell = "1.3.1" 19once_cell = "1.3.1"
@@ -33,4 +33,4 @@ profile = { path = "../profile", version = "0.0.0" }
33[dev-dependencies] 33[dev-dependencies]
34walkdir = "2.3.1" 34walkdir = "2.3.1"
35rayon = "1" 35rayon = "1"
36expect-test = "1.0" 36expect-test = "1.1"
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs
index 5696c014f..384d031e7 100644
--- a/crates/syntax/src/algo.rs
+++ b/crates/syntax/src/algo.rs
@@ -19,7 +19,7 @@ use crate::{
19 19
20/// Returns ancestors of the node at the offset, sorted by length. This should 20/// Returns ancestors of the node at the offset, sorted by length. This should
21/// do the right thing at an edge, e.g. when searching for expressions at `{ 21/// do the right thing at an edge, e.g. when searching for expressions at `{
22/// <|>foo }` we will get the name reference instead of the whole block, which 22/// $0foo }` we will get the name reference instead of the whole block, which
23/// we would get if we just did `find_token_at_offset(...).flat_map(|t| 23/// we would get if we just did `find_token_at_offset(...).flat_map(|t|
24/// t.parent().ancestors())`. 24/// t.parent().ancestors())`.
25pub fn ancestors_at_offset( 25pub fn ancestors_at_offset(
@@ -88,8 +88,8 @@ pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNod
88 let keep = u_depth.min(v_depth); 88 let keep = u_depth.min(v_depth);
89 89
90 let u_candidates = u.ancestors().skip(u_depth - keep); 90 let u_candidates = u.ancestors().skip(u_depth - keep);
91 let v_canidates = v.ancestors().skip(v_depth - keep); 91 let v_candidates = v.ancestors().skip(v_depth - keep);
92 let (res, _) = u_candidates.zip(v_canidates).find(|(x, y)| x == y)?; 92 let (res, _) = u_candidates.zip(v_candidates).find(|(x, y)| x == y)?;
93 Some(res) 93 Some(res)
94} 94}
95 95
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 77233ab31..824ebf41c 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -220,7 +220,7 @@ impl ast::RecordExprFieldList {
220 InsertPosition::After($anchor.syntax().clone().into()) 220 InsertPosition::After($anchor.syntax().clone().into())
221 } 221 }
222 }; 222 };
223 }; 223 }
224 224
225 let position = match position { 225 let position = match position {
226 InsertPosition::First => after_l_curly!(), 226 InsertPosition::First => after_l_curly!(),
@@ -533,7 +533,7 @@ impl ast::GenericParamList {
533 InsertPosition::After($anchor.syntax().clone().into()) 533 InsertPosition::After($anchor.syntax().clone().into())
534 } 534 }
535 }; 535 };
536 }; 536 }
537 537
538 let position = match self.generic_params().last() { 538 let position = match self.generic_params().last() {
539 Some(it) => after_field!(it), 539 Some(it) => after_field!(it),
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index c5b80bffe..92ed2ee9d 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -484,7 +484,7 @@ impl ast::AttrsOwner for BlockExpr {}
484impl BlockExpr { 484impl BlockExpr {
485 pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) } 485 pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
486 pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) } 486 pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) }
487 pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } 487 pub fn tail_expr(&self) -> Option<Expr> { support::child(&self.syntax) }
488 pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } 488 pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
489} 489}
490#[derive(Debug, Clone, PartialEq, Eq, Hash)] 490#[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index cafa4c198..1ed8a96e5 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -241,7 +241,7 @@ pub fn wildcard_pat() -> ast::WildcardPat {
241 } 241 }
242} 242}
243 243
244/// Creates a tuple of patterns from an interator of patterns. 244/// Creates a tuple of patterns from an iterator of patterns.
245/// 245///
246/// Invariant: `pats` must be length > 1 246/// Invariant: `pats` must be length > 1
247/// 247///
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 2aa472fb4..27381ba80 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -133,7 +133,7 @@ impl ast::Attr {
133 first_token.and_then(|token| token.next_token()).as_ref().map(SyntaxToken::kind); 133 first_token.and_then(|token| token.next_token()).as_ref().map(SyntaxToken::kind);
134 134
135 match (first_token_kind, second_token_kind) { 135 match (first_token_kind, second_token_kind) {
136 (Some(SyntaxKind::POUND), Some(T![!])) => AttrKind::Inner, 136 (Some(T![#]), Some(T![!])) => AttrKind::Inner,
137 _ => AttrKind::Outer, 137 _ => AttrKind::Outer,
138 } 138 }
139 } 139 }
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index 4d272f367..ea7482bb1 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -212,6 +212,13 @@ impl ast::Attr {
212 } 212 }
213} 213}
214 214
215impl ast::Stmt {
216 /// Returns `text`, parsed as statement, but only if it has no errors.
217 pub fn parse(text: &str) -> Result<Self, ()> {
218 parsing::parse_text_fragment(text, parser::FragmentKind::StatementOptionalSemi)
219 }
220}
221
215/// Matches a `SyntaxNode` against an `ast` type. 222/// Matches a `SyntaxNode` against an `ast` type.
216/// 223///
217/// # Example: 224/// # Example:
@@ -283,7 +290,7 @@ fn api_walkthrough() {
283 290
284 // Let's get the `1 + 1` expression! 291 // Let's get the `1 + 1` expression!
285 let body: ast::BlockExpr = func.body().unwrap(); 292 let body: ast::BlockExpr = func.body().unwrap();
286 let expr: ast::Expr = body.expr().unwrap(); 293 let expr: ast::Expr = body.tail_expr().unwrap();
287 294
288 // Enums are used to group related ast nodes together, and can be used for 295 // Enums are used to group related ast nodes together, and can be used for
289 // matching. However, because there are no public fields, it's possible to 296 // matching. However, because there are no public fields, it's possible to
diff --git a/crates/syntax/src/parsing/lexer.rs b/crates/syntax/src/parsing/lexer.rs
index 0cbba73c5..7c8d0a4c4 100644
--- a/crates/syntax/src/parsing/lexer.rs
+++ b/crates/syntax/src/parsing/lexer.rs
@@ -24,7 +24,7 @@ pub struct Token {
24/// Beware that it checks for shebang first and its length contributes to resulting 24/// Beware that it checks for shebang first and its length contributes to resulting
25/// tokens offsets. 25/// tokens offsets.
26pub fn tokenize(text: &str) -> (Vec<Token>, Vec<SyntaxError>) { 26pub fn tokenize(text: &str) -> (Vec<Token>, Vec<SyntaxError>) {
27 // non-empty string is a precondtion of `rustc_lexer::strip_shebang()`. 27 // non-empty string is a precondition of `rustc_lexer::strip_shebang()`.
28 if text.is_empty() { 28 if text.is_empty() {
29 return Default::default(); 29 return Default::default();
30 } 30 }
@@ -76,7 +76,7 @@ pub fn lex_single_syntax_kind(text: &str) -> Option<(SyntaxKind, Option<SyntaxEr
76} 76}
77 77
78/// The same as `lex_single_syntax_kind()` but returns only `SyntaxKind` and 78/// The same as `lex_single_syntax_kind()` but returns only `SyntaxKind` and
79/// returns `None` if any tokenization error occured. 79/// returns `None` if any tokenization error occurred.
80/// 80///
81/// Beware that unescape errors are not checked at tokenization time. 81/// Beware that unescape errors are not checked at tokenization time.
82pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> { 82pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> {
@@ -96,7 +96,7 @@ pub fn lex_single_valid_syntax_kind(text: &str) -> Option<SyntaxKind> {
96/// 96///
97/// Beware that unescape errors are not checked at tokenization time. 97/// Beware that unescape errors are not checked at tokenization time.
98fn lex_first_token(text: &str) -> Option<(Token, Option<SyntaxError>)> { 98fn lex_first_token(text: &str) -> Option<(Token, Option<SyntaxError>)> {
99 // non-empty string is a precondtion of `rustc_lexer::first_token()`. 99 // non-empty string is a precondition of `rustc_lexer::first_token()`.
100 if text.is_empty() { 100 if text.is_empty() {
101 return None; 101 return None;
102 } 102 }
@@ -117,7 +117,7 @@ fn rustc_token_kind_to_syntax_kind(
117 token_text: &str, 117 token_text: &str,
118) -> (SyntaxKind, Option<&'static str>) { 118) -> (SyntaxKind, Option<&'static str>) {
119 // A note on an intended tradeoff: 119 // A note on an intended tradeoff:
120 // We drop some useful infromation here (see patterns with double dots `..`) 120 // We drop some useful information here (see patterns with double dots `..`)
121 // Storing that info in `SyntaxKind` is not possible due to its layout requirements of 121 // Storing that info in `SyntaxKind` is not possible due to its layout requirements of
122 // being `u16` that come from `rowan::SyntaxKind`. 122 // being `u16` that come from `rowan::SyntaxKind`.
123 123
diff --git a/crates/syntax/src/parsing/reparsing.rs b/crates/syntax/src/parsing/reparsing.rs
index 190f5f67a..78eaf3410 100644
--- a/crates/syntax/src/parsing/reparsing.rs
+++ b/crates/syntax/src/parsing/reparsing.rs
@@ -223,7 +223,7 @@ mod tests {
223 do_check( 223 do_check(
224 r" 224 r"
225fn foo() { 225fn foo() {
226 let x = foo + <|>bar<|> 226 let x = foo + $0bar$0
227} 227}
228", 228",
229 "baz", 229 "baz",
@@ -232,7 +232,7 @@ fn foo() {
232 do_check( 232 do_check(
233 r" 233 r"
234fn foo() { 234fn foo() {
235 let x = foo<|> + bar<|> 235 let x = foo$0 + bar$0
236} 236}
237", 237",
238 "baz", 238 "baz",
@@ -241,7 +241,7 @@ fn foo() {
241 do_check( 241 do_check(
242 r" 242 r"
243struct Foo { 243struct Foo {
244 f: foo<|><|> 244 f: foo$0$0
245} 245}
246", 246",
247 ",\n g: (),", 247 ",\n g: (),",
@@ -252,7 +252,7 @@ struct Foo {
252fn foo { 252fn foo {
253 let; 253 let;
254 1 + 1; 254 1 + 1;
255 <|>92<|>; 255 $092$0;
256} 256}
257", 257",
258 "62", 258 "62",
@@ -261,7 +261,7 @@ fn foo {
261 do_check( 261 do_check(
262 r" 262 r"
263mod foo { 263mod foo {
264 fn <|><|> 264 fn $0$0
265} 265}
266", 266",
267 "bar", 267 "bar",
@@ -271,7 +271,7 @@ mod foo {
271 do_check( 271 do_check(
272 r" 272 r"
273trait Foo { 273trait Foo {
274 type <|>Foo<|>; 274 type $0Foo$0;
275} 275}
276", 276",
277 "Output", 277 "Output",
@@ -280,17 +280,17 @@ trait Foo {
280 do_check( 280 do_check(
281 r" 281 r"
282impl IntoIterator<Item=i32> for Foo { 282impl IntoIterator<Item=i32> for Foo {
283 f<|><|> 283 f$0$0
284} 284}
285", 285",
286 "n next(", 286 "n next(",
287 9, 287 9,
288 ); 288 );
289 do_check(r"use a::b::{foo,<|>,bar<|>};", "baz", 10); 289 do_check(r"use a::b::{foo,$0,bar$0};", "baz", 10);
290 do_check( 290 do_check(
291 r" 291 r"
292pub enum A { 292pub enum A {
293 Foo<|><|> 293 Foo$0$0
294} 294}
295", 295",
296 "\nBar;\n", 296 "\nBar;\n",
@@ -298,7 +298,7 @@ pub enum A {
298 ); 298 );
299 do_check( 299 do_check(
300 r" 300 r"
301foo!{a, b<|><|> d} 301foo!{a, b$0$0 d}
302", 302",
303 ", c[3]", 303 ", c[3]",
304 8, 304 8,
@@ -306,7 +306,7 @@ foo!{a, b<|><|> d}
306 do_check( 306 do_check(
307 r" 307 r"
308fn foo() { 308fn foo() {
309 vec![<|><|>] 309 vec![$0$0]
310} 310}
311", 311",
312 "123", 312 "123",
@@ -315,7 +315,7 @@ fn foo() {
315 do_check( 315 do_check(
316 r" 316 r"
317extern { 317extern {
318 fn<|>;<|> 318 fn$0;$0
319} 319}
320", 320",
321 " exit(code: c_int)", 321 " exit(code: c_int)",
@@ -326,7 +326,7 @@ extern {
326 #[test] 326 #[test]
327 fn reparse_token_tests() { 327 fn reparse_token_tests() {
328 do_check( 328 do_check(
329 r"<|><|> 329 r"$0$0
330fn foo() -> i32 { 1 } 330fn foo() -> i32 { 1 }
331", 331",
332 "\n\n\n \n", 332 "\n\n\n \n",
@@ -334,49 +334,49 @@ fn foo() -> i32 { 1 }
334 ); 334 );
335 do_check( 335 do_check(
336 r" 336 r"
337fn foo() -> <|><|> {} 337fn foo() -> $0$0 {}
338", 338",
339 " \n", 339 " \n",
340 2, 340 2,
341 ); 341 );
342 do_check( 342 do_check(
343 r" 343 r"
344fn <|>foo<|>() -> i32 { 1 } 344fn $0foo$0() -> i32 { 1 }
345", 345",
346 "bar", 346 "bar",
347 3, 347 3,
348 ); 348 );
349 do_check( 349 do_check(
350 r" 350 r"
351fn foo<|><|>foo() { } 351fn foo$0$0foo() { }
352", 352",
353 "bar", 353 "bar",
354 6, 354 6,
355 ); 355 );
356 do_check( 356 do_check(
357 r" 357 r"
358fn foo /* <|><|> */ () {} 358fn foo /* $0$0 */ () {}
359", 359",
360 "some comment", 360 "some comment",
361 6, 361 6,
362 ); 362 );
363 do_check( 363 do_check(
364 r" 364 r"
365fn baz <|><|> () {} 365fn baz $0$0 () {}
366", 366",
367 " \t\t\n\n", 367 " \t\t\n\n",
368 2, 368 2,
369 ); 369 );
370 do_check( 370 do_check(
371 r" 371 r"
372fn baz <|><|> () {} 372fn baz $0$0 () {}
373", 373",
374 " \t\t\n\n", 374 " \t\t\n\n",
375 2, 375 2,
376 ); 376 );
377 do_check( 377 do_check(
378 r" 378 r"
379/// foo <|><|>omment 379/// foo $0$0omment
380mod { } 380mod { }
381", 381",
382 "c", 382 "c",
@@ -384,28 +384,28 @@ mod { }
384 ); 384 );
385 do_check( 385 do_check(
386 r#" 386 r#"
387fn -> &str { "Hello<|><|>" } 387fn -> &str { "Hello$0$0" }
388"#, 388"#,
389 ", world", 389 ", world",
390 7, 390 7,
391 ); 391 );
392 do_check( 392 do_check(
393 r#" 393 r#"
394fn -> &str { // "Hello<|><|>" 394fn -> &str { // "Hello$0$0"
395"#, 395"#,
396 ", world", 396 ", world",
397 10, 397 10,
398 ); 398 );
399 do_check( 399 do_check(
400 r##" 400 r##"
401fn -> &str { r#"Hello<|><|>"# 401fn -> &str { r#"Hello$0$0"#
402"##, 402"##,
403 ", world", 403 ", world",
404 10, 404 10,
405 ); 405 );
406 do_check( 406 do_check(
407 r" 407 r"
408#[derive(<|>Copy<|>)] 408#[derive($0Copy$0)]
409enum Foo { 409enum Foo {
410 410
411} 411}
@@ -417,12 +417,12 @@ enum Foo {
417 417
418 #[test] 418 #[test]
419 fn reparse_str_token_with_error_unchanged() { 419 fn reparse_str_token_with_error_unchanged() {
420 do_check(r#""<|>Unclosed<|> string literal"#, "Still unclosed", 24); 420 do_check(r#""$0Unclosed$0 string literal"#, "Still unclosed", 24);
421 } 421 }
422 422
423 #[test] 423 #[test]
424 fn reparse_str_token_with_error_fixed() { 424 fn reparse_str_token_with_error_fixed() {
425 do_check(r#""unterinated<|><|>"#, "\"", 12); 425 do_check(r#""unterinated$0$0"#, "\"", 12);
426 } 426 }
427 427
428 #[test] 428 #[test]
@@ -430,7 +430,7 @@ enum Foo {
430 do_check( 430 do_check(
431 r#"fn main() { 431 r#"fn main() {
432 if {} 432 if {}
433 32 + 4<|><|> 433 32 + 4$0$0
434 return 434 return
435 if {} 435 if {}
436 }"#, 436 }"#,
@@ -444,7 +444,7 @@ enum Foo {
444 do_check( 444 do_check(
445 r#"fn main() { 445 r#"fn main() {
446 if {} 446 if {}
447 32 + 4<|><|> 447 32 + 4$0$0
448 return 448 return
449 if {} 449 if {}
450 }"#, 450 }"#,
diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs
index 8c217dfe0..9d3433c9d 100644
--- a/crates/syntax/src/tests.rs
+++ b/crates/syntax/src/tests.rs
@@ -103,6 +103,15 @@ fn type_parser_tests() {
103} 103}
104 104
105#[test] 105#[test]
106fn stmt_parser_tests() {
107 fragment_parser_dir_test(
108 &["parser/fragments/stmt/ok"],
109 &["parser/fragments/stmt/err"],
110 crate::ast::Stmt::parse,
111 );
112}
113
114#[test]
106fn parser_fuzz_tests() { 115fn parser_fuzz_tests() {
107 for (_, text) in collect_rust_files(&test_data_dir(), &["parser/fuzz-failures"]) { 116 for (_, text) in collect_rust_files(&test_data_dir(), &["parser/fuzz-failures"]) {
108 fuzz::check_parser(&text) 117 fuzz::check_parser(&text)
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 2ddaeb176..bfa2dc4ba 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -173,7 +173,7 @@ pub(crate) fn validate_block_structure(root: &SyntaxNode) {
173 assert_eq!( 173 assert_eq!(
174 node.parent(), 174 node.parent(),
175 pair.parent(), 175 pair.parent(),
176 "\nunpaired curleys:\n{}\n{:#?}\n", 176 "\nunpaired curlys:\n{}\n{:#?}\n",
177 root.text(), 177 root.text(),
178 root, 178 root,
179 ); 179 );
@@ -344,9 +344,9 @@ fn validate_trait_object_ty(ty: ast::DynTraitType) -> Option<SyntaxError> {
344 344
345 if tbl.bounds().count() > 1 { 345 if tbl.bounds().count() > 1 {
346 let dyn_token = ty.dyn_token()?; 346 let dyn_token = ty.dyn_token()?;
347 let potential_parentheses = 347 let potential_parenthesis =
348 algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?; 348 algo::skip_trivia_token(dyn_token.prev_token()?, Direction::Prev)?;
349 let kind = potential_parentheses.kind(); 349 let kind = potential_parenthesis.kind();
350 if !matches!(kind, T!['('] | T![<] | T![=]) { 350 if !matches!(kind, T!['('] | T![<] | T![=]) {
351 return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range())); 351 return Some(SyntaxError::new("ambiguous `+` in a type", ty.syntax().text_range()));
352 } 352 }
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast
new file mode 100644
index 000000000..5df7507e2
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast
@@ -0,0 +1 @@
ERROR
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs
new file mode 100644
index 000000000..988df0705
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs
@@ -0,0 +1 @@
#[foo]
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast
new file mode 100644
index 000000000..5df7507e2
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast
@@ -0,0 +1 @@
ERROR
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs
new file mode 100644
index 000000000..7e3b2fd49
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs
@@ -0,0 +1 @@
a(); b(); c()
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast
new file mode 100644
index 000000000..5df7507e2
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast
@@ -0,0 +1 @@
ERROR
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs
new file mode 100644
index 000000000..2d06f3766
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs
@@ -0,0 +1 @@
(
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast
new file mode 100644
index 000000000..5df7507e2
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast
@@ -0,0 +1 @@
ERROR
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs
new file mode 100644
index 000000000..092bc2b04
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs
@@ -0,0 +1 @@
;
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast
new file mode 100644
index 000000000..5df7507e2
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast
@@ -0,0 +1 @@
ERROR
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs
new file mode 100644
index 000000000..ca49acb07
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs
@@ -0,0 +1 @@
1 +
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast
new file mode 100644
index 000000000..274fdf16d
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast
@@ -0,0 +1,9 @@
1[email protected]
2 [email protected]
3 [email protected]
4 [email protected] "1"
5 [email protected] " "
6 [email protected] "+"
7 [email protected] " "
8 [email protected]
9 [email protected] "1"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs
new file mode 100644
index 000000000..8d2f0971e
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs
@@ -0,0 +1 @@
1 + 1
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast
new file mode 100644
index 000000000..6c946091f
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast
@@ -0,0 +1,69 @@
1[email protected]
2 [email protected]
3 [email protected] "{"
4 [email protected] "\n "
5 [email protected]
6 [email protected] "let"
7 [email protected] " "
8 [email protected]
9 [email protected]
10 [email protected] "x"
11 [email protected] " "
12 [email protected] "="
13 [email protected] " "
14 [email protected]
15 [email protected]
16 [email protected]
17 [email protected]
18 [email protected]
19 [email protected] "foo"
20 [email protected]
21 [email protected] "("
22 [email protected] ")"
23 [email protected] ";"
24 [email protected] "\n "
25 [email protected]
26 [email protected] "let"
27 [email protected] " "
28 [email protected]
29 [email protected]
30 [email protected] "y"
31 [email protected] " "
32 [email protected] "="
33 [email protected] " "
34 [email protected]
35 [email protected]
36 [email protected]
37 [email protected]
38 [email protected]
39 [email protected] "bar"
40 [email protected]
41 [email protected] "("
42 [email protected] ")"
43 [email protected] ";"
44 [email protected] "\n "
45 [email protected]
46 [email protected]
47 [email protected]
48 [email protected]
49 [email protected]
50 [email protected] "Ok"
51 [email protected]
52 [email protected] "("
53 [email protected]
54 [email protected]
55 [email protected]
56 [email protected]
57 [email protected]
58 [email protected] "x"
59 [email protected] " "
60 [email protected] "+"
61 [email protected] " "
62 [email protected]
63 [email protected]
64 [email protected]
65 [email protected]
66 [email protected] "y"
67 [email protected] ")"
68 [email protected] "\n"
69 [email protected] "}"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs
new file mode 100644
index 000000000..ffa5c1e66
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs
@@ -0,0 +1,5 @@
1{
2 let x = foo();
3 let y = bar();
4 Ok(x + y)
5}
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast
new file mode 100644
index 000000000..8c186da93
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast
@@ -0,0 +1,11 @@
1[email protected]
2 [email protected]
3 [email protected]
4 [email protected]
5 [email protected]
6 [email protected]
7 [email protected] "foo"
8 [email protected]
9 [email protected] "("
10 [email protected] ")"
11 [email protected] ";"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs
new file mode 100644
index 000000000..a280f9a5c
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs
@@ -0,0 +1 @@
foo();
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast
new file mode 100644
index 000000000..8ab38da21
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast
@@ -0,0 +1,12 @@
1[email protected]
2 [email protected] "let"
3 [email protected] " "
4 [email protected]
5 [email protected]
6 [email protected] "x"
7 [email protected] " "
8 [email protected] "="
9 [email protected] " "
10 [email protected]
11 [email protected] "10"
12 [email protected] ";"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs
new file mode 100644
index 000000000..de8a7f1fc
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs
@@ -0,0 +1 @@
let x = 10;
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast
new file mode 100644
index 000000000..81d6df29a
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast
@@ -0,0 +1,21 @@
1[email protected]
2 [email protected]
3 [email protected]
4 [email protected]
5 [email protected]
6 [email protected] "m1"
7 [email protected] "!"
8 [email protected]
9 [email protected] "{"
10 [email protected] " "
11 [email protected] "let"
12 [email protected] " "
13 [email protected] "a"
14 [email protected] " "
15 [email protected] "="
16 [email protected] " "
17 [email protected] "0"
18 [email protected] ";"
19 [email protected] " "
20 [email protected] "}"
21 [email protected] ";"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs
new file mode 100644
index 000000000..075f30159
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs
@@ -0,0 +1 @@
m1!{ let a = 0; };
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast
new file mode 100644
index 000000000..81d6df29a
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast
@@ -0,0 +1,21 @@
1[email protected]
2 [email protected]
3 [email protected]
4 [email protected]
5 [email protected]
6 [email protected] "m1"
7 [email protected] "!"
8 [email protected]
9 [email protected] "{"
10 [email protected] " "
11 [email protected] "let"
12 [email protected] " "
13 [email protected] "a"
14 [email protected] " "
15 [email protected] "="
16 [email protected] " "
17 [email protected] "0"
18 [email protected] ";"
19 [email protected] " "
20 [email protected] "}"
21 [email protected] ";"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs
new file mode 100644
index 000000000..075f30159
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs
@@ -0,0 +1 @@
m1!{ let a = 0; };
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast
new file mode 100644
index 000000000..64c5d2969
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast
@@ -0,0 +1,22 @@
1[email protected]
2 [email protected] "struct"
3 [email protected] " "
4 [email protected]
5 [email protected] "Foo"
6 [email protected] " "
7 [email protected]
8 [email protected] "{"
9 [email protected] "\n "
10 [email protected]
11 [email protected]
12 [email protected] "bar"
13 [email protected] ":"
14 [email protected] " "
15 [email protected]
16 [email protected]
17 [email protected]
18 [email protected]
19 [email protected] "u32"
20 [email protected] ","
21 [email protected] "\n"
22 [email protected] "}"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs
new file mode 100644
index 000000000..e5473e3ac
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs
@@ -0,0 +1,3 @@
1struct Foo {
2 bar: u32,
3}
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast
new file mode 100644
index 000000000..9089906bc
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast
@@ -0,0 +1,10 @@
1[email protected]
2 [email protected]
3 [email protected]
4 [email protected]
5 [email protected]
6 [email protected]
7 [email protected] "foo"
8 [email protected]
9 [email protected] "("
10 [email protected] ")"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs
new file mode 100644
index 000000000..eb28ef440
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs
@@ -0,0 +1 @@
foo()
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast
new file mode 100644
index 000000000..37663671f
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast
@@ -0,0 +1,11 @@
1[email protected]
2 [email protected] "let"
3 [email protected] " "
4 [email protected]
5 [email protected]
6 [email protected] "x"
7 [email protected] " "
8 [email protected] "="
9 [email protected] " "
10 [email protected]
11 [email protected] "10"
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs
new file mode 100644
index 000000000..78364b2a9
--- /dev/null
+++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs
@@ -0,0 +1 @@
let x = 10
diff --git a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast
index b1fb75ed1..f40500e38 100644
--- a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rast
@@ -1,4 +1,4 @@
1[email protected]9 1[email protected]8
2 [email protected] 2 [email protected]
3 [email protected] "use" 3 [email protected] "use"
4 [email protected] " " 4 [email protected] " "
@@ -75,62 +75,62 @@ [email protected]
75 [email protected] "}" 75 [email protected] "}"
76 [email protected] ";" 76 [email protected] ";"
77 [email protected] " " 77 [email protected] " "
78 [email protected]6 78 [email protected]5
79 [email protected] "// Rust 2015" 79 [email protected] "// Rust 2015"
80 [email protected] "\n" 80 [email protected] "\n"
81 [email protected] "use" 81 [email protected] "use"
82 [email protected] " " 82 [email protected] " "
83 [email protected]5 83 [email protected]4
84 [email protected] "::" 84 [email protected] "::"
85 [email protected]5 85 [email protected]4
86 [email protected] "{" 86 [email protected] "{"
87 [email protected]4 87 [email protected]3
88 [email protected]4 88 [email protected]3
89 [email protected]8 89 [email protected]7
90 [email protected] 90 [email protected]
91 [email protected] 91 [email protected]
92 [email protected] 92 [email protected]
93 [email protected] "some" 93 [email protected] "some"
94 [email protected] "::" 94 [email protected] "::"
95 [email protected]8 95 [email protected]7
96 [email protected]8 96 [email protected]7
97 [email protected]8 "arbritrary" 97 [email protected]7 "arbitrary"
98 COLON2@158..160 "::" 98 COLON2@157..159 "::"
99 PATH_SEGMENT@160..164 99 PATH_SEGMENT@159..163
100 NAME_REF@160..164 100 NAME_REF@159..163
101 IDENT@160..164 "path" 101 IDENT@159..163 "path"
102 R_CURLY@164..165 "}" 102 R_CURLY@163..164 "}"
103 SEMICOLON@165..166 ";" 103 SEMICOLON@164..165 ";"
104 WHITESPACE@166..167 " " 104 WHITESPACE@165..166 " "
105 USE@167..205 105 USE@166..204
106 COMMENT@167..179 "// Rust 2015" 106 COMMENT@166..178 "// Rust 2015"
107 WHITESPACE@179..180 "\n" 107 WHITESPACE@178..179 "\n"
108 USE_KW@180..183 "use" 108 USE_KW@179..182 "use"
109 WHITESPACE@183..184 " " 109 WHITESPACE@182..183 " "
110 USE_TREE@184..204 110 USE_TREE@183..203
111 COLON2@184..186 "::" 111 COLON2@183..185 "::"
112 USE_TREE_LIST@186..204 112 USE_TREE_LIST@185..203
113 L_CURLY@186..187 "{" 113 L_CURLY@185..186 "{"
114 USE_TREE@187..203 114 USE_TREE@186..202
115 USE_TREE_LIST@187..203 115 USE_TREE_LIST@186..202
116 L_CURLY@187..188 "{" 116 L_CURLY@186..187 "{"
117 USE_TREE@188..202 117 USE_TREE@187..201
118 USE_TREE_LIST@188..202 118 USE_TREE_LIST@187..201
119 L_CURLY@188..189 "{" 119 L_CURLY@187..188 "{"
120 USE_TREE@189..201 120 USE_TREE@188..200
121 PATH@189..201 121 PATH@188..200
122 PATH@189..193 122 PATH@188..192
123 PATH_SEGMENT@189..193 123 PATH_SEGMENT@188..192
124 NAME_REF@189..193 124 NAME_REF@188..192
125 IDENT@189..193 "root" 125 IDENT@188..192 "root"
126 COLON2@193..195 "::" 126 COLON2@192..194 "::"
127 PATH_SEGMENT@195..201 127 PATH_SEGMENT@194..200
128 NAME_REF@195..201 128 NAME_REF@194..200
129 IDENT@195..201 "export" 129 IDENT@194..200 "export"
130 R_CURLY@201..202 "}" 130 R_CURLY@200..201 "}"
131 R_CURLY@202..203 "}" 131 R_CURLY@201..202 "}"
132 R_CURLY@203..204 "}" 132 R_CURLY@202..203 "}"
133 SEMICOLON@204..205 ";" 133 SEMICOLON@203..204 ";"
134 WHITESPACE@205..206 " " 134 WHITESPACE@204..205 " "
135 COMMENT@206..248 "// Nonsensical but pe ..." 135 COMMENT@205..247 "// Nonsensical but pe ..."
136 WHITESPACE@248..249 "\n" 136 WHITESPACE@247..248 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs
index 381cba1e2..02af4b446 100644
--- a/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0002_use_tree_list.rs
@@ -1,4 +1,4 @@
1use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`) 1use {crate::path::from::root, or::path::from::crate_name}; // Rust 2018 (with a crate named `or`)
2use {path::from::root}; // Rust 2015 2use {path::from::root}; // Rust 2015
3use ::{some::arbritrary::path}; // Rust 2015 3use ::{some::arbitrary::path}; // Rust 2015
4use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting 4use ::{{{root::export}}}; // Nonsensical but perfectly legal nesting
diff --git a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
index 51e881a8e..68c0f1c66 100644
--- a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rast
@@ -1,5 +1,5 @@
1SOURCE_FILE@0..46 1SOURCE_FILE@0..59
2 TYPE_ALIAS@0..45 2 [email protected]8
3 [email protected] "type" 3 [email protected] "type"
4 [email protected] " " 4 [email protected] " "
5 [email protected] 5 [email protected]
@@ -7,12 +7,12 @@ [email protected]
7 [email protected] " " 7 [email protected] " "
8 [email protected] "=" 8 [email protected] "="
9 [email protected] " " 9 [email protected] " "
10 PATH_TYPE@9..44 10 PATH_TYPE@9..57
11 PATH@9..44 11 PATH@9..57
12 PATH_SEGMENT@9..44 12 PATH_SEGMENT@9..57
13 [email protected] 13 [email protected]
14 [email protected] "B" 14 [email protected] "B"
15 GENERIC_ARG_LIST@10..44 15 GENERIC_ARG_LIST@10..57
16 [email protected] "<" 16 [email protected] "<"
17 [email protected] 17 [email protected]
18 [email protected] 18 [email protected]
@@ -51,6 +51,16 @@ [email protected]
51 [email protected] 51 [email protected]
52 [email protected] 52 [email protected]
53 [email protected] "u64" 53 [email protected] "u64"
54 [email protected] ">" 54 [email protected] ","
55 [email protected] ";" 55 [email protected] " "
56 [email protected] "\n" 56 [email protected]
57 [email protected]
58 [email protected] "true"
59 [email protected] ","
60 [email protected] " "
61 [email protected]
62 [email protected]
63 [email protected] "false"
64 [email protected] ">"
65 [email protected] ";"
66 [email protected] "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs
index 0d07d7651..6a8721a73 100644
--- a/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0039_type_arg.rs
@@ -1 +1 @@
type A = B<'static, i32, 1, { 2 }, Item=u64>; type A = B<'static, i32, 1, { 2 }, Item=u64, true, false>;
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml
index 93eecc678..06341f003 100644
--- a/crates/test_utils/Cargo.toml
+++ b/crates/test_utils/Cargo.toml
@@ -11,7 +11,7 @@ doctest = false
11 11
12[dependencies] 12[dependencies]
13# Avoid adding deps here, this crate is widely used in tests it should compile fast! 13# Avoid adding deps here, this crate is widely used in tests it should compile fast!
14difference = "2.0.0" 14dissimilar = "1.0.2"
15text-size = "1.0.0" 15text-size = "1.0.0"
16serde_json = "1.0.48" 16serde_json = "1.0.48"
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 05940a546..e19d2ad61 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -3,7 +3,7 @@
3//! Most notable things are: 3//! Most notable things are:
4//! 4//!
5//! * Rich text comparison, which outputs a diff. 5//! * Rich text comparison, which outputs a diff.
6//! * Extracting markup (mainly, `<|>` markers) out of fixture strings. 6//! * Extracting markup (mainly, `$0` markers) out of fixture strings.
7//! * marks (see the eponymous module). 7//! * marks (see the eponymous module).
8 8
9#[macro_use] 9#[macro_use]
@@ -20,12 +20,13 @@ use serde_json::Value;
20use stdx::lines_with_ends; 20use stdx::lines_with_ends;
21use text_size::{TextRange, TextSize}; 21use text_size::{TextRange, TextSize};
22 22
23pub use difference::Changeset as __Changeset; 23pub use dissimilar::diff as __diff;
24pub use rustc_hash::FxHashMap; 24pub use rustc_hash::FxHashMap;
25 25
26pub use crate::fixture::Fixture; 26pub use crate::fixture::Fixture;
27 27
28pub const CURSOR_MARKER: &str = "<|>"; 28pub const CURSOR_MARKER: &str = "$0";
29pub const ESCAPED_CURSOR_MARKER: &str = "\\$0";
29 30
30/// Asserts that two strings are equal, otherwise displays a rich diff between them. 31/// Asserts that two strings are equal, otherwise displays a rich diff between them.
31/// 32///
@@ -45,8 +46,8 @@ macro_rules! assert_eq_text {
45 if left.trim() == right.trim() { 46 if left.trim() == right.trim() {
46 std::eprintln!("Left:\n{:?}\n\nRight:\n{:?}\n\nWhitespace difference\n", left, right); 47 std::eprintln!("Left:\n{:?}\n\nRight:\n{:?}\n\nWhitespace difference\n", left, right);
47 } else { 48 } else {
48 let changeset = $crate::__Changeset::new(left, right, "\n"); 49 let diff = $crate::__diff(left, right);
49 std::eprintln!("Left:\n{}\n\nRight:\n{}\n\nDiff:\n{}\n", left, right, changeset); 50 std::eprintln!("Left:\n{}\n\nRight:\n{}\n\nDiff:\n{}\n", left, right, $crate::format_diff(diff));
50 } 51 }
51 std::eprintln!($($tt)*); 52 std::eprintln!($($tt)*);
52 panic!("text differs"); 53 panic!("text differs");
@@ -62,7 +63,7 @@ pub fn extract_offset(text: &str) -> (TextSize, String) {
62 } 63 }
63} 64}
64 65
65/// Returns the offset of the first occurence of `<|>` marker and the copy of `text` 66/// Returns the offset of the first occurrence of `$0` marker and the copy of `text`
66/// without the marker. 67/// without the marker.
67fn try_extract_offset(text: &str) -> Option<(TextSize, String)> { 68fn try_extract_offset(text: &str) -> Option<(TextSize, String)> {
68 let cursor_pos = text.find(CURSOR_MARKER)?; 69 let cursor_pos = text.find(CURSOR_MARKER)?;
@@ -81,7 +82,7 @@ pub fn extract_range(text: &str) -> (TextRange, String) {
81 } 82 }
82} 83}
83 84
84/// Returns `TextRange` between the first two markers `<|>...<|>` and the copy 85/// Returns `TextRange` between the first two markers `$0...$0` and the copy
85/// of `text` without both of these markers. 86/// of `text` without both of these markers.
86fn try_extract_range(text: &str) -> Option<(TextRange, String)> { 87fn try_extract_range(text: &str) -> Option<(TextRange, String)> {
87 let (start, text) = try_extract_offset(text)?; 88 let (start, text) = try_extract_offset(text)?;
@@ -104,11 +105,11 @@ impl From<RangeOrOffset> for TextRange {
104 } 105 }
105} 106}
106 107
107/// Extracts `TextRange` or `TextSize` depending on the amount of `<|>` markers 108/// Extracts `TextRange` or `TextSize` depending on the amount of `$0` markers
108/// found in `text`. 109/// found in `text`.
109/// 110///
110/// # Panics 111/// # Panics
111/// Panics if no `<|>` marker is present in the `text`. 112/// Panics if no `$0` marker is present in the `text`.
112pub fn extract_range_or_offset(text: &str) -> (RangeOrOffset, String) { 113pub fn extract_range_or_offset(text: &str) -> (RangeOrOffset, String) {
113 if let Some((range, text)) = try_extract_range(text) { 114 if let Some((range, text)) = try_extract_range(text) {
114 return (RangeOrOffset::Range(range), text); 115 return (RangeOrOffset::Range(range), text);
@@ -164,12 +165,12 @@ fn test_extract_tags() {
164 assert_eq!(actual, vec![("fn main() {}", Some("fn".into())), ("main", None),]); 165 assert_eq!(actual, vec![("fn main() {}", Some("fn".into())), ("main", None),]);
165} 166}
166 167
167/// Inserts `<|>` marker into the `text` at `offset`. 168/// Inserts `$0` marker into the `text` at `offset`.
168pub fn add_cursor(text: &str, offset: TextSize) -> String { 169pub fn add_cursor(text: &str, offset: TextSize) -> String {
169 let offset: usize = offset.into(); 170 let offset: usize = offset.into();
170 let mut res = String::new(); 171 let mut res = String::new();
171 res.push_str(&text[..offset]); 172 res.push_str(&text[..offset]);
172 res.push_str("<|>"); 173 res.push_str("$0");
173 res.push_str(&text[offset..]); 174 res.push_str(&text[offset..]);
174 res 175 res
175} 176}
@@ -392,3 +393,16 @@ pub fn project_dir() -> PathBuf {
392 let dir = env!("CARGO_MANIFEST_DIR"); 393 let dir = env!("CARGO_MANIFEST_DIR");
393 PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned() 394 PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned()
394} 395}
396
397pub fn format_diff(chunks: Vec<dissimilar::Chunk>) -> String {
398 let mut buf = String::new();
399 for chunk in chunks {
400 let formatted = match chunk {
401 dissimilar::Chunk::Equal(text) => text.into(),
402 dissimilar::Chunk::Delete(text) => format!("\x1b[41m{}\x1b[0m", text),
403 dissimilar::Chunk::Insert(text) => format!("\x1b[42m{}\x1b[0m", text),
404 };
405 buf.push_str(&formatted);
406 }
407 buf
408}
diff --git a/crates/tt/src/buffer.rs b/crates/tt/src/buffer.rs
index 02c771f70..3606c887d 100644
--- a/crates/tt/src/buffer.rs
+++ b/crates/tt/src/buffer.rs
@@ -1,6 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use crate::{Subtree, TokenTree}; 3use crate::{Leaf, Subtree, TokenTree};
4 4
5#[derive(Copy, Clone, Debug, Eq, PartialEq)] 5#[derive(Copy, Clone, Debug, Eq, PartialEq)]
6struct EntryId(usize); 6struct EntryId(usize);
@@ -13,7 +13,7 @@ struct EntryPtr(EntryId, usize);
13#[derive(Debug)] 13#[derive(Debug)]
14enum Entry<'t> { 14enum Entry<'t> {
15 // Mimicking types from proc-macro. 15 // Mimicking types from proc-macro.
16 Subtree(&'t TokenTree, EntryId), 16 Subtree(Option<&'t TokenTree>, &'t Subtree, EntryId),
17 Leaf(&'t TokenTree), 17 Leaf(&'t TokenTree),
18 // End entries contain a pointer to the entry from the containing 18 // End entries contain a pointer to the entry from the containing
19 // token tree, or None if this is the outermost level. 19 // token tree, or None if this is the outermost level.
@@ -27,37 +27,64 @@ pub struct TokenBuffer<'t> {
27 buffers: Vec<Box<[Entry<'t>]>>, 27 buffers: Vec<Box<[Entry<'t>]>>,
28} 28}
29 29
30impl<'t> TokenBuffer<'t> { 30trait TokenList<'a> {
31 pub fn new(tokens: &'t [TokenTree]) -> TokenBuffer<'t> { 31 fn entries(&self) -> (Vec<(usize, (&'a Subtree, Option<&'a TokenTree>))>, Vec<Entry<'a>>);
32 let mut buffers = vec![]; 32}
33
34 let idx = TokenBuffer::new_inner(tokens, &mut buffers, None);
35 assert_eq!(idx, 0);
36
37 TokenBuffer { buffers }
38 }
39 33
40 fn new_inner( 34impl<'a> TokenList<'a> for &'a [TokenTree] {
41 tokens: &'t [TokenTree], 35 fn entries(&self) -> (Vec<(usize, (&'a Subtree, Option<&'a TokenTree>))>, Vec<Entry<'a>>) {
42 buffers: &mut Vec<Box<[Entry<'t>]>>,
43 next: Option<EntryPtr>,
44 ) -> usize {
45 // Must contain everything in tokens and then the Entry::End 36 // Must contain everything in tokens and then the Entry::End
46 let start_capacity = tokens.len() + 1; 37 let start_capacity = self.len() + 1;
47 let mut entries = Vec::with_capacity(start_capacity); 38 let mut entries = Vec::with_capacity(start_capacity);
48 let mut children = vec![]; 39 let mut children = vec![];
49 40 for (idx, tt) in self.iter().enumerate() {
50 for (idx, tt) in tokens.iter().enumerate() {
51 match tt { 41 match tt {
52 TokenTree::Leaf(_) => { 42 TokenTree::Leaf(_) => {
53 entries.push(Entry::Leaf(tt)); 43 entries.push(Entry::Leaf(tt));
54 } 44 }
55 TokenTree::Subtree(subtree) => { 45 TokenTree::Subtree(subtree) => {
56 entries.push(Entry::End(None)); 46 entries.push(Entry::End(None));
57 children.push((idx, (subtree, tt))); 47 children.push((idx, (subtree, Some(tt))));
58 } 48 }
59 } 49 }
60 } 50 }
51 (children, entries)
52 }
53}
54
55impl<'a> TokenList<'a> for &'a Subtree {
56 fn entries(&self) -> (Vec<(usize, (&'a Subtree, Option<&'a TokenTree>))>, Vec<Entry<'a>>) {
57 // Must contain everything in tokens and then the Entry::End
58 let mut entries = vec![];
59 let mut children = vec![];
60 entries.push(Entry::End(None));
61 children.push((0usize, (*self, None)));
62 (children, entries)
63 }
64}
65
66impl<'t> TokenBuffer<'t> {
67 pub fn from_tokens(tokens: &'t [TokenTree]) -> TokenBuffer<'t> {
68 Self::new(tokens)
69 }
70
71 pub fn from_subtree(subtree: &'t Subtree) -> TokenBuffer<'t> {
72 Self::new(subtree)
73 }
74
75 fn new<T: TokenList<'t>>(tokens: T) -> TokenBuffer<'t> {
76 let mut buffers = vec![];
77 let idx = TokenBuffer::new_inner(tokens, &mut buffers, None);
78 assert_eq!(idx, 0);
79 TokenBuffer { buffers }
80 }
81
82 fn new_inner<T: TokenList<'t>>(
83 tokens: T,
84 buffers: &mut Vec<Box<[Entry<'t>]>>,
85 next: Option<EntryPtr>,
86 ) -> usize {
87 let (children, mut entries) = tokens.entries();
61 88
62 entries.push(Entry::End(next)); 89 entries.push(Entry::End(next));
63 let res = buffers.len(); 90 let res = buffers.len();
@@ -65,11 +92,11 @@ impl<'t> TokenBuffer<'t> {
65 92
66 for (child_idx, (subtree, tt)) in children { 93 for (child_idx, (subtree, tt)) in children {
67 let idx = TokenBuffer::new_inner( 94 let idx = TokenBuffer::new_inner(
68 &subtree.token_trees, 95 subtree.token_trees.as_slice(),
69 buffers, 96 buffers,
70 Some(EntryPtr(EntryId(res), child_idx + 1)), 97 Some(EntryPtr(EntryId(res), child_idx + 1)),
71 ); 98 );
72 buffers[res].as_mut()[child_idx] = Entry::Subtree(tt, EntryId(idx)); 99 buffers[res].as_mut()[child_idx] = Entry::Subtree(tt, subtree, EntryId(idx));
73 } 100 }
74 101
75 res 102 res
@@ -87,6 +114,24 @@ impl<'t> TokenBuffer<'t> {
87 } 114 }
88} 115}
89 116
117#[derive(Debug)]
118pub enum TokenTreeRef<'a> {
119 Subtree(&'a Subtree, Option<&'a TokenTree>),
120 Leaf(&'a Leaf, &'a TokenTree),
121}
122
123impl<'a> TokenTreeRef<'a> {
124 pub fn cloned(&self) -> TokenTree {
125 match &self {
126 TokenTreeRef::Subtree(subtree, tt) => match tt {
127 Some(it) => (*it).clone(),
128 None => (*subtree).clone().into(),
129 },
130 TokenTreeRef::Leaf(_, tt) => (*tt).clone(),
131 }
132 }
133}
134
90/// A safe version of `Cursor` from `syn` crate https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L125 135/// A safe version of `Cursor` from `syn` crate https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L125
91#[derive(Copy, Clone, Debug)] 136#[derive(Copy, Clone, Debug)]
92pub struct Cursor<'a> { 137pub struct Cursor<'a> {
@@ -114,12 +159,11 @@ impl<'a> Cursor<'a> {
114 match self.entry() { 159 match self.entry() {
115 Some(Entry::End(Some(ptr))) => { 160 Some(Entry::End(Some(ptr))) => {
116 let idx = ptr.1; 161 let idx = ptr.1;
117 if let Some(Entry::Subtree(TokenTree::Subtree(subtree), _)) = 162 if let Some(Entry::Subtree(_, subtree, _)) =
118 self.buffer.entry(&EntryPtr(ptr.0, idx - 1)) 163 self.buffer.entry(&EntryPtr(ptr.0, idx - 1))
119 { 164 {
120 return Some(subtree); 165 return Some(subtree);
121 } 166 }
122
123 None 167 None
124 } 168 }
125 _ => None, 169 _ => None,
@@ -134,7 +178,7 @@ impl<'a> Cursor<'a> {
134 /// a cursor into that subtree 178 /// a cursor into that subtree
135 pub fn subtree(self) -> Option<Cursor<'a>> { 179 pub fn subtree(self) -> Option<Cursor<'a>> {
136 match self.entry() { 180 match self.entry() {
137 Some(Entry::Subtree(_, entry_id)) => { 181 Some(Entry::Subtree(_, _, entry_id)) => {
138 Some(Cursor::create(self.buffer, EntryPtr(*entry_id, 0))) 182 Some(Cursor::create(self.buffer, EntryPtr(*entry_id, 0)))
139 } 183 }
140 _ => None, 184 _ => None,
@@ -142,10 +186,13 @@ impl<'a> Cursor<'a> {
142 } 186 }
143 187
144 /// If the cursor is pointing at a `TokenTree`, returns it 188 /// If the cursor is pointing at a `TokenTree`, returns it
145 pub fn token_tree(self) -> Option<&'a TokenTree> { 189 pub fn token_tree(self) -> Option<TokenTreeRef<'a>> {
146 match self.entry() { 190 match self.entry() {
147 Some(Entry::Leaf(tt)) => Some(tt), 191 Some(Entry::Leaf(tt)) => match tt {
148 Some(Entry::Subtree(tt, _)) => Some(tt), 192 TokenTree::Leaf(leaf) => Some(TokenTreeRef::Leaf(leaf, *tt)),
193 TokenTree::Subtree(subtree) => Some(TokenTreeRef::Subtree(subtree, Some(tt))),
194 },
195 Some(Entry::Subtree(tt, subtree, _)) => Some(TokenTreeRef::Subtree(subtree, *tt)),
149 Some(Entry::End(_)) => None, 196 Some(Entry::End(_)) => None,
150 None => None, 197 None => None,
151 } 198 }
@@ -172,7 +219,7 @@ impl<'a> Cursor<'a> {
172 /// a cursor into that subtree 219 /// a cursor into that subtree
173 pub fn bump_subtree(self) -> Cursor<'a> { 220 pub fn bump_subtree(self) -> Cursor<'a> {
174 match self.entry() { 221 match self.entry() {
175 Some(Entry::Subtree(_, _)) => self.subtree().unwrap(), 222 Some(Entry::Subtree(_, _, _)) => self.subtree().unwrap(),
176 _ => self.bump(), 223 _ => self.bump(),
177 } 224 }
178 } 225 }
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index 9cf2afd33..2b7b14524 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -2,43 +2,46 @@
2//! 2//!
3//! VFS stores all files read by rust-analyzer. Reading file contents from VFS 3//! VFS stores all files read by rust-analyzer. Reading file contents from VFS
4//! always returns the same contents, unless VFS was explicitly modified with 4//! always returns the same contents, unless VFS was explicitly modified with
5//! `set_file_contents`. All changes to VFS are logged, and can be retrieved via 5//! [`set_file_contents`]. All changes to VFS are logged, and can be retrieved via
6//! `take_changes` method. The pack of changes is then pushed to `salsa` and 6//! [`take_changes`] method. The pack of changes is then pushed to `salsa` and
7//! triggers incremental recomputation. 7//! triggers incremental recomputation.
8//! 8//!
9//! Files in VFS are identified with `FileId`s -- interned paths. The notion of 9//! Files in VFS are identified with [`FileId`]s -- interned paths. The notion of
10//! the path, `VfsPath` is somewhat abstract: at the moment, it is represented 10//! the path, [`VfsPath`] is somewhat abstract: at the moment, it is represented
11//! as an `std::path::PathBuf` internally, but this is an implementation detail. 11//! as an [`std::path::PathBuf`] internally, but this is an implementation detail.
12//! 12//!
13//! VFS doesn't do IO or file watching itself. For that, see the `loader` 13//! VFS doesn't do IO or file watching itself. For that, see the [`loader`]
14//! module. `loader::Handle` is an object-safe trait which abstracts both file 14//! module. [`loader::Handle`] is an object-safe trait which abstracts both file
15//! loading and file watching. `Handle` is dynamically configured with a set of 15//! loading and file watching. [`Handle`] is dynamically configured with a set of
16//! directory entries which should be scanned and watched. `Handle` then 16//! directory entries which should be scanned and watched. [`Handle`] then
17//! asynchronously pushes file changes. Directory entries are configured in 17//! asynchronously pushes file changes. Directory entries are configured in
18//! free-form via list of globs, it's up to the `Handle` to interpret the globs 18//! free-form via list of globs, it's up to the [`Handle`] to interpret the globs
19//! in any specific way. 19//! in any specific way.
20//! 20//!
21//! A simple `WalkdirLoaderHandle` is provided, which doesn't implement watching 21//! VFS stores a flat list of files. [`file_set::FileSet`] can partition this list
22//! and just scans the directory using walkdir. 22//! of files into disjoint sets of files. Traversal-like operations (including
23//! 23//! getting the neighbor file by the relative path) are handled by the [`FileSet`].
24//! VFS stores a flat list of files. `FileSet` can partition this list of files 24//! [`FileSet`]s are also pushed to salsa and cause it to re-check `mod foo;`
25//! into disjoint sets of files. Traversal-like operations (including getting
26//! the neighbor file by the relative path) are handled by the `FileSet`.
27//! `FileSet`s are also pushed to salsa and cause it to re-check `mod foo;`
28//! declarations when files are created or deleted. 25//! declarations when files are created or deleted.
29//! 26//!
30//! `file_set::FileSet` and `loader::Entry` play similar, but different roles. 27//! [`FileSet`] and [`loader::Entry`] play similar, but different roles.
31//! Both specify the "set of paths/files", one is geared towards file watching, 28//! Both specify the "set of paths/files", one is geared towards file watching,
32//! the other towards salsa changes. In particular, single `file_set::FileSet` 29//! the other towards salsa changes. In particular, single [`FileSet`]
33//! may correspond to several `loader::Entry`. For example, a crate from 30//! may correspond to several [`loader::Entry`]. For example, a crate from
34//! crates.io which uses code generation would have two `Entries` -- for sources 31//! crates.io which uses code generation would have two [`Entries`] -- for sources
35//! in `~/.cargo`, and for generated code in `./target/debug/build`. It will 32//! in `~/.cargo`, and for generated code in `./target/debug/build`. It will
36//! have a single `FileSet` which unions the two sources. 33//! have a single [`FileSet`] which unions the two sources.
37mod vfs_path; 34//!
38mod path_interner; 35//! [`set_file_contents`]: Vfs::set_file_contents
36//! [`take_changes`]: Vfs::take_changes
37//! [`FileSet`]: file_set::FileSet
38//! [`Handle`]: loader::Handle
39//! [`Entries`]: loader::Entry
39mod anchored_path; 40mod anchored_path;
40pub mod file_set; 41pub mod file_set;
41pub mod loader; 42pub mod loader;
43mod path_interner;
44mod vfs_path;
42 45
43use std::{fmt, mem}; 46use std::{fmt, mem};
44 47