aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/expect/Cargo.toml14
-rw-r--r--crates/expect/src/lib.rs356
-rw-r--r--crates/flycheck/Cargo.toml (renamed from crates/ra_flycheck/Cargo.toml)3
-rw-r--r--crates/flycheck/src/lib.rs317
-rw-r--r--crates/paths/Cargo.toml9
-rw-r--r--crates/paths/src/lib.rs217
-rw-r--r--crates/ra_arena/Cargo.toml1
-rw-r--r--crates/ra_arena/src/lib.rs3
-rw-r--r--crates/ra_assists/Cargo.toml1
-rw-r--r--crates/ra_assists/src/assist_config.rs5
-rw-r--r--crates/ra_assists/src/assist_context.rs87
-rw-r--r--crates/ra_assists/src/ast_transform.rs18
-rw-r--r--crates/ra_assists/src/handlers/add_custom_impl.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs10
-rw-r--r--crates/ra_assists/src/handlers/add_impl.rs98
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs35
-rw-r--r--crates/ra_assists/src/handlers/add_turbo_fish.rs13
-rw-r--r--crates/ra_assists/src/handlers/apply_demorgan.rs17
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs279
-rw-r--r--crates/ra_assists/src/handlers/change_return_type_to_result.rs37
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs48
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs156
-rw-r--r--crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs321
-rw-r--r--crates/ra_assists/src/handlers/extract_variable.rs (renamed from crates/ra_assists/src/handlers/introduce_variable.rs)270
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs257
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs208
-rw-r--r--crates/ra_assists/src/handlers/flip_binexpr.rs21
-rw-r--r--crates/ra_assists/src/handlers/flip_comma.rs15
-rw-r--r--crates/ra_assists/src/handlers/flip_trait_bound.rs15
-rw-r--r--crates/ra_assists/src/handlers/generate_derive.rs (renamed from crates/ra_assists/src/handlers/add_derive.rs)63
-rw-r--r--crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs (renamed from crates/ra_assists/src/handlers/add_from_impl_for_enum.rs)24
-rw-r--r--crates/ra_assists/src/handlers/generate_function.rs (renamed from crates/ra_assists/src/handlers/add_function.rs)91
-rw-r--r--crates/ra_assists/src/handlers/generate_impl.rs109
-rw-r--r--crates/ra_assists/src/handlers/generate_new.rs (renamed from crates/ra_assists/src/handlers/add_new.rs)46
-rw-r--r--crates/ra_assists/src/handlers/inline_local_variable.rs24
-rw-r--r--crates/ra_assists/src/handlers/introduce_named_lifetime.rs (renamed from crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs)85
-rw-r--r--crates/ra_assists/src/handlers/invert_if.rs4
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs25
-rw-r--r--crates/ra_assists/src/handlers/merge_match_arms.rs50
-rw-r--r--crates/ra_assists/src/handlers/move_bounds.rs55
-rw-r--r--crates/ra_assists/src/handlers/move_guard.rs29
-rw-r--r--crates/ra_assists/src/handlers/raw_string.rs135
-rw-r--r--crates/ra_assists/src/handlers/remove_dbg.rs4
-rw-r--r--crates/ra_assists/src/handlers/remove_mut.rs13
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs27
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs81
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs41
-rw-r--r--crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs282
-rw-r--r--crates/ra_assists/src/handlers/replace_unwrap_with_match.rs60
-rw-r--r--crates/ra_assists/src/handlers/split_import.rs14
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs168
-rw-r--r--crates/ra_assists/src/lib.rs62
-rw-r--r--crates/ra_assists/src/tests.rs88
-rw-r--r--crates/ra_assists/src/tests/generated.rs297
-rw-r--r--crates/ra_assists/src/utils.rs16
-rw-r--r--crates/ra_assists/src/utils/insert_use.rs24
-rw-r--r--crates/ra_cfg/Cargo.toml1
-rw-r--r--crates/ra_cfg/src/lib.rs10
-rw-r--r--crates/ra_db/Cargo.toml6
-rw-r--r--crates/ra_db/src/fixture.rs188
-rw-r--r--crates/ra_db/src/input.rs161
-rw-r--r--crates/ra_db/src/lib.rs73
-rw-r--r--crates/ra_flycheck/src/lib.rs302
-rw-r--r--crates/ra_fmt/Cargo.toml1
-rw-r--r--crates/ra_hir/Cargo.toml2
-rw-r--r--crates/ra_hir/src/code_model.rs231
-rw-r--r--crates/ra_hir/src/db.rs18
-rw-r--r--crates/ra_hir/src/diagnostics.rs4
-rw-r--r--crates/ra_hir/src/has_source.rs10
-rw-r--r--crates/ra_hir/src/lib.rs21
-rw-r--r--crates/ra_hir/src/semantics.rs329
-rw-r--r--crates/ra_hir/src/semantics/source_to_def.rs12
-rw-r--r--crates/ra_hir/src/source_analyzer.rs60
-rw-r--r--crates/ra_hir_def/Cargo.toml6
-rw-r--r--crates/ra_hir_def/src/adt.rs94
-rw-r--r--crates/ra_hir_def/src/attr.rs57
-rw-r--r--crates/ra_hir_def/src/body.rs67
-rw-r--r--crates/ra_hir_def/src/body/lower.rs159
-rw-r--r--crates/ra_hir_def/src/body/scope.rs140
-rw-r--r--crates/ra_hir_def/src/data.rs384
-rw-r--r--crates/ra_hir_def/src/db.rs25
-rw-r--r--crates/ra_hir_def/src/diagnostics.rs3
-rw-r--r--crates/ra_hir_def/src/docs.rs50
-rw-r--r--crates/ra_hir_def/src/expr.rs5
-rw-r--r--crates/ra_hir_def/src/find_path.rs247
-rw-r--r--crates/ra_hir_def/src/generics.rs62
-rw-r--r--crates/ra_hir_def/src/import_map.rs737
-rw-r--r--crates/ra_hir_def/src/item_scope.rs174
-rw-r--r--crates/ra_hir_def/src/item_tree.rs754
-rw-r--r--crates/ra_hir_def/src/item_tree/lower.rs708
-rw-r--r--crates/ra_hir_def/src/item_tree/tests.rs439
-rw-r--r--crates/ra_hir_def/src/lib.rs166
-rw-r--r--crates/ra_hir_def/src/nameres.rs12
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs514
-rw-r--r--crates/ra_hir_def/src/nameres/mod_resolution.rs121
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs482
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs25
-rw-r--r--crates/ra_hir_def/src/nameres/tests/globs.rs137
-rw-r--r--crates/ra_hir_def/src/nameres/tests/incremental.rs38
-rw-r--r--crates/ra_hir_def/src/nameres/tests/macros.rs24
-rw-r--r--crates/ra_hir_def/src/nameres/tests/mod_resolution.rs28
-rw-r--r--crates/ra_hir_def/src/path.rs37
-rw-r--r--crates/ra_hir_def/src/per_ns.rs10
-rw-r--r--crates/ra_hir_def/src/resolver.rs8
-rw-r--r--crates/ra_hir_def/src/src.rs31
-rw-r--r--crates/ra_hir_def/src/test_db.rs51
-rw-r--r--crates/ra_hir_def/src/type_ref.rs13
-rw-r--r--crates/ra_hir_def/src/visibility.rs21
-rw-r--r--crates/ra_hir_expand/Cargo.toml2
-rw-r--r--crates/ra_hir_expand/src/ast_id_map.rs13
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs31
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs111
-rw-r--r--crates/ra_hir_expand/src/diagnostics.rs2
-rw-r--r--crates/ra_hir_expand/src/eager.rs32
-rw-r--r--crates/ra_hir_expand/src/lib.rs30
-rw-r--r--crates/ra_hir_expand/src/name.rs5
-rw-r--r--crates/ra_hir_expand/src/test_db.rs44
-rw-r--r--crates/ra_hir_ty/Cargo.toml11
-rw-r--r--crates/ra_hir_ty/src/_match.rs2085
-rw-r--r--crates/ra_hir_ty/src/autoderef.rs15
-rw-r--r--crates/ra_hir_ty/src/db.rs61
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs327
-rw-r--r--crates/ra_hir_ty/src/diagnostics/expr.rs (renamed from crates/ra_hir_ty/src/expr.rs)277
-rw-r--r--crates/ra_hir_ty/src/diagnostics/match_check.rs1421
-rw-r--r--crates/ra_hir_ty/src/diagnostics/unsafe_check.rs173
-rw-r--r--crates/ra_hir_ty/src/display.rs78
-rw-r--r--crates/ra_hir_ty/src/infer.rs89
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs133
-rw-r--r--crates/ra_hir_ty/src/infer/pat.rs42
-rw-r--r--crates/ra_hir_ty/src/infer/path.rs5
-rw-r--r--crates/ra_hir_ty/src/infer/unify.rs55
-rw-r--r--crates/ra_hir_ty/src/lib.rs268
-rw-r--r--crates/ra_hir_ty/src/lower.rs329
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs393
-rw-r--r--crates/ra_hir_ty/src/primitive.rs54
-rw-r--r--crates/ra_hir_ty/src/test_db.rs129
-rw-r--r--crates/ra_hir_ty/src/tests.rs311
-rw-r--r--crates/ra_hir_ty/src/tests/coercion.rs690
-rw-r--r--crates/ra_hir_ty/src/tests/display_source_code.rs49
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs236
-rw-r--r--crates/ra_hir_ty/src/tests/method_resolution.rs590
-rw-r--r--crates/ra_hir_ty/src/tests/never_type.rs308
-rw-r--r--crates/ra_hir_ty/src/tests/patterns.rs652
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs527
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs1818
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs2041
-rw-r--r--crates/ra_hir_ty/src/traits.rs113
-rw-r--r--crates/ra_hir_ty/src/traits/builtin.rs377
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs294
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/interner.rs32
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/mapping.rs286
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/tls.rs33
-rw-r--r--crates/ra_hir_ty/src/utils.rs46
-rw-r--r--crates/ra_ide/Cargo.toml4
-rw-r--r--crates/ra_ide/src/call_hierarchy.rs214
-rw-r--r--crates/ra_ide/src/call_info.rs549
-rw-r--r--crates/ra_ide/src/completion.rs91
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs861
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs994
-rw-r--r--crates/ra_ide/src/completion/complete_fn_param.rs131
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs1102
-rw-r--r--crates/ra_ide/src/completion/complete_macro_in_item_position.rs137
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs126
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs639
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs1506
-rw-r--r--crates/ra_ide/src/completion/complete_record.rs546
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs106
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs464
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs1615
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs83
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs32
-rw-r--r--crates/ra_ide/src/completion/patterns.rs194
-rw-r--r--crates/ra_ide/src/completion/presentation.rs1639
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs111
-rw-r--r--crates/ra_ide/src/diagnostics.rs880
-rw-r--r--crates/ra_ide/src/display.rs29
-rw-r--r--crates/ra_ide/src/display/function_signature.rs120
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs134
-rw-r--r--crates/ra_ide/src/display/structure.rs429
-rw-r--r--crates/ra_ide/src/expand_macro.rs241
-rw-r--r--crates/ra_ide/src/extend_selection.rs12
-rw-r--r--crates/ra_ide/src/folding_ranges.rs174
-rw-r--r--crates/ra_ide/src/goto_definition.rs1164
-rw-r--r--crates/ra_ide/src/goto_implementation.rs197
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs132
-rw-r--r--crates/ra_ide/src/hover.rs2625
-rw-r--r--crates/ra_ide/src/inlay_hints.rs992
-rw-r--r--crates/ra_ide/src/join_lines.rs5
-rw-r--r--crates/ra_ide/src/lib.rs87
-rw-r--r--crates/ra_ide/src/markup.rs38
-rw-r--r--crates/ra_ide/src/matching_brace.rs33
-rw-r--r--crates/ra_ide/src/mock_analysis.rs214
-rw-r--r--crates/ra_ide/src/parent_module.rs13
-rw-r--r--crates/ra_ide/src/prime_caches.rs2
-rw-r--r--crates/ra_ide/src/references.rs515
-rw-r--r--crates/ra_ide/src/references/rename.rs1210
-rw-r--r--crates/ra_ide/src/runnables.rs898
-rw-r--r--crates/ra_ide/src/snapshots/highlight_injection.html41
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html84
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html101
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html42
-rw-r--r--crates/ra_ide/src/ssr.rs618
-rw-r--r--crates/ra_ide/src/status.rs28
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs333
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs11
-rw-r--r--crates/ra_ide/src/syntax_highlighting/injection.rs188
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs18
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs201
-rw-r--r--crates/ra_ide/src/syntax_tree.rs12
-rw-r--r--crates/ra_ide/src/typing.rs44
-rw-r--r--crates/ra_ide/src/typing/on_enter.rs15
-rw-r--r--crates/ra_ide/test_data/highlight_doctest.html102
-rw-r--r--crates/ra_ide/test_data/highlight_injection.html48
-rw-r--r--crates/ra_ide/test_data/highlight_strings.html96
-rw-r--r--crates/ra_ide/test_data/highlight_unsafe.html54
-rw-r--r--crates/ra_ide/test_data/highlighting.html128
-rw-r--r--crates/ra_ide/test_data/rainbow_highlighting.html49
-rw-r--r--crates/ra_ide_db/Cargo.toml1
-rw-r--r--crates/ra_ide_db/src/change.rs254
-rw-r--r--crates/ra_ide_db/src/defs.rs69
-rw-r--r--crates/ra_ide_db/src/imports_locator.rs84
-rw-r--r--crates/ra_ide_db/src/lib.rs80
-rw-r--r--crates/ra_ide_db/src/search.rs16
-rw-r--r--crates/ra_ide_db/src/source_change.rs31
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs138
-rw-r--r--crates/ra_mbe/Cargo.toml1
-rw-r--r--crates/ra_mbe/src/lib.rs6
-rw-r--r--crates/ra_mbe/src/mbe_expander/matcher.rs2
-rw-r--r--crates/ra_mbe/src/parser.rs5
-rw-r--r--crates/ra_parser/Cargo.toml1
-rw-r--r--crates/ra_parser/src/grammar.rs12
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs6
-rw-r--r--crates/ra_parser/src/grammar/items.rs20
-rw-r--r--crates/ra_parser/src/grammar/paths.rs15
-rw-r--r--crates/ra_parser/src/grammar/patterns.rs8
-rw-r--r--crates/ra_parser/src/grammar/type_params.rs11
-rw-r--r--crates/ra_parser/src/grammar/types.rs16
-rw-r--r--crates/ra_parser/src/parser.rs15
-rw-r--r--crates/ra_parser/src/syntax_kind.rs5
-rw-r--r--crates/ra_proc_macro/Cargo.toml1
-rw-r--r--crates/ra_proc_macro_srv/Cargo.toml2
-rw-r--r--crates/ra_proc_macro_srv/src/dylib.rs2
-rw-r--r--crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt2
-rw-r--r--crates/ra_proc_macro_srv/src/tests/mod.rs6
-rw-r--r--crates/ra_proc_macro_srv/src/tests/utils.rs7
-rw-r--r--crates/ra_prof/Cargo.toml8
-rw-r--r--crates/ra_prof/src/lib.rs13
-rw-r--r--crates/ra_project_model/Cargo.toml3
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs79
-rw-r--r--crates/ra_project_model/src/json_project.rs56
-rw-r--r--crates/ra_project_model/src/lib.rs220
-rw-r--r--crates/ra_project_model/src/project_json.rs129
-rw-r--r--crates/ra_project_model/src/sysroot.rs28
-rw-r--r--crates/ra_ssr/Cargo.toml20
-rw-r--r--crates/ra_ssr/src/errors.rs29
-rw-r--r--crates/ra_ssr/src/lib.rs285
-rw-r--r--crates/ra_ssr/src/matching.rs623
-rw-r--r--crates/ra_ssr/src/parsing.rs343
-rw-r--r--crates/ra_ssr/src/replacing.rs66
-rw-r--r--crates/ra_ssr/src/tests.rs582
-rw-r--r--crates/ra_syntax/Cargo.toml4
-rw-r--r--crates/ra_syntax/src/algo.rs12
-rw-r--r--crates/ra_syntax/src/ast.rs8
-rw-r--r--crates/ra_syntax/src/ast/edit.rs36
-rw-r--r--crates/ra_syntax/src/ast/expr_extensions.rs5
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs16
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs276
-rw-r--r--crates/ra_syntax/src/ast/generated/tokens.rs8
-rw-r--r--crates/ra_syntax/src/ast/make.rs4
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs69
-rw-r--r--crates/ra_syntax/src/ast/traits.rs13
-rw-r--r--crates/ra_syntax/src/lib.rs38
-rw-r--r--crates/ra_syntax/src/parsing.rs32
-rw-r--r--crates/ra_syntax/src/parsing/lexer.rs24
-rw-r--r--crates/ra_syntax/src/parsing/reparsing.rs5
-rw-r--r--crates/ra_syntax/src/parsing/text_token_source.rs89
-rw-r--r--crates/ra_syntax/src/parsing/text_tree_sink.rs5
-rw-r--r--crates/ra_syntax/src/syntax_node.rs4
-rw-r--r--crates/ra_syntax/src/tests.rs182
-rw-r--r--crates/ra_syntax/test_data/parser/err/0004_use_path_bad_segment.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast74
-rw-r--r--crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast15
-rw-r--r--crates/ra_syntax/test_data/parser/err/0043_default_const.rast40
-rw-r--r--crates/ra_syntax/test_data/parser/err/0043_default_const.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast240
-rw-r--r--crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs9
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rast1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rast8
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rast1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rast12
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rast1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rast1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rast4
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rast14
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rast1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rast1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rast10
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rast1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rast22
-rw-r--r--crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast34
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0015_empty_segment.rast15
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0015_empty_segment.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast117
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs2
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast292
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast40
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast18
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast38
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0066_default_const.rast44
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0066_default_const.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast392
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs30
-rw-r--r--crates/ra_text_edit/Cargo.toml1
-rw-r--r--crates/ra_toolchain/Cargo.toml4
-rw-r--r--crates/ra_toolchain/src/lib.rs25
-rw-r--r--crates/ra_tt/Cargo.toml2
-rw-r--r--crates/ra_tt/src/buffer.rs5
-rw-r--r--crates/ra_tt/src/lib.rs19
-rw-r--r--crates/rust-analyzer/Cargo.toml22
-rw-r--r--crates/rust-analyzer/build.rs5
-rw-r--r--crates/rust-analyzer/src/bin/args.rs95
-rw-r--r--crates/rust-analyzer/src/bin/main.rs84
-rw-r--r--crates/rust-analyzer/src/caps.rs25
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs133
-rw-r--r--crates/rust-analyzer/src/cli.rs12
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs70
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs80
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs80
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs187
-rw-r--r--crates/rust-analyzer/src/cli/ssr.rs71
-rw-r--r--crates/rust-analyzer/src/config.rs348
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs72
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs499
-rw-r--r--crates/rust-analyzer/src/dispatch.rs170
-rw-r--r--crates/rust-analyzer/src/from_proto.rs42
-rw-r--r--crates/rust-analyzer/src/global_state.rs292
-rw-r--r--crates/rust-analyzer/src/handlers.rs (renamed from crates/rust-analyzer/src/main_loop/handlers.rs)988
-rw-r--r--crates/rust-analyzer/src/lib.rs66
-rw-r--r--crates/rust-analyzer/src/line_endings.rs52
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs123
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs195
-rw-r--r--crates/rust-analyzer/src/main_loop.rs1444
-rw-r--r--crates/rust-analyzer/src/main_loop/pending_requests.rs75
-rw-r--r--crates/rust-analyzer/src/main_loop/subscriptions.rs22
-rw-r--r--crates/rust-analyzer/src/reload.rs324
-rw-r--r--crates/rust-analyzer/src/request_metrics.rs37
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs6
-rw-r--r--crates/rust-analyzer/src/thread_pool.rs35
-rw-r--r--crates/rust-analyzer/src/to_proto.rs276
-rw-r--r--crates/rust-analyzer/src/vfs_glob.rs98
-rw-r--r--crates/rust-analyzer/src/world.rs343
-rw-r--r--crates/rust-analyzer/test_data/clippy_pass_by_ref.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap)18
-rw-r--r--crates/rust-analyzer/test_data/handles_macro_location.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap)18
-rw-r--r--crates/rust-analyzer/test_data/macro_compiler_error.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap)18
-rw-r--r--crates/rust-analyzer/test_data/rustc_incompatible_type_for_trait.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap)18
-rw-r--r--crates/rust-analyzer/test_data/rustc_mismatched_type.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap)18
-rw-r--r--crates/rust-analyzer/test_data/rustc_unused_variable.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap)24
-rw-r--r--crates/rust-analyzer/test_data/rustc_unused_variable_as_hint.txt71
-rw-r--r--crates/rust-analyzer/test_data/rustc_unused_variable_as_info.txt71
-rw-r--r--crates/rust-analyzer/test_data/rustc_wrong_number_of_parameters.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap)18
-rw-r--r--crates/rust-analyzer/test_data/snap_multi_line_fix.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap)24
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs285
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs91
-rw-r--r--crates/stdx/Cargo.toml1
-rw-r--r--crates/stdx/src/lib.rs111
-rw-r--r--crates/stdx/src/macros.rs40
-rw-r--r--crates/test_utils/Cargo.toml6
-rw-r--r--crates/test_utils/src/fixture.rs142
-rw-r--r--crates/test_utils/src/lib.rs501
-rw-r--r--crates/test_utils/src/mark.rs4
-rw-r--r--crates/vfs-notify/Cargo.toml21
-rw-r--r--crates/vfs-notify/src/include.rs42
-rw-r--r--crates/vfs-notify/src/lib.rs230
-rw-r--r--crates/vfs/Cargo.toml15
-rw-r--r--crates/vfs/src/file_set.rs172
-rw-r--r--crates/vfs/src/lib.rs140
-rw-r--r--crates/vfs/src/loader.rs71
-rw-r--r--crates/vfs/src/path_interner.rs31
-rw-r--r--crates/vfs/src/vfs_path.rs146
397 files changed, 36615 insertions, 27931 deletions
diff --git a/crates/expect/Cargo.toml b/crates/expect/Cargo.toml
new file mode 100644
index 000000000..77775630d
--- /dev/null
+++ b/crates/expect/Cargo.toml
@@ -0,0 +1,14 @@
1[package]
2name = "expect"
3version = "0.1.0"
4authors = ["rust-analyzer developers"]
5edition = "2018"
6license = "MIT OR Apache-2.0"
7
8[lib]
9doctest = false
10
11[dependencies]
12once_cell = "1"
13difference = "2"
14stdx = { path = "../stdx" }
diff --git a/crates/expect/src/lib.rs b/crates/expect/src/lib.rs
new file mode 100644
index 000000000..21a458d47
--- /dev/null
+++ b/crates/expect/src/lib.rs
@@ -0,0 +1,356 @@
1//! Snapshot testing library, see
2//! https://github.com/rust-analyzer/rust-analyzer/pull/5101
3use std::{
4 collections::HashMap,
5 env, fmt, fs, mem,
6 ops::Range,
7 panic,
8 path::{Path, PathBuf},
9 sync::Mutex,
10};
11
12use difference::Changeset;
13use once_cell::sync::Lazy;
14use stdx::{lines_with_ends, trim_indent};
15
16const HELP: &str = "
17You can update all `expect![[]]` tests by running:
18
19 env UPDATE_EXPECT=1 cargo test
20
21To update a single test, place the cursor on `expect` token and use `run` feature of rust-analyzer.
22";
23
24fn update_expect() -> bool {
25 env::var("UPDATE_EXPECT").is_ok()
26}
27
28/// expect![[r#"inline snapshot"#]]
29#[macro_export]
30macro_rules! expect {
31 [[$data:literal]] => {$crate::Expect {
32 position: $crate::Position {
33 file: file!(),
34 line: line!(),
35 column: column!(),
36 },
37 data: $data,
38 }};
39 [[]] => { $crate::expect![[""]] };
40}
41
42/// expect_file!["/crates/foo/test_data/bar.html"]
43#[macro_export]
44macro_rules! expect_file {
45 [$path:expr] => {$crate::ExpectFile {
46 path: std::path::PathBuf::from($path)
47 }};
48}
49
50#[derive(Debug)]
51pub struct Expect {
52 pub position: Position,
53 pub data: &'static str,
54}
55
56#[derive(Debug)]
57pub struct ExpectFile {
58 pub path: PathBuf,
59}
60
61#[derive(Debug)]
62pub struct Position {
63 pub file: &'static str,
64 pub line: u32,
65 pub column: u32,
66}
67
68impl fmt::Display for Position {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 write!(f, "{}:{}:{}", self.file, self.line, self.column)
71 }
72}
73
74impl Expect {
75 pub fn assert_eq(&self, actual: &str) {
76 let trimmed = self.trimmed();
77 if &trimmed == actual {
78 return;
79 }
80 Runtime::fail_expect(self, &trimmed, actual);
81 }
82 pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
83 let actual = format!("{:#?}\n", actual);
84 self.assert_eq(&actual)
85 }
86
87 fn trimmed(&self) -> String {
88 if !self.data.contains('\n') {
89 return self.data.to_string();
90 }
91 trim_indent(self.data)
92 }
93
94 fn locate(&self, file: &str) -> Location {
95 let mut target_line = None;
96 let mut line_start = 0;
97 for (i, line) in lines_with_ends(file).enumerate() {
98 if i == self.position.line as usize - 1 {
99 let pat = "expect![[";
100 let offset = line.find(pat).unwrap();
101 let literal_start = line_start + offset + pat.len();
102 let indent = line.chars().take_while(|&it| it == ' ').count();
103 target_line = Some((literal_start, indent));
104 break;
105 }
106 line_start += line.len();
107 }
108 let (literal_start, line_indent) = target_line.unwrap();
109 let literal_length =
110 file[literal_start..].find("]]").expect("Couldn't find matching `]]` for `expect![[`.");
111 let literal_range = literal_start..literal_start + literal_length;
112 Location { line_indent, literal_range }
113 }
114}
115
116impl ExpectFile {
117 pub fn assert_eq(&self, actual: &str) {
118 let expected = self.read();
119 if actual == expected {
120 return;
121 }
122 Runtime::fail_file(self, &expected, actual);
123 }
124 pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
125 let actual = format!("{:#?}\n", actual);
126 self.assert_eq(&actual)
127 }
128 fn read(&self) -> String {
129 fs::read_to_string(self.abs_path()).unwrap_or_default().replace("\r\n", "\n")
130 }
131 fn write(&self, contents: &str) {
132 fs::write(self.abs_path(), contents).unwrap()
133 }
134 fn abs_path(&self) -> PathBuf {
135 WORKSPACE_ROOT.join(&self.path)
136 }
137}
138
139#[derive(Default)]
140struct Runtime {
141 help_printed: bool,
142 per_file: HashMap<&'static str, FileRuntime>,
143}
144static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default);
145
146impl Runtime {
147 fn fail_expect(expect: &Expect, expected: &str, actual: &str) {
148 let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
149 if update_expect() {
150 println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.position);
151 rt.per_file
152 .entry(expect.position.file)
153 .or_insert_with(|| FileRuntime::new(expect))
154 .update(expect, actual);
155 return;
156 }
157 rt.panic(expect.position.to_string(), expected, actual);
158 }
159
160 fn fail_file(expect: &ExpectFile, expected: &str, actual: &str) {
161 let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
162 if update_expect() {
163 println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.path.display());
164 expect.write(actual);
165 return;
166 }
167 rt.panic(expect.path.display().to_string(), expected, actual);
168 }
169
170 fn panic(&mut self, position: String, expected: &str, actual: &str) {
171 let print_help = !mem::replace(&mut self.help_printed, true);
172 let help = if print_help { HELP } else { "" };
173
174 let diff = Changeset::new(actual, expected, "\n");
175
176 println!(
177 "\n
178\x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m
179 \x1b[1m\x1b[34m-->\x1b[0m {}
180{}
181\x1b[1mExpect\x1b[0m:
182----
183{}
184----
185
186\x1b[1mActual\x1b[0m:
187----
188{}
189----
190
191\x1b[1mDiff\x1b[0m:
192----
193{}
194----
195",
196 position, help, expected, actual, diff
197 );
198 // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise.
199 panic::resume_unwind(Box::new(()));
200 }
201}
202
203struct FileRuntime {
204 path: PathBuf,
205 original_text: String,
206 patchwork: Patchwork,
207}
208
209impl FileRuntime {
210 fn new(expect: &Expect) -> FileRuntime {
211 let path = WORKSPACE_ROOT.join(expect.position.file);
212 let original_text = fs::read_to_string(&path).unwrap();
213 let patchwork = Patchwork::new(original_text.clone());
214 FileRuntime { path, original_text, patchwork }
215 }
216 fn update(&mut self, expect: &Expect, actual: &str) {
217 let loc = expect.locate(&self.original_text);
218 let patch = format_patch(loc.line_indent.clone(), actual);
219 self.patchwork.patch(loc.literal_range, &patch);
220 fs::write(&self.path, &self.patchwork.text).unwrap()
221 }
222}
223
224#[derive(Debug)]
225struct Location {
226 line_indent: usize,
227 literal_range: Range<usize>,
228}
229
230#[derive(Debug)]
231struct Patchwork {
232 text: String,
233 indels: Vec<(Range<usize>, usize)>,
234}
235
236impl Patchwork {
237 fn new(text: String) -> Patchwork {
238 Patchwork { text, indels: Vec::new() }
239 }
240 fn patch(&mut self, mut range: Range<usize>, patch: &str) {
241 self.indels.push((range.clone(), patch.len()));
242 self.indels.sort_by_key(|(delete, _insert)| delete.start);
243
244 let (delete, insert) = self
245 .indels
246 .iter()
247 .take_while(|(delete, _)| delete.start < range.start)
248 .map(|(delete, insert)| (delete.end - delete.start, insert))
249 .fold((0usize, 0usize), |(x1, y1), (x2, y2)| (x1 + x2, y1 + y2));
250
251 for pos in &mut [&mut range.start, &mut range.end] {
252 **pos -= delete;
253 **pos += insert;
254 }
255
256 self.text.replace_range(range, &patch);
257 }
258}
259
260fn format_patch(line_indent: usize, patch: &str) -> String {
261 let mut max_hashes = 0;
262 let mut cur_hashes = 0;
263 for byte in patch.bytes() {
264 if byte != b'#' {
265 cur_hashes = 0;
266 continue;
267 }
268 cur_hashes += 1;
269 max_hashes = max_hashes.max(cur_hashes);
270 }
271 let hashes = &"#".repeat(max_hashes + 1);
272 let indent = &" ".repeat(line_indent);
273 let is_multiline = patch.contains('\n');
274
275 let mut buf = String::new();
276 buf.push('r');
277 buf.push_str(hashes);
278 buf.push('"');
279 if is_multiline {
280 buf.push('\n');
281 }
282 let mut final_newline = false;
283 for line in lines_with_ends(patch) {
284 if is_multiline && !line.trim().is_empty() {
285 buf.push_str(indent);
286 buf.push_str(" ");
287 }
288 buf.push_str(line);
289 final_newline = line.ends_with('\n');
290 }
291 if final_newline {
292 buf.push_str(indent);
293 }
294 buf.push('"');
295 buf.push_str(hashes);
296 buf
297}
298
299static WORKSPACE_ROOT: Lazy<PathBuf> = Lazy::new(|| {
300 let my_manifest =
301 env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned());
302 // Heuristic, see https://github.com/rust-lang/cargo/issues/3946
303 Path::new(&my_manifest)
304 .ancestors()
305 .filter(|it| it.join("Cargo.toml").exists())
306 .last()
307 .unwrap()
308 .to_path_buf()
309});
310
311#[cfg(test)]
312mod tests {
313 use super::*;
314
315 #[test]
316 fn test_format_patch() {
317 let patch = format_patch(0, "hello\nworld\n");
318 expect![[r##"
319 r#"
320 hello
321 world
322 "#"##]]
323 .assert_eq(&patch);
324
325 let patch = format_patch(4, "single line");
326 expect![[r##"r#"single line"#"##]].assert_eq(&patch);
327 }
328
329 #[test]
330 fn test_patchwork() {
331 let mut patchwork = Patchwork::new("one two three".to_string());
332 patchwork.patch(4..7, "zwei");
333 patchwork.patch(0..3, "один");
334 patchwork.patch(8..13, "3");
335 expect![[r#"
336 Patchwork {
337 text: "один zwei 3",
338 indels: [
339 (
340 0..3,
341 8,
342 ),
343 (
344 4..7,
345 4,
346 ),
347 (
348 8..13,
349 1,
350 ),
351 ],
352 }
353 "#]]
354 .assert_debug_eq(&patchwork);
355 }
356}
diff --git a/crates/ra_flycheck/Cargo.toml b/crates/flycheck/Cargo.toml
index 1aa39bade..bea485694 100644
--- a/crates/ra_flycheck/Cargo.toml
+++ b/crates/flycheck/Cargo.toml
@@ -1,8 +1,9 @@
1[package] 1[package]
2edition = "2018" 2edition = "2018"
3name = "ra_flycheck" 3name = "flycheck"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
new file mode 100644
index 000000000..6804d9bda
--- /dev/null
+++ b/crates/flycheck/src/lib.rs
@@ -0,0 +1,317 @@
1//! cargo_check provides the functionality needed to run `cargo check` or
2//! another compatible command (f.x. clippy) in a background thread and provide
3//! LSP diagnostics based on the output of the command.
4
5use std::{
6 fmt,
7 io::{self, BufReader},
8 ops,
9 path::PathBuf,
10 process::{self, Command, Stdio},
11 time::Duration,
12};
13
14use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
15
16pub use cargo_metadata::diagnostic::{
17 Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
18 DiagnosticSpanMacroExpansion,
19};
20
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub enum FlycheckConfig {
23 CargoCommand {
24 command: String,
25 all_targets: bool,
26 all_features: bool,
27 features: Vec<String>,
28 extra_args: Vec<String>,
29 },
30 CustomCommand {
31 command: String,
32 args: Vec<String>,
33 },
34}
35
36impl fmt::Display for FlycheckConfig {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 match self {
39 FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command),
40 FlycheckConfig::CustomCommand { command, args } => {
41 write!(f, "{} {}", command, args.join(" "))
42 }
43 }
44 }
45}
46
47/// Flycheck wraps the shared state and communication machinery used for
48/// running `cargo check` (or other compatible command) and providing
49/// diagnostics based on the output.
50/// The spawned thread is shut down when this struct is dropped.
51#[derive(Debug)]
52pub struct FlycheckHandle {
53 // XXX: drop order is significant
54 sender: Sender<Restart>,
55 thread: jod_thread::JoinHandle,
56}
57
58impl FlycheckHandle {
59 pub fn spawn(
60 sender: Box<dyn Fn(Message) + Send>,
61 config: FlycheckConfig,
62 workspace_root: PathBuf,
63 ) -> FlycheckHandle {
64 let actor = FlycheckActor::new(sender, config, workspace_root);
65 let (sender, receiver) = unbounded::<Restart>();
66 let thread = jod_thread::spawn(move || actor.run(receiver));
67 FlycheckHandle { sender, thread }
68 }
69
70 /// Schedule a re-start of the cargo check worker.
71 pub fn update(&self) {
72 self.sender.send(Restart).unwrap();
73 }
74}
75
76#[derive(Debug)]
77pub enum Message {
78 /// Request adding a diagnostic with fixes included to a file
79 AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic },
80
81 /// Request check progress notification to client
82 Progress(Progress),
83}
84
85#[derive(Debug)]
86pub enum Progress {
87 DidStart,
88 DidCheckCrate(String),
89 DidFinish(io::Result<()>),
90 DidCancel,
91}
92
93struct Restart;
94
95struct FlycheckActor {
96 sender: Box<dyn Fn(Message) + Send>,
97 config: FlycheckConfig,
98 workspace_root: PathBuf,
99 /// WatchThread exists to wrap around the communication needed to be able to
100 /// run `cargo check` without blocking. Currently the Rust standard library
101 /// doesn't provide a way to read sub-process output without blocking, so we
102 /// have to wrap sub-processes output handling in a thread and pass messages
103 /// back over a channel.
104 cargo_handle: Option<CargoHandle>,
105}
106
107enum Event {
108 Restart(Restart),
109 CheckEvent(Option<cargo_metadata::Message>),
110}
111
112impl FlycheckActor {
113 fn new(
114 sender: Box<dyn Fn(Message) + Send>,
115 config: FlycheckConfig,
116 workspace_root: PathBuf,
117 ) -> FlycheckActor {
118 FlycheckActor { sender, config, workspace_root, cargo_handle: None }
119 }
120 fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
121 let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
122 select! {
123 recv(inbox) -> msg => msg.ok().map(Event::Restart),
124 recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
125 }
126 }
127 fn run(mut self, inbox: Receiver<Restart>) {
128 while let Some(event) = self.next_event(&inbox) {
129 match event {
130 Event::Restart(Restart) => {
131 while let Ok(Restart) = inbox.recv_timeout(Duration::from_millis(50)) {}
132
133 self.cancel_check_process();
134
135 let mut command = self.check_command();
136 log::info!("restart flycheck {:?}", command);
137 command.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null());
138 if let Ok(child) = command.spawn().map(JodChild) {
139 self.cargo_handle = Some(CargoHandle::spawn(child));
140 self.send(Message::Progress(Progress::DidStart));
141 }
142 }
143 Event::CheckEvent(None) => {
144 // Watcher finished, replace it with a never channel to
145 // avoid busy-waiting.
146 let cargo_handle = self.cargo_handle.take().unwrap();
147 let res = cargo_handle.join();
148 self.send(Message::Progress(Progress::DidFinish(res)));
149 }
150 Event::CheckEvent(Some(message)) => match message {
151 cargo_metadata::Message::CompilerArtifact(msg) => {
152 self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name)));
153 }
154
155 cargo_metadata::Message::CompilerMessage(msg) => {
156 self.send(Message::AddDiagnostic {
157 workspace_root: self.workspace_root.clone(),
158 diagnostic: msg.message,
159 });
160 }
161
162 cargo_metadata::Message::BuildScriptExecuted(_)
163 | cargo_metadata::Message::BuildFinished(_)
164 | cargo_metadata::Message::TextLine(_)
165 | cargo_metadata::Message::Unknown => {}
166 },
167 }
168 }
169 // If we rerun the thread, we need to discard the previous check results first
170 self.cancel_check_process();
171 }
172 fn cancel_check_process(&mut self) {
173 if self.cargo_handle.take().is_some() {
174 self.send(Message::Progress(Progress::DidCancel));
175 }
176 }
177 fn check_command(&self) -> Command {
178 let mut cmd = match &self.config {
179 FlycheckConfig::CargoCommand {
180 command,
181 all_targets,
182 all_features,
183 extra_args,
184 features,
185 } => {
186 let mut cmd = Command::new(ra_toolchain::cargo());
187 cmd.arg(command);
188 cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
189 .arg(self.workspace_root.join("Cargo.toml"));
190 if *all_targets {
191 cmd.arg("--all-targets");
192 }
193 if *all_features {
194 cmd.arg("--all-features");
195 } else if !features.is_empty() {
196 cmd.arg("--features");
197 cmd.arg(features.join(" "));
198 }
199 cmd.args(extra_args);
200 cmd
201 }
202 FlycheckConfig::CustomCommand { command, args } => {
203 let mut cmd = Command::new(command);
204 cmd.args(args);
205 cmd
206 }
207 };
208 cmd.current_dir(&self.workspace_root);
209 cmd
210 }
211
212 fn send(&self, check_task: Message) {
213 (self.sender)(check_task)
214 }
215}
216
217struct CargoHandle {
218 child: JodChild,
219 #[allow(unused)]
220 thread: jod_thread::JoinHandle<io::Result<bool>>,
221 receiver: Receiver<cargo_metadata::Message>,
222}
223
224impl CargoHandle {
225 fn spawn(mut child: JodChild) -> CargoHandle {
226 let child_stdout = child.stdout.take().unwrap();
227 let (sender, receiver) = unbounded();
228 let actor = CargoActor::new(child_stdout, sender);
229 let thread = jod_thread::spawn(move || actor.run());
230 CargoHandle { child, thread, receiver }
231 }
232 fn join(mut self) -> io::Result<()> {
233 // It is okay to ignore the result, as it only errors if the process is already dead
234 let _ = self.child.kill();
235 let exit_status = self.child.wait()?;
236 let read_at_least_one_message = self.thread.join()?;
237 if !exit_status.success() && !read_at_least_one_message {
238 // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
239 // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
240 return Err(io::Error::new(
241 io::ErrorKind::Other,
242 format!(
243 "Cargo watcher failed,the command produced no valid metadata (exit code: {:?})",
244 exit_status
245 ),
246 ));
247 }
248 Ok(())
249 }
250}
251
252struct CargoActor {
253 child_stdout: process::ChildStdout,
254 sender: Sender<cargo_metadata::Message>,
255}
256
257impl CargoActor {
258 fn new(
259 child_stdout: process::ChildStdout,
260 sender: Sender<cargo_metadata::Message>,
261 ) -> CargoActor {
262 CargoActor { child_stdout, sender }
263 }
264 fn run(self) -> io::Result<bool> {
265 // We manually read a line at a time, instead of using serde's
266 // stream deserializers, because the deserializer cannot recover
267 // from an error, resulting in it getting stuck, because we try to
268 // be resilient against failures.
269 //
270 // Because cargo only outputs one JSON object per line, we can
271 // simply skip a line if it doesn't parse, which just ignores any
272 // erroneus output.
273 let stdout = BufReader::new(self.child_stdout);
274 let mut read_at_least_one_message = false;
275 for message in cargo_metadata::Message::parse_stream(stdout) {
276 let message = match message {
277 Ok(message) => message,
278 Err(err) => {
279 log::error!("Invalid json from cargo check, ignoring ({})", err);
280 continue;
281 }
282 };
283
284 read_at_least_one_message = true;
285
286 // Skip certain kinds of messages to only spend time on what's useful
287 match &message {
288 cargo_metadata::Message::CompilerArtifact(artifact) if artifact.fresh => (),
289 cargo_metadata::Message::BuildScriptExecuted(_)
290 | cargo_metadata::Message::Unknown => (),
291 _ => self.sender.send(message).unwrap(),
292 }
293 }
294 Ok(read_at_least_one_message)
295 }
296}
297
298struct JodChild(process::Child);
299
300impl ops::Deref for JodChild {
301 type Target = process::Child;
302 fn deref(&self) -> &process::Child {
303 &self.0
304 }
305}
306
307impl ops::DerefMut for JodChild {
308 fn deref_mut(&mut self) -> &mut process::Child {
309 &mut self.0
310 }
311}
312
313impl Drop for JodChild {
314 fn drop(&mut self) {
315 let _ = self.0.kill();
316 }
317}
diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml
new file mode 100644
index 000000000..cbe2c26e2
--- /dev/null
+++ b/crates/paths/Cargo.toml
@@ -0,0 +1,9 @@
1[package]
2name = "paths"
3version = "0.1.0"
4authors = ["rust-analyzer developers"]
5edition = "2018"
6license = "MIT OR Apache-2.0"
7
8[lib]
9doctest = false
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
new file mode 100644
index 000000000..1b259682d
--- /dev/null
+++ b/crates/paths/src/lib.rs
@@ -0,0 +1,217 @@
1//! Thin wrappers around `std::path`, distinguishing between absolute and
2//! relative paths.
3use std::{
4 convert::{TryFrom, TryInto},
5 ops,
6 path::{Component, Path, PathBuf},
7};
8
9#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
10pub struct AbsPathBuf(PathBuf);
11
12impl From<AbsPathBuf> for PathBuf {
13 fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf {
14 path_buf
15 }
16}
17
18impl ops::Deref for AbsPathBuf {
19 type Target = AbsPath;
20 fn deref(&self) -> &AbsPath {
21 self.as_path()
22 }
23}
24
25impl AsRef<Path> for AbsPathBuf {
26 fn as_ref(&self) -> &Path {
27 self.0.as_path()
28 }
29}
30
31impl AsRef<AbsPath> for AbsPathBuf {
32 fn as_ref(&self) -> &AbsPath {
33 self.as_path()
34 }
35}
36
37impl TryFrom<PathBuf> for AbsPathBuf {
38 type Error = PathBuf;
39 fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
40 if !path_buf.is_absolute() {
41 return Err(path_buf);
42 }
43 Ok(AbsPathBuf(path_buf))
44 }
45}
46
47impl TryFrom<&str> for AbsPathBuf {
48 type Error = PathBuf;
49 fn try_from(path: &str) -> Result<AbsPathBuf, PathBuf> {
50 AbsPathBuf::try_from(PathBuf::from(path))
51 }
52}
53
54impl PartialEq<AbsPath> for AbsPathBuf {
55 fn eq(&self, other: &AbsPath) -> bool {
56 self.as_path() == other
57 }
58}
59
60impl AbsPathBuf {
61 pub fn assert(path: PathBuf) -> AbsPathBuf {
62 AbsPathBuf::try_from(path)
63 .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display()))
64 }
65 pub fn as_path(&self) -> &AbsPath {
66 AbsPath::assert(self.0.as_path())
67 }
68 pub fn pop(&mut self) -> bool {
69 self.0.pop()
70 }
71}
72
73#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
74#[repr(transparent)]
75pub struct AbsPath(Path);
76
77impl ops::Deref for AbsPath {
78 type Target = Path;
79 fn deref(&self) -> &Path {
80 &self.0
81 }
82}
83
84impl AsRef<Path> for AbsPath {
85 fn as_ref(&self) -> &Path {
86 &self.0
87 }
88}
89
90impl<'a> TryFrom<&'a Path> for &'a AbsPath {
91 type Error = &'a Path;
92 fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> {
93 if !path.is_absolute() {
94 return Err(path);
95 }
96 Ok(AbsPath::assert(path))
97 }
98}
99
100impl AbsPath {
101 pub fn assert(path: &Path) -> &AbsPath {
102 assert!(path.is_absolute());
103 unsafe { &*(path as *const Path as *const AbsPath) }
104 }
105
106 pub fn parent(&self) -> Option<&AbsPath> {
107 self.0.parent().map(AbsPath::assert)
108 }
109 pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
110 self.as_ref().join(path).try_into().unwrap()
111 }
112 pub fn normalize(&self) -> AbsPathBuf {
113 AbsPathBuf(normalize_path(&self.0))
114 }
115 pub fn to_path_buf(&self) -> AbsPathBuf {
116 AbsPathBuf::try_from(self.0.to_path_buf()).unwrap()
117 }
118 pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> {
119 self.0.strip_prefix(base).ok().map(RelPath::new_unchecked)
120 }
121}
122
123#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
124pub struct RelPathBuf(PathBuf);
125
126impl From<RelPathBuf> for PathBuf {
127 fn from(RelPathBuf(path_buf): RelPathBuf) -> PathBuf {
128 path_buf
129 }
130}
131
132impl ops::Deref for RelPathBuf {
133 type Target = RelPath;
134 fn deref(&self) -> &RelPath {
135 self.as_path()
136 }
137}
138
139impl AsRef<Path> for RelPathBuf {
140 fn as_ref(&self) -> &Path {
141 self.0.as_path()
142 }
143}
144
145impl TryFrom<PathBuf> for RelPathBuf {
146 type Error = PathBuf;
147 fn try_from(path_buf: PathBuf) -> Result<RelPathBuf, PathBuf> {
148 if !path_buf.is_relative() {
149 return Err(path_buf);
150 }
151 Ok(RelPathBuf(path_buf))
152 }
153}
154
155impl TryFrom<&str> for RelPathBuf {
156 type Error = PathBuf;
157 fn try_from(path: &str) -> Result<RelPathBuf, PathBuf> {
158 RelPathBuf::try_from(PathBuf::from(path))
159 }
160}
161
162impl RelPathBuf {
163 pub fn as_path(&self) -> &RelPath {
164 RelPath::new_unchecked(self.0.as_path())
165 }
166}
167
168#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
169#[repr(transparent)]
170pub struct RelPath(Path);
171
172impl ops::Deref for RelPath {
173 type Target = Path;
174 fn deref(&self) -> &Path {
175 &self.0
176 }
177}
178
179impl AsRef<Path> for RelPath {
180 fn as_ref(&self) -> &Path {
181 &self.0
182 }
183}
184
185impl RelPath {
186 pub fn new_unchecked(path: &Path) -> &RelPath {
187 unsafe { &*(path as *const Path as *const RelPath) }
188 }
189}
190
191// https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85
192fn normalize_path(path: &Path) -> PathBuf {
193 let mut components = path.components().peekable();
194 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
195 components.next();
196 PathBuf::from(c.as_os_str())
197 } else {
198 PathBuf::new()
199 };
200
201 for component in components {
202 match component {
203 Component::Prefix(..) => unreachable!(),
204 Component::RootDir => {
205 ret.push(component.as_os_str());
206 }
207 Component::CurDir => {}
208 Component::ParentDir => {
209 ret.pop();
210 }
211 Component::Normal(c) => {
212 ret.push(c);
213 }
214 }
215 }
216 ret
217}
diff --git a/crates/ra_arena/Cargo.toml b/crates/ra_arena/Cargo.toml
index d287dbb73..66c3738f4 100644
--- a/crates/ra_arena/Cargo.toml
+++ b/crates/ra_arena/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_arena" 3name = "ra_arena"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
diff --git a/crates/ra_arena/src/lib.rs b/crates/ra_arena/src/lib.rs
index 441fbb3cb..3169aa5b8 100644
--- a/crates/ra_arena/src/lib.rs
+++ b/crates/ra_arena/src/lib.rs
@@ -116,6 +116,9 @@ impl<T> Arena<T> {
116 ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator { 116 ) -> impl Iterator<Item = (Idx<T>, &T)> + ExactSizeIterator + DoubleEndedIterator {
117 self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawId(idx as u32)), value)) 117 self.data.iter().enumerate().map(|(idx, value)| (Idx::from_raw(RawId(idx as u32)), value))
118 } 118 }
119 pub fn shrink_to_fit(&mut self) {
120 self.data.shrink_to_fit();
121 }
119} 122}
120 123
121impl<T> Default for Arena<T> { 124impl<T> Default for Arena<T> {
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml
index 3bcf58ba4..bd2905f08 100644
--- a/crates/ra_assists/Cargo.toml
+++ b/crates/ra_assists/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_assists" 3name = "ra_assists"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
diff --git a/crates/ra_assists/src/assist_config.rs b/crates/ra_assists/src/assist_config.rs
index c0a0226fb..cda2abfb9 100644
--- a/crates/ra_assists/src/assist_config.rs
+++ b/crates/ra_assists/src/assist_config.rs
@@ -4,9 +4,12 @@
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 crate::AssistKind;
8
7#[derive(Clone, Debug, PartialEq, Eq)] 9#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct AssistConfig { 10pub struct AssistConfig {
9 pub snippet_cap: Option<SnippetCap>, 11 pub snippet_cap: Option<SnippetCap>,
12 pub allowed: Option<Vec<AssistKind>>,
10} 13}
11 14
12impl AssistConfig { 15impl AssistConfig {
@@ -22,6 +25,6 @@ pub struct SnippetCap {
22 25
23impl Default for AssistConfig { 26impl Default for AssistConfig {
24 fn default() -> Self { 27 fn default() -> Self {
25 AssistConfig { snippet_cap: Some(SnippetCap { _private: () }) } 28 AssistConfig { snippet_cap: Some(SnippetCap { _private: () }), allowed: None }
26 } 29 }
27} 30}
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index 5b1a4680b..3407df856 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -1,5 +1,7 @@
1//! See `AssistContext` 1//! See `AssistContext`
2 2
3use std::mem;
4
3use algo::find_covering_element; 5use algo::find_covering_element;
4use hir::Semantics; 6use hir::Semantics;
5use ra_db::{FileId, FileRange}; 7use ra_db::{FileId, FileRange};
@@ -17,7 +19,7 @@ use ra_text_edit::TextEditBuilder;
17 19
18use crate::{ 20use crate::{
19 assist_config::{AssistConfig, SnippetCap}, 21 assist_config::{AssistConfig, SnippetCap},
20 Assist, AssistId, GroupLabel, ResolvedAssist, 22 Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist,
21}; 23};
22 24
23/// `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.
@@ -53,7 +55,6 @@ use crate::{
53pub(crate) struct AssistContext<'a> { 55pub(crate) struct AssistContext<'a> {
54 pub(crate) config: &'a AssistConfig, 56 pub(crate) config: &'a AssistConfig,
55 pub(crate) sema: Semantics<'a, RootDatabase>, 57 pub(crate) sema: Semantics<'a, RootDatabase>,
56 pub(crate) db: &'a RootDatabase,
57 pub(crate) frange: FileRange, 58 pub(crate) frange: FileRange,
58 source_file: SourceFile, 59 source_file: SourceFile,
59} 60}
@@ -65,8 +66,11 @@ impl<'a> AssistContext<'a> {
65 frange: FileRange, 66 frange: FileRange,
66 ) -> AssistContext<'a> { 67 ) -> AssistContext<'a> {
67 let source_file = sema.parse(frange.file_id); 68 let source_file = sema.parse(frange.file_id);
68 let db = sema.db; 69 AssistContext { config, sema, frange, source_file }
69 AssistContext { config, sema, db, frange, source_file } 70 }
71
72 pub(crate) fn db(&self) -> &RootDatabase {
73 self.sema.db
70 } 74 }
71 75
72 // NB, this ignores active selection. 76 // NB, this ignores active selection.
@@ -99,14 +103,26 @@ pub(crate) struct Assists {
99 resolve: bool, 103 resolve: bool,
100 file: FileId, 104 file: FileId,
101 buf: Vec<(Assist, Option<SourceChange>)>, 105 buf: Vec<(Assist, Option<SourceChange>)>,
106 allowed: Option<Vec<AssistKind>>,
102} 107}
103 108
104impl Assists { 109impl Assists {
105 pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { 110 pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists {
106 Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() } 111 Assists {
112 resolve: true,
113 file: ctx.frange.file_id,
114 buf: Vec::new(),
115 allowed: ctx.config.allowed.clone(),
116 }
107 } 117 }
118
108 pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { 119 pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists {
109 Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() } 120 Assists {
121 resolve: false,
122 file: ctx.frange.file_id,
123 buf: Vec::new(),
124 allowed: ctx.config.allowed.clone(),
125 }
110 } 126 }
111 127
112 pub(crate) fn finish_unresolved(self) -> Vec<Assist> { 128 pub(crate) fn finish_unresolved(self) -> Vec<Assist> {
@@ -135,9 +151,13 @@ impl Assists {
135 target: TextRange, 151 target: TextRange,
136 f: impl FnOnce(&mut AssistBuilder), 152 f: impl FnOnce(&mut AssistBuilder),
137 ) -> Option<()> { 153 ) -> Option<()> {
154 if !self.is_allowed(&id) {
155 return None;
156 }
138 let label = Assist::new(id, label.into(), None, target); 157 let label = Assist::new(id, label.into(), None, target);
139 self.add_impl(label, f) 158 self.add_impl(label, f)
140 } 159 }
160
141 pub(crate) fn add_group( 161 pub(crate) fn add_group(
142 &mut self, 162 &mut self,
143 group: &GroupLabel, 163 group: &GroupLabel,
@@ -146,9 +166,14 @@ impl Assists {
146 target: TextRange, 166 target: TextRange,
147 f: impl FnOnce(&mut AssistBuilder), 167 f: impl FnOnce(&mut AssistBuilder),
148 ) -> Option<()> { 168 ) -> Option<()> {
169 if !self.is_allowed(&id) {
170 return None;
171 }
172
149 let label = Assist::new(id, label.into(), Some(group.clone()), target); 173 let label = Assist::new(id, label.into(), Some(group.clone()), target);
150 self.add_impl(label, f) 174 self.add_impl(label, f)
151 } 175 }
176
152 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { 177 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
153 let source_change = if self.resolve { 178 let source_change = if self.resolve {
154 let mut builder = AssistBuilder::new(self.file); 179 let mut builder = AssistBuilder::new(self.file);
@@ -166,17 +191,43 @@ impl Assists {
166 self.buf.sort_by_key(|(label, _edit)| label.target.len()); 191 self.buf.sort_by_key(|(label, _edit)| label.target.len());
167 self.buf 192 self.buf
168 } 193 }
194
195 fn is_allowed(&self, id: &AssistId) -> bool {
196 match &self.allowed {
197 Some(allowed) => allowed.iter().any(|kind| kind.contains(id.1)),
198 None => true,
199 }
200 }
169} 201}
170 202
171pub(crate) struct AssistBuilder { 203pub(crate) struct AssistBuilder {
172 edit: TextEditBuilder, 204 edit: TextEditBuilder,
173 file: FileId, 205 file_id: FileId,
174 is_snippet: bool, 206 is_snippet: bool,
207 change: SourceChange,
175} 208}
176 209
177impl AssistBuilder { 210impl AssistBuilder {
178 pub(crate) fn new(file: FileId) -> AssistBuilder { 211 pub(crate) fn new(file_id: FileId) -> AssistBuilder {
179 AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false } 212 AssistBuilder {
213 edit: TextEditBuilder::default(),
214 file_id,
215 is_snippet: false,
216 change: SourceChange::default(),
217 }
218 }
219
220 pub(crate) fn edit_file(&mut self, file_id: FileId) {
221 self.file_id = file_id;
222 }
223
224 fn commit(&mut self) {
225 let edit = mem::take(&mut self.edit).finish();
226 if !edit.is_empty() {
227 let new_edit = SourceFileEdit { file_id: self.file_id, edit };
228 assert!(!self.change.source_file_edits.iter().any(|it| it.file_id == new_edit.file_id));
229 self.change.source_file_edits.push(new_edit);
230 }
180 } 231 }
181 232
182 /// Remove specified `range` of text. 233 /// Remove specified `range` of text.
@@ -231,12 +282,7 @@ impl AssistBuilder {
231 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { 282 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
232 let node = rewriter.rewrite_root().unwrap(); 283 let node = rewriter.rewrite_root().unwrap();
233 let new = rewriter.rewrite(&node); 284 let new = rewriter.rewrite(&node);
234 algo::diff(&node, &new).into_text_edit(&mut self.edit) 285 algo::diff(&node, &new).into_text_edit(&mut self.edit);
235 }
236
237 // FIXME: better API
238 pub(crate) fn set_file(&mut self, assist_file: FileId) {
239 self.file = assist_file;
240 } 286 }
241 287
242 // FIXME: kill this API 288 // FIXME: kill this API
@@ -245,13 +291,12 @@ impl AssistBuilder {
245 &mut self.edit 291 &mut self.edit
246 } 292 }
247 293
248 fn finish(self) -> SourceChange { 294 fn finish(mut self) -> SourceChange {
249 let edit = self.edit.finish(); 295 self.commit();
250 let source_file_edit = SourceFileEdit { file_id: self.file, edit }; 296 let mut change = mem::take(&mut self.change);
251 let mut res: SourceChange = source_file_edit.into();
252 if self.is_snippet { 297 if self.is_snippet {
253 res.is_snippet = true; 298 change.is_snippet = true;
254 } 299 }
255 res 300 change
256 } 301 }
257} 302}
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 3079a02a2..01adb834c 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -2,7 +2,6 @@
2use rustc_hash::FxHashMap; 2use rustc_hash::FxHashMap;
3 3
4use hir::{HirDisplay, PathResolution, SemanticsScope}; 4use hir::{HirDisplay, PathResolution, SemanticsScope};
5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 5use ra_syntax::{
7 algo::SyntaxRewriter, 6 algo::SyntaxRewriter,
8 ast::{self, AstNode}, 7 ast::{self, AstNode},
@@ -32,14 +31,14 @@ impl<'a> AstTransform<'a> for NullTransformer {
32} 31}
33 32
34pub struct SubstituteTypeParams<'a> { 33pub struct SubstituteTypeParams<'a> {
35 source_scope: &'a SemanticsScope<'a, RootDatabase>, 34 source_scope: &'a SemanticsScope<'a>,
36 substs: FxHashMap<hir::TypeParam, ast::TypeRef>, 35 substs: FxHashMap<hir::TypeParam, ast::TypeRef>,
37 previous: Box<dyn AstTransform<'a> + 'a>, 36 previous: Box<dyn AstTransform<'a> + 'a>,
38} 37}
39 38
40impl<'a> SubstituteTypeParams<'a> { 39impl<'a> SubstituteTypeParams<'a> {
41 pub fn for_trait_impl( 40 pub fn for_trait_impl(
42 source_scope: &'a SemanticsScope<'a, RootDatabase>, 41 source_scope: &'a SemanticsScope<'a>,
43 // FIXME: there's implicit invariant that `trait_` and `source_scope` match... 42 // FIXME: there's implicit invariant that `trait_` and `source_scope` match...
44 trait_: hir::Trait, 43 trait_: hir::Trait,
45 impl_def: ast::ImplDef, 44 impl_def: ast::ImplDef,
@@ -106,6 +105,7 @@ impl<'a> SubstituteTypeParams<'a> {
106 _ => return None, 105 _ => return None,
107 }; 106 };
108 // FIXME: use `hir::Path::from_src` instead. 107 // FIXME: use `hir::Path::from_src` instead.
108 #[allow(deprecated)]
109 let path = hir::Path::from_ast(path)?; 109 let path = hir::Path::from_ast(path)?;
110 let resolution = self.source_scope.resolve_hir_path(&path)?; 110 let resolution = self.source_scope.resolve_hir_path(&path)?;
111 match resolution { 111 match resolution {
@@ -125,16 +125,13 @@ impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> {
125} 125}
126 126
127pub struct QualifyPaths<'a> { 127pub struct QualifyPaths<'a> {
128 target_scope: &'a SemanticsScope<'a, RootDatabase>, 128 target_scope: &'a SemanticsScope<'a>,
129 source_scope: &'a SemanticsScope<'a, RootDatabase>, 129 source_scope: &'a SemanticsScope<'a>,
130 previous: Box<dyn AstTransform<'a> + 'a>, 130 previous: Box<dyn AstTransform<'a> + 'a>,
131} 131}
132 132
133impl<'a> QualifyPaths<'a> { 133impl<'a> QualifyPaths<'a> {
134 pub fn new( 134 pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self {
135 target_scope: &'a SemanticsScope<'a, RootDatabase>,
136 source_scope: &'a SemanticsScope<'a, RootDatabase>,
137 ) -> Self {
138 Self { target_scope, source_scope, previous: Box::new(NullTransformer) } 135 Self { target_scope, source_scope, previous: Box::new(NullTransformer) }
139 } 136 }
140 137
@@ -150,11 +147,12 @@ impl<'a> QualifyPaths<'a> {
150 return None; 147 return None;
151 } 148 }
152 // FIXME: use `hir::Path::from_src` instead. 149 // FIXME: use `hir::Path::from_src` instead.
150 #[allow(deprecated)]
153 let hir_path = hir::Path::from_ast(p.clone()); 151 let hir_path = hir::Path::from_ast(p.clone());
154 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; 152 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
155 match resolution { 153 match resolution {
156 PathResolution::Def(def) => { 154 PathResolution::Def(def) => {
157 let found_path = from.find_use_path(self.source_scope.db, def)?; 155 let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?;
158 let mut path = path_to_ast(found_path); 156 let mut path = path_to_ast(found_path);
159 157
160 let type_args = p 158 let type_args = p
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs
index fa70c8496..acb07e36a 100644
--- a/crates/ra_assists/src/handlers/add_custom_impl.rs
+++ b/crates/ra_assists/src/handlers/add_custom_impl.rs
@@ -8,7 +8,7 @@ use stdx::SepBy;
8 8
9use crate::{ 9use crate::{
10 assist_context::{AssistContext, Assists}, 10 assist_context::{AssistContext, Assists},
11 AssistId, 11 AssistId, AssistKind,
12}; 12};
13 13
14// Assist: add_custom_impl 14// Assist: add_custom_impl
@@ -52,7 +52,7 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
52 format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); 52 format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name);
53 53
54 let target = attr.syntax().text_range(); 54 let target = attr.syntax().text_range();
55 acc.add(AssistId("add_custom_impl"), label, target, |builder| { 55 acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| {
56 let new_attr_input = input 56 let new_attr_input = input
57 .syntax() 57 .syntax()
58 .descendants_with_tokens() 58 .descendants_with_tokens()
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index ab20c6649..39a5321d1 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4 TextRange, 4 TextRange,
5}; 5};
6 6
7use crate::{AssistContext, AssistId, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
8 8
9// Assist: add_explicit_type 9// Assist: add_explicit_type
10// 10//
@@ -57,9 +57,9 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
57 return None; 57 return None;
58 } 58 }
59 59
60 let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; 60 let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
61 acc.add( 61 acc.add(
62 AssistId("add_explicit_type"), 62 AssistId("add_explicit_type", AssistKind::RefactorRewrite),
63 format!("Insert explicit type `{}`", inferred_type), 63 format!("Insert explicit type `{}`", inferred_type),
64 pat_range, 64 pat_range,
65 |builder| match ascribed_ty { 65 |builder| match ascribed_ty {
@@ -195,7 +195,7 @@ struct Test<K, T = u8> {
195} 195}
196 196
197fn main() { 197fn main() {
198 let test<|> = Test { t: 23, k: 33 }; 198 let test<|> = Test { t: 23u8, k: 33 };
199}"#, 199}"#,
200 r#" 200 r#"
201struct Test<K, T = u8> { 201struct Test<K, T = u8> {
@@ -204,7 +204,7 @@ struct Test<K, T = u8> {
204} 204}
205 205
206fn main() { 206fn main() {
207 let test: Test<i32> = Test { t: 23, k: 33 }; 207 let test: Test<i32> = Test { t: 23u8, k: 33 };
208}"#, 208}"#,
209 ); 209 );
210 } 210 }
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs
deleted file mode 100644
index eceba7d0a..000000000
--- a/crates/ra_assists/src/handlers/add_impl.rs
+++ /dev/null
@@ -1,98 +0,0 @@
1use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
2use stdx::{format_to, SepBy};
3
4use crate::{AssistContext, AssistId, Assists};
5
6// Assist: add_impl
7//
8// Adds a new inherent impl for a type.
9//
10// ```
11// struct Ctx<T: Clone> {
12// data: T,<|>
13// }
14// ```
15// ->
16// ```
17// struct Ctx<T: Clone> {
18// data: T,
19// }
20//
21// impl<T: Clone> Ctx<T> {
22// $0
23// }
24// ```
25pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
27 let name = nominal.name()?;
28 let target = nominal.syntax().text_range();
29 acc.add(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), target, |edit| {
30 let type_params = nominal.type_param_list();
31 let start_offset = nominal.syntax().text_range().end();
32 let mut buf = String::new();
33 buf.push_str("\n\nimpl");
34 if let Some(type_params) = &type_params {
35 format_to!(buf, "{}", type_params.syntax());
36 }
37 buf.push_str(" ");
38 buf.push_str(name.text().as_str());
39 if let Some(type_params) = type_params {
40 let lifetime_params = type_params
41 .lifetime_params()
42 .filter_map(|it| it.lifetime_token())
43 .map(|it| it.text().clone());
44 let type_params =
45 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
46
47 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
48 format_to!(buf, "<{}>", generic_params)
49 }
50 match ctx.config.snippet_cap {
51 Some(cap) => {
52 buf.push_str(" {\n $0\n}");
53 edit.insert_snippet(cap, start_offset, buf);
54 }
55 None => {
56 buf.push_str(" {\n}");
57 edit.insert(start_offset, buf);
58 }
59 }
60 })
61}
62
63#[cfg(test)]
64mod tests {
65 use crate::tests::{check_assist, check_assist_target};
66
67 use super::*;
68
69 #[test]
70 fn test_add_impl() {
71 check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n $0\n}\n");
72 check_assist(
73 add_impl,
74 "struct Foo<T: Clone> {<|>}",
75 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}",
76 );
77 check_assist(
78 add_impl,
79 "struct Foo<'a, T: Foo<'a>> {<|>}",
80 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}",
81 );
82 }
83
84 #[test]
85 fn add_impl_target() {
86 check_assist_target(
87 add_impl,
88 "
89struct SomeThingIrrelevant;
90/// Has a lifetime parameter
91struct Foo<'a, T: Foo<'a>> {<|>}
92struct EvenMoreIrrelevant;
93",
94 "/// Has a lifetime parameter
95struct Foo<'a, T: Foo<'a>> {}",
96 );
97 }
98}
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index abacd4065..f185e61e5 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -12,7 +12,7 @@ use crate::{
12 assist_context::{AssistContext, Assists}, 12 assist_context::{AssistContext, Assists},
13 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 13 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
14 utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor}, 14 utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor},
15 AssistId, 15 AssistId, AssistKind,
16}; 16};
17 17
18#[derive(PartialEq)] 18#[derive(PartialEq)]
@@ -128,9 +128,9 @@ fn add_missing_impl_members_inner(
128 let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def) 128 let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def)
129 .iter() 129 .iter()
130 .map(|i| match i { 130 .map(|i| match i {
131 hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db).value), 131 hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db()).value),
132 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db).value), 132 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAliasDef(i.source(ctx.db()).value),
133 hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db).value), 133 hir::AssocItem::Const(i) => ast::AssocItem::ConstDef(i.source(ctx.db()).value),
134 }) 134 })
135 .filter(|t| def_name(&t).is_some()) 135 .filter(|t| def_name(&t).is_some())
136 .filter(|t| match t { 136 .filter(|t| match t {
@@ -147,7 +147,7 @@ fn add_missing_impl_members_inner(
147 } 147 }
148 148
149 let target = impl_def.syntax().text_range(); 149 let target = impl_def.syntax().text_range();
150 acc.add(AssistId(assist_id), label, target, |builder| { 150 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
151 let n_existing_items = impl_item_list.assoc_items().count(); 151 let n_existing_items = impl_item_list.assoc_items().count();
152 let source_scope = ctx.sema.scope_for_def(trait_); 152 let source_scope = ctx.sema.scope_for_def(trait_);
153 let target_scope = ctx.sema.scope(impl_item_list.syntax()); 153 let target_scope = ctx.sema.scope(impl_item_list.syntax());
@@ -158,6 +158,9 @@ fn add_missing_impl_members_inner(
158 .map(|it| ast_transform::apply(&*ast_transform, it)) 158 .map(|it| ast_transform::apply(&*ast_transform, it))
159 .map(|it| match it { 159 .map(|it| match it {
160 ast::AssocItem::FnDef(def) => ast::AssocItem::FnDef(add_body(def)), 160 ast::AssocItem::FnDef(def) => ast::AssocItem::FnDef(add_body(def)),
161 ast::AssocItem::TypeAliasDef(def) => {
162 ast::AssocItem::TypeAliasDef(def.remove_bounds())
163 }
161 _ => it, 164 _ => it,
162 }) 165 })
163 .map(|it| edit::remove_attrs_and_docs(&it)); 166 .map(|it| edit::remove_attrs_and_docs(&it));
@@ -684,4 +687,26 @@ impl Foo<T> for S<T> {
684}"#, 687}"#,
685 ) 688 )
686 } 689 }
690
691 #[test]
692 fn test_assoc_type_bounds_are_removed() {
693 check_assist(
694 add_missing_impl_members,
695 r#"
696trait Tr {
697 type Ty: Copy + 'static;
698}
699
700impl Tr for ()<|> {
701}"#,
702 r#"
703trait Tr {
704 type Ty: Copy + 'static;
705}
706
707impl Tr for () {
708 $0type Ty;
709}"#,
710 )
711 }
687} 712}
diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs
index 26acf81f2..f7e1a7b05 100644
--- a/crates/ra_assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs
@@ -4,7 +4,7 @@ use test_utils::mark;
4 4
5use crate::{ 5use crate::{
6 assist_context::{AssistContext, Assists}, 6 assist_context::{AssistContext, Assists},
7 AssistId, 7 AssistId, AssistKind,
8}; 8};
9 9
10// Assist: add_turbo_fish 10// Assist: add_turbo_fish
@@ -45,12 +45,15 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
45 mark::hit!(add_turbo_fish_non_generic); 45 mark::hit!(add_turbo_fish_non_generic);
46 return None; 46 return None;
47 } 47 }
48 acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| { 48 acc.add(
49 match ctx.config.snippet_cap { 49 AssistId("add_turbo_fish", AssistKind::RefactorRewrite),
50 "Add `::<>`",
51 ident.text_range(),
52 |builder| match ctx.config.snippet_cap {
50 Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"), 53 Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"),
51 None => builder.insert(ident.text_range().end(), "::<_>"), 54 None => builder.insert(ident.text_range().end(), "::<_>"),
52 } 55 },
53 }) 56 )
54} 57}
55 58
56#[cfg(test)] 59#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs
index 233e8fb8e..de701f8b8 100644
--- a/crates/ra_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ra_assists/src/handlers/apply_demorgan.rs
@@ -1,6 +1,6 @@
1use ra_syntax::ast::{self, AstNode}; 1use ra_syntax::ast::{self, AstNode};
2 2
3use crate::{utils::invert_boolean_expression, AssistContext, AssistId, Assists}; 3use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
4 4
5// Assist: apply_demorgan 5// Assist: apply_demorgan
6// 6//
@@ -39,11 +39,16 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
39 let rhs_range = rhs.syntax().text_range(); 39 let rhs_range = rhs.syntax().text_range();
40 let not_rhs = invert_boolean_expression(rhs); 40 let not_rhs = invert_boolean_expression(rhs);
41 41
42 acc.add(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| { 42 acc.add(
43 edit.replace(op_range, opposite_op); 43 AssistId("apply_demorgan", AssistKind::RefactorRewrite),
44 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); 44 "Apply De Morgan's law",
45 edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); 45 op_range,
46 }) 46 |edit| {
47 edit.replace(op_range, opposite_op);
48 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
49 edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
50 },
51 )
47} 52}
48 53
49// Return the opposite text for a given logical operator, if it makes sense 54// Return the opposite text for a given logical operator, if it makes sense
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index edf96d50e..947be3b9b 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -5,7 +5,7 @@ use hir::{
5 AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, 5 AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait,
6 Type, 6 Type,
7}; 7};
8use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase}; 8use ra_ide_db::{imports_locator, RootDatabase};
9use ra_prof::profile; 9use ra_prof::profile;
10use ra_syntax::{ 10use ra_syntax::{
11 ast::{self, AstNode}, 11 ast::{self, AstNode},
@@ -13,7 +13,9 @@ use ra_syntax::{
13}; 13};
14use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15 15
16use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, GroupLabel}; 16use crate::{
17 utils::insert_use_statement, AssistContext, AssistId, AssistKind, Assists, GroupLabel,
18};
17 19
18// Assist: auto_import 20// Assist: auto_import
19// 21//
@@ -35,8 +37,8 @@ use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, Group
35// # pub mod std { pub mod collections { pub struct HashMap { } } } 37// # pub mod std { pub mod collections { pub struct HashMap { } } }
36// ``` 38// ```
37pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 39pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
38 let auto_import_assets = AutoImportAssets::new(&ctx)?; 40 let auto_import_assets = AutoImportAssets::new(ctx)?;
39 let proposed_imports = auto_import_assets.search_for_imports(ctx.db); 41 let proposed_imports = auto_import_assets.search_for_imports(ctx);
40 if proposed_imports.is_empty() { 42 if proposed_imports.is_empty() {
41 return None; 43 return None;
42 } 44 }
@@ -46,7 +48,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
46 for import in proposed_imports { 48 for import in proposed_imports {
47 acc.add_group( 49 acc.add_group(
48 &group, 50 &group,
49 AssistId("auto_import"), 51 AssistId("auto_import", AssistKind::QuickFix),
50 format!("Import `{}`", &import), 52 format!("Import `{}`", &import),
51 range, 53 range,
52 |builder| { 54 |builder| {
@@ -127,11 +129,11 @@ impl AutoImportAssets {
127 GroupLabel(name) 129 GroupLabel(name)
128 } 130 }
129 131
130 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { 132 fn search_for_imports(&self, ctx: &AssistContext) -> BTreeSet<ModPath> {
131 let _p = profile("auto_import::search_for_imports"); 133 let _p = profile("auto_import::search_for_imports");
134 let db = ctx.db();
132 let current_crate = self.module_with_name_to_import.krate(); 135 let current_crate = self.module_with_name_to_import.krate();
133 ImportsLocator::new(db) 136 imports_locator::find_imports(&ctx.sema, current_crate, &self.get_search_query())
134 .find_imports(&self.get_search_query())
135 .into_iter() 137 .into_iter()
136 .filter_map(|candidate| match &self.import_candidate { 138 .filter_map(|candidate| match &self.import_candidate {
137 ImportCandidate::TraitAssocItem(assoc_item_type, _) => { 139 ImportCandidate::TraitAssocItem(assoc_item_type, _) => {
@@ -488,16 +490,17 @@ mod tests {
488 check_assist( 490 check_assist(
489 auto_import, 491 auto_import,
490 r" 492 r"
491 //- /lib.rs crate:crate_with_macro 493//- /lib.rs crate:crate_with_macro
492 #[macro_export] 494#[macro_export]
493 macro_rules! foo { 495macro_rules! foo {
494 () => () 496 () => ()
495 } 497}
496 498
497 //- /main.rs crate:main deps:crate_with_macro 499//- /main.rs crate:main deps:crate_with_macro
498 fn main() { 500fn main() {
499 foo<|> 501 foo<|>
500 }", 502}
503",
501 r"use crate_with_macro::foo; 504 r"use crate_with_macro::foo;
502 505
503fn main() { 506fn main() {
@@ -810,6 +813,146 @@ fn main() {
810 } 813 }
811 814
812 #[test] 815 #[test]
816 fn trait_method_cross_crate() {
817 check_assist(
818 auto_import,
819 r"
820 //- /main.rs crate:main deps:dep
821 fn main() {
822 let test_struct = dep::test_mod::TestStruct {};
823 test_struct.test_meth<|>od()
824 }
825 //- /dep.rs crate:dep
826 pub mod test_mod {
827 pub trait TestTrait {
828 fn test_method(&self);
829 }
830 pub struct TestStruct {}
831 impl TestTrait for TestStruct {
832 fn test_method(&self) {}
833 }
834 }
835 ",
836 r"
837 use dep::test_mod::TestTrait;
838
839 fn main() {
840 let test_struct = dep::test_mod::TestStruct {};
841 test_struct.test_method()
842 }
843 ",
844 );
845 }
846
847 #[test]
848 fn assoc_fn_cross_crate() {
849 check_assist(
850 auto_import,
851 r"
852 //- /main.rs crate:main deps:dep
853 fn main() {
854 dep::test_mod::TestStruct::test_func<|>tion
855 }
856 //- /dep.rs crate:dep
857 pub mod test_mod {
858 pub trait TestTrait {
859 fn test_function();
860 }
861 pub struct TestStruct {}
862 impl TestTrait for TestStruct {
863 fn test_function() {}
864 }
865 }
866 ",
867 r"
868 use dep::test_mod::TestTrait;
869
870 fn main() {
871 dep::test_mod::TestStruct::test_function
872 }
873 ",
874 );
875 }
876
877 #[test]
878 fn assoc_const_cross_crate() {
879 check_assist(
880 auto_import,
881 r"
882 //- /main.rs crate:main deps:dep
883 fn main() {
884 dep::test_mod::TestStruct::CONST<|>
885 }
886 //- /dep.rs crate:dep
887 pub mod test_mod {
888 pub trait TestTrait {
889 const CONST: bool;
890 }
891 pub struct TestStruct {}
892 impl TestTrait for TestStruct {
893 const CONST: bool = true;
894 }
895 }
896 ",
897 r"
898 use dep::test_mod::TestTrait;
899
900 fn main() {
901 dep::test_mod::TestStruct::CONST
902 }
903 ",
904 );
905 }
906
907 #[test]
908 fn assoc_fn_as_method_cross_crate() {
909 check_assist_not_applicable(
910 auto_import,
911 r"
912 //- /main.rs crate:main deps:dep
913 fn main() {
914 let test_struct = dep::test_mod::TestStruct {};
915 test_struct.test_func<|>tion()
916 }
917 //- /dep.rs crate:dep
918 pub mod test_mod {
919 pub trait TestTrait {
920 fn test_function();
921 }
922 pub struct TestStruct {}
923 impl TestTrait for TestStruct {
924 fn test_function() {}
925 }
926 }
927 ",
928 );
929 }
930
931 #[test]
932 fn private_trait_cross_crate() {
933 check_assist_not_applicable(
934 auto_import,
935 r"
936 //- /main.rs crate:main deps:dep
937 fn main() {
938 let test_struct = dep::test_mod::TestStruct {};
939 test_struct.test_meth<|>od()
940 }
941 //- /dep.rs crate:dep
942 pub mod test_mod {
943 trait TestTrait {
944 fn test_method(&self);
945 }
946 pub struct TestStruct {}
947 impl TestTrait for TestStruct {
948 fn test_method(&self) {}
949 }
950 }
951 ",
952 );
953 }
954
955 #[test]
813 fn not_applicable_for_imported_trait_for_method() { 956 fn not_applicable_for_imported_trait_for_method() {
814 check_assist_not_applicable( 957 check_assist_not_applicable(
815 auto_import, 958 auto_import,
@@ -841,4 +984,106 @@ fn main() {
841 ", 984 ",
842 ) 985 )
843 } 986 }
987
988 #[test]
989 fn dep_import() {
990 check_assist(
991 auto_import,
992 r"
993//- /lib.rs crate:dep
994pub struct Struct;
995
996//- /main.rs crate:main deps:dep
997fn main() {
998 Struct<|>
999}
1000",
1001 r"use dep::Struct;
1002
1003fn main() {
1004 Struct
1005}
1006",
1007 );
1008 }
1009
1010 #[test]
1011 fn whole_segment() {
1012 // Tests that only imports whose last segment matches the identifier get suggested.
1013 check_assist(
1014 auto_import,
1015 r"
1016//- /lib.rs crate:dep
1017pub mod fmt {
1018 pub trait Display {}
1019}
1020
1021pub fn panic_fmt() {}
1022
1023//- /main.rs crate:main deps:dep
1024struct S;
1025
1026impl f<|>mt::Display for S {}
1027",
1028 r"use dep::fmt;
1029
1030struct S;
1031
1032impl fmt::Display for S {}
1033",
1034 );
1035 }
1036
1037 #[test]
1038 fn macro_generated() {
1039 // Tests that macro-generated items are suggested from external crates.
1040 check_assist(
1041 auto_import,
1042 r"
1043//- /lib.rs crate:dep
1044macro_rules! mac {
1045 () => {
1046 pub struct Cheese;
1047 };
1048}
1049
1050mac!();
1051
1052//- /main.rs crate:main deps:dep
1053fn main() {
1054 Cheese<|>;
1055}
1056",
1057 r"use dep::Cheese;
1058
1059fn main() {
1060 Cheese;
1061}
1062",
1063 );
1064 }
1065
1066 #[test]
1067 fn casing() {
1068 // Tests that differently cased names don't interfere and we only suggest the matching one.
1069 check_assist(
1070 auto_import,
1071 r"
1072//- /lib.rs crate:dep
1073pub struct FMT;
1074pub struct fmt;
1075
1076//- /main.rs crate:main deps:dep
1077fn main() {
1078 FMT<|>;
1079}
1080",
1081 r"use dep::FMT;
1082
1083fn main() {
1084 FMT;
1085}
1086",
1087 );
1088 }
844} 1089}
diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs
index c6baa0a57..24e5f6963 100644
--- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs
+++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs
@@ -3,7 +3,8 @@ use ra_syntax::{
3 AstNode, SyntaxNode, 3 AstNode, SyntaxNode,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
7use test_utils::mark;
7 8
8// Assist: change_return_type_to_result 9// Assist: change_return_type_to_result
9// 10//
@@ -22,14 +23,19 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex
22 let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?; 23 let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?;
23 24
24 let type_ref = &ret_type.type_ref()?; 25 let type_ref = &ret_type.type_ref()?;
25 if type_ref.syntax().text().to_string().starts_with("Result<") { 26 let ret_type_str = type_ref.syntax().text().to_string();
26 return None; 27 let first_part_ret_type = ret_type_str.splitn(2, '<').next();
28 if let Some(ret_type_first_part) = first_part_ret_type {
29 if ret_type_first_part.ends_with("Result") {
30 mark::hit!(change_return_type_to_result_simple_return_type_already_result);
31 return None;
32 }
27 } 33 }
28 34
29 let block_expr = &fn_def.body()?; 35 let block_expr = &fn_def.body()?;
30 36
31 acc.add( 37 acc.add(
32 AssistId("change_return_type_to_result"), 38 AssistId("change_return_type_to_result", AssistKind::RefactorRewrite),
33 "Change return type to Result", 39 "Change return type to Result",
34 type_ref.syntax().text_range(), 40 type_ref.syntax().text_range(),
35 |builder| { 41 |builder| {
@@ -297,6 +303,29 @@ mod tests {
297 } 303 }
298 304
299 #[test] 305 #[test]
306 fn change_return_type_to_result_simple_return_type_already_result_std() {
307 check_assist_not_applicable(
308 change_return_type_to_result,
309 r#"fn foo() -> std::result::Result<i32<|>, String> {
310 let test = "test";
311 return 42i32;
312 }"#,
313 );
314 }
315
316 #[test]
317 fn change_return_type_to_result_simple_return_type_already_result() {
318 mark::check!(change_return_type_to_result_simple_return_type_already_result);
319 check_assist_not_applicable(
320 change_return_type_to_result,
321 r#"fn foo() -> Result<i32<|>, String> {
322 let test = "test";
323 return 42i32;
324 }"#,
325 );
326 }
327
328 #[test]
300 fn change_return_type_to_result_simple_with_cursor() { 329 fn change_return_type_to_result_simple_with_cursor() {
301 check_assist( 330 check_assist(
302 change_return_type_to_result, 331 change_return_type_to_result,
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index c21d75be0..4343b423c 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -2,14 +2,13 @@ use ra_syntax::{
2 ast::{self, NameOwner, VisibilityOwner}, 2 ast::{self, NameOwner, VisibilityOwner},
3 AstNode, 3 AstNode,
4 SyntaxKind::{ 4 SyntaxKind::{
5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY, 5 CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STATIC_DEF, STRUCT_DEF, TRAIT_DEF, VISIBILITY,
6 WHITESPACE,
7 }, 6 },
8 SyntaxNode, TextSize, T, 7 T,
9}; 8};
10use test_utils::mark; 9use test_utils::mark;
11 10
12use crate::{AssistContext, AssistId, Assists}; 11use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
13 12
14// Assist: change_visibility 13// Assist: change_visibility
15// 14//
@@ -30,14 +29,16 @@ pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Optio
30} 29}
31 30
32fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 31fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
33 let item_keyword = ctx.token_at_offset().find(|leaf| match leaf.kind() { 32 let item_keyword = ctx.token_at_offset().find(|leaf| {
34 T![const] | T![fn] | T![mod] | T![struct] | T![enum] | T![trait] => true, 33 matches!(
35 _ => false, 34 leaf.kind(),
35 T![const] | T![static] | T![fn] | T![mod] | T![struct] | T![enum] | T![trait]
36 )
36 }); 37 });
37 38
38 let (offset, target) = if let Some(keyword) = item_keyword { 39 let (offset, target) = if let Some(keyword) = item_keyword {
39 let parent = keyword.parent(); 40 let parent = keyword.parent();
40 let def_kws = vec![CONST_DEF, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF]; 41 let def_kws = vec![CONST_DEF, STATIC_DEF, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF];
41 // Parent is not a definition, can't add visibility 42 // Parent is not a definition, can't add visibility
42 if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { 43 if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) {
43 return None; 44 return None;
@@ -66,27 +67,21 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
66 return None; 67 return None;
67 }; 68 };
68 69
69 acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| { 70 acc.add(
70 edit.insert(offset, "pub(crate) "); 71 AssistId("change_visibility", AssistKind::RefactorRewrite),
71 }) 72 "Change visibility to pub(crate)",
72} 73 target,
73 74 |edit| {
74fn vis_offset(node: &SyntaxNode) -> TextSize { 75 edit.insert(offset, "pub(crate) ");
75 node.children_with_tokens() 76 },
76 .skip_while(|it| match it.kind() { 77 )
77 WHITESPACE | COMMENT | ATTR => true,
78 _ => false,
79 })
80 .next()
81 .map(|it| it.text_range().start())
82 .unwrap_or_else(|| node.text_range().start())
83} 78}
84 79
85fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { 80fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
86 if vis.syntax().text() == "pub" { 81 if vis.syntax().text() == "pub" {
87 let target = vis.syntax().text_range(); 82 let target = vis.syntax().text_range();
88 return acc.add( 83 return acc.add(
89 AssistId("change_visibility"), 84 AssistId("change_visibility", AssistKind::RefactorRewrite),
90 "Change Visibility to pub(crate)", 85 "Change Visibility to pub(crate)",
91 target, 86 target,
92 |edit| { 87 |edit| {
@@ -97,7 +92,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
97 if vis.syntax().text() == "pub(crate)" { 92 if vis.syntax().text() == "pub(crate)" {
98 let target = vis.syntax().text_range(); 93 let target = vis.syntax().text_range();
99 return acc.add( 94 return acc.add(
100 AssistId("change_visibility"), 95 AssistId("change_visibility", AssistKind::RefactorRewrite),
101 "Change visibility to pub", 96 "Change visibility to pub",
102 target, 97 target,
103 |edit| { 98 |edit| {
@@ -162,6 +157,11 @@ mod tests {
162 } 157 }
163 158
164 #[test] 159 #[test]
160 fn change_visibility_static() {
161 check_assist(change_visibility, "<|>static FOO = 3u8;", "pub(crate) static FOO = 3u8;");
162 }
163
164 #[test]
165 fn change_visibility_handles_comment_attrs() { 165 fn change_visibility_handles_comment_attrs() {
166 check_assist( 166 check_assist(
167 change_visibility, 167 change_visibility,
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index 4cc75a7ce..330459f3c 100644
--- a/crates/ra_assists/src/handlers/early_return.rs
+++ b/crates/ra_assists/src/handlers/early_return.rs
@@ -15,7 +15,7 @@ use ra_syntax::{
15use crate::{ 15use crate::{
16 assist_context::{AssistContext, Assists}, 16 assist_context::{AssistContext, Assists},
17 utils::invert_boolean_expression, 17 utils::invert_boolean_expression,
18 AssistId, 18 AssistId, AssistKind,
19}; 19};
20 20
21// Assist: convert_to_guarded_return 21// Assist: convert_to_guarded_return
@@ -99,86 +99,92 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
99 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; 99 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
100 100
101 let target = if_expr.syntax().text_range(); 101 let target = if_expr.syntax().text_range();
102 acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { 102 acc.add(
103 let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); 103 AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
104 let new_block = match if_let_pat { 104 "Convert to guarded return",
105 None => { 105 target,
106 // If. 106 |edit| {
107 let new_expr = { 107 let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
108 let then_branch = 108 let new_block = match if_let_pat {
109 make::block_expr(once(make::expr_stmt(early_expression).into()), None); 109 None => {
110 let cond = invert_boolean_expression(cond_expr); 110 // If.
111 make::expr_if(make::condition(cond, None), then_branch).indent(if_indent_level) 111 let new_expr = {
112 }; 112 let then_branch =
113 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) 113 make::block_expr(once(make::expr_stmt(early_expression).into()), None);
114 } 114 let cond = invert_boolean_expression(cond_expr);
115 Some((path, bound_ident)) => { 115 make::expr_if(make::condition(cond, None), then_branch)
116 // If-let. 116 .indent(if_indent_level)
117 let match_expr = {
118 let happy_arm = {
119 let pat = make::tuple_struct_pat(
120 path,
121 once(make::bind_pat(make::name("it")).into()),
122 );
123 let expr = {
124 let name_ref = make::name_ref("it");
125 let segment = make::path_segment(name_ref);
126 let path = make::path_unqualified(segment);
127 make::expr_path(path)
128 };
129 make::match_arm(once(pat.into()), expr)
130 }; 117 };
118 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
119 }
120 Some((path, bound_ident)) => {
121 // If-let.
122 let match_expr = {
123 let happy_arm = {
124 let pat = make::tuple_struct_pat(
125 path,
126 once(make::bind_pat(make::name("it")).into()),
127 );
128 let expr = {
129 let name_ref = make::name_ref("it");
130 let segment = make::path_segment(name_ref);
131 let path = make::path_unqualified(segment);
132 make::expr_path(path)
133 };
134 make::match_arm(once(pat.into()), expr)
135 };
131 136
132 let sad_arm = make::match_arm( 137 let sad_arm = make::match_arm(
133 // FIXME: would be cool to use `None` or `Err(_)` if appropriate 138 // FIXME: would be cool to use `None` or `Err(_)` if appropriate
134 once(make::placeholder_pat().into()), 139 once(make::placeholder_pat().into()),
135 early_expression, 140 early_expression,
136 ); 141 );
137 142
138 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) 143 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
139 }; 144 };
140 145
141 let let_stmt = make::let_stmt( 146 let let_stmt = make::let_stmt(
142 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), 147 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
143 Some(match_expr), 148 Some(match_expr),
149 );
150 let let_stmt = let_stmt.indent(if_indent_level);
151 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
152 }
153 };
154 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
155
156 fn replace(
157 new_expr: &SyntaxNode,
158 then_block: &ast::BlockExpr,
159 parent_block: &ast::BlockExpr,
160 if_expr: &ast::IfExpr,
161 ) -> SyntaxNode {
162 let then_block_items = then_block.dedent(IndentLevel(1));
163 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
164 let end_of_then =
165 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
166 end_of_then.prev_sibling_or_token().unwrap()
167 } else {
168 end_of_then
169 };
170 let mut then_statements = new_expr.children_with_tokens().chain(
171 then_block_items
172 .syntax()
173 .children_with_tokens()
174 .skip(1)
175 .take_while(|i| *i != end_of_then),
144 ); 176 );
145 let let_stmt = let_stmt.indent(if_indent_level); 177 replace_children(
146 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) 178 &parent_block.syntax(),
179 RangeInclusive::new(
180 if_expr.clone().syntax().clone().into(),
181 if_expr.syntax().clone().into(),
182 ),
183 &mut then_statements,
184 )
147 } 185 }
148 }; 186 },
149 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); 187 )
150
151 fn replace(
152 new_expr: &SyntaxNode,
153 then_block: &ast::BlockExpr,
154 parent_block: &ast::BlockExpr,
155 if_expr: &ast::IfExpr,
156 ) -> SyntaxNode {
157 let then_block_items = then_block.dedent(IndentLevel::from(1));
158 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
159 let end_of_then =
160 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
161 end_of_then.prev_sibling_or_token().unwrap()
162 } else {
163 end_of_then
164 };
165 let mut then_statements = new_expr.children_with_tokens().chain(
166 then_block_items
167 .syntax()
168 .children_with_tokens()
169 .skip(1)
170 .take_while(|i| *i != end_of_then),
171 );
172 replace_children(
173 &parent_block.syntax(),
174 RangeInclusive::new(
175 if_expr.clone().syntax().clone().into(),
176 if_expr.syntax().clone().into(),
177 ),
178 &mut then_statements,
179 )
180 }
181 })
182} 188}
183 189
184#[cfg(test)] 190#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
new file mode 100644
index 000000000..2b8e273b3
--- /dev/null
+++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -0,0 +1,321 @@
1use hir::{EnumVariant, Module, ModuleDef, Name};
2use ra_db::FileId;
3use ra_fmt::leading_indent;
4use ra_ide_db::{defs::Definition, search::Reference, RootDatabase};
5use ra_syntax::{
6 algo::find_node_at_offset,
7 ast::{self, ArgListOwner, AstNode, NameOwner, VisibilityOwner},
8 SourceFile, SyntaxNode, TextRange, TextSize,
9};
10use rustc_hash::FxHashSet;
11
12use crate::{
13 assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId,
14 AssistKind, Assists,
15};
16
17// Assist: extract_struct_from_enum_variant
18//
19// Extracts a struct from enum variant.
20//
21// ```
22// enum A { <|>One(u32, u32) }
23// ```
24// ->
25// ```
26// struct One(pub u32, pub u32);
27//
28// enum A { One(One) }
29// ```
30pub(crate) fn extract_struct_from_enum_variant(
31 acc: &mut Assists,
32 ctx: &AssistContext,
33) -> Option<()> {
34 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
35 let field_list = match variant.kind() {
36 ast::StructKind::Tuple(field_list) => field_list,
37 _ => return None,
38 };
39 let variant_name = variant.name()?.to_string();
40 let variant_hir = ctx.sema.to_def(&variant)?;
41 if existing_struct_def(ctx.db(), &variant_name, &variant_hir) {
42 return None;
43 }
44 let enum_ast = variant.parent_enum();
45 let visibility = enum_ast.visibility();
46 let enum_hir = ctx.sema.to_def(&enum_ast)?;
47 let variant_hir_name = variant_hir.name(ctx.db());
48 let enum_module_def = ModuleDef::from(enum_hir);
49 let current_module = enum_hir.module(ctx.db());
50 let target = variant.syntax().text_range();
51 acc.add(
52 AssistId("extract_struct_from_enum_variant", AssistKind::RefactorRewrite),
53 "Extract struct from enum variant",
54 target,
55 |builder| {
56 let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir));
57 let res = definition.find_usages(&ctx.sema, None);
58 let start_offset = variant.parent_enum().syntax().text_range().start();
59 let mut visited_modules_set = FxHashSet::default();
60 visited_modules_set.insert(current_module);
61 for reference in res {
62 let source_file = ctx.sema.parse(reference.file_range.file_id);
63 update_reference(
64 ctx,
65 builder,
66 reference,
67 &source_file,
68 &enum_module_def,
69 &variant_hir_name,
70 &mut visited_modules_set,
71 );
72 }
73 extract_struct_def(
74 builder,
75 enum_ast.syntax(),
76 &variant_name,
77 &field_list.to_string(),
78 start_offset,
79 ctx.frange.file_id,
80 &visibility,
81 );
82 let list_range = field_list.syntax().text_range();
83 update_variant(builder, &variant_name, ctx.frange.file_id, list_range);
84 },
85 )
86}
87
88fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVariant) -> bool {
89 variant
90 .parent_enum(db)
91 .module(db)
92 .scope(db, None)
93 .into_iter()
94 .any(|(name, _)| name.to_string() == variant_name.to_string())
95}
96
97fn insert_import(
98 ctx: &AssistContext,
99 builder: &mut AssistBuilder,
100 path: &ast::PathExpr,
101 module: &Module,
102 enum_module_def: &ModuleDef,
103 variant_hir_name: &Name,
104) -> Option<()> {
105 let db = ctx.db();
106 let mod_path = module.find_use_path(db, enum_module_def.clone());
107 if let Some(mut mod_path) = mod_path {
108 mod_path.segments.pop();
109 mod_path.segments.push(variant_hir_name.clone());
110 insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder());
111 }
112 Some(())
113}
114
115fn extract_struct_def(
116 builder: &mut AssistBuilder,
117 enum_ast: &SyntaxNode,
118 variant_name: &str,
119 variant_list: &str,
120 start_offset: TextSize,
121 file_id: FileId,
122 visibility: &Option<ast::Visibility>,
123) -> Option<()> {
124 let visibility_string = if let Some(visibility) = visibility {
125 format!("{} ", visibility.to_string())
126 } else {
127 "".to_string()
128 };
129 let indent = if let Some(indent) = leading_indent(enum_ast) {
130 indent.to_string()
131 } else {
132 "".to_string()
133 };
134 let struct_def = format!(
135 r#"{}struct {}{};
136
137{}"#,
138 visibility_string,
139 variant_name,
140 list_with_visibility(variant_list),
141 indent
142 );
143 builder.edit_file(file_id);
144 builder.insert(start_offset, struct_def);
145 Some(())
146}
147
148fn update_variant(
149 builder: &mut AssistBuilder,
150 variant_name: &str,
151 file_id: FileId,
152 list_range: TextRange,
153) -> Option<()> {
154 let inside_variant_range = TextRange::new(
155 list_range.start().checked_add(TextSize::from(1))?,
156 list_range.end().checked_sub(TextSize::from(1))?,
157 );
158 builder.edit_file(file_id);
159 builder.replace(inside_variant_range, variant_name);
160 Some(())
161}
162
163fn update_reference(
164 ctx: &AssistContext,
165 builder: &mut AssistBuilder,
166 reference: Reference,
167 source_file: &SourceFile,
168 enum_module_def: &ModuleDef,
169 variant_hir_name: &Name,
170 visited_modules_set: &mut FxHashSet<Module>,
171) -> Option<()> {
172 let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>(
173 source_file.syntax(),
174 reference.file_range.range.start(),
175 )?;
176 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
177 let list = call.arg_list()?;
178 let segment = path_expr.path()?.segment()?;
179 let module = ctx.sema.scope(&path_expr.syntax()).module()?;
180 let list_range = list.syntax().text_range();
181 let inside_list_range = TextRange::new(
182 list_range.start().checked_add(TextSize::from(1))?,
183 list_range.end().checked_sub(TextSize::from(1))?,
184 );
185 builder.edit_file(reference.file_range.file_id);
186 if !visited_modules_set.contains(&module) {
187 if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name)
188 .is_some()
189 {
190 visited_modules_set.insert(module);
191 }
192 }
193 builder.replace(inside_list_range, format!("{}{}", segment, list));
194 Some(())
195}
196
197fn list_with_visibility(list: &str) -> String {
198 list.split(',')
199 .map(|part| {
200 let index = if part.chars().next().unwrap() == '(' { 1usize } else { 0 };
201 let mut mod_part = part.trim().to_string();
202 mod_part.insert_str(index, "pub ");
203 mod_part
204 })
205 .collect::<Vec<String>>()
206 .join(", ")
207}
208
209#[cfg(test)]
210mod tests {
211
212 use crate::{
213 tests::{check_assist, check_assist_not_applicable},
214 utils::FamousDefs,
215 };
216
217 use super::*;
218
219 #[test]
220 fn test_extract_struct_several_fields() {
221 check_assist(
222 extract_struct_from_enum_variant,
223 "enum A { <|>One(u32, u32) }",
224 r#"struct One(pub u32, pub u32);
225
226enum A { One(One) }"#,
227 );
228 }
229
230 #[test]
231 fn test_extract_struct_one_field() {
232 check_assist(
233 extract_struct_from_enum_variant,
234 "enum A { <|>One(u32) }",
235 r#"struct One(pub u32);
236
237enum A { One(One) }"#,
238 );
239 }
240
241 #[test]
242 fn test_extract_struct_pub_visibility() {
243 check_assist(
244 extract_struct_from_enum_variant,
245 "pub enum A { <|>One(u32, u32) }",
246 r#"pub struct One(pub u32, pub u32);
247
248pub enum A { One(One) }"#,
249 );
250 }
251
252 #[test]
253 fn test_extract_struct_with_complex_imports() {
254 check_assist(
255 extract_struct_from_enum_variant,
256 r#"mod my_mod {
257 fn another_fn() {
258 let m = my_other_mod::MyEnum::MyField(1, 1);
259 }
260
261 pub mod my_other_mod {
262 fn another_fn() {
263 let m = MyEnum::MyField(1, 1);
264 }
265
266 pub enum MyEnum {
267 <|>MyField(u8, u8),
268 }
269 }
270}
271
272fn another_fn() {
273 let m = my_mod::my_other_mod::MyEnum::MyField(1, 1);
274}"#,
275 r#"use my_mod::my_other_mod::MyField;
276
277mod my_mod {
278 use my_other_mod::MyField;
279
280 fn another_fn() {
281 let m = my_other_mod::MyEnum::MyField(MyField(1, 1));
282 }
283
284 pub mod my_other_mod {
285 fn another_fn() {
286 let m = MyEnum::MyField(MyField(1, 1));
287 }
288
289 pub struct MyField(pub u8, pub u8);
290
291 pub enum MyEnum {
292 MyField(MyField),
293 }
294 }
295}
296
297fn another_fn() {
298 let m = my_mod::my_other_mod::MyEnum::MyField(MyField(1, 1));
299}"#,
300 );
301 }
302
303 fn check_not_applicable(ra_fixture: &str) {
304 let fixture =
305 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
306 check_assist_not_applicable(extract_struct_from_enum_variant, &fixture)
307 }
308
309 #[test]
310 fn test_extract_enum_not_applicable_for_element_with_no_fields() {
311 check_not_applicable("enum A { <|>One }");
312 }
313
314 #[test]
315 fn test_extract_enum_not_applicable_if_struct_exists() {
316 check_not_applicable(
317 r#"struct One;
318 enum A { <|>One(u8) }"#,
319 );
320 }
321}
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/extract_variable.rs
index 31d6539f7..481baf1a4 100644
--- a/crates/ra_assists/src/handlers/introduce_variable.rs
+++ b/crates/ra_assists/src/handlers/extract_variable.rs
@@ -9,9 +9,9 @@ use ra_syntax::{
9use stdx::format_to; 9use stdx::format_to;
10use test_utils::mark; 10use test_utils::mark;
11 11
12use crate::{AssistContext, AssistId, Assists}; 12use crate::{AssistContext, AssistId, AssistKind, Assists};
13 13
14// Assist: introduce_variable 14// Assist: extract_variable
15// 15//
16// Extracts subexpression into a variable. 16// Extracts subexpression into a variable.
17// 17//
@@ -27,13 +27,13 @@ use crate::{AssistContext, AssistId, Assists};
27// var_name * 4; 27// var_name * 4;
28// } 28// }
29// ``` 29// ```
30pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 30pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
31 if ctx.frange.range.is_empty() { 31 if ctx.frange.range.is_empty() {
32 return None; 32 return None;
33 } 33 }
34 let node = ctx.covering_element(); 34 let node = ctx.covering_element();
35 if node.kind() == COMMENT { 35 if node.kind() == COMMENT {
36 mark::hit!(introduce_var_in_comment_is_not_applicable); 36 mark::hit!(extract_var_in_comment_is_not_applicable);
37 return None; 37 return None;
38 } 38 }
39 let expr = node.ancestors().find_map(valid_target_expr)?; 39 let expr = node.ancestors().find_map(valid_target_expr)?;
@@ -43,65 +43,85 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti
43 return None; 43 return None;
44 } 44 }
45 let target = expr.syntax().text_range(); 45 let target = expr.syntax().text_range();
46 acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { 46 acc.add(
47 let mut buf = String::new(); 47 AssistId("extract_variable", AssistKind::RefactorExtract),
48 48 "Extract into variable",
49 if wrap_in_block { 49 target,
50 buf.push_str("{ let var_name = "); 50 move |edit| {
51 } else { 51 let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) {
52 buf.push_str("let var_name = "); 52 Some(field) => field.name_ref(),
53 }; 53 None => None,
54 format_to!(buf, "{}", expr.syntax()); 54 };
55 55
56 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); 56 let mut buf = String::new();
57 let is_full_stmt = if let Some(expr_stmt) = &full_stmt { 57
58 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) 58 let var_name = match &field_shorthand {
59 } else { 59 Some(it) => it.to_string(),
60 false 60 None => "var_name".to_string(),
61 }; 61 };
62 if is_full_stmt { 62 let expr_range = match &field_shorthand {
63 mark::hit!(test_introduce_var_expr_stmt); 63 Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()),
64 if full_stmt.unwrap().semicolon_token().is_none() { 64 None => expr.syntax().text_range(),
65 buf.push_str(";"); 65 };
66
67 if wrap_in_block {
68 format_to!(buf, "{{ let {} = ", var_name);
69 } else {
70 format_to!(buf, "let {} = ", var_name);
71 };
72 format_to!(buf, "{}", expr.syntax());
73
74 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
75 let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
76 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
77 } else {
78 false
79 };
80 if is_full_stmt {
81 mark::hit!(test_extract_var_expr_stmt);
82 if full_stmt.unwrap().semicolon_token().is_none() {
83 buf.push_str(";");
84 }
85 match ctx.config.snippet_cap {
86 Some(cap) => {
87 let snip = buf
88 .replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
89 edit.replace_snippet(cap, expr_range, snip)
90 }
91 None => edit.replace(expr_range, buf),
92 }
93 return;
94 }
95
96 buf.push_str(";");
97
98 // We want to maintain the indent level,
99 // but we do not want to duplicate possible
100 // extra newlines in the indent block
101 let text = indent.text();
102 if text.starts_with('\n') {
103 buf.push_str("\n");
104 buf.push_str(text.trim_start_matches('\n'));
105 } else {
106 buf.push_str(text);
66 } 107 }
67 let offset = expr.syntax().text_range(); 108
109 edit.replace(expr_range, var_name.clone());
110 let offset = anchor_stmt.text_range().start();
68 match ctx.config.snippet_cap { 111 match ctx.config.snippet_cap {
69 Some(cap) => { 112 Some(cap) => {
70 let snip = buf.replace("let var_name", "let $0var_name"); 113 let snip =
71 edit.replace_snippet(cap, offset, snip) 114 buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
115 edit.insert_snippet(cap, offset, snip)
72 } 116 }
73 None => edit.replace(offset, buf), 117 None => edit.insert(offset, buf),
74 } 118 }
75 return;
76 }
77 119
78 buf.push_str(";"); 120 if wrap_in_block {
79 121 edit.insert(anchor_stmt.text_range().end(), " }");
80 // We want to maintain the indent level,
81 // but we do not want to duplicate possible
82 // extra newlines in the indent block
83 let text = indent.text();
84 if text.starts_with('\n') {
85 buf.push_str("\n");
86 buf.push_str(text.trim_start_matches('\n'));
87 } else {
88 buf.push_str(text);
89 }
90
91 edit.replace(expr.syntax().text_range(), "var_name".to_string());
92 let offset = anchor_stmt.text_range().start();
93 match ctx.config.snippet_cap {
94 Some(cap) => {
95 let snip = buf.replace("let var_name", "let $0var_name");
96 edit.insert_snippet(cap, offset, snip)
97 } 122 }
98 None => edit.insert(offset, buf), 123 },
99 } 124 )
100
101 if wrap_in_block {
102 edit.insert(anchor_stmt.text_range().end(), " }");
103 }
104 })
105} 125}
106 126
107/// Check whether the node is a valid expression which can be extracted to a variable. 127/// Check whether the node is a valid expression which can be extracted to a variable.
@@ -118,7 +138,7 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
118 } 138 }
119} 139}
120 140
121/// Returns the syntax node which will follow the freshly introduced var 141/// Returns the syntax node which will follow the freshly extractd var
122/// and a boolean indicating whether we have to wrap it within a { } block 142/// and a boolean indicating whether we have to wrap it within a { } block
123/// to produce correct code. 143/// to produce correct code.
124/// It can be a statement, the last in a block expression or a wanna be block 144/// It can be a statement, the last in a block expression or a wanna be block
@@ -127,7 +147,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
127 expr.syntax().ancestors().find_map(|node| { 147 expr.syntax().ancestors().find_map(|node| {
128 if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) { 148 if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) {
129 if expr.syntax() == &node { 149 if expr.syntax() == &node {
130 mark::hit!(test_introduce_var_last_expr); 150 mark::hit!(test_extract_var_last_expr);
131 return Some((node, false)); 151 return Some((node, false));
132 } 152 }
133 } 153 }
@@ -155,9 +175,9 @@ mod tests {
155 use super::*; 175 use super::*;
156 176
157 #[test] 177 #[test]
158 fn test_introduce_var_simple() { 178 fn test_extract_var_simple() {
159 check_assist( 179 check_assist(
160 introduce_variable, 180 extract_variable,
161 r#" 181 r#"
162fn foo() { 182fn foo() {
163 foo(<|>1 + 1<|>); 183 foo(<|>1 + 1<|>);
@@ -171,16 +191,16 @@ fn foo() {
171 } 191 }
172 192
173 #[test] 193 #[test]
174 fn introduce_var_in_comment_is_not_applicable() { 194 fn extract_var_in_comment_is_not_applicable() {
175 mark::check!(introduce_var_in_comment_is_not_applicable); 195 mark::check!(extract_var_in_comment_is_not_applicable);
176 check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }"); 196 check_assist_not_applicable(extract_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }");
177 } 197 }
178 198
179 #[test] 199 #[test]
180 fn test_introduce_var_expr_stmt() { 200 fn test_extract_var_expr_stmt() {
181 mark::check!(test_introduce_var_expr_stmt); 201 mark::check!(test_extract_var_expr_stmt);
182 check_assist( 202 check_assist(
183 introduce_variable, 203 extract_variable,
184 r#" 204 r#"
185fn foo() { 205fn foo() {
186 <|>1 + 1<|>; 206 <|>1 + 1<|>;
@@ -191,7 +211,7 @@ fn foo() {
191}"#, 211}"#,
192 ); 212 );
193 check_assist( 213 check_assist(
194 introduce_variable, 214 extract_variable,
195 " 215 "
196fn foo() { 216fn foo() {
197 <|>{ let x = 0; x }<|> 217 <|>{ let x = 0; x }<|>
@@ -206,9 +226,9 @@ fn foo() {
206 } 226 }
207 227
208 #[test] 228 #[test]
209 fn test_introduce_var_part_of_expr_stmt() { 229 fn test_extract_var_part_of_expr_stmt() {
210 check_assist( 230 check_assist(
211 introduce_variable, 231 extract_variable,
212 " 232 "
213fn foo() { 233fn foo() {
214 <|>1<|> + 1; 234 <|>1<|> + 1;
@@ -222,38 +242,42 @@ fn foo() {
222 } 242 }
223 243
224 #[test] 244 #[test]
225 fn test_introduce_var_last_expr() { 245 fn test_extract_var_last_expr() {
226 mark::check!(test_introduce_var_last_expr); 246 mark::check!(test_extract_var_last_expr);
227 check_assist( 247 check_assist(
228 introduce_variable, 248 extract_variable,
229 " 249 r#"
230fn foo() { 250fn foo() {
231 bar(<|>1 + 1<|>) 251 bar(<|>1 + 1<|>)
232}", 252}
233 " 253"#,
254 r#"
234fn foo() { 255fn foo() {
235 let $0var_name = 1 + 1; 256 let $0var_name = 1 + 1;
236 bar(var_name) 257 bar(var_name)
237}", 258}
259"#,
238 ); 260 );
239 check_assist( 261 check_assist(
240 introduce_variable, 262 extract_variable,
241 " 263 r#"
242fn foo() { 264fn foo() {
243 <|>bar(1 + 1)<|> 265 <|>bar(1 + 1)<|>
244}", 266}
245 " 267"#,
268 r#"
246fn foo() { 269fn foo() {
247 let $0var_name = bar(1 + 1); 270 let $0var_name = bar(1 + 1);
248 var_name 271 var_name
249}", 272}
273"#,
250 ) 274 )
251 } 275 }
252 276
253 #[test] 277 #[test]
254 fn test_introduce_var_in_match_arm_no_block() { 278 fn test_extract_var_in_match_arm_no_block() {
255 check_assist( 279 check_assist(
256 introduce_variable, 280 extract_variable,
257 " 281 "
258fn main() { 282fn main() {
259 let x = true; 283 let x = true;
@@ -276,9 +300,9 @@ fn main() {
276 } 300 }
277 301
278 #[test] 302 #[test]
279 fn test_introduce_var_in_match_arm_with_block() { 303 fn test_extract_var_in_match_arm_with_block() {
280 check_assist( 304 check_assist(
281 introduce_variable, 305 extract_variable,
282 " 306 "
283fn main() { 307fn main() {
284 let x = true; 308 let x = true;
@@ -308,9 +332,9 @@ fn main() {
308 } 332 }
309 333
310 #[test] 334 #[test]
311 fn test_introduce_var_in_closure_no_block() { 335 fn test_extract_var_in_closure_no_block() {
312 check_assist( 336 check_assist(
313 introduce_variable, 337 extract_variable,
314 " 338 "
315fn main() { 339fn main() {
316 let lambda = |x: u32| <|>x * 2<|>; 340 let lambda = |x: u32| <|>x * 2<|>;
@@ -325,9 +349,9 @@ fn main() {
325 } 349 }
326 350
327 #[test] 351 #[test]
328 fn test_introduce_var_in_closure_with_block() { 352 fn test_extract_var_in_closure_with_block() {
329 check_assist( 353 check_assist(
330 introduce_variable, 354 extract_variable,
331 " 355 "
332fn main() { 356fn main() {
333 let lambda = |x: u32| { <|>x * 2<|> }; 357 let lambda = |x: u32| { <|>x * 2<|> };
@@ -342,9 +366,9 @@ fn main() {
342 } 366 }
343 367
344 #[test] 368 #[test]
345 fn test_introduce_var_path_simple() { 369 fn test_extract_var_path_simple() {
346 check_assist( 370 check_assist(
347 introduce_variable, 371 extract_variable,
348 " 372 "
349fn main() { 373fn main() {
350 let o = <|>Some(true)<|>; 374 let o = <|>Some(true)<|>;
@@ -360,9 +384,9 @@ fn main() {
360 } 384 }
361 385
362 #[test] 386 #[test]
363 fn test_introduce_var_path_method() { 387 fn test_extract_var_path_method() {
364 check_assist( 388 check_assist(
365 introduce_variable, 389 extract_variable,
366 " 390 "
367fn main() { 391fn main() {
368 let v = <|>bar.foo()<|>; 392 let v = <|>bar.foo()<|>;
@@ -378,9 +402,9 @@ fn main() {
378 } 402 }
379 403
380 #[test] 404 #[test]
381 fn test_introduce_var_return() { 405 fn test_extract_var_return() {
382 check_assist( 406 check_assist(
383 introduce_variable, 407 extract_variable,
384 " 408 "
385fn foo() -> u32 { 409fn foo() -> u32 {
386 <|>return 2 + 2<|>; 410 <|>return 2 + 2<|>;
@@ -396,9 +420,9 @@ fn foo() -> u32 {
396 } 420 }
397 421
398 #[test] 422 #[test]
399 fn test_introduce_var_does_not_add_extra_whitespace() { 423 fn test_extract_var_does_not_add_extra_whitespace() {
400 check_assist( 424 check_assist(
401 introduce_variable, 425 extract_variable,
402 " 426 "
403fn foo() -> u32 { 427fn foo() -> u32 {
404 428
@@ -417,7 +441,7 @@ fn foo() -> u32 {
417 ); 441 );
418 442
419 check_assist( 443 check_assist(
420 introduce_variable, 444 extract_variable,
421 " 445 "
422fn foo() -> u32 { 446fn foo() -> u32 {
423 447
@@ -434,7 +458,7 @@ fn foo() -> u32 {
434 ); 458 );
435 459
436 check_assist( 460 check_assist(
437 introduce_variable, 461 extract_variable,
438 " 462 "
439fn foo() -> u32 { 463fn foo() -> u32 {
440 let foo = 1; 464 let foo = 1;
@@ -460,9 +484,9 @@ fn foo() -> u32 {
460 } 484 }
461 485
462 #[test] 486 #[test]
463 fn test_introduce_var_break() { 487 fn test_extract_var_break() {
464 check_assist( 488 check_assist(
465 introduce_variable, 489 extract_variable,
466 " 490 "
467fn main() { 491fn main() {
468 let result = loop { 492 let result = loop {
@@ -482,9 +506,9 @@ fn main() {
482 } 506 }
483 507
484 #[test] 508 #[test]
485 fn test_introduce_var_for_cast() { 509 fn test_extract_var_for_cast() {
486 check_assist( 510 check_assist(
487 introduce_variable, 511 extract_variable,
488 " 512 "
489fn main() { 513fn main() {
490 let v = <|>0f32 as u32<|>; 514 let v = <|>0f32 as u32<|>;
@@ -500,22 +524,48 @@ fn main() {
500 } 524 }
501 525
502 #[test] 526 #[test]
503 fn test_introduce_var_for_return_not_applicable() { 527 fn extract_var_field_shorthand() {
504 check_assist_not_applicable(introduce_variable, "fn foo() { <|>return<|>; } "); 528 check_assist(
529 extract_variable,
530 r#"
531struct S {
532 foo: i32
533}
534
535fn main() {
536 S { foo: <|>1 + 1<|> }
537}
538"#,
539 r#"
540struct S {
541 foo: i32
542}
543
544fn main() {
545 let $0foo = 1 + 1;
546 S { foo }
547}
548"#,
549 )
550 }
551
552 #[test]
553 fn test_extract_var_for_return_not_applicable() {
554 check_assist_not_applicable(extract_variable, "fn foo() { <|>return<|>; } ");
505 } 555 }
506 556
507 #[test] 557 #[test]
508 fn test_introduce_var_for_break_not_applicable() { 558 fn test_extract_var_for_break_not_applicable() {
509 check_assist_not_applicable(introduce_variable, "fn main() { loop { <|>break<|>; }; }"); 559 check_assist_not_applicable(extract_variable, "fn main() { loop { <|>break<|>; }; }");
510 } 560 }
511 561
512 // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic 562 // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic
513 #[test] 563 #[test]
514 fn introduce_var_target() { 564 fn extract_var_target() {
515 check_assist_target(introduce_variable, "fn foo() -> u32 { <|>return 2 + 2<|>; }", "2 + 2"); 565 check_assist_target(extract_variable, "fn foo() -> u32 { <|>return 2 + 2<|>; }", "2 + 2");
516 566
517 check_assist_target( 567 check_assist_target(
518 introduce_variable, 568 extract_variable,
519 " 569 "
520fn main() { 570fn main() {
521 let x = true; 571 let x = true;
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index cc303285b..708e1bc6c 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -8,7 +8,7 @@ use test_utils::mark;
8 8
9use crate::{ 9use crate::{
10 utils::{render_snippet, Cursor, FamousDefs}, 10 utils::{render_snippet, Cursor, FamousDefs},
11 AssistContext, AssistId, Assists, 11 AssistContext, AssistId, AssistKind, Assists,
12}; 12};
13 13
14// Assist: fill_match_arms 14// Assist: fill_match_arms
@@ -51,11 +51,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
51 let module = ctx.sema.scope(expr.syntax()).module()?; 51 let module = ctx.sema.scope(expr.syntax()).module()?;
52 52
53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { 53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
54 let variants = enum_def.variants(ctx.db); 54 let variants = enum_def.variants(ctx.db());
55 55
56 let mut variants = variants 56 let mut variants = variants
57 .into_iter() 57 .into_iter()
58 .filter_map(|variant| build_pat(ctx.db, module, variant)) 58 .filter_map(|variant| build_pat(ctx.db(), module, variant))
59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
61 .collect::<Vec<_>>(); 61 .collect::<Vec<_>>();
@@ -84,11 +84,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
84 // where each tuple represents a proposed match arm. 84 // where each tuple represents a proposed match arm.
85 enum_defs 85 enum_defs
86 .into_iter() 86 .into_iter()
87 .map(|enum_def| enum_def.variants(ctx.db)) 87 .map(|enum_def| enum_def.variants(ctx.db()))
88 .multi_cartesian_product() 88 .multi_cartesian_product()
89 .map(|variants| { 89 .map(|variants| {
90 let patterns = 90 let patterns =
91 variants.into_iter().filter_map(|variant| build_pat(ctx.db, module, variant)); 91 variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
92 ast::Pat::from(make::tuple_pat(patterns)) 92 ast::Pat::from(make::tuple_pat(patterns))
93 }) 93 })
94 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 94 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
@@ -103,24 +103,37 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
103 } 103 }
104 104
105 let target = match_expr.syntax().text_range(); 105 let target = match_expr.syntax().text_range();
106 acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |builder| { 106 acc.add(
107 let new_arm_list = match_arm_list.remove_placeholder(); 107 AssistId("fill_match_arms", AssistKind::QuickFix),
108 let n_old_arms = new_arm_list.arms().count(); 108 "Fill match arms",
109 let new_arm_list = new_arm_list.append_arms(missing_arms); 109 target,
110 let first_new_arm = new_arm_list.arms().nth(n_old_arms); 110 |builder| {
111 let old_range = match_arm_list.syntax().text_range(); 111 let new_arm_list = match_arm_list.remove_placeholder();
112 match (first_new_arm, ctx.config.snippet_cap) { 112 let n_old_arms = new_arm_list.arms().count();
113 (Some(first_new_arm), Some(cap)) => { 113 let new_arm_list = new_arm_list.append_arms(missing_arms);
114 let snippet = render_snippet( 114 let first_new_arm = new_arm_list.arms().nth(n_old_arms);
115 cap, 115 let old_range = match_arm_list.syntax().text_range();
116 new_arm_list.syntax(), 116 match (first_new_arm, ctx.config.snippet_cap) {
117 Cursor::Before(first_new_arm.syntax()), 117 (Some(first_new_arm), Some(cap)) => {
118 ); 118 let extend_lifetime;
119 builder.replace_snippet(cap, old_range, snippet); 119 let cursor = match first_new_arm
120 } 120 .syntax()
121 _ => builder.replace(old_range, new_arm_list.to_string()), 121 .descendants()
122 } 122 .find_map(ast::PlaceholderPat::cast)
123 }) 123 {
124 Some(it) => {
125 extend_lifetime = it.syntax().clone();
126 Cursor::Replace(&extend_lifetime)
127 }
128 None => Cursor::Before(first_new_arm.syntax()),
129 };
130 let snippet = render_snippet(cap, new_arm_list.syntax(), cursor);
131 builder.replace_snippet(cap, old_range, snippet);
132 }
133 _ => builder.replace(old_range, new_arm_list.to_string()),
134 }
135 },
136 )
124} 137}
125 138
126fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { 139fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
@@ -136,8 +149,20 @@ fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
136} 149}
137 150
138fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { 151fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
139 let pat_head = pat.syntax().first_child().map(|node| node.text()); 152 let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text());
140 let var_head = var.syntax().first_child().map(|node| node.text()); 153
154 let pat_head = match pat {
155 Pat::BindPat(bind_pat) => {
156 if let Some(p) = bind_pat.pat() {
157 first_node_text(&p)
158 } else {
159 return false;
160 }
161 }
162 pat => first_node_text(pat),
163 };
164
165 let var_head = first_node_text(var);
141 166
142 pat_head == var_head 167 pat_head == var_head
143} 168}
@@ -274,30 +299,22 @@ mod tests {
274 check_assist( 299 check_assist(
275 fill_match_arms, 300 fill_match_arms,
276 r#" 301 r#"
277 enum A { 302enum A { As, Bs, Cs(Option<i32>) }
278 As, 303fn main() {
279 Bs, 304 match A::As<|> {
280 Cs(Option<i32>), 305 A::Cs(_) | A::Bs => {}
281 } 306 }
282 fn main() { 307}
283 match A::As<|> { 308"#,
284 A::Cs(_) | A::Bs => {}
285 }
286 }
287 "#,
288 r#" 309 r#"
289 enum A { 310enum A { As, Bs, Cs(Option<i32>) }
290 As, 311fn main() {
291 Bs, 312 match A::As {
292 Cs(Option<i32>), 313 A::Cs(_) | A::Bs => {}
293 } 314 $0A::As => {}
294 fn main() { 315 }
295 match A::As { 316}
296 A::Cs(_) | A::Bs => {} 317"#,
297 $0A::As => {}
298 }
299 }
300 "#,
301 ); 318 );
302 } 319 }
303 320
@@ -306,47 +323,55 @@ mod tests {
306 check_assist( 323 check_assist(
307 fill_match_arms, 324 fill_match_arms,
308 r#" 325 r#"
309 enum A { 326enum A { As, Bs, Cs, Ds(String), Es(B) }
310 As, 327enum B { Xs, Ys }
311 Bs, 328fn main() {
312 Cs, 329 match A::As<|> {
313 Ds(String), 330 A::Bs if 0 < 1 => {}
314 Es(B), 331 A::Ds(_value) => { let x = 1; }
315 } 332 A::Es(B::Xs) => (),
316 enum B { 333 }
317 Xs, 334}
318 Ys, 335"#,
319 }
320 fn main() {
321 match A::As<|> {
322 A::Bs if 0 < 1 => {}
323 A::Ds(_value) => { let x = 1; }
324 A::Es(B::Xs) => (),
325 }
326 }
327 "#,
328 r#" 336 r#"
329 enum A { 337enum A { As, Bs, Cs, Ds(String), Es(B) }
330 As, 338enum B { Xs, Ys }
331 Bs, 339fn main() {
332 Cs, 340 match A::As {
333 Ds(String), 341 A::Bs if 0 < 1 => {}
334 Es(B), 342 A::Ds(_value) => { let x = 1; }
335 } 343 A::Es(B::Xs) => (),
336 enum B { 344 $0A::As => {}
337 Xs, 345 A::Cs => {}
338 Ys, 346 }
339 } 347}
340 fn main() { 348"#,
341 match A::As { 349 );
342 A::Bs if 0 < 1 => {} 350 }
343 A::Ds(_value) => { let x = 1; } 351
344 A::Es(B::Xs) => (), 352 #[test]
345 $0A::As => {} 353 fn partial_fill_bind_pat() {
346 A::Cs => {} 354 check_assist(
347 } 355 fill_match_arms,
348 } 356 r#"
349 "#, 357enum A { As, Bs, Cs(Option<i32>) }
358fn main() {
359 match A::As<|> {
360 A::As(_) => {}
361 a @ A::Bs(_) => {}
362 }
363}
364"#,
365 r#"
366enum A { As, Bs, Cs(Option<i32>) }
367fn main() {
368 match A::As {
369 A::As(_) => {}
370 a @ A::Bs(_) => {}
371 A::Cs(${0:_}) => {}
372 }
373}
374"#,
350 ); 375 );
351 } 376 }
352 377
@@ -355,39 +380,27 @@ mod tests {
355 check_assist( 380 check_assist(
356 fill_match_arms, 381 fill_match_arms,
357 r#" 382 r#"
358 enum A { 383enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
359 As,
360 Bs,
361 Cs(String),
362 Ds(String, String),
363 Es { x: usize, y: usize }
364 }
365 384
366 fn main() { 385fn main() {
367 let a = A::As; 386 let a = A::As;
368 match a<|> {} 387 match a<|> {}
369 } 388}
370 "#, 389"#,
371 r#" 390 r#"
372 enum A { 391enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
373 As,
374 Bs,
375 Cs(String),
376 Ds(String, String),
377 Es { x: usize, y: usize }
378 }
379 392
380 fn main() { 393fn main() {
381 let a = A::As; 394 let a = A::As;
382 match a { 395 match a {
383 $0A::As => {} 396 $0A::As => {}
384 A::Bs => {} 397 A::Bs => {}
385 A::Cs(_) => {} 398 A::Cs(_) => {}
386 A::Ds(_, _) => {} 399 A::Ds(_, _) => {}
387 A::Es { x, y } => {} 400 A::Es { x, y } => {}
388 } 401 }
389 } 402}
390 "#, 403"#,
391 ); 404 );
392 } 405 }
393 406
@@ -717,9 +730,9 @@ mod tests {
717fn foo(opt: Option<i32>) { 730fn foo(opt: Option<i32>) {
718 match opt<|> { 731 match opt<|> {
719 } 732 }
720}"#; 733}
721 let before = 734"#;
722 &format!("//- main.rs crate:main deps:core\n{}{}", before, FamousDefs::FIXTURE); 735 let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
723 736
724 check_assist( 737 check_assist(
725 fill_match_arms, 738 fill_match_arms,
@@ -727,7 +740,7 @@ fn foo(opt: Option<i32>) {
727 r#" 740 r#"
728fn foo(opt: Option<i32>) { 741fn foo(opt: Option<i32>) {
729 match opt { 742 match opt {
730 $0Some(_) => {} 743 Some(${0:_}) => {}
731 None => {} 744 None => {}
732 } 745 }
733} 746}
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs
index 9ec42f568..e212557c8 100644
--- a/crates/ra_assists/src/handlers/fix_visibility.rs
+++ b/crates/ra_assists/src/handlers/fix_visibility.rs
@@ -1,12 +1,8 @@
1use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; 1use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
2use ra_db::FileId; 2use ra_db::FileId;
3use ra_syntax::{ 3use ra_syntax::{ast, AstNode, TextRange, TextSize};
4 ast, AstNode,
5 SyntaxKind::{ATTR, COMMENT, WHITESPACE},
6 SyntaxNode, TextRange, TextSize,
7};
8 4
9use crate::{AssistContext, AssistId, Assists}; 5use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
10 6
11// FIXME: this really should be a fix for diagnostic, rather than an assist. 7// FIXME: this really should be a fix for diagnostic, rather than an assist.
12 8
@@ -45,14 +41,14 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
45 }; 41 };
46 42
47 let current_module = ctx.sema.scope(&path.syntax()).module()?; 43 let current_module = ctx.sema.scope(&path.syntax()).module()?;
48 let target_module = def.module(ctx.db)?; 44 let target_module = def.module(ctx.db())?;
49 45
50 let vis = target_module.visibility_of(ctx.db, &def)?; 46 let vis = target_module.visibility_of(ctx.db(), &def)?;
51 if vis.is_visible_from(ctx.db, current_module.into()) { 47 if vis.is_visible_from(ctx.db(), current_module.into()) {
52 return None; 48 return None;
53 }; 49 };
54 50
55 let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?; 51 let (offset, target, target_file, target_name) = target_data_for_def(ctx.db(), def)?;
56 52
57 let missing_visibility = 53 let missing_visibility =
58 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; 54 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
@@ -62,8 +58,8 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
62 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), 58 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
63 }; 59 };
64 60
65 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { 61 acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
66 builder.set_file(target_file); 62 builder.edit_file(target_file);
67 match ctx.config.snippet_cap { 63 match ctx.config.snippet_cap {
68 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), 64 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
69 None => builder.insert(offset, format!("{} ", missing_visibility)), 65 None => builder.insert(offset, format!("{} ", missing_visibility)),
@@ -76,16 +72,16 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
76 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; 72 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?;
77 73
78 let current_module = ctx.sema.scope(record_field.syntax()).module()?; 74 let current_module = ctx.sema.scope(record_field.syntax()).module()?;
79 let visibility = record_field_def.visibility(ctx.db); 75 let visibility = record_field_def.visibility(ctx.db());
80 if visibility.is_visible_from(ctx.db, current_module.into()) { 76 if visibility.is_visible_from(ctx.db(), current_module.into()) {
81 return None; 77 return None;
82 } 78 }
83 79
84 let parent = record_field_def.parent_def(ctx.db); 80 let parent = record_field_def.parent_def(ctx.db());
85 let parent_name = parent.name(ctx.db); 81 let parent_name = parent.name(ctx.db());
86 let target_module = parent.module(ctx.db); 82 let target_module = parent.module(ctx.db());
87 83
88 let in_file_source = record_field_def.source(ctx.db); 84 let in_file_source = record_field_def.source(ctx.db());
89 let (offset, target) = match in_file_source.value { 85 let (offset, target) = match in_file_source.value {
90 hir::FieldSource::Named(it) => { 86 hir::FieldSource::Named(it) => {
91 let s = it.syntax(); 87 let s = it.syntax();
@@ -99,14 +95,14 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
99 95
100 let missing_visibility = 96 let missing_visibility =
101 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; 97 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
102 let target_file = in_file_source.file_id.original_file(ctx.db); 98 let target_file = in_file_source.file_id.original_file(ctx.db());
103 99
104 let target_name = record_field_def.name(ctx.db); 100 let target_name = record_field_def.name(ctx.db());
105 let assist_label = 101 let assist_label =
106 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); 102 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
107 103
108 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { 104 acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
109 builder.set_file(target_file); 105 builder.edit_file(target_file);
110 match ctx.config.snippet_cap { 106 match ctx.config.snippet_cap {
111 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), 107 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
112 None => builder.insert(offset, format!("{} ", missing_visibility)), 108 None => builder.insert(offset, format!("{} ", missing_visibility)),
@@ -177,17 +173,6 @@ fn target_data_for_def(
177 Some((offset, target, target_file, target_name)) 173 Some((offset, target, target_file, target_name))
178} 174}
179 175
180fn vis_offset(node: &SyntaxNode) -> TextSize {
181 node.children_with_tokens()
182 .skip_while(|it| match it.kind() {
183 WHITESPACE | COMMENT | ATTR => true,
184 _ => false,
185 })
186 .next()
187 .map(|it| it.text_range().start())
188 .unwrap_or_else(|| node.text_range().start())
189}
190
191#[cfg(test)] 176#[cfg(test)]
192mod tests { 177mod tests {
193 use crate::tests::{check_assist, check_assist_not_applicable}; 178 use crate::tests::{check_assist, check_assist_not_applicable};
@@ -255,15 +240,14 @@ mod tests {
255 check_assist( 240 check_assist(
256 fix_visibility, 241 fix_visibility,
257 r" 242 r"
258 //- /main.rs 243//- /main.rs
259 mod foo; 244mod foo;
260 fn main() { foo::Foo<|> } 245fn main() { foo::Foo<|> }
261 246
262 //- /foo.rs 247//- /foo.rs
263 struct Foo; 248struct Foo;
264 ", 249",
265 r"$0pub(crate) struct Foo; 250 r"$0pub(crate) struct Foo;
266
267", 251",
268 ); 252 );
269 } 253 }
@@ -279,14 +263,14 @@ mod tests {
279 ); 263 );
280 check_assist( 264 check_assist(
281 fix_visibility, 265 fix_visibility,
282 r"//- /lib.rs 266 r"
283 mod foo; 267//- /lib.rs
284 fn main() { foo::Foo { <|>bar: () }; } 268mod foo;
285 //- /foo.rs 269fn main() { foo::Foo { <|>bar: () }; }
286 pub struct Foo { bar: () } 270//- /foo.rs
287 ", 271pub struct Foo { bar: () }
272",
288 r"pub struct Foo { $0pub(crate) bar: () } 273 r"pub struct Foo { $0pub(crate) bar: () }
289
290", 274",
291 ); 275 );
292 check_assist_not_applicable( 276 check_assist_not_applicable(
@@ -296,12 +280,13 @@ mod tests {
296 ); 280 );
297 check_assist_not_applicable( 281 check_assist_not_applicable(
298 fix_visibility, 282 fix_visibility,
299 r"//- /lib.rs 283 r"
300 mod foo; 284//- /lib.rs
301 fn main() { foo::Foo { <|>bar: () }; } 285mod foo;
302 //- /foo.rs 286fn main() { foo::Foo { <|>bar: () }; }
303 pub struct Foo { pub bar: () } 287//- /foo.rs
304 ", 288pub struct Foo { pub bar: () }
289",
305 ); 290 );
306 } 291 }
307 292
@@ -316,14 +301,14 @@ mod tests {
316 ); 301 );
317 check_assist( 302 check_assist(
318 fix_visibility, 303 fix_visibility,
319 r"//- /lib.rs 304 r"
320 mod foo; 305//- /lib.rs
321 fn main() { foo::Foo::Bar { <|>bar: () }; } 306mod foo;
322 //- /foo.rs 307fn main() { foo::Foo::Bar { <|>bar: () }; }
323 pub enum Foo { Bar { bar: () } } 308//- /foo.rs
324 ", 309pub enum Foo { Bar { bar: () } }
310",
325 r"pub enum Foo { Bar { $0pub(crate) bar: () } } 311 r"pub enum Foo { Bar { $0pub(crate) bar: () } }
326
327", 312",
328 ); 313 );
329 check_assist_not_applicable( 314 check_assist_not_applicable(
@@ -333,12 +318,13 @@ mod tests {
333 ); 318 );
334 check_assist_not_applicable( 319 check_assist_not_applicable(
335 fix_visibility, 320 fix_visibility,
336 r"//- /lib.rs 321 r"
337 mod foo; 322//- /lib.rs
338 fn main() { foo::Foo { <|>bar: () }; } 323mod foo;
339 //- /foo.rs 324fn main() { foo::Foo { <|>bar: () }; }
340 pub struct Foo { pub bar: () } 325//- /foo.rs
341 ", 326pub struct Foo { pub bar: () }
327",
342 ); 328 );
343 } 329 }
344 330
@@ -355,14 +341,14 @@ mod tests {
355 ); 341 );
356 check_assist( 342 check_assist(
357 fix_visibility, 343 fix_visibility,
358 r"//- /lib.rs 344 r"
359 mod foo; 345//- /lib.rs
360 fn main() { foo::Foo { <|>bar: () }; } 346mod foo;
361 //- /foo.rs 347fn main() { foo::Foo { <|>bar: () }; }
362 pub union Foo { bar: () } 348//- /foo.rs
363 ", 349pub union Foo { bar: () }
350",
364 r"pub union Foo { $0pub(crate) bar: () } 351 r"pub union Foo { $0pub(crate) bar: () }
365
366", 352",
367 ); 353 );
368 check_assist_not_applicable( 354 check_assist_not_applicable(
@@ -372,12 +358,13 @@ mod tests {
372 ); 358 );
373 check_assist_not_applicable( 359 check_assist_not_applicable(
374 fix_visibility, 360 fix_visibility,
375 r"//- /lib.rs 361 r"
376 mod foo; 362//- /lib.rs
377 fn main() { foo::Foo { <|>bar: () }; } 363mod foo;
378 //- /foo.rs 364fn main() { foo::Foo { <|>bar: () }; }
379 pub union Foo { pub bar: () } 365//- /foo.rs
380 ", 366pub union Foo { pub bar: () }
367",
381 ); 368 );
382 } 369 }
383 370
@@ -458,19 +445,18 @@ mod tests {
458 check_assist( 445 check_assist(
459 fix_visibility, 446 fix_visibility,
460 r" 447 r"
461 //- /main.rs 448//- /main.rs
462 mod foo; 449mod foo;
463 fn main() { foo::bar<|>::baz(); } 450fn main() { foo::bar<|>::baz(); }
464 451
465 //- /foo.rs 452//- /foo.rs
466 mod bar { 453mod bar {
467 pub fn baz() {} 454 pub fn baz() {}
468 } 455}
469 ", 456",
470 r"$0pub(crate) mod bar { 457 r"$0pub(crate) mod bar {
471 pub fn baz() {} 458 pub fn baz() {}
472} 459}
473
474", 460",
475 ); 461 );
476 462
@@ -486,17 +472,15 @@ mod tests {
486 check_assist( 472 check_assist(
487 fix_visibility, 473 fix_visibility,
488 r" 474 r"
489 //- /main.rs 475//- /main.rs
490 mod foo; 476mod foo;
491 fn main() { foo::bar<|>::baz(); } 477fn main() { foo::bar<|>::baz(); }
492 478
493 //- /foo.rs 479//- /foo.rs
494 mod bar; 480mod bar;
495 481//- /foo/bar.rs
496 //- /foo/bar.rs 482pub fn baz() {}
497 pub fn baz() {} 483",
498 }
499 ",
500 r"$0pub(crate) mod bar; 484 r"$0pub(crate) mod bar;
501", 485",
502 ); 486 );
@@ -506,14 +490,16 @@ mod tests {
506 fn fix_visibility_of_module_declaration_in_other_file() { 490 fn fix_visibility_of_module_declaration_in_other_file() {
507 check_assist( 491 check_assist(
508 fix_visibility, 492 fix_visibility,
509 r"//- /main.rs 493 r"
510 mod foo; 494//- /main.rs
511 fn main() { foo::bar<|>>::baz(); } 495mod foo;
496fn main() { foo::bar<|>>::baz(); }
512 497
513 //- /foo.rs 498//- /foo.rs
514 mod bar { 499mod bar {
515 pub fn baz() {} 500 pub fn baz() {}
516 }", 501}
502",
517 r"$0pub(crate) mod bar { 503 r"$0pub(crate) mod bar {
518 pub fn baz() {} 504 pub fn baz() {}
519} 505}
@@ -525,10 +511,12 @@ mod tests {
525 fn adds_pub_when_target_is_in_another_crate() { 511 fn adds_pub_when_target_is_in_another_crate() {
526 check_assist( 512 check_assist(
527 fix_visibility, 513 fix_visibility,
528 r"//- /main.rs crate:a deps:foo 514 r"
529 foo::Bar<|> 515//- /main.rs crate:a deps:foo
530 //- /lib.rs crate:foo 516foo::Bar<|>
531 struct Bar;", 517//- /lib.rs crate:foo
518struct Bar;
519",
532 r"$0pub struct Bar; 520 r"$0pub struct Bar;
533", 521",
534 ) 522 )
diff --git a/crates/ra_assists/src/handlers/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs
index 573196576..3cd532650 100644
--- a/crates/ra_assists/src/handlers/flip_binexpr.rs
+++ b/crates/ra_assists/src/handlers/flip_binexpr.rs
@@ -1,6 +1,6 @@
1use ra_syntax::ast::{AstNode, BinExpr, BinOp}; 1use ra_syntax::ast::{AstNode, BinExpr, BinOp};
2 2
3use crate::{AssistContext, AssistId, Assists}; 3use crate::{AssistContext, AssistId, AssistKind, Assists};
4 4
5// Assist: flip_binexpr 5// Assist: flip_binexpr
6// 6//
@@ -33,13 +33,18 @@ pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
33 return None; 33 return None;
34 } 34 }
35 35
36 acc.add(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| { 36 acc.add(
37 if let FlipAction::FlipAndReplaceOp(new_op) = action { 37 AssistId("flip_binexpr", AssistKind::RefactorRewrite),
38 edit.replace(op_range, new_op); 38 "Flip binary expression",
39 } 39 op_range,
40 edit.replace(lhs.text_range(), rhs.text()); 40 |edit| {
41 edit.replace(rhs.text_range(), lhs.text()); 41 if let FlipAction::FlipAndReplaceOp(new_op) = action {
42 }) 42 edit.replace(op_range, new_op);
43 }
44 edit.replace(lhs.text_range(), rhs.text());
45 edit.replace(rhs.text_range(), lhs.text());
46 },
47 )
43} 48}
44 49
45enum FlipAction { 50enum FlipAction {
diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs
index a57a1c463..55a971dc7 100644
--- a/crates/ra_assists/src/handlers/flip_comma.rs
+++ b/crates/ra_assists/src/handlers/flip_comma.rs
@@ -1,6 +1,6 @@
1use ra_syntax::{algo::non_trivia_sibling, Direction, T}; 1use ra_syntax::{algo::non_trivia_sibling, Direction, T};
2 2
3use crate::{AssistContext, AssistId, Assists}; 3use crate::{AssistContext, AssistId, AssistKind, Assists};
4 4
5// Assist: flip_comma 5// Assist: flip_comma
6// 6//
@@ -28,10 +28,15 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 return None; 28 return None;
29 } 29 }
30 30
31 acc.add(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| { 31 acc.add(
32 edit.replace(prev.text_range(), next.to_string()); 32 AssistId("flip_comma", AssistKind::RefactorRewrite),
33 edit.replace(next.text_range(), prev.to_string()); 33 "Flip comma",
34 }) 34 comma.text_range(),
35 |edit| {
36 edit.replace(prev.text_range(), next.to_string());
37 edit.replace(next.text_range(), prev.to_string());
38 },
39 )
35} 40}
36 41
37#[cfg(test)] 42#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs
index 0115adc8b..1234f4d29 100644
--- a/crates/ra_assists/src/handlers/flip_trait_bound.rs
+++ b/crates/ra_assists/src/handlers/flip_trait_bound.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4 Direction, T, 4 Direction, T,
5}; 5};
6 6
7use crate::{AssistContext, AssistId, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
8 8
9// Assist: flip_trait_bound 9// Assist: flip_trait_bound
10// 10//
@@ -33,10 +33,15 @@ pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext) -> Option
33 ); 33 );
34 34
35 let target = plus.text_range(); 35 let target = plus.text_range();
36 acc.add(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| { 36 acc.add(
37 edit.replace(before.text_range(), after.to_string()); 37 AssistId("flip_trait_bound", AssistKind::RefactorRewrite),
38 edit.replace(after.text_range(), before.to_string()); 38 "Flip trait bounds",
39 }) 39 target,
40 |edit| {
41 edit.replace(before.text_range(), after.to_string());
42 edit.replace(after.text_range(), before.to_string());
43 },
44 )
40} 45}
41 46
42#[cfg(test)] 47#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/generate_derive.rs
index b123b8498..6ccf39900 100644
--- a/crates/ra_assists/src/handlers/add_derive.rs
+++ b/crates/ra_assists/src/handlers/generate_derive.rs
@@ -4,9 +4,9 @@ use ra_syntax::{
4 TextSize, 4 TextSize,
5}; 5};
6 6
7use crate::{AssistContext, AssistId, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
8 8
9// Assist: add_derive 9// Assist: generate_derive
10// 10//
11// Adds a new `#[derive()]` clause to a struct or enum. 11// Adds a new `#[derive()]` clause to a struct or enum.
12// 12//
@@ -24,32 +24,37 @@ use crate::{AssistContext, AssistId, Assists};
24// y: u32, 24// y: u32,
25// } 25// }
26// ``` 26// ```
27pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 27pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 let cap = ctx.config.snippet_cap?; 28 let cap = ctx.config.snippet_cap?;
29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
30 let node_start = derive_insertion_offset(&nominal)?; 30 let node_start = derive_insertion_offset(&nominal)?;
31 let target = nominal.syntax().text_range(); 31 let target = nominal.syntax().text_range();
32 acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |builder| { 32 acc.add(
33 let derive_attr = nominal 33 AssistId("generate_derive", AssistKind::Generate),
34 .attrs() 34 "Add `#[derive]`",
35 .filter_map(|x| x.as_simple_call()) 35 target,
36 .filter(|(name, _arg)| name == "derive") 36 |builder| {
37 .map(|(_name, arg)| arg) 37 let derive_attr = nominal
38 .next(); 38 .attrs()
39 match derive_attr { 39 .filter_map(|x| x.as_simple_call())
40 None => { 40 .filter(|(name, _arg)| name == "derive")
41 builder.insert_snippet(cap, node_start, "#[derive($0)]\n"); 41 .map(|(_name, arg)| arg)
42 } 42 .next();
43 Some(tt) => { 43 match derive_attr {
44 // Just move the cursor. 44 None => {
45 builder.insert_snippet( 45 builder.insert_snippet(cap, node_start, "#[derive($0)]\n");
46 cap, 46 }
47 tt.syntax().text_range().end() - TextSize::of(')'), 47 Some(tt) => {
48 "$0", 48 // Just move the cursor.
49 ) 49 builder.insert_snippet(
50 } 50 cap,
51 }; 51 tt.syntax().text_range().end() - TextSize::of(')'),
52 }) 52 "$0",
53 )
54 }
55 };
56 },
57 )
53} 58}
54 59
55// Insert `derive` after doc comments. 60// Insert `derive` after doc comments.
@@ -70,12 +75,12 @@ mod tests {
70 #[test] 75 #[test]
71 fn add_derive_new() { 76 fn add_derive_new() {
72 check_assist( 77 check_assist(
73 add_derive, 78 generate_derive,
74 "struct Foo { a: i32, <|>}", 79 "struct Foo { a: i32, <|>}",
75 "#[derive($0)]\nstruct Foo { a: i32, }", 80 "#[derive($0)]\nstruct Foo { a: i32, }",
76 ); 81 );
77 check_assist( 82 check_assist(
78 add_derive, 83 generate_derive,
79 "struct Foo { <|> a: i32, }", 84 "struct Foo { <|> a: i32, }",
80 "#[derive($0)]\nstruct Foo { a: i32, }", 85 "#[derive($0)]\nstruct Foo { a: i32, }",
81 ); 86 );
@@ -84,7 +89,7 @@ mod tests {
84 #[test] 89 #[test]
85 fn add_derive_existing() { 90 fn add_derive_existing() {
86 check_assist( 91 check_assist(
87 add_derive, 92 generate_derive,
88 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", 93 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
89 "#[derive(Clone$0)]\nstruct Foo { a: i32, }", 94 "#[derive(Clone$0)]\nstruct Foo { a: i32, }",
90 ); 95 );
@@ -93,7 +98,7 @@ mod tests {
93 #[test] 98 #[test]
94 fn add_derive_new_with_doc_comment() { 99 fn add_derive_new_with_doc_comment() {
95 check_assist( 100 check_assist(
96 add_derive, 101 generate_derive,
97 " 102 "
98/// `Foo` is a pretty important struct. 103/// `Foo` is a pretty important struct.
99/// It does stuff. 104/// It does stuff.
@@ -111,7 +116,7 @@ struct Foo { a: i32, }
111 #[test] 116 #[test]
112 fn add_derive_target() { 117 fn add_derive_target() {
113 check_assist_target( 118 check_assist_target(
114 add_derive, 119 generate_derive,
115 " 120 "
116struct SomeThingIrrelevant; 121struct SomeThingIrrelevant;
117/// `Foo` is a pretty important struct. 122/// `Foo` is a pretty important struct.
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs
index 776bddf91..a347e3c2e 100644
--- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
+++ b/crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs
@@ -2,9 +2,9 @@ use ra_ide_db::RootDatabase;
2use ra_syntax::ast::{self, AstNode, NameOwner}; 2use ra_syntax::ast::{self, AstNode, NameOwner};
3use test_utils::mark; 3use test_utils::mark;
4 4
5use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; 5use crate::{utils::FamousDefs, AssistContext, AssistId, AssistKind, Assists};
6 6
7// Assist: add_from_impl_for_enum 7// Assist: generate_from_impl_for_enum
8// 8//
9// Adds a From impl for an enum variant with one tuple field. 9// Adds a From impl for an enum variant with one tuple field.
10// 10//
@@ -21,7 +21,7 @@ use crate::{utils::FamousDefs, AssistContext, AssistId, Assists};
21// } 21// }
22// } 22// }
23// ``` 23// ```
24pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 24pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
25 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; 25 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
26 let variant_name = variant.name()?; 26 let variant_name = variant.name()?;
27 let enum_name = variant.parent_enum().name()?; 27 let enum_name = variant.parent_enum().name()?;
@@ -45,8 +45,8 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) ->
45 45
46 let target = variant.syntax().text_range(); 46 let target = variant.syntax().text_range();
47 acc.add( 47 acc.add(
48 AssistId("add_from_impl_for_enum"), 48 AssistId("generate_from_impl_for_enum", AssistKind::Generate),
49 "Add From impl for this enum variant", 49 "Generate `From` impl for this enum variant",
50 target, 50 target,
51 |edit| { 51 |edit| {
52 let start_offset = variant.parent_enum().syntax().text_range().end(); 52 let start_offset = variant.parent_enum().syntax().text_range().end();
@@ -97,9 +97,9 @@ mod tests {
97 use super::*; 97 use super::*;
98 98
99 #[test] 99 #[test]
100 fn test_add_from_impl_for_enum() { 100 fn test_generate_from_impl_for_enum() {
101 check_assist( 101 check_assist(
102 add_from_impl_for_enum, 102 generate_from_impl_for_enum,
103 "enum A { <|>One(u32) }", 103 "enum A { <|>One(u32) }",
104 r#"enum A { One(u32) } 104 r#"enum A { One(u32) }
105 105
@@ -112,9 +112,9 @@ impl From<u32> for A {
112 } 112 }
113 113
114 #[test] 114 #[test]
115 fn test_add_from_impl_for_enum_complicated_path() { 115 fn test_generate_from_impl_for_enum_complicated_path() {
116 check_assist( 116 check_assist(
117 add_from_impl_for_enum, 117 generate_from_impl_for_enum,
118 r#"enum A { <|>One(foo::bar::baz::Boo) }"#, 118 r#"enum A { <|>One(foo::bar::baz::Boo) }"#,
119 r#"enum A { One(foo::bar::baz::Boo) } 119 r#"enum A { One(foo::bar::baz::Boo) }
120 120
@@ -128,8 +128,8 @@ impl From<foo::bar::baz::Boo> for A {
128 128
129 fn check_not_applicable(ra_fixture: &str) { 129 fn check_not_applicable(ra_fixture: &str) {
130 let fixture = 130 let fixture =
131 format!("//- main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); 131 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
132 check_assist_not_applicable(add_from_impl_for_enum, &fixture) 132 check_assist_not_applicable(generate_from_impl_for_enum, &fixture)
133 } 133 }
134 134
135 #[test] 135 #[test]
@@ -166,7 +166,7 @@ impl From<u32> for A {
166 #[test] 166 #[test]
167 fn test_add_from_impl_different_variant_impl_exists() { 167 fn test_add_from_impl_different_variant_impl_exists() {
168 check_assist( 168 check_assist(
169 add_from_impl_for_enum, 169 generate_from_impl_for_enum,
170 r#"enum A { <|>One(u32), Two(String), } 170 r#"enum A { <|>One(u32), Two(String), }
171 171
172impl From<String> for A { 172impl From<String> for A {
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/generate_function.rs
index 24f931a85..b721b96bb 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/generate_function.rs
@@ -13,10 +13,10 @@ use rustc_hash::{FxHashMap, FxHashSet};
13use crate::{ 13use crate::{
14 assist_config::SnippetCap, 14 assist_config::SnippetCap,
15 utils::{render_snippet, Cursor}, 15 utils::{render_snippet, Cursor},
16 AssistContext, AssistId, Assists, 16 AssistContext, AssistId, AssistKind, Assists,
17}; 17};
18 18
19// Assist: add_function 19// Assist: generate_function
20// 20//
21// Adds a stub function with a signature matching the function under the cursor. 21// Adds a stub function with a signature matching the function under the cursor.
22// 22//
@@ -41,7 +41,7 @@ use crate::{
41// } 41// }
42// 42//
43// ``` 43// ```
44pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 44pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
45 let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; 45 let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
46 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; 46 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
47 let path = path_expr.path()?; 47 let path = path_expr.path()?;
@@ -62,15 +62,20 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
62 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; 62 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
63 63
64 let target = call.syntax().text_range(); 64 let target = call.syntax().text_range();
65 acc.add(AssistId("add_function"), "Add function", target, |builder| { 65 acc.add(
66 let function_template = function_builder.render(); 66 AssistId("generate_function", AssistKind::Generate),
67 builder.set_file(function_template.file); 67 format!("Generate `{}` function", function_builder.fn_name),
68 let new_fn = function_template.to_string(ctx.config.snippet_cap); 68 target,
69 match ctx.config.snippet_cap { 69 |builder| {
70 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), 70 let function_template = function_builder.render();
71 None => builder.insert(function_template.insert_offset, new_fn), 71 builder.edit_file(function_template.file);
72 } 72 let new_fn = function_template.to_string(ctx.config.snippet_cap);
73 }) 73 match ctx.config.snippet_cap {
74 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
75 None => builder.insert(function_template.insert_offset, new_fn),
76 }
77 },
78 )
74} 79}
75 80
76struct FunctionTemplate { 81struct FunctionTemplate {
@@ -117,7 +122,7 @@ impl FunctionBuilder {
117 let mut file = ctx.frange.file_id; 122 let mut file = ctx.frange.file_id;
118 let target = match &target_module { 123 let target = match &target_module {
119 Some(target_module) => { 124 Some(target_module) => {
120 let module_source = target_module.definition_source(ctx.db); 125 let module_source = target_module.definition_source(ctx.db());
121 let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; 126 let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?;
122 file = in_file; 127 file = in_file;
123 target 128 target
@@ -269,7 +274,7 @@ fn fn_arg_type(
269 return None; 274 return None;
270 } 275 }
271 276
272 if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) { 277 if let Ok(rendered) = ty.display_source_code(ctx.db(), target_module.into()) {
273 Some(rendered) 278 Some(rendered)
274 } else { 279 } else {
275 None 280 None
@@ -333,7 +338,7 @@ mod tests {
333 #[test] 338 #[test]
334 fn add_function_with_no_args() { 339 fn add_function_with_no_args() {
335 check_assist( 340 check_assist(
336 add_function, 341 generate_function,
337 r" 342 r"
338fn foo() { 343fn foo() {
339 bar<|>(); 344 bar<|>();
@@ -356,7 +361,7 @@ fn bar() {
356 // This ensures that the function is correctly generated 361 // This ensures that the function is correctly generated
357 // in the next outer mod or file 362 // in the next outer mod or file
358 check_assist( 363 check_assist(
359 add_function, 364 generate_function,
360 r" 365 r"
361impl Foo { 366impl Foo {
362 fn foo() { 367 fn foo() {
@@ -382,7 +387,7 @@ fn bar() {
382 fn add_function_directly_after_current_block() { 387 fn add_function_directly_after_current_block() {
383 // The new fn should not be created at the end of the file or module 388 // The new fn should not be created at the end of the file or module
384 check_assist( 389 check_assist(
385 add_function, 390 generate_function,
386 r" 391 r"
387fn foo1() { 392fn foo1() {
388 bar<|>(); 393 bar<|>();
@@ -407,7 +412,7 @@ fn foo2() {}
407 #[test] 412 #[test]
408 fn add_function_with_no_args_in_same_module() { 413 fn add_function_with_no_args_in_same_module() {
409 check_assist( 414 check_assist(
410 add_function, 415 generate_function,
411 r" 416 r"
412mod baz { 417mod baz {
413 fn foo() { 418 fn foo() {
@@ -432,7 +437,7 @@ mod baz {
432 #[test] 437 #[test]
433 fn add_function_with_function_call_arg() { 438 fn add_function_with_function_call_arg() {
434 check_assist( 439 check_assist(
435 add_function, 440 generate_function,
436 r" 441 r"
437struct Baz; 442struct Baz;
438fn baz() -> Baz { todo!() } 443fn baz() -> Baz { todo!() }
@@ -457,7 +462,7 @@ fn bar(baz: Baz) {
457 #[test] 462 #[test]
458 fn add_function_with_method_call_arg() { 463 fn add_function_with_method_call_arg() {
459 check_assist( 464 check_assist(
460 add_function, 465 generate_function,
461 r" 466 r"
462struct Baz; 467struct Baz;
463impl Baz { 468impl Baz {
@@ -490,7 +495,7 @@ fn bar(baz: Baz) {
490 #[test] 495 #[test]
491 fn add_function_with_string_literal_arg() { 496 fn add_function_with_string_literal_arg() {
492 check_assist( 497 check_assist(
493 add_function, 498 generate_function,
494 r#" 499 r#"
495fn foo() { 500fn foo() {
496 <|>bar("bar") 501 <|>bar("bar")
@@ -511,7 +516,7 @@ fn bar(arg: &str) {
511 #[test] 516 #[test]
512 fn add_function_with_char_literal_arg() { 517 fn add_function_with_char_literal_arg() {
513 check_assist( 518 check_assist(
514 add_function, 519 generate_function,
515 r#" 520 r#"
516fn foo() { 521fn foo() {
517 <|>bar('x') 522 <|>bar('x')
@@ -532,7 +537,7 @@ fn bar(arg: char) {
532 #[test] 537 #[test]
533 fn add_function_with_int_literal_arg() { 538 fn add_function_with_int_literal_arg() {
534 check_assist( 539 check_assist(
535 add_function, 540 generate_function,
536 r" 541 r"
537fn foo() { 542fn foo() {
538 <|>bar(42) 543 <|>bar(42)
@@ -553,7 +558,7 @@ fn bar(arg: i32) {
553 #[test] 558 #[test]
554 fn add_function_with_cast_int_literal_arg() { 559 fn add_function_with_cast_int_literal_arg() {
555 check_assist( 560 check_assist(
556 add_function, 561 generate_function,
557 r" 562 r"
558fn foo() { 563fn foo() {
559 <|>bar(42 as u8) 564 <|>bar(42 as u8)
@@ -576,7 +581,7 @@ fn bar(arg: u8) {
576 // Ensures that the name of the cast type isn't used 581 // Ensures that the name of the cast type isn't used
577 // in the generated function signature. 582 // in the generated function signature.
578 check_assist( 583 check_assist(
579 add_function, 584 generate_function,
580 r" 585 r"
581fn foo() { 586fn foo() {
582 let x = 42; 587 let x = 42;
@@ -599,7 +604,7 @@ fn bar(x: u8) {
599 #[test] 604 #[test]
600 fn add_function_with_variable_arg() { 605 fn add_function_with_variable_arg() {
601 check_assist( 606 check_assist(
602 add_function, 607 generate_function,
603 r" 608 r"
604fn foo() { 609fn foo() {
605 let worble = (); 610 let worble = ();
@@ -622,7 +627,7 @@ fn bar(worble: ()) {
622 #[test] 627 #[test]
623 fn add_function_with_impl_trait_arg() { 628 fn add_function_with_impl_trait_arg() {
624 check_assist( 629 check_assist(
625 add_function, 630 generate_function,
626 r" 631 r"
627trait Foo {} 632trait Foo {}
628fn foo() -> impl Foo { 633fn foo() -> impl Foo {
@@ -651,7 +656,7 @@ fn bar(foo: impl Foo) {
651 #[test] 656 #[test]
652 fn borrowed_arg() { 657 fn borrowed_arg() {
653 check_assist( 658 check_assist(
654 add_function, 659 generate_function,
655 r" 660 r"
656struct Baz; 661struct Baz;
657fn baz() -> Baz { todo!() } 662fn baz() -> Baz { todo!() }
@@ -678,7 +683,7 @@ fn bar(baz: &Baz) {
678 #[test] 683 #[test]
679 fn add_function_with_qualified_path_arg() { 684 fn add_function_with_qualified_path_arg() {
680 check_assist( 685 check_assist(
681 add_function, 686 generate_function,
682 r" 687 r"
683mod Baz { 688mod Baz {
684 pub struct Bof; 689 pub struct Bof;
@@ -709,7 +714,7 @@ fn bar(baz: Baz::Bof) {
709 // FIXME fix printing the generics of a `Ty` to make this test pass 714 // FIXME fix printing the generics of a `Ty` to make this test pass
710 fn add_function_with_generic_arg() { 715 fn add_function_with_generic_arg() {
711 check_assist( 716 check_assist(
712 add_function, 717 generate_function,
713 r" 718 r"
714fn foo<T>(t: T) { 719fn foo<T>(t: T) {
715 <|>bar(t) 720 <|>bar(t)
@@ -732,7 +737,7 @@ fn bar<T>(t: T) {
732 // FIXME Fix function type printing to make this test pass 737 // FIXME Fix function type printing to make this test pass
733 fn add_function_with_fn_arg() { 738 fn add_function_with_fn_arg() {
734 check_assist( 739 check_assist(
735 add_function, 740 generate_function,
736 r" 741 r"
737struct Baz; 742struct Baz;
738impl Baz { 743impl Baz {
@@ -763,7 +768,7 @@ fn bar(arg: fn() -> Baz) {
763 // FIXME Fix closure type printing to make this test pass 768 // FIXME Fix closure type printing to make this test pass
764 fn add_function_with_closure_arg() { 769 fn add_function_with_closure_arg() {
765 check_assist( 770 check_assist(
766 add_function, 771 generate_function,
767 r" 772 r"
768fn foo() { 773fn foo() {
769 let closure = |x: i64| x - 1; 774 let closure = |x: i64| x - 1;
@@ -786,7 +791,7 @@ fn bar(closure: impl Fn(i64) -> i64) {
786 #[test] 791 #[test]
787 fn unresolveable_types_default_to_unit() { 792 fn unresolveable_types_default_to_unit() {
788 check_assist( 793 check_assist(
789 add_function, 794 generate_function,
790 r" 795 r"
791fn foo() { 796fn foo() {
792 <|>bar(baz) 797 <|>bar(baz)
@@ -807,7 +812,7 @@ fn bar(baz: ()) {
807 #[test] 812 #[test]
808 fn arg_names_dont_overlap() { 813 fn arg_names_dont_overlap() {
809 check_assist( 814 check_assist(
810 add_function, 815 generate_function,
811 r" 816 r"
812struct Baz; 817struct Baz;
813fn baz() -> Baz { Baz } 818fn baz() -> Baz { Baz }
@@ -832,7 +837,7 @@ fn bar(baz_1: Baz, baz_2: Baz) {
832 #[test] 837 #[test]
833 fn arg_name_counters_start_at_1_per_name() { 838 fn arg_name_counters_start_at_1_per_name() {
834 check_assist( 839 check_assist(
835 add_function, 840 generate_function,
836 r#" 841 r#"
837struct Baz; 842struct Baz;
838fn baz() -> Baz { Baz } 843fn baz() -> Baz { Baz }
@@ -857,7 +862,7 @@ fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) {
857 #[test] 862 #[test]
858 fn add_function_in_module() { 863 fn add_function_in_module() {
859 check_assist( 864 check_assist(
860 add_function, 865 generate_function,
861 r" 866 r"
862mod bar {} 867mod bar {}
863 868
@@ -885,7 +890,7 @@ fn foo() {
885 // See https://github.com/rust-analyzer/rust-analyzer/issues/1165 890 // See https://github.com/rust-analyzer/rust-analyzer/issues/1165
886 fn qualified_path_uses_correct_scope() { 891 fn qualified_path_uses_correct_scope() {
887 check_assist( 892 check_assist(
888 add_function, 893 generate_function,
889 " 894 "
890mod foo { 895mod foo {
891 pub struct Foo; 896 pub struct Foo;
@@ -916,7 +921,7 @@ fn baz(foo: foo::Foo) {
916 #[test] 921 #[test]
917 fn add_function_in_module_containing_other_items() { 922 fn add_function_in_module_containing_other_items() {
918 check_assist( 923 check_assist(
919 add_function, 924 generate_function,
920 r" 925 r"
921mod bar { 926mod bar {
922 fn something_else() {} 927 fn something_else() {}
@@ -945,7 +950,7 @@ fn foo() {
945 #[test] 950 #[test]
946 fn add_function_in_nested_module() { 951 fn add_function_in_nested_module() {
947 check_assist( 952 check_assist(
948 add_function, 953 generate_function,
949 r" 954 r"
950mod bar { 955mod bar {
951 mod baz {} 956 mod baz {}
@@ -974,7 +979,7 @@ fn foo() {
974 #[test] 979 #[test]
975 fn add_function_in_another_file() { 980 fn add_function_in_another_file() {
976 check_assist( 981 check_assist(
977 add_function, 982 generate_function,
978 r" 983 r"
979//- /main.rs 984//- /main.rs
980mod foo; 985mod foo;
@@ -996,7 +1001,7 @@ pub(crate) fn bar() {
996 #[test] 1001 #[test]
997 fn add_function_not_applicable_if_function_already_exists() { 1002 fn add_function_not_applicable_if_function_already_exists() {
998 check_assist_not_applicable( 1003 check_assist_not_applicable(
999 add_function, 1004 generate_function,
1000 r" 1005 r"
1001fn foo() { 1006fn foo() {
1002 bar<|>(); 1007 bar<|>();
@@ -1013,7 +1018,7 @@ fn bar() {}
1013 // bar is resolved, but baz isn't. 1018 // bar is resolved, but baz isn't.
1014 // The assist is only active if the cursor is on an unresolved path, 1019 // The assist is only active if the cursor is on an unresolved path,
1015 // but the assist should only be offered if the path is a function call. 1020 // but the assist should only be offered if the path is a function call.
1016 add_function, 1021 generate_function,
1017 r" 1022 r"
1018fn foo() { 1023fn foo() {
1019 bar(b<|>az); 1024 bar(b<|>az);
@@ -1028,7 +1033,7 @@ fn bar(baz: ()) {}
1028 #[ignore] 1033 #[ignore]
1029 fn create_method_with_no_args() { 1034 fn create_method_with_no_args() {
1030 check_assist( 1035 check_assist(
1031 add_function, 1036 generate_function,
1032 r" 1037 r"
1033struct Foo; 1038struct Foo;
1034impl Foo { 1039impl Foo {
diff --git a/crates/ra_assists/src/handlers/generate_impl.rs b/crates/ra_assists/src/handlers/generate_impl.rs
new file mode 100644
index 000000000..cbbac1d7f
--- /dev/null
+++ b/crates/ra_assists/src/handlers/generate_impl.rs
@@ -0,0 +1,109 @@
1use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
2use stdx::{format_to, SepBy};
3
4use crate::{AssistContext, AssistId, AssistKind, Assists};
5
6// Assist: generate_impl
7//
8// Adds a new inherent impl for a type.
9//
10// ```
11// struct Ctx<T: Clone> {
12// data: T,<|>
13// }
14// ```
15// ->
16// ```
17// struct Ctx<T: Clone> {
18// data: T,
19// }
20//
21// impl<T: Clone> Ctx<T> {
22// $0
23// }
24// ```
25pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
27 let name = nominal.name()?;
28 let target = nominal.syntax().text_range();
29 acc.add(
30 AssistId("generate_impl", AssistKind::Generate),
31 format!("Generate impl for `{}`", name),
32 target,
33 |edit| {
34 let type_params = nominal.type_param_list();
35 let start_offset = nominal.syntax().text_range().end();
36 let mut buf = String::new();
37 buf.push_str("\n\nimpl");
38 if let Some(type_params) = &type_params {
39 format_to!(buf, "{}", type_params.syntax());
40 }
41 buf.push_str(" ");
42 buf.push_str(name.text().as_str());
43 if let Some(type_params) = type_params {
44 let lifetime_params = type_params
45 .lifetime_params()
46 .filter_map(|it| it.lifetime_token())
47 .map(|it| it.text().clone());
48 let type_params = type_params
49 .type_params()
50 .filter_map(|it| it.name())
51 .map(|it| it.text().clone());
52
53 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
54 format_to!(buf, "<{}>", generic_params)
55 }
56 match ctx.config.snippet_cap {
57 Some(cap) => {
58 buf.push_str(" {\n $0\n}");
59 edit.insert_snippet(cap, start_offset, buf);
60 }
61 None => {
62 buf.push_str(" {\n}");
63 edit.insert(start_offset, buf);
64 }
65 }
66 },
67 )
68}
69
70#[cfg(test)]
71mod tests {
72 use crate::tests::{check_assist, check_assist_target};
73
74 use super::*;
75
76 #[test]
77 fn test_add_impl() {
78 check_assist(
79 generate_impl,
80 "struct Foo {<|>}\n",
81 "struct Foo {}\n\nimpl Foo {\n $0\n}\n",
82 );
83 check_assist(
84 generate_impl,
85 "struct Foo<T: Clone> {<|>}",
86 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}",
87 );
88 check_assist(
89 generate_impl,
90 "struct Foo<'a, T: Foo<'a>> {<|>}",
91 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}",
92 );
93 }
94
95 #[test]
96 fn add_impl_target() {
97 check_assist_target(
98 generate_impl,
99 "
100struct SomeThingIrrelevant;
101/// Has a lifetime parameter
102struct Foo<'a, T: Foo<'a>> {<|>}
103struct EvenMoreIrrelevant;
104",
105 "/// Has a lifetime parameter
106struct Foo<'a, T: Foo<'a>> {}",
107 );
108 }
109}
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/generate_new.rs
index 837aa8377..e27def1d8 100644
--- a/crates/ra_assists/src/handlers/add_new.rs
+++ b/crates/ra_assists/src/handlers/generate_new.rs
@@ -7,9 +7,9 @@ use ra_syntax::{
7}; 7};
8use stdx::{format_to, SepBy}; 8use stdx::{format_to, SepBy};
9 9
10use crate::{AssistContext, AssistId, Assists}; 10use crate::{AssistContext, AssistId, AssistKind, Assists};
11 11
12// Assist: add_new 12// Assist: generate_new
13// 13//
14// Adds a new inherent impl for a type. 14// Adds a new inherent impl for a type.
15// 15//
@@ -29,7 +29,7 @@ use crate::{AssistContext, AssistId, Assists};
29// } 29// }
30// 30//
31// ``` 31// ```
32pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 32pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
33 let strukt = ctx.find_node_at_offset::<ast::StructDef>()?; 33 let strukt = ctx.find_node_at_offset::<ast::StructDef>()?;
34 34
35 // We want to only apply this to non-union structs with named fields 35 // We want to only apply this to non-union structs with named fields
@@ -42,7 +42,7 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
42 let impl_def = find_struct_impl(&ctx, &strukt)?; 42 let impl_def = find_struct_impl(&ctx, &strukt)?;
43 43
44 let target = strukt.syntax().text_range(); 44 let target = strukt.syntax().text_range();
45 acc.add(AssistId("add_new"), "Add default constructor", target, |builder| { 45 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
46 let mut buf = String::with_capacity(512); 46 let mut buf = String::with_capacity(512);
47 47
48 if impl_def.is_some() { 48 if impl_def.is_some() {
@@ -122,7 +122,7 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String {
122// FIXME: change the new fn checking to a more semantic approach when that's more 122// FIXME: change the new fn checking to a more semantic approach when that's more
123// viable (e.g. we process proc macros, etc) 123// viable (e.g. we process proc macros, etc)
124fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> { 124fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> {
125 let db = ctx.db; 125 let db = ctx.db();
126 let module = strukt.syntax().ancestors().find(|node| { 126 let module = strukt.syntax().ancestors().find(|node| {
127 ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) 127 ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
128 })?; 128 })?;
@@ -181,10 +181,10 @@ mod tests {
181 181
182 #[test] 182 #[test]
183 #[rustfmt::skip] 183 #[rustfmt::skip]
184 fn test_add_new() { 184 fn test_generate_new() {
185 // Check output of generation 185 // Check output of generation
186 check_assist( 186 check_assist(
187 add_new, 187 generate_new,
188"struct Foo {<|>}", 188"struct Foo {<|>}",
189"struct Foo {} 189"struct Foo {}
190 190
@@ -194,7 +194,7 @@ impl Foo {
194", 194",
195 ); 195 );
196 check_assist( 196 check_assist(
197 add_new, 197 generate_new,
198"struct Foo<T: Clone> {<|>}", 198"struct Foo<T: Clone> {<|>}",
199"struct Foo<T: Clone> {} 199"struct Foo<T: Clone> {}
200 200
@@ -204,7 +204,7 @@ impl<T: Clone> Foo<T> {
204", 204",
205 ); 205 );
206 check_assist( 206 check_assist(
207 add_new, 207 generate_new,
208"struct Foo<'a, T: Foo<'a>> {<|>}", 208"struct Foo<'a, T: Foo<'a>> {<|>}",
209"struct Foo<'a, T: Foo<'a>> {} 209"struct Foo<'a, T: Foo<'a>> {}
210 210
@@ -214,7 +214,7 @@ impl<'a, T: Foo<'a>> Foo<'a, T> {
214", 214",
215 ); 215 );
216 check_assist( 216 check_assist(
217 add_new, 217 generate_new,
218"struct Foo { baz: String <|>}", 218"struct Foo { baz: String <|>}",
219"struct Foo { baz: String } 219"struct Foo { baz: String }
220 220
@@ -224,7 +224,7 @@ impl Foo {
224", 224",
225 ); 225 );
226 check_assist( 226 check_assist(
227 add_new, 227 generate_new,
228"struct Foo { baz: String, qux: Vec<i32> <|>}", 228"struct Foo { baz: String, qux: Vec<i32> <|>}",
229"struct Foo { baz: String, qux: Vec<i32> } 229"struct Foo { baz: String, qux: Vec<i32> }
230 230
@@ -236,7 +236,7 @@ impl Foo {
236 236
237 // Check that visibility modifiers don't get brought in for fields 237 // Check that visibility modifiers don't get brought in for fields
238 check_assist( 238 check_assist(
239 add_new, 239 generate_new,
240"struct Foo { pub baz: String, pub qux: Vec<i32> <|>}", 240"struct Foo { pub baz: String, pub qux: Vec<i32> <|>}",
241"struct Foo { pub baz: String, pub qux: Vec<i32> } 241"struct Foo { pub baz: String, pub qux: Vec<i32> }
242 242
@@ -248,7 +248,7 @@ impl Foo {
248 248
249 // Check that it reuses existing impls 249 // Check that it reuses existing impls
250 check_assist( 250 check_assist(
251 add_new, 251 generate_new,
252"struct Foo {<|>} 252"struct Foo {<|>}
253 253
254impl Foo {} 254impl Foo {}
@@ -261,7 +261,7 @@ impl Foo {
261", 261",
262 ); 262 );
263 check_assist( 263 check_assist(
264 add_new, 264 generate_new,
265"struct Foo {<|>} 265"struct Foo {<|>}
266 266
267impl Foo { 267impl Foo {
@@ -279,7 +279,7 @@ impl Foo {
279 ); 279 );
280 280
281 check_assist( 281 check_assist(
282 add_new, 282 generate_new,
283"struct Foo {<|>} 283"struct Foo {<|>}
284 284
285impl Foo { 285impl Foo {
@@ -304,7 +304,7 @@ impl Foo {
304 304
305 // Check visibility of new fn based on struct 305 // Check visibility of new fn based on struct
306 check_assist( 306 check_assist(
307 add_new, 307 generate_new,
308"pub struct Foo {<|>}", 308"pub struct Foo {<|>}",
309"pub struct Foo {} 309"pub struct Foo {}
310 310
@@ -314,7 +314,7 @@ impl Foo {
314", 314",
315 ); 315 );
316 check_assist( 316 check_assist(
317 add_new, 317 generate_new,
318"pub(crate) struct Foo {<|>}", 318"pub(crate) struct Foo {<|>}",
319"pub(crate) struct Foo {} 319"pub(crate) struct Foo {}
320 320
@@ -326,9 +326,9 @@ impl Foo {
326 } 326 }
327 327
328 #[test] 328 #[test]
329 fn add_new_not_applicable_if_fn_exists() { 329 fn generate_new_not_applicable_if_fn_exists() {
330 check_assist_not_applicable( 330 check_assist_not_applicable(
331 add_new, 331 generate_new,
332 " 332 "
333struct Foo {<|>} 333struct Foo {<|>}
334 334
@@ -340,7 +340,7 @@ impl Foo {
340 ); 340 );
341 341
342 check_assist_not_applicable( 342 check_assist_not_applicable(
343 add_new, 343 generate_new,
344 " 344 "
345struct Foo {<|>} 345struct Foo {<|>}
346 346
@@ -353,9 +353,9 @@ impl Foo {
353 } 353 }
354 354
355 #[test] 355 #[test]
356 fn add_new_target() { 356 fn generate_new_target() {
357 check_assist_target( 357 check_assist_target(
358 add_new, 358 generate_new,
359 " 359 "
360struct SomeThingIrrelevant; 360struct SomeThingIrrelevant;
361/// Has a lifetime parameter 361/// Has a lifetime parameter
@@ -370,7 +370,7 @@ struct Foo<'a, T: Foo<'a>> {}",
370 #[test] 370 #[test]
371 fn test_unrelated_new() { 371 fn test_unrelated_new() {
372 check_assist( 372 check_assist(
373 add_new, 373 generate_new,
374 r##" 374 r##"
375pub struct AstId<N: AstNode> { 375pub struct AstId<N: AstNode> {
376 file_id: HirFileId, 376 file_id: HirFileId,
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs
index d26e68847..2fdfabaf5 100644
--- a/crates/ra_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ra_assists/src/handlers/inline_local_variable.rs
@@ -7,7 +7,7 @@ use test_utils::mark;
7 7
8use crate::{ 8use crate::{
9 assist_context::{AssistContext, Assists}, 9 assist_context::{AssistContext, Assists},
10 AssistId, 10 AssistId, AssistKind,
11}; 11};
12 12
13// Assist: inline_local_variable 13// Assist: inline_local_variable
@@ -44,7 +44,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
44 44
45 let def = ctx.sema.to_def(&bind_pat)?; 45 let def = ctx.sema.to_def(&bind_pat)?;
46 let def = Definition::Local(def); 46 let def = Definition::Local(def);
47 let refs = def.find_usages(ctx.db, None); 47 let refs = def.find_usages(&ctx.sema, None);
48 if refs.is_empty() { 48 if refs.is_empty() {
49 mark::hit!(test_not_applicable_if_variable_unused); 49 mark::hit!(test_not_applicable_if_variable_unused);
50 return None; 50 return None;
@@ -110,13 +110,19 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
110 let init_in_paren = format!("({})", &init_str); 110 let init_in_paren = format!("({})", &init_str);
111 111
112 let target = bind_pat.syntax().text_range(); 112 let target = bind_pat.syntax().text_range();
113 acc.add(AssistId("inline_local_variable"), "Inline variable", target, move |builder| { 113 acc.add(
114 builder.delete(delete_range); 114 AssistId("inline_local_variable", AssistKind::RefactorInline),
115 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { 115 "Inline variable",
116 let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 116 target,
117 builder.replace(desc.file_range.range, replacement) 117 move |builder| {
118 } 118 builder.delete(delete_range);
119 }) 119 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) {
120 let replacement =
121 if should_wrap { init_in_paren.clone() } else { init_str.clone() };
122 builder.replace(desc.file_range.range, replacement)
123 }
124 },
125 )
120} 126}
121 127
122#[cfg(test)] 128#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
index 999aec421..967593031 100644
--- a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
+++ b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
@@ -1,12 +1,15 @@
1use crate::{assist_context::AssistBuilder, AssistContext, AssistId, Assists}; 1use ra_syntax::{
2use ast::{NameOwner, ParamList, TypeAscriptionOwner, TypeParamList, TypeRef}; 2 ast::{self, NameOwner, TypeAscriptionOwner, TypeParamsOwner},
3use ra_syntax::{ast, ast::TypeParamsOwner, AstNode, SyntaxKind, TextRange, TextSize}; 3 AstNode, SyntaxKind, TextRange, TextSize,
4};
4use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
5 6
6static ASSIST_NAME: &str = "change_lifetime_anon_to_named"; 7use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists};
7static ASSIST_LABEL: &str = "Give anonymous lifetime a name";
8 8
9// Assist: change_lifetime_anon_to_named 9static ASSIST_NAME: &str = "introduce_named_lifetime";
10static ASSIST_LABEL: &str = "Introduce named lifetime";
11
12// Assist: introduce_named_lifetime
10// 13//
11// Change an anonymous lifetime to a named lifetime. 14// Change an anonymous lifetime to a named lifetime.
12// 15//
@@ -31,15 +34,13 @@ static ASSIST_LABEL: &str = "Give anonymous lifetime a name";
31// ``` 34// ```
32// 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?
33// FIXME: should also add support for the case fun(f: &Foo) -> &<|>Foo 36// FIXME: should also add support for the case fun(f: &Foo) -> &<|>Foo
34pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 37pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
35 let lifetime_token = ctx 38 let lifetime_token = ctx
36 .find_token_at_offset(SyntaxKind::LIFETIME) 39 .find_token_at_offset(SyntaxKind::LIFETIME)
37 .filter(|lifetime| lifetime.text() == "'_")?; 40 .filter(|lifetime| lifetime.text() == "'_")?;
38 if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) { 41 if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) {
39 generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range()) 42 generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range())
40 } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) { 43 } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) {
41 // only allow naming the last anonymous lifetime
42 lifetime_token.next_token().filter(|tok| tok.kind() == SyntaxKind::R_ANGLE)?;
43 generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range()) 44 generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range())
44 } else { 45 } else {
45 None 46 None
@@ -52,7 +53,7 @@ fn generate_fn_def_assist(
52 fn_def: &ast::FnDef, 53 fn_def: &ast::FnDef,
53 lifetime_loc: TextRange, 54 lifetime_loc: TextRange,
54) -> Option<()> { 55) -> Option<()> {
55 let param_list: ParamList = fn_def.param_list()?; 56 let param_list: ast::ParamList = fn_def.param_list()?;
56 let new_lifetime_param = generate_unique_lifetime_param_name(&fn_def.type_param_list())?; 57 let new_lifetime_param = generate_unique_lifetime_param_name(&fn_def.type_param_list())?;
57 let end_of_fn_ident = fn_def.name()?.ident_token()?.text_range().end(); 58 let end_of_fn_ident = fn_def.name()?.ident_token()?.text_range().end();
58 let self_param = 59 let self_param =
@@ -67,7 +68,7 @@ fn generate_fn_def_assist(
67 let fn_params_without_lifetime: Vec<_> = param_list 68 let fn_params_without_lifetime: Vec<_> = param_list
68 .params() 69 .params()
69 .filter_map(|param| match param.ascribed_type() { 70 .filter_map(|param| match param.ascribed_type() {
70 Some(TypeRef::ReferenceType(ascribed_type)) 71 Some(ast::TypeRef::ReferenceType(ascribed_type))
71 if ascribed_type.lifetime_token() == None => 72 if ascribed_type.lifetime_token() == None =>
72 { 73 {
73 Some(ascribed_type.amp_token()?.text_range().end()) 74 Some(ascribed_type.amp_token()?.text_range().end())
@@ -82,7 +83,7 @@ fn generate_fn_def_assist(
82 _ => return None, 83 _ => return None,
83 } 84 }
84 }; 85 };
85 acc.add(AssistId(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| { 86 acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| {
86 add_lifetime_param(fn_def, builder, end_of_fn_ident, new_lifetime_param); 87 add_lifetime_param(fn_def, builder, end_of_fn_ident, new_lifetime_param);
87 builder.replace(lifetime_loc, format!("'{}", new_lifetime_param)); 88 builder.replace(lifetime_loc, format!("'{}", new_lifetime_param));
88 loc_needing_lifetime.map(|loc| builder.insert(loc, format!("'{} ", new_lifetime_param))); 89 loc_needing_lifetime.map(|loc| builder.insert(loc, format!("'{} ", new_lifetime_param)));
@@ -97,7 +98,7 @@ fn generate_impl_def_assist(
97) -> Option<()> { 98) -> Option<()> {
98 let new_lifetime_param = generate_unique_lifetime_param_name(&impl_def.type_param_list())?; 99 let new_lifetime_param = generate_unique_lifetime_param_name(&impl_def.type_param_list())?;
99 let end_of_impl_kw = impl_def.impl_token()?.text_range().end(); 100 let end_of_impl_kw = impl_def.impl_token()?.text_range().end();
100 acc.add(AssistId(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| { 101 acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| {
101 add_lifetime_param(impl_def, builder, end_of_impl_kw, new_lifetime_param); 102 add_lifetime_param(impl_def, builder, end_of_impl_kw, new_lifetime_param);
102 builder.replace(lifetime_loc, format!("'{}", new_lifetime_param)); 103 builder.replace(lifetime_loc, format!("'{}", new_lifetime_param));
103 }) 104 })
@@ -106,7 +107,7 @@ fn generate_impl_def_assist(
106/// Given a type parameter list, generate a unique lifetime parameter name 107/// Given a type parameter list, generate a unique lifetime parameter name
107/// which is not in the list 108/// which is not in the list
108fn generate_unique_lifetime_param_name( 109fn generate_unique_lifetime_param_name(
109 existing_type_param_list: &Option<TypeParamList>, 110 existing_type_param_list: &Option<ast::TypeParamList>,
110) -> Option<char> { 111) -> Option<char> {
111 match existing_type_param_list { 112 match existing_type_param_list {
112 Some(type_params) => { 113 Some(type_params) => {
@@ -151,7 +152,7 @@ mod tests {
151 #[test] 152 #[test]
152 fn test_example_case() { 153 fn test_example_case() {
153 check_assist( 154 check_assist(
154 change_lifetime_anon_to_named, 155 introduce_named_lifetime,
155 r#"impl Cursor<'_<|>> { 156 r#"impl Cursor<'_<|>> {
156 fn node(self) -> &SyntaxNode { 157 fn node(self) -> &SyntaxNode {
157 match self { 158 match self {
@@ -172,7 +173,7 @@ mod tests {
172 #[test] 173 #[test]
173 fn test_example_case_simplified() { 174 fn test_example_case_simplified() {
174 check_assist( 175 check_assist(
175 change_lifetime_anon_to_named, 176 introduce_named_lifetime,
176 r#"impl Cursor<'_<|>> {"#, 177 r#"impl Cursor<'_<|>> {"#,
177 r#"impl<'a> Cursor<'a> {"#, 178 r#"impl<'a> Cursor<'a> {"#,
178 ); 179 );
@@ -181,16 +182,33 @@ mod tests {
181 #[test] 182 #[test]
182 fn test_example_case_cursor_after_tick() { 183 fn test_example_case_cursor_after_tick() {
183 check_assist( 184 check_assist(
184 change_lifetime_anon_to_named, 185 introduce_named_lifetime,
185 r#"impl Cursor<'<|>_> {"#, 186 r#"impl Cursor<'<|>_> {"#,
186 r#"impl<'a> Cursor<'a> {"#, 187 r#"impl<'a> Cursor<'a> {"#,
187 ); 188 );
188 } 189 }
189 190
190 #[test] 191 #[test]
192 fn test_impl_with_other_type_param() {
193 check_assist(
194 introduce_named_lifetime,
195 "impl<I> fmt::Display for SepByBuilder<'_<|>, I>
196 where
197 I: Iterator,
198 I::Item: fmt::Display,
199 {",
200 "impl<I, 'a> fmt::Display for SepByBuilder<'a, I>
201 where
202 I: Iterator,
203 I::Item: fmt::Display,
204 {",
205 )
206 }
207
208 #[test]
191 fn test_example_case_cursor_before_tick() { 209 fn test_example_case_cursor_before_tick() {
192 check_assist( 210 check_assist(
193 change_lifetime_anon_to_named, 211 introduce_named_lifetime,
194 r#"impl Cursor<<|>'_> {"#, 212 r#"impl Cursor<<|>'_> {"#,
195 r#"impl<'a> Cursor<'a> {"#, 213 r#"impl<'a> Cursor<'a> {"#,
196 ); 214 );
@@ -198,23 +216,20 @@ mod tests {
198 216
199 #[test] 217 #[test]
200 fn test_not_applicable_cursor_position() { 218 fn test_not_applicable_cursor_position() {
201 check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'_><|> {"#); 219 check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<'_><|> {"#);
202 check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<|><'_> {"#); 220 check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<|><'_> {"#);
203 } 221 }
204 222
205 #[test] 223 #[test]
206 fn test_not_applicable_lifetime_already_name() { 224 fn test_not_applicable_lifetime_already_name() {
207 check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'a<|>> {"#); 225 check_assist_not_applicable(introduce_named_lifetime, r#"impl Cursor<'a<|>> {"#);
208 check_assist_not_applicable( 226 check_assist_not_applicable(introduce_named_lifetime, r#"fn my_fun<'a>() -> X<'a<|>>"#);
209 change_lifetime_anon_to_named,
210 r#"fn my_fun<'a>() -> X<'a<|>>"#,
211 );
212 } 227 }
213 228
214 #[test] 229 #[test]
215 fn test_with_type_parameter() { 230 fn test_with_type_parameter() {
216 check_assist( 231 check_assist(
217 change_lifetime_anon_to_named, 232 introduce_named_lifetime,
218 r#"impl<T> Cursor<T, '_<|>>"#, 233 r#"impl<T> Cursor<T, '_<|>>"#,
219 r#"impl<T, 'a> Cursor<T, 'a>"#, 234 r#"impl<T, 'a> Cursor<T, 'a>"#,
220 ); 235 );
@@ -223,7 +238,7 @@ mod tests {
223 #[test] 238 #[test]
224 fn test_with_existing_lifetime_name_conflict() { 239 fn test_with_existing_lifetime_name_conflict() {
225 check_assist( 240 check_assist(
226 change_lifetime_anon_to_named, 241 introduce_named_lifetime,
227 r#"impl<'a, 'b> Cursor<'a, 'b, '_<|>>"#, 242 r#"impl<'a, 'b> Cursor<'a, 'b, '_<|>>"#,
228 r#"impl<'a, 'b, 'c> Cursor<'a, 'b, 'c>"#, 243 r#"impl<'a, 'b, 'c> Cursor<'a, 'b, 'c>"#,
229 ); 244 );
@@ -232,7 +247,7 @@ mod tests {
232 #[test] 247 #[test]
233 fn test_function_return_value_anon_lifetime_param() { 248 fn test_function_return_value_anon_lifetime_param() {
234 check_assist( 249 check_assist(
235 change_lifetime_anon_to_named, 250 introduce_named_lifetime,
236 r#"fn my_fun() -> X<'_<|>>"#, 251 r#"fn my_fun() -> X<'_<|>>"#,
237 r#"fn my_fun<'a>() -> X<'a>"#, 252 r#"fn my_fun<'a>() -> X<'a>"#,
238 ); 253 );
@@ -241,7 +256,7 @@ mod tests {
241 #[test] 256 #[test]
242 fn test_function_return_value_anon_reference_lifetime() { 257 fn test_function_return_value_anon_reference_lifetime() {
243 check_assist( 258 check_assist(
244 change_lifetime_anon_to_named, 259 introduce_named_lifetime,
245 r#"fn my_fun() -> &'_<|> X"#, 260 r#"fn my_fun() -> &'_<|> X"#,
246 r#"fn my_fun<'a>() -> &'a X"#, 261 r#"fn my_fun<'a>() -> &'a X"#,
247 ); 262 );
@@ -250,7 +265,7 @@ mod tests {
250 #[test] 265 #[test]
251 fn test_function_param_anon_lifetime() { 266 fn test_function_param_anon_lifetime() {
252 check_assist( 267 check_assist(
253 change_lifetime_anon_to_named, 268 introduce_named_lifetime,
254 r#"fn my_fun(x: X<'_<|>>)"#, 269 r#"fn my_fun(x: X<'_<|>>)"#,
255 r#"fn my_fun<'a>(x: X<'a>)"#, 270 r#"fn my_fun<'a>(x: X<'a>)"#,
256 ); 271 );
@@ -259,7 +274,7 @@ mod tests {
259 #[test] 274 #[test]
260 fn test_function_add_lifetime_to_params() { 275 fn test_function_add_lifetime_to_params() {
261 check_assist( 276 check_assist(
262 change_lifetime_anon_to_named, 277 introduce_named_lifetime,
263 r#"fn my_fun(f: &Foo) -> X<'_<|>>"#, 278 r#"fn my_fun(f: &Foo) -> X<'_<|>>"#,
264 r#"fn my_fun<'a>(f: &'a Foo) -> X<'a>"#, 279 r#"fn my_fun<'a>(f: &'a Foo) -> X<'a>"#,
265 ); 280 );
@@ -268,7 +283,7 @@ mod tests {
268 #[test] 283 #[test]
269 fn test_function_add_lifetime_to_params_in_presence_of_other_lifetime() { 284 fn test_function_add_lifetime_to_params_in_presence_of_other_lifetime() {
270 check_assist( 285 check_assist(
271 change_lifetime_anon_to_named, 286 introduce_named_lifetime,
272 r#"fn my_fun<'other>(f: &Foo, b: &'other Bar) -> X<'_<|>>"#, 287 r#"fn my_fun<'other>(f: &Foo, b: &'other Bar) -> X<'_<|>>"#,
273 r#"fn my_fun<'other, 'a>(f: &'a Foo, b: &'other Bar) -> X<'a>"#, 288 r#"fn my_fun<'other, 'a>(f: &'a Foo, b: &'other Bar) -> X<'a>"#,
274 ); 289 );
@@ -278,7 +293,7 @@ mod tests {
278 fn test_function_not_applicable_without_self_and_multiple_unnamed_param_lifetimes() { 293 fn test_function_not_applicable_without_self_and_multiple_unnamed_param_lifetimes() {
279 // this is not permitted under lifetime elision rules 294 // this is not permitted under lifetime elision rules
280 check_assist_not_applicable( 295 check_assist_not_applicable(
281 change_lifetime_anon_to_named, 296 introduce_named_lifetime,
282 r#"fn my_fun(f: &Foo, b: &Bar) -> X<'_<|>>"#, 297 r#"fn my_fun(f: &Foo, b: &Bar) -> X<'_<|>>"#,
283 ); 298 );
284 } 299 }
@@ -286,7 +301,7 @@ mod tests {
286 #[test] 301 #[test]
287 fn test_function_add_lifetime_to_self_ref_param() { 302 fn test_function_add_lifetime_to_self_ref_param() {
288 check_assist( 303 check_assist(
289 change_lifetime_anon_to_named, 304 introduce_named_lifetime,
290 r#"fn my_fun<'other>(&self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#, 305 r#"fn my_fun<'other>(&self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#,
291 r#"fn my_fun<'other, 'a>(&'a self, f: &Foo, b: &'other Bar) -> X<'a>"#, 306 r#"fn my_fun<'other, 'a>(&'a self, f: &Foo, b: &'other Bar) -> X<'a>"#,
292 ); 307 );
@@ -295,7 +310,7 @@ mod tests {
295 #[test] 310 #[test]
296 fn test_function_add_lifetime_to_param_with_non_ref_self() { 311 fn test_function_add_lifetime_to_param_with_non_ref_self() {
297 check_assist( 312 check_assist(
298 change_lifetime_anon_to_named, 313 introduce_named_lifetime,
299 r#"fn my_fun<'other>(self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#, 314 r#"fn my_fun<'other>(self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#,
300 r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#, 315 r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#,
301 ); 316 );
diff --git a/crates/ra_assists/src/handlers/invert_if.rs b/crates/ra_assists/src/handlers/invert_if.rs
index 59d278eb9..bbe3f3643 100644
--- a/crates/ra_assists/src/handlers/invert_if.rs
+++ b/crates/ra_assists/src/handlers/invert_if.rs
@@ -6,7 +6,7 @@ use ra_syntax::{
6use crate::{ 6use crate::{
7 assist_context::{AssistContext, Assists}, 7 assist_context::{AssistContext, Assists},
8 utils::invert_boolean_expression, 8 utils::invert_boolean_expression,
9 AssistId, 9 AssistId, AssistKind,
10}; 10};
11 11
12// Assist: invert_if 12// Assist: invert_if
@@ -54,7 +54,7 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
54 let else_node = else_block.syntax(); 54 let else_node = else_block.syntax();
55 let else_range = else_node.text_range(); 55 let else_range = else_node.text_range();
56 let then_range = then_node.text_range(); 56 let then_range = then_node.text_range();
57 acc.add(AssistId("invert_if"), "Invert if", if_range, |edit| { 57 acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| {
58 edit.replace(cond_range, flip_cond.syntax().text()); 58 edit.replace(cond_range, flip_cond.syntax().text());
59 edit.replace(else_range, then_node.text()); 59 edit.replace(else_range, then_node.text());
60 edit.replace(then_range, else_node.text()); 60 edit.replace(then_range, else_node.text());
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs
index 972d16241..1beccb61c 100644
--- a/crates/ra_assists/src/handlers/merge_imports.rs
+++ b/crates/ra_assists/src/handlers/merge_imports.rs
@@ -8,7 +8,7 @@ use ra_syntax::{
8 8
9use crate::{ 9use crate::{
10 assist_context::{AssistContext, Assists}, 10 assist_context::{AssistContext, Assists},
11 AssistId, 11 AssistId, AssistKind,
12}; 12};
13 13
14// Assist: merge_imports 14// Assist: merge_imports
@@ -56,9 +56,14 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
56 }; 56 };
57 57
58 let target = tree.syntax().text_range(); 58 let target = tree.syntax().text_range();
59 acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| { 59 acc.add(
60 builder.rewrite(rewriter); 60 AssistId("merge_imports", AssistKind::RefactorRewrite),
61 }) 61 "Merge imports",
62 target,
63 |builder| {
64 builder.rewrite(rewriter);
65 },
66 )
62} 67}
63 68
64fn next_prev() -> impl Iterator<Item = Direction> { 69fn next_prev() -> impl Iterator<Item = Direction> {
@@ -127,7 +132,7 @@ fn first_path(path: &ast::Path) -> ast::Path {
127 132
128#[cfg(test)] 133#[cfg(test)]
129mod tests { 134mod tests {
130 use crate::tests::check_assist; 135 use crate::tests::{check_assist, check_assist_not_applicable};
131 136
132 use super::*; 137 use super::*;
133 138
@@ -276,4 +281,14 @@ bar::baz};
276", 281",
277 ) 282 )
278 } 283 }
284
285 #[test]
286 fn test_empty_use() {
287 check_assist_not_applicable(
288 merge_imports,
289 r"
290use std::<|>
291fn main() {}",
292 );
293 }
279} 294}
diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs
index ca04ec671..186a1f618 100644
--- a/crates/ra_assists/src/handlers/merge_match_arms.rs
+++ b/crates/ra_assists/src/handlers/merge_match_arms.rs
@@ -6,7 +6,7 @@ use ra_syntax::{
6 Direction, 6 Direction,
7}; 7};
8 8
9use crate::{AssistContext, AssistId, Assists, TextRange}; 9use crate::{AssistContext, AssistId, AssistKind, Assists, TextRange};
10 10
11// Assist: merge_match_arms 11// Assist: merge_match_arms
12// 12//
@@ -59,32 +59,34 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option
59 return None; 59 return None;
60 } 60 }
61 61
62 acc.add(AssistId("merge_match_arms"), "Merge match arms", current_text_range, |edit| { 62 acc.add(
63 let pats = if arms_to_merge.iter().any(contains_placeholder) { 63 AssistId("merge_match_arms", AssistKind::RefactorRewrite),
64 "_".into() 64 "Merge match arms",
65 } else { 65 current_text_range,
66 arms_to_merge 66 |edit| {
67 .iter() 67 let pats = if arms_to_merge.iter().any(contains_placeholder) {
68 .filter_map(ast::MatchArm::pat) 68 "_".into()
69 .map(|x| x.syntax().to_string()) 69 } else {
70 .collect::<Vec<String>>() 70 arms_to_merge
71 .join(" | ") 71 .iter()
72 }; 72 .filter_map(ast::MatchArm::pat)
73 73 .map(|x| x.syntax().to_string())
74 let arm = format!("{} => {}", pats, current_expr.syntax().text()); 74 .collect::<Vec<String>>()
75 75 .join(" | ")
76 let start = arms_to_merge.first().unwrap().syntax().text_range().start(); 76 };
77 let end = arms_to_merge.last().unwrap().syntax().text_range().end(); 77
78 78 let arm = format!("{} => {}", pats, current_expr.syntax().text());
79 edit.replace(TextRange::new(start, end), arm); 79
80 }) 80 let start = arms_to_merge.first().unwrap().syntax().text_range().start();
81 let end = arms_to_merge.last().unwrap().syntax().text_range().end();
82
83 edit.replace(TextRange::new(start, end), arm);
84 },
85 )
81} 86}
82 87
83fn contains_placeholder(a: &ast::MatchArm) -> bool { 88fn contains_placeholder(a: &ast::MatchArm) -> bool {
84 match a.pat() { 89 matches!(a.pat(), Some(ast::Pat::PlaceholderPat(..)))
85 Some(ra_syntax::ast::Pat::PlaceholderPat(..)) => true,
86 _ => false,
87 }
88} 90}
89 91
90#[cfg(test)] 92#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/move_bounds.rs b/crates/ra_assists/src/handlers/move_bounds.rs
index be2a7eddc..ba3dafb99 100644
--- a/crates/ra_assists/src/handlers/move_bounds.rs
+++ b/crates/ra_assists/src/handlers/move_bounds.rs
@@ -5,7 +5,7 @@ use ra_syntax::{
5 T, 5 T,
6}; 6};
7 7
8use crate::{AssistContext, AssistId, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
9 9
10// Assist: move_bounds_to_where_clause 10// Assist: move_bounds_to_where_clause
11// 11//
@@ -50,29 +50,36 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext
50 }; 50 };
51 51
52 let target = type_param_list.syntax().text_range(); 52 let target = type_param_list.syntax().text_range();
53 acc.add(AssistId("move_bounds_to_where_clause"), "Move to where clause", target, |edit| { 53 acc.add(
54 let new_params = type_param_list 54 AssistId("move_bounds_to_where_clause", AssistKind::RefactorRewrite),
55 .type_params() 55 "Move to where clause",
56 .filter(|it| it.type_bound_list().is_some()) 56 target,
57 .map(|type_param| { 57 |edit| {
58 let without_bounds = type_param.remove_bounds(); 58 let new_params = type_param_list
59 (type_param, without_bounds) 59 .type_params()
60 }); 60 .filter(|it| it.type_bound_list().is_some())
61 61 .map(|type_param| {
62 let new_type_param_list = type_param_list.replace_descendants(new_params); 62 let without_bounds = type_param.remove_bounds();
63 edit.replace_ast(type_param_list.clone(), new_type_param_list); 63 (type_param, without_bounds)
64 64 });
65 let where_clause = { 65
66 let predicates = type_param_list.type_params().filter_map(build_predicate); 66 let new_type_param_list = type_param_list.replace_descendants(new_params);
67 make::where_clause(predicates) 67 edit.replace_ast(type_param_list.clone(), new_type_param_list);
68 }; 68
69 69 let where_clause = {
70 let to_insert = match anchor.prev_sibling_or_token() { 70 let predicates = type_param_list.type_params().filter_map(build_predicate);
71 Some(ref elem) if elem.kind() == WHITESPACE => format!("{} ", where_clause.syntax()), 71 make::where_clause(predicates)
72 _ => format!(" {}", where_clause.syntax()), 72 };
73 }; 73
74 edit.insert(anchor.text_range().start(), to_insert); 74 let to_insert = match anchor.prev_sibling_or_token() {
75 }) 75 Some(ref elem) if elem.kind() == WHITESPACE => {
76 format!("{} ", where_clause.syntax())
77 }
78 _ => format!(" {}", where_clause.syntax()),
79 };
80 edit.insert(anchor.text_range().start(), to_insert);
81 },
82 )
76} 83}
77 84
78fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { 85fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs
index 7edcf0748..4060d34c6 100644
--- a/crates/ra_assists/src/handlers/move_guard.rs
+++ b/crates/ra_assists/src/handlers/move_guard.rs
@@ -3,7 +3,7 @@ use ra_syntax::{
3 SyntaxKind::WHITESPACE, 3 SyntaxKind::WHITESPACE,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
7 7
8// Assist: move_guard_to_arm_body 8// Assist: move_guard_to_arm_body
9// 9//
@@ -40,17 +40,22 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
40 let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text()); 40 let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
41 41
42 let target = guard.syntax().text_range(); 42 let target = guard.syntax().text_range();
43 acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| { 43 acc.add(
44 match space_before_guard { 44 AssistId("move_guard_to_arm_body", AssistKind::RefactorRewrite),
45 Some(element) if element.kind() == WHITESPACE => { 45 "Move guard to arm body",
46 edit.delete(element.text_range()); 46 target,
47 } 47 |edit| {
48 _ => (), 48 match space_before_guard {
49 }; 49 Some(element) if element.kind() == WHITESPACE => {
50 edit.delete(element.text_range());
51 }
52 _ => (),
53 };
50 54
51 edit.delete(guard.syntax().text_range()); 55 edit.delete(guard.syntax().text_range());
52 edit.replace_node_and_indent(arm_expr.syntax(), buf); 56 edit.replace_node_and_indent(arm_expr.syntax(), buf);
53 }) 57 },
58 )
54} 59}
55 60
56// Assist: move_arm_cond_to_match_guard 61// Assist: move_arm_cond_to_match_guard
@@ -100,7 +105,7 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
100 105
101 let target = if_expr.syntax().text_range(); 106 let target = if_expr.syntax().text_range();
102 acc.add( 107 acc.add(
103 AssistId("move_arm_cond_to_match_guard"), 108 AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite),
104 "Move condition to match guard", 109 "Move condition to match guard",
105 target, 110 target,
106 |edit| { 111 |edit| {
diff --git a/crates/ra_assists/src/handlers/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs
index 16002d2ac..6d77dff13 100644
--- a/crates/ra_assists/src/handlers/raw_string.rs
+++ b/crates/ra_assists/src/handlers/raw_string.rs
@@ -1,11 +1,13 @@
1use std::borrow::Cow;
2
1use ra_syntax::{ 3use ra_syntax::{
2 ast::{self, HasStringValue}, 4 ast::{self, HasQuotes, HasStringValue},
3 AstToken, 5 AstToken,
4 SyntaxKind::{RAW_STRING, STRING}, 6 SyntaxKind::{RAW_STRING, STRING},
5 TextSize, 7 TextSize,
6}; 8};
7 9
8use crate::{AssistContext, AssistId, Assists}; 10use crate::{AssistContext, AssistId, AssistKind, Assists};
9 11
10// Assist: make_raw_string 12// Assist: make_raw_string
11// 13//
@@ -26,14 +28,25 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext) -> Option<
26 let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?; 28 let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?;
27 let value = token.value()?; 29 let value = token.value()?;
28 let target = token.syntax().text_range(); 30 let target = token.syntax().text_range();
29 acc.add(AssistId("make_raw_string"), "Rewrite as raw string", target, |edit| { 31 acc.add(
30 let max_hash_streak = count_hashes(&value); 32 AssistId("make_raw_string", AssistKind::RefactorRewrite),
31 let mut hashes = String::with_capacity(max_hash_streak + 1); 33 "Rewrite as raw string",
32 for _ in 0..hashes.capacity() { 34 target,
33 hashes.push('#'); 35 |edit| {
34 } 36 let max_hash_streak = count_hashes(&value);
35 edit.replace(token.syntax().text_range(), format!("r{}\"{}\"{}", hashes, value, hashes)); 37 let hashes = "#".repeat(max_hash_streak + 1);
36 }) 38 if matches!(value, Cow::Borrowed(_)) {
39 // Avoid replacing the whole string to better position the cursor.
40 edit.insert(token.syntax().text_range().start(), format!("r{}", hashes));
41 edit.insert(token.syntax().text_range().end(), format!("{}", hashes));
42 } else {
43 edit.replace(
44 token.syntax().text_range(),
45 format!("r{}\"{}\"{}", hashes, value, hashes),
46 );
47 }
48 },
49 )
37} 50}
38 51
39// Assist: make_usual_string 52// Assist: make_usual_string
@@ -55,11 +68,24 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Optio
55 let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?; 68 let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?;
56 let value = token.value()?; 69 let value = token.value()?;
57 let target = token.syntax().text_range(); 70 let target = token.syntax().text_range();
58 acc.add(AssistId("make_usual_string"), "Rewrite as regular string", target, |edit| { 71 acc.add(
59 // parse inside string to escape `"` 72 AssistId("make_usual_string", AssistKind::RefactorRewrite),
60 let escaped = value.escape_default().to_string(); 73 "Rewrite as regular string",
61 edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped)); 74 target,
62 }) 75 |edit| {
76 // parse inside string to escape `"`
77 let escaped = value.escape_default().to_string();
78 if let Some(offsets) = token.quote_offsets() {
79 if token.text()[offsets.contents - token.syntax().text_range().start()] == escaped {
80 edit.replace(offsets.quotes.0, "\"");
81 edit.replace(offsets.quotes.1, "\"");
82 return;
83 }
84 }
85
86 edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped));
87 },
88 )
63} 89}
64 90
65// Assist: add_hash 91// Assist: add_hash
@@ -80,7 +106,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Optio
80pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 106pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
81 let token = ctx.find_token_at_offset(RAW_STRING)?; 107 let token = ctx.find_token_at_offset(RAW_STRING)?;
82 let target = token.text_range(); 108 let target = token.text_range();
83 acc.add(AssistId("add_hash"), "Add # to raw string", target, |edit| { 109 acc.add(AssistId("add_hash", AssistKind::Refactor), "Add # to raw string", target, |edit| {
84 edit.insert(token.text_range().start() + TextSize::of('r'), "#"); 110 edit.insert(token.text_range().start() + TextSize::of('r'), "#");
85 edit.insert(token.text_range().end(), "#"); 111 edit.insert(token.text_range().end(), "#");
86 }) 112 })
@@ -109,18 +135,23 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
109 return None; 135 return None;
110 } 136 }
111 let target = token.text_range(); 137 let target = token.text_range();
112 acc.add(AssistId("remove_hash"), "Remove hash from raw string", target, |edit| { 138 acc.add(
113 let result = &text[2..text.len() - 1]; 139 AssistId("remove_hash", AssistKind::RefactorRewrite),
114 let result = if result.starts_with('\"') { 140 "Remove hash from raw string",
115 // FIXME: this logic is wrong, not only the last has has to handled specially 141 target,
116 // no more hash, escape 142 |edit| {
117 let internal_str = &result[1..result.len() - 1]; 143 let result = &text[2..text.len() - 1];
118 format!("\"{}\"", internal_str.escape_default().to_string()) 144 let result = if result.starts_with('\"') {
119 } else { 145 // FIXME: this logic is wrong, not only the last has has to handled specially
120 result.to_owned() 146 // no more hash, escape
121 }; 147 let internal_str = &result[1..result.len() - 1];
122 edit.replace(token.text_range(), format!("r{}", result)); 148 format!("\"{}\"", internal_str.escape_default().to_string())
123 }) 149 } else {
150 result.to_owned()
151 };
152 edit.replace(token.text_range(), format!("r{}", result));
153 },
154 )
124} 155}
125 156
126fn count_hashes(s: &str) -> usize { 157fn count_hashes(s: &str) -> usize {
@@ -158,16 +189,16 @@ mod test {
158 check_assist( 189 check_assist(
159 make_raw_string, 190 make_raw_string,
160 r#" 191 r#"
161 fn f() { 192fn f() {
162 let s = <|>"random\nstring"; 193 let s = <|>"random\nstring";
163 } 194}
164 "#, 195"#,
165 r##" 196 r##"
166 fn f() { 197fn f() {
167 let s = r#"random 198 let s = r#"random
168string"#; 199string"#;
169 } 200}
170 "##, 201"##,
171 ) 202 )
172 } 203 }
173 204
@@ -193,16 +224,16 @@ string"#;
193 check_assist( 224 check_assist(
194 make_raw_string, 225 make_raw_string,
195 r###" 226 r###"
196 fn f() { 227fn f() {
197 let s = <|>"#random##\nstring"; 228 let s = <|>"#random##\nstring";
198 } 229}
199 "###, 230"###,
200 r####" 231 r####"
201 fn f() { 232fn f() {
202 let s = r#"#random## 233 let s = r#"#random##
203string"#; 234string"#;
204 } 235}
205 "####, 236"####,
206 ) 237 )
207 } 238 }
208 239
@@ -211,16 +242,16 @@ string"#;
211 check_assist( 242 check_assist(
212 make_raw_string, 243 make_raw_string,
213 r###" 244 r###"
214 fn f() { 245fn f() {
215 let s = <|>"#random\"##\nstring"; 246 let s = <|>"#random\"##\nstring";
216 } 247}
217 "###, 248"###,
218 r####" 249 r####"
219 fn f() { 250fn f() {
220 let s = r###"#random"## 251 let s = r###"#random"##
221string"###; 252string"###;
222 } 253}
223 "####, 254"####,
224 ) 255 )
225 } 256 }
226 257
diff --git a/crates/ra_assists/src/handlers/remove_dbg.rs b/crates/ra_assists/src/handlers/remove_dbg.rs
index 961ee1731..a616cca57 100644
--- a/crates/ra_assists/src/handlers/remove_dbg.rs
+++ b/crates/ra_assists/src/handlers/remove_dbg.rs
@@ -3,7 +3,7 @@ use ra_syntax::{
3 TextSize, T, 3 TextSize, T,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
7 7
8// Assist: remove_dbg 8// Assist: remove_dbg
9// 9//
@@ -38,7 +38,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
38 }; 38 };
39 39
40 let target = macro_call.syntax().text_range(); 40 let target = macro_call.syntax().text_range();
41 acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |builder| { 41 acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", target, |builder| {
42 builder.replace(macro_range, macro_content); 42 builder.replace(macro_range, macro_content);
43 }) 43 })
44} 44}
diff --git a/crates/ra_assists/src/handlers/remove_mut.rs b/crates/ra_assists/src/handlers/remove_mut.rs
index fe4eada03..ef55c354e 100644
--- a/crates/ra_assists/src/handlers/remove_mut.rs
+++ b/crates/ra_assists/src/handlers/remove_mut.rs
@@ -1,6 +1,6 @@
1use ra_syntax::{SyntaxKind, TextRange, T}; 1use ra_syntax::{SyntaxKind, TextRange, T};
2 2
3use crate::{AssistContext, AssistId, Assists}; 3use crate::{AssistContext, AssistId, AssistKind, Assists};
4 4
5// Assist: remove_mut 5// Assist: remove_mut
6// 6//
@@ -26,7 +26,12 @@ pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 }; 26 };
27 27
28 let target = mut_token.text_range(); 28 let target = mut_token.text_range();
29 acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |builder| { 29 acc.add(
30 builder.delete(TextRange::new(delete_from, delete_to)); 30 AssistId("remove_mut", AssistKind::Refactor),
31 }) 31 "Remove `mut` keyword",
32 target,
33 |builder| {
34 builder.delete(TextRange::new(delete_from, delete_to));
35 },
36 )
32} 37}
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs
index 897da2832..2ac1c56cf 100644
--- a/crates/ra_assists/src/handlers/reorder_fields.rs
+++ b/crates/ra_assists/src/handlers/reorder_fields.rs
@@ -1,11 +1,11 @@
1use std::collections::HashMap; 1use itertools::Itertools;
2use rustc_hash::FxHashMap;
2 3
3use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; 4use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
4use itertools::Itertools;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; 6use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode};
7 7
8use crate::{AssistContext, AssistId, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
9 9
10// Assist: reorder_fields 10// Assist: reorder_fields
11// 11//
@@ -42,11 +42,16 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
42 } 42 }
43 43
44 let target = record.syntax().text_range(); 44 let target = record.syntax().text_range();
45 acc.add(AssistId("reorder_fields"), "Reorder record fields", target, |edit| { 45 acc.add(
46 for (old, new) in fields.iter().zip(&sorted_fields) { 46 AssistId("reorder_fields", AssistKind::RefactorRewrite),
47 algo::diff(old, new).into_text_edit(edit.text_edit_builder()); 47 "Reorder record fields",
48 } 48 target,
49 }) 49 |edit| {
50 for (old, new) in fields.iter().zip(&sorted_fields) {
51 algo::diff(old, new).into_text_edit(edit.text_edit_builder());
52 }
53 },
54 )
50} 55}
51 56
52fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> { 57fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
@@ -87,13 +92,13 @@ fn struct_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option
87 } 92 }
88} 93}
89 94
90fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<HashMap<String, usize>> { 95fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashMap<String, usize>> {
91 Some( 96 Some(
92 struct_definition(path, &ctx.sema)? 97 struct_definition(path, &ctx.sema)?
93 .fields(ctx.db) 98 .fields(ctx.db())
94 .iter() 99 .iter()
95 .enumerate() 100 .enumerate()
96 .map(|(idx, field)| (field.name(ctx.db).to_string(), idx)) 101 .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx))
97 .collect(), 102 .collect(),
98 ) 103 )
99} 104}
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
index e016f51c3..b7e30a7f2 100644
--- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
@@ -8,7 +8,7 @@ use ra_syntax::{
8 AstNode, 8 AstNode,
9}; 9};
10 10
11use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; 11use crate::{utils::TryEnum, AssistContext, AssistId, AssistKind, Assists};
12 12
13// Assist: replace_if_let_with_match 13// Assist: replace_if_let_with_match
14// 14//
@@ -48,28 +48,35 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
48 }; 48 };
49 49
50 let target = if_expr.syntax().text_range(); 50 let target = if_expr.syntax().text_range();
51 acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| { 51 acc.add(
52 let match_expr = { 52 AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite),
53 let then_arm = { 53 "Replace with match",
54 let then_expr = unwrap_trivial_block(then_block); 54 target,
55 make::match_arm(vec![pat.clone()], then_expr) 55 move |edit| {
56 let match_expr = {
57 let then_arm = {
58 let then_block = then_block.reset_indent().indent(IndentLevel(1));
59 let then_expr = unwrap_trivial_block(then_block);
60 make::match_arm(vec![pat.clone()], then_expr)
61 };
62 let else_arm = {
63 let pattern = ctx
64 .sema
65 .type_of_pat(&pat)
66 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
67 .map(|it| it.sad_pattern())
68 .unwrap_or_else(|| make::placeholder_pat().into());
69 let else_expr = unwrap_trivial_block(else_block);
70 make::match_arm(vec![pattern], else_expr)
71 };
72 let match_expr =
73 make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]));
74 match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
56 }; 75 };
57 let else_arm = {
58 let pattern = ctx
59 .sema
60 .type_of_pat(&pat)
61 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
62 .map(|it| it.sad_pattern())
63 .unwrap_or_else(|| make::placeholder_pat().into());
64 let else_expr = unwrap_trivial_block(else_block);
65 make::match_arm(vec![pattern], else_expr)
66 };
67 make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]))
68 .indent(IndentLevel::from_node(if_expr.syntax()))
69 };
70 76
71 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); 77 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
72 }) 78 },
79 )
73} 80}
74 81
75#[cfg(test)] 82#[cfg(test)]
@@ -213,4 +220,36 @@ fn foo(x: Result<i32, ()>) {
213 "#, 220 "#,
214 ); 221 );
215 } 222 }
223
224 #[test]
225 fn nested_indent() {
226 check_assist(
227 replace_if_let_with_match,
228 r#"
229fn main() {
230 if true {
231 <|>if let Ok(rel_path) = path.strip_prefix(root_path) {
232 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
233 Some((*id, rel_path))
234 } else {
235 None
236 }
237 }
238}
239"#,
240 r#"
241fn main() {
242 if true {
243 match path.strip_prefix(root_path) {
244 Ok(rel_path) => {
245 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
246 Some((*id, rel_path))
247 }
248 _ => None,
249 }
250 }
251}
252"#,
253 )
254 }
216} 255}
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
index 761557ac0..a49292c97 100644
--- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
@@ -9,7 +9,7 @@ use ra_syntax::{
9 AstNode, T, 9 AstNode, T,
10}; 10};
11 11
12use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; 12use crate::{utils::TryEnum, AssistContext, AssistId, AssistKind, Assists};
13 13
14// Assist: replace_let_with_if_let 14// Assist: replace_let_with_if_let
15// 15//
@@ -44,24 +44,31 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
44 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty).map(|it| it.happy_case()); 44 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty).map(|it| it.happy_case());
45 45
46 let target = let_kw.text_range(); 46 let target = let_kw.text_range();
47 acc.add(AssistId("replace_let_with_if_let"), "Replace with if-let", target, |edit| { 47 acc.add(
48 let with_placeholder: ast::Pat = match happy_variant { 48 AssistId("replace_let_with_if_let", AssistKind::RefactorRewrite),
49 None => make::placeholder_pat().into(), 49 "Replace with if-let",
50 Some(var_name) => make::tuple_struct_pat( 50 target,
51 make::path_unqualified(make::path_segment(make::name_ref(var_name))), 51 |edit| {
52 once(make::placeholder_pat().into()), 52 let with_placeholder: ast::Pat = match happy_variant {
53 ) 53 None => make::placeholder_pat().into(),
54 .into(), 54 Some(var_name) => make::tuple_struct_pat(
55 }; 55 make::path_unqualified(make::path_segment(make::name_ref(var_name))),
56 let block = make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax())); 56 once(make::placeholder_pat().into()),
57 let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); 57 )
58 let stmt = make::expr_stmt(if_); 58 .into(),
59 };
60 let block =
61 make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax()));
62 let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block);
63 let stmt = make::expr_stmt(if_);
59 64
60 let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap(); 65 let placeholder =
61 let stmt = stmt.replace_descendant(placeholder.into(), original_pat); 66 stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap();
67 let stmt = stmt.replace_descendant(placeholder.into(), original_pat);
62 68
63 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); 69 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
64 }) 70 },
71 )
65} 72}
66 73
67#[cfg(test)] 74#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
index 0197a8cf0..dfd314abf 100644
--- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -1,7 +1,10 @@
1use hir; 1use hir;
2use ra_syntax::{ast, AstNode, SmolStr, TextRange}; 2use ra_syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SmolStr, SyntaxNode};
3 3
4use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists}; 4use crate::{
5 utils::{find_insert_use_container, insert_use_statement},
6 AssistContext, AssistId, AssistKind, Assists,
7};
5 8
6// Assist: replace_qualified_name_with_use 9// Assist: replace_qualified_name_with_use
7// 10//
@@ -34,21 +37,23 @@ pub(crate) fn replace_qualified_name_with_use(
34 37
35 let target = path.syntax().text_range(); 38 let target = path.syntax().text_range();
36 acc.add( 39 acc.add(
37 AssistId("replace_qualified_name_with_use"), 40 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
38 "Replace qualified path with use", 41 "Replace qualified path with use",
39 target, 42 target,
40 |builder| { 43 |builder| {
41 let path_to_import = hir_path.mod_path().clone(); 44 let path_to_import = hir_path.mod_path().clone();
45 let container = match find_insert_use_container(path.syntax(), ctx) {
46 Some(c) => c,
47 None => return,
48 };
42 insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder()); 49 insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder());
43 50
44 if let Some(last) = path.segment() { 51 // Now that we've brought the name into scope, re-qualify all paths that could be
45 // Here we are assuming the assist will provide a correct use statement 52 // affected (that is, all paths inside the node we added the `use` to).
46 // so we can delete the path qualifier 53 let mut rewriter = SyntaxRewriter::default();
47 builder.delete(TextRange::new( 54 let syntax = container.either(|l| l.syntax().clone(), |r| r.syntax().clone());
48 path.syntax().text_range().start(), 55 shorten_paths(&mut rewriter, syntax, path);
49 last.syntax().text_range().start(), 56 builder.rewrite(rewriter);
50 ));
51 }
52 }, 57 },
53 ) 58 )
54} 59}
@@ -73,6 +78,69 @@ fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
73 Some(ps) 78 Some(ps)
74} 79}
75 80
81/// Adds replacements to `re` that shorten `path` in all descendants of `node`.
82fn shorten_paths(rewriter: &mut SyntaxRewriter<'static>, node: SyntaxNode, path: ast::Path) {
83 for child in node.children() {
84 match_ast! {
85 match child {
86 // Don't modify `use` items, as this can break the `use` item when injecting a new
87 // import into the use tree.
88 ast::UseItem(_it) => continue,
89 // Don't descend into submodules, they don't have the same `use` items in scope.
90 ast::Module(_it) => continue,
91
92 ast::Path(p) => {
93 match maybe_replace_path(rewriter, p.clone(), path.clone()) {
94 Some(()) => {},
95 None => shorten_paths(rewriter, p.syntax().clone(), path.clone()),
96 }
97 },
98 _ => shorten_paths(rewriter, child, path.clone()),
99 }
100 }
101 }
102}
103
104fn maybe_replace_path(
105 rewriter: &mut SyntaxRewriter<'static>,
106 path: ast::Path,
107 target: ast::Path,
108) -> Option<()> {
109 if !path_eq(path.clone(), target.clone()) {
110 return None;
111 }
112
113 // Shorten `path`, leaving only its last segment.
114 if let Some(parent) = path.qualifier() {
115 rewriter.delete(parent.syntax());
116 }
117 if let Some(double_colon) = path.coloncolon_token() {
118 rewriter.delete(&double_colon);
119 }
120
121 Some(())
122}
123
124fn path_eq(lhs: ast::Path, rhs: ast::Path) -> bool {
125 let mut lhs_curr = lhs;
126 let mut rhs_curr = rhs;
127 loop {
128 match (lhs_curr.segment(), rhs_curr.segment()) {
129 (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
130 _ => return false,
131 }
132
133 match (lhs_curr.qualifier(), rhs_curr.qualifier()) {
134 (Some(lhs), Some(rhs)) => {
135 lhs_curr = lhs;
136 rhs_curr = rhs;
137 }
138 (None, None) => return true,
139 _ => return false,
140 }
141 }
142}
143
76#[cfg(test)] 144#[cfg(test)]
77mod tests { 145mod tests {
78 use crate::tests::{check_assist, check_assist_not_applicable}; 146 use crate::tests::{check_assist, check_assist_not_applicable};
@@ -83,10 +151,10 @@ mod tests {
83 fn test_replace_add_use_no_anchor() { 151 fn test_replace_add_use_no_anchor() {
84 check_assist( 152 check_assist(
85 replace_qualified_name_with_use, 153 replace_qualified_name_with_use,
86 " 154 r"
87std::fmt::Debug<|> 155std::fmt::Debug<|>
88 ", 156 ",
89 " 157 r"
90use std::fmt::Debug; 158use std::fmt::Debug;
91 159
92Debug 160Debug
@@ -97,13 +165,13 @@ Debug
97 fn test_replace_add_use_no_anchor_with_item_below() { 165 fn test_replace_add_use_no_anchor_with_item_below() {
98 check_assist( 166 check_assist(
99 replace_qualified_name_with_use, 167 replace_qualified_name_with_use,
100 " 168 r"
101std::fmt::Debug<|> 169std::fmt::Debug<|>
102 170
103fn main() { 171fn main() {
104} 172}
105 ", 173 ",
106 " 174 r"
107use std::fmt::Debug; 175use std::fmt::Debug;
108 176
109Debug 177Debug
@@ -118,13 +186,13 @@ fn main() {
118 fn test_replace_add_use_no_anchor_with_item_above() { 186 fn test_replace_add_use_no_anchor_with_item_above() {
119 check_assist( 187 check_assist(
120 replace_qualified_name_with_use, 188 replace_qualified_name_with_use,
121 " 189 r"
122fn main() { 190fn main() {
123} 191}
124 192
125std::fmt::Debug<|> 193std::fmt::Debug<|>
126 ", 194 ",
127 " 195 r"
128use std::fmt::Debug; 196use std::fmt::Debug;
129 197
130fn main() { 198fn main() {
@@ -139,10 +207,10 @@ Debug
139 fn test_replace_add_use_no_anchor_2seg() { 207 fn test_replace_add_use_no_anchor_2seg() {
140 check_assist( 208 check_assist(
141 replace_qualified_name_with_use, 209 replace_qualified_name_with_use,
142 " 210 r"
143std::fmt<|>::Debug 211std::fmt<|>::Debug
144 ", 212 ",
145 " 213 r"
146use std::fmt; 214use std::fmt;
147 215
148fmt::Debug 216fmt::Debug
@@ -154,13 +222,13 @@ fmt::Debug
154 fn test_replace_add_use() { 222 fn test_replace_add_use() {
155 check_assist( 223 check_assist(
156 replace_qualified_name_with_use, 224 replace_qualified_name_with_use,
157 " 225 r"
158use stdx; 226use stdx;
159 227
160impl std::fmt::Debug<|> for Foo { 228impl std::fmt::Debug<|> for Foo {
161} 229}
162 ", 230 ",
163 " 231 r"
164use stdx; 232use stdx;
165use std::fmt::Debug; 233use std::fmt::Debug;
166 234
@@ -174,11 +242,11 @@ impl Debug for Foo {
174 fn test_replace_file_use_other_anchor() { 242 fn test_replace_file_use_other_anchor() {
175 check_assist( 243 check_assist(
176 replace_qualified_name_with_use, 244 replace_qualified_name_with_use,
177 " 245 r"
178impl std::fmt::Debug<|> for Foo { 246impl std::fmt::Debug<|> for Foo {
179} 247}
180 ", 248 ",
181 " 249 r"
182use std::fmt::Debug; 250use std::fmt::Debug;
183 251
184impl Debug for Foo { 252impl Debug for Foo {
@@ -191,11 +259,11 @@ impl Debug for Foo {
191 fn test_replace_add_use_other_anchor_indent() { 259 fn test_replace_add_use_other_anchor_indent() {
192 check_assist( 260 check_assist(
193 replace_qualified_name_with_use, 261 replace_qualified_name_with_use,
194 " 262 r"
195 impl std::fmt::Debug<|> for Foo { 263 impl std::fmt::Debug<|> for Foo {
196 } 264 }
197 ", 265 ",
198 " 266 r"
199 use std::fmt::Debug; 267 use std::fmt::Debug;
200 268
201 impl Debug for Foo { 269 impl Debug for Foo {
@@ -208,13 +276,13 @@ impl Debug for Foo {
208 fn test_replace_split_different() { 276 fn test_replace_split_different() {
209 check_assist( 277 check_assist(
210 replace_qualified_name_with_use, 278 replace_qualified_name_with_use,
211 " 279 r"
212use std::fmt; 280use std::fmt;
213 281
214impl std::io<|> for Foo { 282impl std::io<|> for Foo {
215} 283}
216 ", 284 ",
217 " 285 r"
218use std::{io, fmt}; 286use std::{io, fmt};
219 287
220impl io for Foo { 288impl io for Foo {
@@ -227,13 +295,13 @@ impl io for Foo {
227 fn test_replace_split_self_for_use() { 295 fn test_replace_split_self_for_use() {
228 check_assist( 296 check_assist(
229 replace_qualified_name_with_use, 297 replace_qualified_name_with_use,
230 " 298 r"
231use std::fmt; 299use std::fmt;
232 300
233impl std::fmt::Debug<|> for Foo { 301impl std::fmt::Debug<|> for Foo {
234} 302}
235 ", 303 ",
236 " 304 r"
237use std::fmt::{self, Debug, }; 305use std::fmt::{self, Debug, };
238 306
239impl Debug for Foo { 307impl Debug for Foo {
@@ -246,13 +314,13 @@ impl Debug for Foo {
246 fn test_replace_split_self_for_target() { 314 fn test_replace_split_self_for_target() {
247 check_assist( 315 check_assist(
248 replace_qualified_name_with_use, 316 replace_qualified_name_with_use,
249 " 317 r"
250use std::fmt::Debug; 318use std::fmt::Debug;
251 319
252impl std::fmt<|> for Foo { 320impl std::fmt<|> for Foo {
253} 321}
254 ", 322 ",
255 " 323 r"
256use std::fmt::{self, Debug}; 324use std::fmt::{self, Debug};
257 325
258impl fmt for Foo { 326impl fmt for Foo {
@@ -265,13 +333,13 @@ impl fmt for Foo {
265 fn test_replace_add_to_nested_self_nested() { 333 fn test_replace_add_to_nested_self_nested() {
266 check_assist( 334 check_assist(
267 replace_qualified_name_with_use, 335 replace_qualified_name_with_use,
268 " 336 r"
269use std::fmt::{Debug, nested::{Display}}; 337use std::fmt::{Debug, nested::{Display}};
270 338
271impl std::fmt::nested<|> for Foo { 339impl std::fmt::nested<|> for Foo {
272} 340}
273", 341",
274 " 342 r"
275use std::fmt::{Debug, nested::{Display, self}}; 343use std::fmt::{Debug, nested::{Display, self}};
276 344
277impl nested for Foo { 345impl nested for Foo {
@@ -284,13 +352,13 @@ impl nested for Foo {
284 fn test_replace_add_to_nested_self_already_included() { 352 fn test_replace_add_to_nested_self_already_included() {
285 check_assist( 353 check_assist(
286 replace_qualified_name_with_use, 354 replace_qualified_name_with_use,
287 " 355 r"
288use std::fmt::{Debug, nested::{self, Display}}; 356use std::fmt::{Debug, nested::{self, Display}};
289 357
290impl std::fmt::nested<|> for Foo { 358impl std::fmt::nested<|> for Foo {
291} 359}
292", 360",
293 " 361 r"
294use std::fmt::{Debug, nested::{self, Display}}; 362use std::fmt::{Debug, nested::{self, Display}};
295 363
296impl nested for Foo { 364impl nested for Foo {
@@ -303,13 +371,13 @@ impl nested for Foo {
303 fn test_replace_add_to_nested_nested() { 371 fn test_replace_add_to_nested_nested() {
304 check_assist( 372 check_assist(
305 replace_qualified_name_with_use, 373 replace_qualified_name_with_use,
306 " 374 r"
307use std::fmt::{Debug, nested::{Display}}; 375use std::fmt::{Debug, nested::{Display}};
308 376
309impl std::fmt::nested::Debug<|> for Foo { 377impl std::fmt::nested::Debug<|> for Foo {
310} 378}
311", 379",
312 " 380 r"
313use std::fmt::{Debug, nested::{Display, Debug}}; 381use std::fmt::{Debug, nested::{Display, Debug}};
314 382
315impl Debug for Foo { 383impl Debug for Foo {
@@ -322,13 +390,13 @@ impl Debug for Foo {
322 fn test_replace_split_common_target_longer() { 390 fn test_replace_split_common_target_longer() {
323 check_assist( 391 check_assist(
324 replace_qualified_name_with_use, 392 replace_qualified_name_with_use,
325 " 393 r"
326use std::fmt::Debug; 394use std::fmt::Debug;
327 395
328impl std::fmt::nested::Display<|> for Foo { 396impl std::fmt::nested::Display<|> for Foo {
329} 397}
330", 398",
331 " 399 r"
332use std::fmt::{nested::Display, Debug}; 400use std::fmt::{nested::Display, Debug};
333 401
334impl Display for Foo { 402impl Display for Foo {
@@ -341,13 +409,13 @@ impl Display for Foo {
341 fn test_replace_split_common_use_longer() { 409 fn test_replace_split_common_use_longer() {
342 check_assist( 410 check_assist(
343 replace_qualified_name_with_use, 411 replace_qualified_name_with_use,
344 " 412 r"
345use std::fmt::nested::Debug; 413use std::fmt::nested::Debug;
346 414
347impl std::fmt::Display<|> for Foo { 415impl std::fmt::Display<|> for Foo {
348} 416}
349", 417",
350 " 418 r"
351use std::fmt::{Display, nested::Debug}; 419use std::fmt::{Display, nested::Debug};
352 420
353impl Display for Foo { 421impl Display for Foo {
@@ -360,7 +428,7 @@ impl Display for Foo {
360 fn test_replace_use_nested_import() { 428 fn test_replace_use_nested_import() {
361 check_assist( 429 check_assist(
362 replace_qualified_name_with_use, 430 replace_qualified_name_with_use,
363 " 431 r"
364use crate::{ 432use crate::{
365 ty::{Substs, Ty}, 433 ty::{Substs, Ty},
366 AssocItem, 434 AssocItem,
@@ -368,7 +436,7 @@ use crate::{
368 436
369fn foo() { crate::ty::lower<|>::trait_env() } 437fn foo() { crate::ty::lower<|>::trait_env() }
370", 438",
371 " 439 r"
372use crate::{ 440use crate::{
373 ty::{Substs, Ty, lower}, 441 ty::{Substs, Ty, lower},
374 AssocItem, 442 AssocItem,
@@ -383,13 +451,13 @@ fn foo() { lower::trait_env() }
383 fn test_replace_alias() { 451 fn test_replace_alias() {
384 check_assist( 452 check_assist(
385 replace_qualified_name_with_use, 453 replace_qualified_name_with_use,
386 " 454 r"
387use std::fmt as foo; 455use std::fmt as foo;
388 456
389impl foo::Debug<|> for Foo { 457impl foo::Debug<|> for Foo {
390} 458}
391", 459",
392 " 460 r"
393use std::fmt as foo; 461use std::fmt as foo;
394 462
395impl Debug for Foo { 463impl Debug for Foo {
@@ -402,7 +470,7 @@ impl Debug for Foo {
402 fn test_replace_not_applicable_one_segment() { 470 fn test_replace_not_applicable_one_segment() {
403 check_assist_not_applicable( 471 check_assist_not_applicable(
404 replace_qualified_name_with_use, 472 replace_qualified_name_with_use,
405 " 473 r"
406impl foo<|> for Foo { 474impl foo<|> for Foo {
407} 475}
408", 476",
@@ -413,7 +481,7 @@ impl foo<|> for Foo {
413 fn test_replace_not_applicable_in_use() { 481 fn test_replace_not_applicable_in_use() {
414 check_assist_not_applicable( 482 check_assist_not_applicable(
415 replace_qualified_name_with_use, 483 replace_qualified_name_with_use,
416 " 484 r"
417use std::fmt<|>; 485use std::fmt<|>;
418", 486",
419 ); 487 );
@@ -423,14 +491,14 @@ use std::fmt<|>;
423 fn test_replace_add_use_no_anchor_in_mod_mod() { 491 fn test_replace_add_use_no_anchor_in_mod_mod() {
424 check_assist( 492 check_assist(
425 replace_qualified_name_with_use, 493 replace_qualified_name_with_use,
426 " 494 r"
427mod foo { 495mod foo {
428 mod bar { 496 mod bar {
429 std::fmt::Debug<|> 497 std::fmt::Debug<|>
430 } 498 }
431} 499}
432 ", 500 ",
433 " 501 r"
434mod foo { 502mod foo {
435 mod bar { 503 mod bar {
436 use std::fmt::Debug; 504 use std::fmt::Debug;
@@ -446,14 +514,14 @@ mod foo {
446 fn inserts_imports_after_inner_attributes() { 514 fn inserts_imports_after_inner_attributes() {
447 check_assist( 515 check_assist(
448 replace_qualified_name_with_use, 516 replace_qualified_name_with_use,
449 " 517 r"
450#![allow(dead_code)] 518#![allow(dead_code)]
451 519
452fn main() { 520fn main() {
453 std::fmt::Debug<|> 521 std::fmt::Debug<|>
454} 522}
455 ", 523 ",
456 " 524 r"
457#![allow(dead_code)] 525#![allow(dead_code)]
458use std::fmt::Debug; 526use std::fmt::Debug;
459 527
@@ -463,4 +531,116 @@ fn main() {
463 ", 531 ",
464 ); 532 );
465 } 533 }
534
535 #[test]
536 fn replaces_all_affected_paths() {
537 check_assist(
538 replace_qualified_name_with_use,
539 r"
540fn main() {
541 std::fmt::Debug<|>;
542 let x: std::fmt::Debug = std::fmt::Debug;
543}
544 ",
545 r"
546use std::fmt::Debug;
547
548fn main() {
549 Debug;
550 let x: Debug = Debug;
551}
552 ",
553 );
554 }
555
556 #[test]
557 fn replaces_all_affected_paths_mod() {
558 check_assist(
559 replace_qualified_name_with_use,
560 r"
561mod m {
562 fn f() {
563 std::fmt::Debug<|>;
564 let x: std::fmt::Debug = std::fmt::Debug;
565 }
566 fn g() {
567 std::fmt::Debug;
568 }
569}
570
571fn f() {
572 std::fmt::Debug;
573}
574 ",
575 r"
576mod m {
577 use std::fmt::Debug;
578
579 fn f() {
580 Debug;
581 let x: Debug = Debug;
582 }
583 fn g() {
584 Debug;
585 }
586}
587
588fn f() {
589 std::fmt::Debug;
590}
591 ",
592 );
593 }
594
595 #[test]
596 fn does_not_replace_in_submodules() {
597 check_assist(
598 replace_qualified_name_with_use,
599 r"
600fn main() {
601 std::fmt::Debug<|>;
602}
603
604mod sub {
605 fn f() {
606 std::fmt::Debug;
607 }
608}
609 ",
610 r"
611use std::fmt::Debug;
612
613fn main() {
614 Debug;
615}
616
617mod sub {
618 fn f() {
619 std::fmt::Debug;
620 }
621}
622 ",
623 );
624 }
625
626 #[test]
627 fn does_not_replace_in_use() {
628 check_assist(
629 replace_qualified_name_with_use,
630 r"
631use std::fmt::Display;
632
633fn main() {
634 std::fmt<|>;
635}
636 ",
637 r"
638use std::fmt::{self, Display};
639
640fn main() {
641 fmt;
642}
643 ",
644 );
645 }
466} 646}
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
index cff7dfb81..e5a4bb23c 100644
--- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11 11
12use crate::{ 12use crate::{
13 utils::{render_snippet, Cursor, TryEnum}, 13 utils::{render_snippet, Cursor, TryEnum},
14 AssistContext, AssistId, Assists, 14 AssistContext, AssistId, AssistKind, Assists,
15}; 15};
16 16
17// Assist: replace_unwrap_with_match 17// Assist: replace_unwrap_with_match
@@ -46,37 +46,43 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
46 let ty = ctx.sema.type_of_expr(&caller)?; 46 let ty = ctx.sema.type_of_expr(&caller)?;
47 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case(); 47 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case();
48 let target = method_call.syntax().text_range(); 48 let target = method_call.syntax().text_range();
49 acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |builder| { 49 acc.add(
50 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); 50 AssistId("replace_unwrap_with_match", AssistKind::RefactorRewrite),
51 let it = make::bind_pat(make::name("a")).into(); 51 "Replace unwrap with match",
52 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); 52 target,
53 |builder| {
54 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
55 let it = make::bind_pat(make::name("a")).into();
56 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
53 57
54 let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); 58 let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
55 let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); 59 let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
56 60
57 let unreachable_call = make::expr_unreachable(); 61 let unreachable_call = make::expr_unreachable();
58 let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); 62 let err_arm =
63 make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
59 64
60 let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); 65 let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
61 let match_expr = make::expr_match(caller.clone(), match_arm_list) 66 let match_expr = make::expr_match(caller.clone(), match_arm_list)
62 .indent(IndentLevel::from_node(method_call.syntax())); 67 .indent(IndentLevel::from_node(method_call.syntax()));
63 68
64 let range = method_call.syntax().text_range(); 69 let range = method_call.syntax().text_range();
65 match ctx.config.snippet_cap { 70 match ctx.config.snippet_cap {
66 Some(cap) => { 71 Some(cap) => {
67 let err_arm = match_expr 72 let err_arm = match_expr
68 .syntax() 73 .syntax()
69 .descendants() 74 .descendants()
70 .filter_map(ast::MatchArm::cast) 75 .filter_map(ast::MatchArm::cast)
71 .last() 76 .last()
72 .unwrap(); 77 .unwrap();
73 let snippet = 78 let snippet =
74 render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax())); 79 render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax()));
75 builder.replace_snippet(cap, range, snippet) 80 builder.replace_snippet(cap, range, snippet)
81 }
82 None => builder.replace(range, match_expr.to_string()),
76 } 83 }
77 None => builder.replace(range, match_expr.to_string()), 84 },
78 } 85 )
79 })
80} 86}
81 87
82#[cfg(test)] 88#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs
index c7a874480..4ca5c3ca1 100644
--- a/crates/ra_assists/src/handlers/split_import.rs
+++ b/crates/ra_assists/src/handlers/split_import.rs
@@ -2,7 +2,7 @@ use std::iter::successors;
2 2
3use ra_syntax::{ast, AstNode, T}; 3use ra_syntax::{ast, AstNode, T};
4 4
5use crate::{AssistContext, AssistId, Assists}; 5use crate::{AssistContext, AssistId, AssistKind, Assists};
6 6
7// Assist: split_import 7// Assist: split_import
8// 8//
@@ -28,7 +28,7 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
28 } 28 }
29 29
30 let target = colon_colon.text_range(); 30 let target = colon_colon.text_range();
31 acc.add(AssistId("split_import"), "Split import", target, |edit| { 31 acc.add(AssistId("split_import", AssistKind::RefactorRewrite), "Split import", target, |edit| {
32 edit.replace_ast(use_tree, new_tree); 32 edit.replace_ast(use_tree, new_tree);
33 }) 33 })
34} 34}
@@ -66,4 +66,14 @@ mod tests {
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::<|>:::self;")
68 } 68 }
69
70 #[test]
71 fn test_empty_use() {
72 check_assist_not_applicable(
73 split_import,
74 r"
75use std::<|>
76fn main() {}",
77 );
78 }
69} 79}
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs
index 8440c7d0f..8b38695a9 100644
--- a/crates/ra_assists/src/handlers/unwrap_block.rs
+++ b/crates/ra_assists/src/handlers/unwrap_block.rs
@@ -1,10 +1,13 @@
1use ra_fmt::unwrap_trivial_block; 1use ra_fmt::unwrap_trivial_block;
2use ra_syntax::{ 2use ra_syntax::{
3 ast::{self, ElseBranch, Expr, LoopBodyOwner}, 3 ast::{
4 match_ast, AstNode, TextRange, T, 4 self,
5 edit::{AstNodeEdit, IndentLevel},
6 },
7 AstNode, TextRange, T,
5}; 8};
6 9
7use crate::{AssistContext, AssistId, Assists}; 10use crate::{AssistContext, AssistId, AssistKind, Assists};
8 11
9// Assist: unwrap_block 12// Assist: unwrap_block
10// 13//
@@ -24,94 +27,73 @@ use crate::{AssistContext, AssistId, Assists};
24// } 27// }
25// ``` 28// ```
26pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 29pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
27 let l_curly_token = ctx.find_token_at_offset(T!['{'])?; 30 let assist_id = AssistId("unwrap_block", AssistKind::RefactorRewrite);
28 let block = ast::BlockExpr::cast(l_curly_token.parent())?;
29 let parent = block.syntax().parent()?;
30 let assist_id = AssistId("unwrap_block");
31 let assist_label = "Unwrap block"; 31 let assist_label = "Unwrap block";
32 32
33 let (expr, expr_to_unwrap) = match_ast! { 33 let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
34 match parent { 34 let mut block = ast::BlockExpr::cast(l_curly_token.parent())?;
35 ast::ForExpr(for_expr) => { 35 let mut parent = block.syntax().parent()?;
36 let block_expr = for_expr.loop_body()?; 36 if ast::MatchArm::can_cast(parent.kind()) {
37 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; 37 parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
38 (ast::Expr::ForExpr(for_expr), expr_to_unwrap) 38 }
39 },
40 ast::WhileExpr(while_expr) => {
41 let block_expr = while_expr.loop_body()?;
42 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
43 (ast::Expr::WhileExpr(while_expr), expr_to_unwrap)
44 },
45 ast::LoopExpr(loop_expr) => {
46 let block_expr = loop_expr.loop_body()?;
47 let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?;
48 (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap)
49 },
50 ast::IfExpr(if_expr) => {
51 let mut resp = None;
52
53 let then_branch = if_expr.then_branch()?;
54 if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) {
55 if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
56 // For `else if` blocks
57 let ancestor_then_branch = ancestor.then_branch()?;
58 let l_curly_token = then_branch.l_curly_token()?;
59
60 let target = then_branch.syntax().text_range();
61 return acc.add(assist_id, assist_label, target, |edit| {
62 let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
63 let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end());
64
65 edit.delete(range_to_del_rest);
66 edit.delete(range_to_del_else_if);
67 edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{']));
68 });
69 } else {
70 resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch)));
71 }
72 } else if let Some(else_branch) = if_expr.else_branch() {
73 match else_branch {
74 ElseBranch::Block(else_block) => {
75 let l_curly_token = else_block.l_curly_token()?;
76 if l_curly_token.text_range().contains_range(ctx.frange.range) {
77 let target = else_block.syntax().text_range();
78 return acc.add(assist_id, assist_label, target, |edit| {
79 let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start());
80
81 edit.delete(range_to_del);
82 edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{']));
83 });
84 }
85 },
86 ElseBranch::IfExpr(_) => {},
87 }
88 }
89 39
90 resp? 40 let parent = ast::Expr::cast(parent)?;
91 }, 41
92 _ => return None, 42 match parent.clone() {
43 ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
44 ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
45 ast::Expr::IfExpr(if_expr) => {
46 let then_branch = if_expr.then_branch()?;
47 if then_branch == block {
48 if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
49 // For `else if` blocks
50 let ancestor_then_branch = ancestor.then_branch()?;
51
52 let target = then_branch.syntax().text_range();
53 return acc.add(assist_id, assist_label, target, |edit| {
54 let range_to_del_else_if = TextRange::new(
55 ancestor_then_branch.syntax().text_range().end(),
56 l_curly_token.text_range().start(),
57 );
58 let range_to_del_rest = TextRange::new(
59 then_branch.syntax().text_range().end(),
60 if_expr.syntax().text_range().end(),
61 );
62
63 edit.delete(range_to_del_rest);
64 edit.delete(range_to_del_else_if);
65 edit.replace(
66 target,
67 update_expr_string(then_branch.to_string(), &[' ', '{']),
68 );
69 });
70 }
71 } else {
72 let target = block.syntax().text_range();
73 return acc.add(assist_id, assist_label, target, |edit| {
74 let range_to_del = TextRange::new(
75 then_branch.syntax().text_range().end(),
76 l_curly_token.text_range().start(),
77 );
78
79 edit.delete(range_to_del);
80 edit.replace(target, update_expr_string(block.to_string(), &[' ', '{']));
81 });
82 }
93 } 83 }
84 _ => return None,
94 }; 85 };
95 86
96 let target = expr_to_unwrap.syntax().text_range(); 87 let unwrapped = unwrap_trivial_block(block);
97 acc.add(assist_id, assist_label, target, |edit| { 88 let target = unwrapped.syntax().text_range();
98 edit.replace( 89 acc.add(assist_id, assist_label, target, |builder| {
99 expr.syntax().text_range(), 90 builder.replace(
100 update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), 91 parent.syntax().text_range(),
92 update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']),
101 ); 93 );
102 }) 94 })
103} 95}
104 96
105fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::Expr> {
106 let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);
107
108 if cursor_in_range {
109 Some(unwrap_trivial_block(block))
110 } else {
111 None
112 }
113}
114
115fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String { 97fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String {
116 let expr_string = expr_str.trim_start_matches(trim_start_pat); 98 let expr_string = expr_str.trim_start_matches(trim_start_pat);
117 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); 99 let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
@@ -490,6 +472,30 @@ mod tests {
490 } 472 }
491 473
492 #[test] 474 #[test]
475 fn unwrap_match_arm() {
476 check_assist(
477 unwrap_block,
478 r#"
479fn main() {
480 match rel_path {
481 Ok(rel_path) => {<|>
482 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
483 Some((*id, rel_path))
484 }
485 Err(_) => None,
486 }
487}
488"#,
489 r#"
490fn main() {
491 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
492 Some((*id, rel_path))
493}
494"#,
495 );
496 }
497
498 #[test]
493 fn simple_if_in_while_bad_cursor_position() { 499 fn simple_if_in_while_bad_cursor_position() {
494 check_assist_not_applicable( 500 check_assist_not_applicable(
495 unwrap_block, 501 unwrap_block,
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 3f8f7ffbf..465b90415 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -26,10 +26,40 @@ pub(crate) use crate::assist_context::{AssistContext, Assists};
26 26
27pub use assist_config::AssistConfig; 27pub use assist_config::AssistConfig;
28 28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum AssistKind {
31 None,
32 QuickFix,
33 Generate,
34 Refactor,
35 RefactorExtract,
36 RefactorInline,
37 RefactorRewrite,
38}
39
40impl AssistKind {
41 pub fn contains(self, other: AssistKind) -> bool {
42 if self == other {
43 return true;
44 }
45
46 match self {
47 AssistKind::None | AssistKind::Generate => return true,
48 AssistKind::Refactor => match other {
49 AssistKind::RefactorExtract
50 | AssistKind::RefactorInline
51 | AssistKind::RefactorRewrite => return true,
52 _ => return false,
53 },
54 _ => return false,
55 }
56 }
57}
58
29/// Unique identifier of the assist, should not be shown to the user 59/// Unique identifier of the assist, should not be shown to the user
30/// directly. 60/// directly.
31#[derive(Debug, Clone, Copy, PartialEq, Eq)] 61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct AssistId(pub &'static str); 62pub struct AssistId(pub &'static str, pub AssistKind);
33 63
34#[derive(Clone, Debug)] 64#[derive(Clone, Debug)]
35pub struct GroupLabel(pub String); 65pub struct GroupLabel(pub String);
@@ -102,27 +132,28 @@ mod handlers {
102 pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>; 132 pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>;
103 133
104 mod add_custom_impl; 134 mod add_custom_impl;
105 mod add_derive;
106 mod add_explicit_type; 135 mod add_explicit_type;
107 mod add_from_impl_for_enum;
108 mod add_function;
109 mod add_impl;
110 mod add_missing_impl_members; 136 mod add_missing_impl_members;
111 mod add_new;
112 mod add_turbo_fish; 137 mod add_turbo_fish;
113 mod apply_demorgan; 138 mod apply_demorgan;
114 mod auto_import; 139 mod auto_import;
115 mod change_lifetime_anon_to_named;
116 mod change_return_type_to_result; 140 mod change_return_type_to_result;
117 mod change_visibility; 141 mod change_visibility;
118 mod early_return; 142 mod early_return;
143 mod extract_struct_from_enum_variant;
144 mod extract_variable;
119 mod fill_match_arms; 145 mod fill_match_arms;
120 mod fix_visibility; 146 mod fix_visibility;
121 mod flip_binexpr; 147 mod flip_binexpr;
122 mod flip_comma; 148 mod flip_comma;
123 mod flip_trait_bound; 149 mod flip_trait_bound;
150 mod generate_derive;
151 mod generate_from_impl_for_enum;
152 mod generate_function;
153 mod generate_impl;
154 mod generate_new;
124 mod inline_local_variable; 155 mod inline_local_variable;
125 mod introduce_variable; 156 mod introduce_named_lifetime;
126 mod invert_if; 157 mod invert_if;
127 mod merge_imports; 158 mod merge_imports;
128 mod merge_match_arms; 159 mod merge_match_arms;
@@ -143,26 +174,27 @@ mod handlers {
143 &[ 174 &[
144 // These are alphabetic for the foolish consistency 175 // These are alphabetic for the foolish consistency
145 add_custom_impl::add_custom_impl, 176 add_custom_impl::add_custom_impl,
146 add_derive::add_derive,
147 add_explicit_type::add_explicit_type, 177 add_explicit_type::add_explicit_type,
148 add_from_impl_for_enum::add_from_impl_for_enum,
149 add_function::add_function,
150 add_impl::add_impl,
151 add_new::add_new,
152 add_turbo_fish::add_turbo_fish, 178 add_turbo_fish::add_turbo_fish,
153 apply_demorgan::apply_demorgan, 179 apply_demorgan::apply_demorgan,
154 auto_import::auto_import, 180 auto_import::auto_import,
155 change_lifetime_anon_to_named::change_lifetime_anon_to_named,
156 change_return_type_to_result::change_return_type_to_result, 181 change_return_type_to_result::change_return_type_to_result,
157 change_visibility::change_visibility, 182 change_visibility::change_visibility,
158 early_return::convert_to_guarded_return, 183 early_return::convert_to_guarded_return,
184 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
185 extract_variable::extract_variable,
159 fill_match_arms::fill_match_arms, 186 fill_match_arms::fill_match_arms,
160 fix_visibility::fix_visibility, 187 fix_visibility::fix_visibility,
161 flip_binexpr::flip_binexpr, 188 flip_binexpr::flip_binexpr,
162 flip_comma::flip_comma, 189 flip_comma::flip_comma,
163 flip_trait_bound::flip_trait_bound, 190 flip_trait_bound::flip_trait_bound,
191 generate_derive::generate_derive,
192 generate_from_impl_for_enum::generate_from_impl_for_enum,
193 generate_function::generate_function,
194 generate_impl::generate_impl,
195 generate_new::generate_new,
164 inline_local_variable::inline_local_variable, 196 inline_local_variable::inline_local_variable,
165 introduce_variable::introduce_variable, 197 introduce_named_lifetime::introduce_named_lifetime,
166 invert_if::invert_if, 198 invert_if::invert_if,
167 merge_imports::merge_imports, 199 merge_imports::merge_imports,
168 merge_match_arms::merge_match_arms, 200 merge_match_arms::merge_match_arms,
diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs
index 62dd3547f..18fcb9049 100644
--- a/crates/ra_assists/src/tests.rs
+++ b/crates/ra_assists/src/tests.rs
@@ -1,27 +1,21 @@
1mod generated; 1mod generated;
2 2
3use std::sync::Arc;
4
5use hir::Semantics; 3use hir::Semantics;
6use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; 4use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
7use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; 5use ra_ide_db::RootDatabase;
8use ra_syntax::TextRange; 6use ra_syntax::TextRange;
9use test_utils::{ 7use test_utils::{assert_eq_text, extract_offset, extract_range};
10 assert_eq_text, extract_offset, extract_range, extract_range_or_offset, RangeOrOffset,
11};
12 8
13use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, Assists}; 9use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
10use stdx::trim_indent;
14 11
15pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { 12pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
16 let (mut db, file_id) = RootDatabase::with_single_file(text); 13 RootDatabase::with_single_file(text)
17 // FIXME: ideally, this should be done by the above `RootDatabase::with_single_file`,
18 // but it looks like this might need specialization? :(
19 db.set_local_roots(Arc::new(vec![db.file_source_root(file_id)]));
20 (db, file_id)
21} 14}
22 15
23pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) { 16pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_after: &str) {
24 check(assist, ra_fixture_before, ExpectedResult::After(ra_fixture_after)); 17 let ra_fixture_after = trim_indent(ra_fixture_after);
18 check(assist, ra_fixture_before, ExpectedResult::After(&ra_fixture_after));
25} 19}
26 20
27// FIXME: instead of having a separate function here, maybe use 21// FIXME: instead of having a separate function here, maybe use
@@ -36,8 +30,9 @@ pub(crate) fn check_assist_not_applicable(assist: Handler, ra_fixture: &str) {
36} 30}
37 31
38fn check_doc_test(assist_id: &str, before: &str, after: &str) { 32fn check_doc_test(assist_id: &str, before: &str, after: &str) {
39 let (selection, before) = extract_range_or_offset(before); 33 let after = trim_indent(after);
40 let (db, file_id) = crate::tests::with_single_file(&before); 34 let (db, file_id, selection) = RootDatabase::with_range_or_offset(&before);
35 let before = db.file_text(file_id).to_string();
41 let frange = FileRange { file_id, range: selection.into() }; 36 let frange = FileRange { file_id, range: selection.into() };
42 37
43 let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange) 38 let mut assist = Assist::resolved(&db, &AssistConfig::default(), frange)
@@ -57,11 +52,11 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
57 52
58 let actual = { 53 let actual = {
59 let change = assist.source_change.source_file_edits.pop().unwrap(); 54 let change = assist.source_change.source_file_edits.pop().unwrap();
60 let mut actual = before.clone(); 55 let mut actual = before;
61 change.edit.apply(&mut actual); 56 change.edit.apply(&mut actual);
62 actual 57 actual
63 }; 58 };
64 assert_eq_text!(after, &actual); 59 assert_eq_text!(&after, &actual);
65} 60}
66 61
67enum ExpectedResult<'a> { 62enum ExpectedResult<'a> {
@@ -71,20 +66,8 @@ enum ExpectedResult<'a> {
71} 66}
72 67
73fn check(handler: Handler, before: &str, expected: ExpectedResult) { 68fn check(handler: Handler, before: &str, expected: ExpectedResult) {
74 let (text_without_caret, file_with_caret_id, range_or_offset, db) = if before.contains("//-") { 69 let (db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
75 let (mut db, position) = RootDatabase::with_position(before); 70 let text_without_caret = db.file_text(file_with_caret_id).to_string();
76 db.set_local_roots(Arc::new(vec![db.file_source_root(position.file_id)]));
77 (
78 db.file_text(position.file_id).as_ref().to_owned(),
79 position.file_id,
80 RangeOrOffset::Offset(position.offset),
81 db,
82 )
83 } else {
84 let (range_or_offset, text_without_caret) = extract_range_or_offset(before);
85 let (db, file_id) = with_single_file(&text_without_caret);
86 (text_without_caret, file_id, range_or_offset, db)
87 };
88 71
89 let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() }; 72 let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
90 73
@@ -151,3 +134,46 @@ fn assist_order_if_expr() {
151 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); 134 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
152 assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); 135 assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match");
153} 136}
137
138#[test]
139fn assist_filter_works() {
140 let before = "
141 pub fn test_some_range(a: int) -> bool {
142 if let 2..6 = <|>5<|> {
143 true
144 } else {
145 false
146 }
147 }";
148 let (range, before) = extract_range(before);
149 let (db, file_id) = with_single_file(&before);
150 let frange = FileRange { file_id, range };
151
152 {
153 let mut cfg = AssistConfig::default();
154 cfg.allowed = Some(vec![AssistKind::Refactor]);
155
156 let assists = Assist::resolved(&db, &cfg, frange);
157 let mut assists = assists.iter();
158
159 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
160 assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match");
161 }
162
163 {
164 let mut cfg = AssistConfig::default();
165 cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
166 let assists = Assist::resolved(&db, &cfg, frange);
167 assert_eq!(assists.len(), 1);
168
169 let mut assists = assists.iter();
170 assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable");
171 }
172
173 {
174 let mut cfg = AssistConfig::default();
175 cfg.allowed = Some(vec![AssistKind::QuickFix]);
176 let assists = Assist::resolved(&db, &cfg, frange);
177 assert!(assists.is_empty(), "All asserts but quickfixes should be filtered out");
178 }
179}
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs
index 73d43283d..eff7feded 100644
--- a/crates/ra_assists/src/tests/generated.rs
+++ b/crates/ra_assists/src/tests/generated.rs
@@ -22,26 +22,6 @@ impl Debug for S {
22} 22}
23 23
24#[test] 24#[test]
25fn doctest_add_derive() {
26 check_doc_test(
27 "add_derive",
28 r#####"
29struct Point {
30 x: u32,
31 y: u32,<|>
32}
33"#####,
34 r#####"
35#[derive($0)]
36struct Point {
37 x: u32,
38 y: u32,
39}
40"#####,
41 )
42}
43
44#[test]
45fn doctest_add_explicit_type() { 25fn doctest_add_explicit_type() {
46 check_doc_test( 26 check_doc_test(
47 "add_explicit_type", 27 "add_explicit_type",
@@ -59,52 +39,6 @@ fn main() {
59} 39}
60 40
61#[test] 41#[test]
62fn doctest_add_from_impl_for_enum() {
63 check_doc_test(
64 "add_from_impl_for_enum",
65 r#####"
66enum A { <|>One(u32) }
67"#####,
68 r#####"
69enum A { One(u32) }
70
71impl From<u32> for A {
72 fn from(v: u32) -> Self {
73 A::One(v)
74 }
75}
76"#####,
77 )
78}
79
80#[test]
81fn doctest_add_function() {
82 check_doc_test(
83 "add_function",
84 r#####"
85struct Baz;
86fn baz() -> Baz { Baz }
87fn foo() {
88 bar<|>("", baz());
89}
90
91"#####,
92 r#####"
93struct Baz;
94fn baz() -> Baz { Baz }
95fn foo() {
96 bar("", baz());
97}
98
99fn bar(arg: &str, baz: Baz) {
100 ${0:todo!()}
101}
102
103"#####,
104 )
105}
106
107#[test]
108fn doctest_add_hash() { 42fn doctest_add_hash() {
109 check_doc_test( 43 check_doc_test(
110 "add_hash", 44 "add_hash",
@@ -122,27 +56,6 @@ fn main() {
122} 56}
123 57
124#[test] 58#[test]
125fn doctest_add_impl() {
126 check_doc_test(
127 "add_impl",
128 r#####"
129struct Ctx<T: Clone> {
130 data: T,<|>
131}
132"#####,
133 r#####"
134struct Ctx<T: Clone> {
135 data: T,
136}
137
138impl<T: Clone> Ctx<T> {
139 $0
140}
141"#####,
142 )
143}
144
145#[test]
146fn doctest_add_impl_default_members() { 59fn doctest_add_impl_default_members() {
147 check_doc_test( 60 check_doc_test(
148 "add_impl_default_members", 61 "add_impl_default_members",
@@ -209,28 +122,6 @@ impl Trait<u32> for () {
209} 122}
210 123
211#[test] 124#[test]
212fn doctest_add_new() {
213 check_doc_test(
214 "add_new",
215 r#####"
216struct Ctx<T: Clone> {
217 data: T,<|>
218}
219"#####,
220 r#####"
221struct Ctx<T: Clone> {
222 data: T,
223}
224
225impl<T: Clone> Ctx<T> {
226 fn $0new(data: T) -> Self { Self { data } }
227}
228
229"#####,
230 )
231}
232
233#[test]
234fn doctest_add_turbo_fish() { 125fn doctest_add_turbo_fish() {
235 check_doc_test( 126 check_doc_test(
236 "add_turbo_fish", 127 "add_turbo_fish",
@@ -288,31 +179,6 @@ pub mod std { pub mod collections { pub struct HashMap { } } }
288} 179}
289 180
290#[test] 181#[test]
291fn doctest_change_lifetime_anon_to_named() {
292 check_doc_test(
293 "change_lifetime_anon_to_named",
294 r#####"
295impl Cursor<'_<|>> {
296 fn node(self) -> &SyntaxNode {
297 match self {
298 Cursor::Replace(node) | Cursor::Before(node) => node,
299 }
300 }
301}
302"#####,
303 r#####"
304impl<'a> Cursor<'a> {
305 fn node(self) -> &SyntaxNode {
306 match self {
307 Cursor::Replace(node) | Cursor::Before(node) => node,
308 }
309 }
310}
311"#####,
312 )
313}
314
315#[test]
316fn doctest_change_return_type_to_result() { 182fn doctest_change_return_type_to_result() {
317 check_doc_test( 183 check_doc_test(
318 "change_return_type_to_result", 184 "change_return_type_to_result",
@@ -363,6 +229,39 @@ fn main() {
363} 229}
364 230
365#[test] 231#[test]
232fn doctest_extract_struct_from_enum_variant() {
233 check_doc_test(
234 "extract_struct_from_enum_variant",
235 r#####"
236enum A { <|>One(u32, u32) }
237"#####,
238 r#####"
239struct One(pub u32, pub u32);
240
241enum A { One(One) }
242"#####,
243 )
244}
245
246#[test]
247fn doctest_extract_variable() {
248 check_doc_test(
249 "extract_variable",
250 r#####"
251fn main() {
252 <|>(1 + 2)<|> * 4;
253}
254"#####,
255 r#####"
256fn main() {
257 let $0var_name = (1 + 2);
258 var_name * 4;
259}
260"#####,
261 )
262}
263
264#[test]
366fn doctest_fill_match_arms() { 265fn doctest_fill_match_arms() {
367 check_doc_test( 266 check_doc_test(
368 "fill_match_arms", 267 "fill_match_arms",
@@ -459,6 +358,115 @@ fn foo<T: Copy + Clone>() { }
459} 358}
460 359
461#[test] 360#[test]
361fn doctest_generate_derive() {
362 check_doc_test(
363 "generate_derive",
364 r#####"
365struct Point {
366 x: u32,
367 y: u32,<|>
368}
369"#####,
370 r#####"
371#[derive($0)]
372struct Point {
373 x: u32,
374 y: u32,
375}
376"#####,
377 )
378}
379
380#[test]
381fn doctest_generate_from_impl_for_enum() {
382 check_doc_test(
383 "generate_from_impl_for_enum",
384 r#####"
385enum A { <|>One(u32) }
386"#####,
387 r#####"
388enum A { One(u32) }
389
390impl From<u32> for A {
391 fn from(v: u32) -> Self {
392 A::One(v)
393 }
394}
395"#####,
396 )
397}
398
399#[test]
400fn doctest_generate_function() {
401 check_doc_test(
402 "generate_function",
403 r#####"
404struct Baz;
405fn baz() -> Baz { Baz }
406fn foo() {
407 bar<|>("", baz());
408}
409
410"#####,
411 r#####"
412struct Baz;
413fn baz() -> Baz { Baz }
414fn foo() {
415 bar("", baz());
416}
417
418fn bar(arg: &str, baz: Baz) {
419 ${0:todo!()}
420}
421
422"#####,
423 )
424}
425
426#[test]
427fn doctest_generate_impl() {
428 check_doc_test(
429 "generate_impl",
430 r#####"
431struct Ctx<T: Clone> {
432 data: T,<|>
433}
434"#####,
435 r#####"
436struct Ctx<T: Clone> {
437 data: T,
438}
439
440impl<T: Clone> Ctx<T> {
441 $0
442}
443"#####,
444 )
445}
446
447#[test]
448fn doctest_generate_new() {
449 check_doc_test(
450 "generate_new",
451 r#####"
452struct Ctx<T: Clone> {
453 data: T,<|>
454}
455"#####,
456 r#####"
457struct Ctx<T: Clone> {
458 data: T,
459}
460
461impl<T: Clone> Ctx<T> {
462 fn $0new(data: T) -> Self { Self { data } }
463}
464
465"#####,
466 )
467}
468
469#[test]
462fn doctest_inline_local_variable() { 470fn doctest_inline_local_variable() {
463 check_doc_test( 471 check_doc_test(
464 "inline_local_variable", 472 "inline_local_variable",
@@ -477,18 +485,25 @@ fn main() {
477} 485}
478 486
479#[test] 487#[test]
480fn doctest_introduce_variable() { 488fn doctest_introduce_named_lifetime() {
481 check_doc_test( 489 check_doc_test(
482 "introduce_variable", 490 "introduce_named_lifetime",
483 r#####" 491 r#####"
484fn main() { 492impl Cursor<'_<|>> {
485 <|>(1 + 2)<|> * 4; 493 fn node(self) -> &SyntaxNode {
494 match self {
495 Cursor::Replace(node) | Cursor::Before(node) => node,
496 }
497 }
486} 498}
487"#####, 499"#####,
488 r#####" 500 r#####"
489fn main() { 501impl<'a> Cursor<'a> {
490 let $0var_name = (1 + 2); 502 fn node(self) -> &SyntaxNode {
491 var_name * 4; 503 match self {
504 Cursor::Replace(node) | Cursor::Before(node) => node,
505 }
506 }
492} 507}
493"#####, 508"#####,
494 ) 509 )
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
index 0038a9764..02de902d6 100644
--- a/crates/ra_assists/src/utils.rs
+++ b/crates/ra_assists/src/utils.rs
@@ -7,13 +7,15 @@ use hir::{Adt, Crate, Enum, ScopeDef, Semantics, Trait, Type};
7use ra_ide_db::RootDatabase; 7use ra_ide_db::RootDatabase;
8use ra_syntax::{ 8use ra_syntax::{
9 ast::{self, make, NameOwner}, 9 ast::{self, make, NameOwner},
10 AstNode, SyntaxNode, T, 10 AstNode,
11 SyntaxKind::*,
12 SyntaxNode, TextSize, T,
11}; 13};
12use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
13 15
14use crate::assist_config::SnippetCap; 16use crate::assist_config::SnippetCap;
15 17
16pub(crate) use insert_use::insert_use_statement; 18pub(crate) use insert_use::{find_insert_use_container, insert_use_statement};
17 19
18#[derive(Clone, Copy, Debug)] 20#[derive(Clone, Copy, Debug)]
19pub(crate) enum Cursor<'a> { 21pub(crate) enum Cursor<'a> {
@@ -120,6 +122,13 @@ pub(crate) fn resolve_target_trait(
120 } 122 }
121} 123}
122 124
125pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize {
126 node.children_with_tokens()
127 .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR))
128 .map(|it| it.text_range().start())
129 .unwrap_or_else(|| node.text_range().start())
130}
131
123pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { 132pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
124 if let Some(expr) = invert_special_case(&expr) { 133 if let Some(expr) = invert_special_case(&expr) {
125 return expr; 134 return expr;
@@ -198,8 +207,7 @@ pub(crate) struct FamousDefs<'a, 'b>(pub(crate) &'a Semantics<'b, RootDatabase>,
198#[allow(non_snake_case)] 207#[allow(non_snake_case)]
199impl FamousDefs<'_, '_> { 208impl FamousDefs<'_, '_> {
200 #[cfg(test)] 209 #[cfg(test)]
201 pub(crate) const FIXTURE: &'static str = r#" 210 pub(crate) const FIXTURE: &'static str = r#"//- /libcore.rs crate:core
202//- /libcore.rs crate:core
203pub mod convert { 211pub mod convert {
204 pub trait From<T> { 212 pub trait From<T> {
205 fn from(T) -> Self; 213 fn from(T) -> Self;
diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs
index 0ee43482f..8c4f33e59 100644
--- a/crates/ra_assists/src/utils/insert_use.rs
+++ b/crates/ra_assists/src/utils/insert_use.rs
@@ -12,6 +12,20 @@ use ra_syntax::{
12use ra_text_edit::TextEditBuilder; 12use ra_text_edit::TextEditBuilder;
13 13
14use crate::assist_context::AssistContext; 14use crate::assist_context::AssistContext;
15use either::Either;
16
17/// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
18pub(crate) fn find_insert_use_container(
19 position: &SyntaxNode,
20 ctx: &AssistContext,
21) -> Option<Either<ast::ItemList, ast::SourceFile>> {
22 ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| {
23 if let Some(module) = ast::Module::cast(n.clone()) {
24 return module.item_list().map(|it| Either::Left(it));
25 }
26 Some(Either::Right(ast::SourceFile::cast(n)?))
27 })
28}
15 29
16/// Creates and inserts a use statement for the given path to import. 30/// Creates and inserts a use statement for the given path to import.
17/// The use statement is inserted in the scope most appropriate to the 31/// The use statement is inserted in the scope most appropriate to the
@@ -24,15 +38,11 @@ pub(crate) fn insert_use_statement(
24 builder: &mut TextEditBuilder, 38 builder: &mut TextEditBuilder,
25) { 39) {
26 let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>(); 40 let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>();
27 let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| { 41 let container = find_insert_use_container(position, ctx);
28 if let Some(module) = ast::Module::cast(n.clone()) {
29 return module.item_list().map(|it| it.syntax().clone());
30 }
31 ast::SourceFile::cast(n).map(|it| it.syntax().clone())
32 });
33 42
34 if let Some(container) = container { 43 if let Some(container) = container {
35 let action = best_action_for_target(container, position.clone(), &target); 44 let syntax = container.either(|l| l.syntax().clone(), |r| r.syntax().clone());
45 let action = best_action_for_target(syntax, position.clone(), &target);
36 make_assist(&action, &target, builder); 46 make_assist(&action, &target, builder);
37 } 47 }
38} 48}
diff --git a/crates/ra_cfg/Cargo.toml b/crates/ra_cfg/Cargo.toml
index 9165076a5..6425cd6d6 100644
--- a/crates/ra_cfg/Cargo.toml
+++ b/crates/ra_cfg/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_cfg" 3name = "ra_cfg"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
diff --git a/crates/ra_cfg/src/lib.rs b/crates/ra_cfg/src/lib.rs
index 57feabcb2..f9c73ece1 100644
--- a/crates/ra_cfg/src/lib.rs
+++ b/crates/ra_cfg/src/lib.rs
@@ -46,4 +46,14 @@ impl CfgOptions {
46 pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) { 46 pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) {
47 self.key_values.insert((key, value)); 47 self.key_values.insert((key, value));
48 } 48 }
49
50 pub fn append(&mut self, other: &CfgOptions) {
51 for atom in &other.atoms {
52 self.atoms.insert(atom.clone());
53 }
54
55 for (key, value) in &other.key_values {
56 self.key_values.insert((key.clone(), value.clone()));
57 }
58 }
49} 59}
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml
index 8ab409158..5f334d04f 100644
--- a/crates/ra_db/Cargo.toml
+++ b/crates/ra_db/Cargo.toml
@@ -3,13 +3,13 @@ edition = "2018"
3name = "ra_db" 3name = "ra_db"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
9 10
10[dependencies] 11[dependencies]
11salsa = "0.14.1" 12salsa = "0.15.0"
12relative-path = "1.0.0"
13rustc-hash = "1.1.0" 13rustc-hash = "1.1.0"
14 14
15ra_syntax = { path = "../ra_syntax" } 15ra_syntax = { path = "../ra_syntax" }
@@ -17,3 +17,5 @@ ra_cfg = { path = "../ra_cfg" }
17ra_prof = { path = "../ra_prof" } 17ra_prof = { path = "../ra_prof" }
18ra_tt = { path = "../ra_tt" } 18ra_tt = { path = "../ra_tt" }
19test_utils = { path = "../test_utils" } 19test_utils = { path = "../test_utils" }
20vfs = { path = "../vfs" }
21stdx = { path = "../stdx" }
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index 482a2f3e6..209713987 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -57,17 +57,16 @@
57//! fn insert_source_code_here() {} 57//! fn insert_source_code_here() {}
58//! " 58//! "
59//! ``` 59//! ```
60 60use std::{str::FromStr, sync::Arc};
61use std::str::FromStr;
62use std::sync::Arc;
63 61
64use ra_cfg::CfgOptions; 62use ra_cfg::CfgOptions;
65use rustc_hash::FxHashMap; 63use rustc_hash::FxHashMap;
66use test_utils::{extract_offset, parse_fixture, parse_single_fixture, FixtureMeta, CURSOR_MARKER}; 64use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER};
65use vfs::{file_set::FileSet, VfsPath};
67 66
68use crate::{ 67use crate::{
69 input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf, 68 input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, SourceDatabaseExt,
70 SourceDatabaseExt, SourceRoot, SourceRootId, 69 SourceRoot, SourceRootId,
71}; 70};
72 71
73pub const WORKSPACE: SourceRootId = SourceRootId(0); 72pub const WORKSPACE: SourceRootId = SourceRootId(0);
@@ -75,21 +74,32 @@ pub const WORKSPACE: SourceRootId = SourceRootId(0);
75pub trait WithFixture: Default + SourceDatabaseExt + 'static { 74pub trait WithFixture: Default + SourceDatabaseExt + 'static {
76 fn with_single_file(text: &str) -> (Self, FileId) { 75 fn with_single_file(text: &str) -> (Self, FileId) {
77 let mut db = Self::default(); 76 let mut db = Self::default();
78 let file_id = with_single_file(&mut db, text); 77 let (_, files) = with_files(&mut db, text);
79 (db, file_id) 78 assert_eq!(files.len(), 1);
79 (db, files[0])
80 } 80 }
81 81
82 fn with_files(ra_fixture: &str) -> Self { 82 fn with_files(ra_fixture: &str) -> Self {
83 let mut db = Self::default(); 83 let mut db = Self::default();
84 let pos = with_files(&mut db, ra_fixture); 84 let (pos, _) = with_files(&mut db, ra_fixture);
85 assert!(pos.is_none()); 85 assert!(pos.is_none());
86 db 86 db
87 } 87 }
88 88
89 fn with_position(ra_fixture: &str) -> (Self, FilePosition) { 89 fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
90 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
91 let offset = match range_or_offset {
92 RangeOrOffset::Range(_) => panic!(),
93 RangeOrOffset::Offset(it) => it,
94 };
95 (db, FilePosition { file_id, offset })
96 }
97
98 fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) {
90 let mut db = Self::default(); 99 let mut db = Self::default();
91 let pos = with_files(&mut db, ra_fixture); 100 let (pos, _) = with_files(&mut db, ra_fixture);
92 (db, pos.unwrap()) 101 let (file_id, range_or_offset) = pos.unwrap();
102 (db, file_id, range_or_offset)
93 } 103 }
94 104
95 fn test_crate(&self) -> CrateId { 105 fn test_crate(&self) -> CrateId {
@@ -103,119 +113,64 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
103 113
104impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {} 114impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {}
105 115
106fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId { 116fn with_files(
107 let file_id = FileId(0); 117 db: &mut dyn SourceDatabaseExt,
108 let rel_path: RelativePathBuf = "/main.rs".into(); 118 fixture: &str,
109 119) -> (Option<(FileId, RangeOrOffset)>, Vec<FileId>) {
110 let mut source_root = SourceRoot::new_local(); 120 let fixture = Fixture::parse(fixture);
111 source_root.insert_file(rel_path.clone(), file_id);
112
113 let fixture = parse_single_fixture(ra_fixture);
114
115 let crate_graph = if let Some(entry) = fixture {
116 let meta = match ParsedMeta::from(&entry.meta) {
117 ParsedMeta::File(it) => it,
118 _ => panic!("with_single_file only support file meta"),
119 };
120
121 let mut crate_graph = CrateGraph::default();
122 crate_graph.add_crate_root(
123 file_id,
124 meta.edition,
125 meta.krate.map(|name| {
126 CrateName::new(&name).expect("Fixture crate name should not contain dashes")
127 }),
128 meta.cfg,
129 meta.env,
130 Default::default(),
131 Default::default(),
132 );
133 crate_graph
134 } else {
135 let mut crate_graph = CrateGraph::default();
136 crate_graph.add_crate_root(
137 file_id,
138 Edition::Edition2018,
139 None,
140 CfgOptions::default(),
141 Env::default(),
142 Default::default(),
143 Default::default(),
144 );
145 crate_graph
146 };
147
148 db.set_file_text(file_id, Arc::new(ra_fixture.to_string()));
149 db.set_file_relative_path(file_id, rel_path);
150 db.set_file_source_root(file_id, WORKSPACE);
151 db.set_source_root(WORKSPACE, Arc::new(source_root));
152 db.set_crate_graph(Arc::new(crate_graph));
153
154 file_id
155}
156
157fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosition> {
158 let fixture = parse_fixture(fixture);
159 121
122 let mut files = Vec::new();
160 let mut crate_graph = CrateGraph::default(); 123 let mut crate_graph = CrateGraph::default();
161 let mut crates = FxHashMap::default(); 124 let mut crates = FxHashMap::default();
162 let mut crate_deps = Vec::new(); 125 let mut crate_deps = Vec::new();
163 let mut default_crate_root: Option<FileId> = None; 126 let mut default_crate_root: Option<FileId> = None;
164 127
165 let mut source_root = SourceRoot::new_local(); 128 let mut file_set = FileSet::default();
166 let mut source_root_id = WORKSPACE; 129 let source_root_id = WORKSPACE;
167 let mut source_root_prefix: RelativePathBuf = "/".into(); 130 let source_root_prefix = "/".to_string();
168 let mut file_id = FileId(0); 131 let mut file_id = FileId(0);
169 132
170 let mut file_position = None; 133 let mut file_position = None;
171 134
172 for entry in fixture.iter() { 135 for entry in fixture {
173 let meta = match ParsedMeta::from(&entry.meta) { 136 let text = if entry.text.contains(CURSOR_MARKER) {
174 ParsedMeta::Root { path } => { 137 let (range_or_offset, text) = extract_range_or_offset(&entry.text);
175 let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local()); 138 assert!(file_position.is_none());
176 db.set_source_root(source_root_id, Arc::new(source_root)); 139 file_position = Some((file_id, range_or_offset));
177 source_root_id.0 += 1; 140 text.to_string()
178 source_root_prefix = path; 141 } else {
179 continue; 142 entry.text.clone()
180 }
181 ParsedMeta::File(it) => it,
182 }; 143 };
144
145 let meta = FileMeta::from(entry);
183 assert!(meta.path.starts_with(&source_root_prefix)); 146 assert!(meta.path.starts_with(&source_root_prefix));
184 147
185 if let Some(krate) = meta.krate { 148 if let Some(krate) = meta.krate {
186 let crate_id = crate_graph.add_crate_root( 149 let crate_id = crate_graph.add_crate_root(
187 file_id, 150 file_id,
188 meta.edition, 151 meta.edition,
189 Some(CrateName::new(&krate).unwrap()), 152 Some(krate.clone()),
190 meta.cfg, 153 meta.cfg,
191 meta.env, 154 meta.env,
192 Default::default(), 155 Default::default(),
193 Default::default(),
194 ); 156 );
195 let prev = crates.insert(krate.clone(), crate_id); 157 let crate_name = CrateName::new(&krate).unwrap();
158 let prev = crates.insert(crate_name.clone(), crate_id);
196 assert!(prev.is_none()); 159 assert!(prev.is_none());
197 for dep in meta.deps { 160 for dep in meta.deps {
198 crate_deps.push((krate.clone(), dep)) 161 let dep = CrateName::new(&dep).unwrap();
162 crate_deps.push((crate_name.clone(), dep))
199 } 163 }
200 } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { 164 } else if meta.path == "/main.rs" || meta.path == "/lib.rs" {
201 assert!(default_crate_root.is_none()); 165 assert!(default_crate_root.is_none());
202 default_crate_root = Some(file_id); 166 default_crate_root = Some(file_id);
203 } 167 }
204 168
205 let text = if entry.text.contains(CURSOR_MARKER) {
206 let (offset, text) = extract_offset(&entry.text);
207 assert!(file_position.is_none());
208 file_position = Some(FilePosition { file_id, offset });
209 text.to_string()
210 } else {
211 entry.text.to_string()
212 };
213
214 db.set_file_text(file_id, Arc::new(text)); 169 db.set_file_text(file_id, Arc::new(text));
215 db.set_file_relative_path(file_id, meta.path.clone());
216 db.set_file_source_root(file_id, source_root_id); 170 db.set_file_source_root(file_id, source_root_id);
217 source_root.insert_file(meta.path, file_id); 171 let path = VfsPath::new_virtual_path(meta.path);
218 172 file_set.insert(file_id, path.into());
173 files.push(file_id);
219 file_id.0 += 1; 174 file_id.0 += 1;
220 } 175 }
221 176
@@ -228,7 +183,6 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
228 CfgOptions::default(), 183 CfgOptions::default(),
229 Env::default(), 184 Env::default(),
230 Default::default(), 185 Default::default(),
231 Default::default(),
232 ); 186 );
233 } else { 187 } else {
234 for (from, to) in crate_deps { 188 for (from, to) in crate_deps {
@@ -238,19 +192,14 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
238 } 192 }
239 } 193 }
240 194
241 db.set_source_root(source_root_id, Arc::new(source_root)); 195 db.set_source_root(source_root_id, Arc::new(SourceRoot::new_local(file_set)));
242 db.set_crate_graph(Arc::new(crate_graph)); 196 db.set_crate_graph(Arc::new(crate_graph));
243 197
244 file_position 198 (file_position, files)
245}
246
247enum ParsedMeta {
248 Root { path: RelativePathBuf },
249 File(FileMeta),
250} 199}
251 200
252struct FileMeta { 201struct FileMeta {
253 path: RelativePathBuf, 202 path: String,
254 krate: Option<String>, 203 krate: Option<String>,
255 deps: Vec<String>, 204 deps: Vec<String>,
256 cfg: CfgOptions, 205 cfg: CfgOptions,
@@ -258,25 +207,22 @@ struct FileMeta {
258 env: Env, 207 env: Env,
259} 208}
260 209
261impl From<&FixtureMeta> for ParsedMeta { 210impl From<Fixture> for FileMeta {
262 fn from(meta: &FixtureMeta) -> Self { 211 fn from(f: Fixture) -> FileMeta {
263 match meta { 212 let mut cfg = CfgOptions::default();
264 FixtureMeta::Root { path } => { 213 f.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into()));
265 // `Self::Root` causes a false warning: 'variant is never constructed: `Root` ' 214 f.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into()));
266 // see https://github.com/rust-lang/rust/issues/69018 215
267 ParsedMeta::Root { path: path.to_owned() } 216 FileMeta {
268 } 217 path: f.path,
269 FixtureMeta::File(f) => Self::File(FileMeta { 218 krate: f.krate,
270 path: f.path.to_owned(), 219 deps: f.deps,
271 krate: f.crate_name.to_owned(), 220 cfg,
272 deps: f.deps.to_owned(), 221 edition: f
273 cfg: f.cfg.to_owned(), 222 .edition
274 edition: f 223 .as_ref()
275 .edition 224 .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()),
276 .as_ref() 225 env: Env::from(f.env.iter()),
277 .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()),
278 env: Env::from(f.env.iter()),
279 }),
280 } 226 }
281 } 227 }
282} 228}
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 4d2d3b48a..aaa492759 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -6,29 +6,15 @@
6//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how 6//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
7//! actual IO is done and lowered to input. 7//! actual IO is done and lowered to input.
8 8
9use std::{ 9use std::{fmt, ops, str::FromStr, sync::Arc};
10 fmt, ops,
11 path::{Path, PathBuf},
12 str::FromStr,
13 sync::Arc,
14};
15 10
16use ra_cfg::CfgOptions; 11use ra_cfg::CfgOptions;
17use ra_syntax::SmolStr; 12use ra_syntax::SmolStr;
18use rustc_hash::FxHashMap;
19use rustc_hash::FxHashSet;
20
21use crate::{RelativePath, RelativePathBuf};
22use fmt::Display;
23use ra_tt::TokenExpander; 13use ra_tt::TokenExpander;
14use rustc_hash::{FxHashMap, FxHashSet};
15use vfs::file_set::FileSet;
24 16
25/// `FileId` is an integer which uniquely identifies a file. File paths are 17pub use vfs::FileId;
26/// messy and system-dependent, so most of the code should work directly with
27/// `FileId`, without inspecting the path. The mapping between `FileId` and path
28/// and `SourceRoot` is constant. A file rename is represented as a pair of
29/// deletion/creation.
30#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
31pub struct FileId(pub u32);
32 18
33/// Files are grouped into source roots. A source root is a directory on the 19/// Files are grouped into source roots. A source root is a directory on the
34/// file systems which is watched for changes. Typically it corresponds to a 20/// file systems which is watched for changes. Typically it corresponds to a
@@ -47,27 +33,18 @@ pub struct SourceRoot {
47 /// Libraries are considered mostly immutable, this assumption is used to 33 /// Libraries are considered mostly immutable, this assumption is used to
48 /// optimize salsa's query structure 34 /// optimize salsa's query structure
49 pub is_library: bool, 35 pub is_library: bool,
50 files: FxHashMap<RelativePathBuf, FileId>, 36 pub(crate) file_set: FileSet,
51} 37}
52 38
53impl SourceRoot { 39impl SourceRoot {
54 pub fn new_local() -> SourceRoot { 40 pub fn new_local(file_set: FileSet) -> SourceRoot {
55 SourceRoot { is_library: false, files: Default::default() } 41 SourceRoot { is_library: false, file_set }
56 }
57 pub fn new_library() -> SourceRoot {
58 SourceRoot { is_library: true, files: Default::default() }
59 }
60 pub fn insert_file(&mut self, path: RelativePathBuf, file_id: FileId) {
61 self.files.insert(path, file_id);
62 } 42 }
63 pub fn remove_file(&mut self, path: &RelativePath) { 43 pub fn new_library(file_set: FileSet) -> SourceRoot {
64 self.files.remove(path); 44 SourceRoot { is_library: true, file_set }
65 } 45 }
66 pub fn walk(&self) -> impl Iterator<Item = FileId> + '_ { 46 pub fn iter(&self) -> impl Iterator<Item = FileId> + '_ {
67 self.files.values().copied() 47 self.file_set.iter()
68 }
69 pub fn file_by_relative_path(&self, path: &RelativePath) -> Option<FileId> {
70 self.files.get(path).copied()
71 } 48 }
72} 49}
73 50
@@ -90,7 +67,7 @@ pub struct CrateGraph {
90#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
91pub struct CrateId(pub u32); 68pub struct CrateId(pub u32);
92 69
93#[derive(Debug, Clone, PartialEq, Eq)] 70#[derive(Debug, Clone, PartialEq, Eq, Hash)]
94pub struct CrateName(SmolStr); 71pub struct CrateName(SmolStr);
95 72
96impl CrateName { 73impl CrateName {
@@ -111,12 +88,19 @@ impl CrateName {
111 } 88 }
112} 89}
113 90
114impl Display for CrateName { 91impl fmt::Display for CrateName {
115 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 write!(f, "{}", self.0) 93 write!(f, "{}", self.0)
117 } 94 }
118} 95}
119 96
97impl ops::Deref for CrateName {
98 type Target = str;
99 fn deref(&self) -> &Self::Target {
100 &*self.0
101 }
102}
103
120#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 104#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
121pub struct ProcMacroId(pub u32); 105pub struct ProcMacroId(pub u32);
122 106
@@ -140,10 +124,9 @@ pub struct CrateData {
140 /// The name to display to the end user. 124 /// The name to display to the end user.
141 /// This actual crate name can be different in a particular dependent crate 125 /// This actual crate name can be different in a particular dependent crate
142 /// or may even be missing for some cases, such as a dummy crate for the code snippet. 126 /// or may even be missing for some cases, such as a dummy crate for the code snippet.
143 pub display_name: Option<CrateName>, 127 pub display_name: Option<String>,
144 pub cfg_options: CfgOptions, 128 pub cfg_options: CfgOptions,
145 pub env: Env, 129 pub env: Env,
146 pub extern_source: ExternSource,
147 pub dependencies: Vec<Dependency>, 130 pub dependencies: Vec<Dependency>,
148 pub proc_macro: Vec<ProcMacro>, 131 pub proc_macro: Vec<ProcMacro>,
149} 132}
@@ -154,26 +137,15 @@ pub enum Edition {
154 Edition2015, 137 Edition2015,
155} 138}
156 139
157#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
158pub struct ExternSourceId(pub u32);
159
160#[derive(Default, Debug, Clone, PartialEq, Eq)] 140#[derive(Default, Debug, Clone, PartialEq, Eq)]
161pub struct Env { 141pub struct Env {
162 entries: FxHashMap<String, String>, 142 entries: FxHashMap<String, String>,
163} 143}
164 144
165// FIXME: Redesign vfs for solve the following limitation ?
166// Note: Some env variables (e.g. OUT_DIR) are located outside of the
167// crate. We store a map to allow remap it to ExternSourceId
168#[derive(Default, Debug, Clone, PartialEq, Eq)]
169pub struct ExternSource {
170 extern_paths: FxHashMap<PathBuf, ExternSourceId>,
171}
172
173#[derive(Debug, Clone, PartialEq, Eq)] 145#[derive(Debug, Clone, PartialEq, Eq)]
174pub struct Dependency { 146pub struct Dependency {
175 pub crate_id: CrateId, 147 pub crate_id: CrateId,
176 pub name: SmolStr, 148 pub name: CrateName,
177} 149}
178 150
179impl CrateGraph { 151impl CrateGraph {
@@ -181,10 +153,9 @@ impl CrateGraph {
181 &mut self, 153 &mut self,
182 file_id: FileId, 154 file_id: FileId,
183 edition: Edition, 155 edition: Edition,
184 display_name: Option<CrateName>, 156 display_name: Option<String>,
185 cfg_options: CfgOptions, 157 cfg_options: CfgOptions,
186 env: Env, 158 env: Env,
187 extern_source: ExternSource,
188 proc_macro: Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)>, 159 proc_macro: Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)>,
189 ) -> CrateId { 160 ) -> CrateId {
190 let proc_macro = 161 let proc_macro =
@@ -196,7 +167,6 @@ impl CrateGraph {
196 display_name, 167 display_name,
197 cfg_options, 168 cfg_options,
198 env, 169 env,
199 extern_source,
200 proc_macro, 170 proc_macro,
201 dependencies: Vec::new(), 171 dependencies: Vec::new(),
202 }; 172 };
@@ -215,7 +185,7 @@ impl CrateGraph {
215 if self.dfs_find(from, to, &mut FxHashSet::default()) { 185 if self.dfs_find(from, to, &mut FxHashSet::default()) {
216 return Err(CyclicDependenciesError); 186 return Err(CyclicDependenciesError);
217 } 187 }
218 self.arena.get_mut(&from).unwrap().add_dep(name.0, to); 188 self.arena.get_mut(&from).unwrap().add_dep(name, to);
219 Ok(()) 189 Ok(())
220 } 190 }
221 191
@@ -227,6 +197,23 @@ impl CrateGraph {
227 self.arena.keys().copied() 197 self.arena.keys().copied()
228 } 198 }
229 199
200 /// Returns an iterator over all transitive dependencies of the given crate.
201 pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> + '_ {
202 let mut worklist = vec![of];
203 let mut deps = FxHashSet::default();
204
205 while let Some(krate) = worklist.pop() {
206 if !deps.insert(krate) {
207 continue;
208 }
209
210 worklist.extend(self[krate].dependencies.iter().map(|dep| dep.crate_id));
211 }
212
213 deps.remove(&of);
214 deps.into_iter()
215 }
216
230 // FIXME: this only finds one crate with the given root; we could have multiple 217 // FIXME: this only finds one crate with the given root; we could have multiple
231 pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { 218 pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
232 let (&crate_id, _) = 219 let (&crate_id, _) =
@@ -256,12 +243,12 @@ impl CrateGraph {
256 return false; 243 return false;
257 } 244 }
258 245
246 if target == from {
247 return true;
248 }
249
259 for dep in &self[from].dependencies { 250 for dep in &self[from].dependencies {
260 let crate_id = dep.crate_id; 251 let crate_id = dep.crate_id;
261 if crate_id == target {
262 return true;
263 }
264
265 if self.dfs_find(target, crate_id, visited) { 252 if self.dfs_find(target, crate_id, visited) {
266 return true; 253 return true;
267 } 254 }
@@ -284,7 +271,7 @@ impl CrateId {
284} 271}
285 272
286impl CrateData { 273impl CrateData {
287 fn add_dep(&mut self, name: SmolStr, crate_id: CrateId) { 274 fn add_dep(&mut self, name: CrateName, crate_id: CrateId) {
288 self.dependencies.push(Dependency { name, crate_id }) 275 self.dependencies.push(Dependency { name, crate_id })
289 } 276 }
290} 277}
@@ -336,24 +323,6 @@ impl Env {
336 } 323 }
337} 324}
338 325
339impl ExternSource {
340 pub fn extern_path(&self, path: impl AsRef<Path>) -> Option<(ExternSourceId, RelativePathBuf)> {
341 let path = path.as_ref();
342 self.extern_paths.iter().find_map(|(root_path, id)| {
343 if let Ok(rel_path) = path.strip_prefix(root_path) {
344 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
345 Some((*id, rel_path))
346 } else {
347 None
348 }
349 })
350 }
351
352 pub fn set_extern_path(&mut self, root_path: &Path, root: ExternSourceId) {
353 self.extern_paths.insert(root_path.to_path_buf(), root);
354 }
355}
356
357#[derive(Debug)] 326#[derive(Debug)]
358pub struct ParseEditionError { 327pub struct ParseEditionError {
359 invalid_input: String, 328 invalid_input: String,
@@ -375,7 +344,7 @@ mod tests {
375 use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId}; 344 use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
376 345
377 #[test] 346 #[test]
378 fn it_should_panic_because_of_cycle_dependencies() { 347 fn detect_cyclic_dependency_indirect() {
379 let mut graph = CrateGraph::default(); 348 let mut graph = CrateGraph::default();
380 let crate1 = graph.add_crate_root( 349 let crate1 = graph.add_crate_root(
381 FileId(1u32), 350 FileId(1u32),
@@ -384,7 +353,6 @@ mod tests {
384 CfgOptions::default(), 353 CfgOptions::default(),
385 Env::default(), 354 Env::default(),
386 Default::default(), 355 Default::default(),
387 Default::default(),
388 ); 356 );
389 let crate2 = graph.add_crate_root( 357 let crate2 = graph.add_crate_root(
390 FileId(2u32), 358 FileId(2u32),
@@ -393,7 +361,6 @@ mod tests {
393 CfgOptions::default(), 361 CfgOptions::default(),
394 Env::default(), 362 Env::default(),
395 Default::default(), 363 Default::default(),
396 Default::default(),
397 ); 364 );
398 let crate3 = graph.add_crate_root( 365 let crate3 = graph.add_crate_root(
399 FileId(3u32), 366 FileId(3u32),
@@ -402,7 +369,6 @@ mod tests {
402 CfgOptions::default(), 369 CfgOptions::default(),
403 Env::default(), 370 Env::default(),
404 Default::default(), 371 Default::default(),
405 Default::default(),
406 ); 372 );
407 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); 373 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
408 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); 374 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
@@ -410,7 +376,7 @@ mod tests {
410 } 376 }
411 377
412 #[test] 378 #[test]
413 fn it_works() { 379 fn detect_cyclic_dependency_direct() {
414 let mut graph = CrateGraph::default(); 380 let mut graph = CrateGraph::default();
415 let crate1 = graph.add_crate_root( 381 let crate1 = graph.add_crate_root(
416 FileId(1u32), 382 FileId(1u32),
@@ -419,7 +385,6 @@ mod tests {
419 CfgOptions::default(), 385 CfgOptions::default(),
420 Env::default(), 386 Env::default(),
421 Default::default(), 387 Default::default(),
422 Default::default(),
423 ); 388 );
424 let crate2 = graph.add_crate_root( 389 let crate2 = graph.add_crate_root(
425 FileId(2u32), 390 FileId(2u32),
@@ -428,6 +393,28 @@ mod tests {
428 CfgOptions::default(), 393 CfgOptions::default(),
429 Env::default(), 394 Env::default(),
430 Default::default(), 395 Default::default(),
396 );
397 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
398 assert!(graph.add_dep(crate2, CrateName::new("crate2").unwrap(), crate2).is_err());
399 }
400
401 #[test]
402 fn it_works() {
403 let mut graph = CrateGraph::default();
404 let crate1 = graph.add_crate_root(
405 FileId(1u32),
406 Edition2018,
407 None,
408 CfgOptions::default(),
409 Env::default(),
410 Default::default(),
411 );
412 let crate2 = graph.add_crate_root(
413 FileId(2u32),
414 Edition2018,
415 None,
416 CfgOptions::default(),
417 Env::default(),
431 Default::default(), 418 Default::default(),
432 ); 419 );
433 let crate3 = graph.add_crate_root( 420 let crate3 = graph.add_crate_root(
@@ -437,7 +424,6 @@ mod tests {
437 CfgOptions::default(), 424 CfgOptions::default(),
438 Env::default(), 425 Env::default(),
439 Default::default(), 426 Default::default(),
440 Default::default(),
441 ); 427 );
442 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); 428 assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
443 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); 429 assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
@@ -453,7 +439,6 @@ mod tests {
453 CfgOptions::default(), 439 CfgOptions::default(),
454 Env::default(), 440 Env::default(),
455 Default::default(), 441 Default::default(),
456 Default::default(),
457 ); 442 );
458 let crate2 = graph.add_crate_root( 443 let crate2 = graph.add_crate_root(
459 FileId(2u32), 444 FileId(2u32),
@@ -462,14 +447,16 @@ mod tests {
462 CfgOptions::default(), 447 CfgOptions::default(),
463 Env::default(), 448 Env::default(),
464 Default::default(), 449 Default::default(),
465 Default::default(),
466 ); 450 );
467 assert!(graph 451 assert!(graph
468 .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) 452 .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2)
469 .is_ok()); 453 .is_ok());
470 assert_eq!( 454 assert_eq!(
471 graph[crate1].dependencies, 455 graph[crate1].dependencies,
472 vec![Dependency { crate_id: crate2, name: "crate_name_with_dashes".into() }] 456 vec![Dependency {
457 crate_id: crate2,
458 name: CrateName::new("crate_name_with_dashes").unwrap()
459 }]
473 ); 460 );
474 } 461 }
475} 462}
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index fd4280de2..f25be24fe 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -7,16 +7,17 @@ use std::{panic, sync::Arc};
7 7
8use ra_prof::profile; 8use ra_prof::profile;
9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; 9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize};
10use rustc_hash::FxHashSet;
10 11
11pub use crate::{ 12pub use crate::{
12 cancellation::Canceled, 13 cancellation::Canceled,
13 input::{ 14 input::{
14 CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId, 15 CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, ProcMacroId,
15 FileId, ProcMacroId, SourceRoot, SourceRootId, 16 SourceRoot, SourceRootId,
16 }, 17 },
17}; 18};
18pub use relative_path::{RelativePath, RelativePathBuf};
19pub use salsa; 19pub use salsa;
20pub use vfs::{file_set::FileSet, VfsPath};
20 21
21#[macro_export] 22#[macro_export]
22macro_rules! impl_intern_key { 23macro_rules! impl_intern_key {
@@ -78,7 +79,7 @@ pub struct FilePosition {
78 pub offset: TextSize, 79 pub offset: TextSize,
79} 80}
80 81
81#[derive(Clone, Copy, Debug)] 82#[derive(Clone, Copy, Debug, Eq, PartialEq)]
82pub struct FileRange { 83pub struct FileRange {
83 pub file_id: FileId, 84 pub file_id: FileId,
84 pub range: TextRange, 85 pub range: TextRange,
@@ -89,15 +90,13 @@ pub const DEFAULT_LRU_CAP: usize = 128;
89pub trait FileLoader { 90pub trait FileLoader {
90 /// Text of the file. 91 /// Text of the file.
91 fn file_text(&self, file_id: FileId) -> Arc<String>; 92 fn file_text(&self, file_id: FileId) -> Arc<String>;
92 fn resolve_relative_path(&self, anchor: FileId, relative_path: &RelativePath) 93 /// Note that we intentionally accept a `&str` and not a `&Path` here. This
93 -> Option<FileId>; 94 /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such,
94 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; 95 /// so the input is guaranteed to be utf-8 string. One might be tempted to
95 96 /// introduce some kind of "utf-8 path with / separators", but that's a bad idea. Behold
96 fn resolve_extern_path( 97 /// `#[path = "C://no/way"]`
97 &self, 98 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>;
98 extern_id: ExternSourceId, 99 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>;
99 relative_path: &RelativePath,
100 ) -> Option<FileId>;
101} 100}
102 101
103/// Database which stores all significant input facts: source code and project 102/// Database which stores all significant input facts: source code and project
@@ -113,8 +112,8 @@ pub trait SourceDatabase: CheckCanceled + FileLoader + std::fmt::Debug {
113 fn crate_graph(&self) -> Arc<CrateGraph>; 112 fn crate_graph(&self) -> Arc<CrateGraph>;
114} 113}
115 114
116fn parse_query(db: &impl SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> { 115fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
117 let _p = profile("parse_query"); 116 let _p = profile("parse_query").detail(|| format!("{:?}", file_id));
118 let text = db.file_text(file_id); 117 let text = db.file_text(file_id);
119 SourceFile::parse(&*text) 118 SourceFile::parse(&*text)
120} 119}
@@ -126,8 +125,6 @@ pub trait SourceDatabaseExt: SourceDatabase {
126 #[salsa::input] 125 #[salsa::input]
127 fn file_text(&self, file_id: FileId) -> Arc<String>; 126 fn file_text(&self, file_id: FileId) -> Arc<String>;
128 /// Path to a file, relative to the root of its source root. 127 /// Path to a file, relative to the root of its source root.
129 #[salsa::input]
130 fn file_relative_path(&self, file_id: FileId) -> RelativePathBuf;
131 /// Source root of the file. 128 /// Source root of the file.
132 #[salsa::input] 129 #[salsa::input]
133 fn file_source_root(&self, file_id: FileId) -> SourceRootId; 130 fn file_source_root(&self, file_id: FileId) -> SourceRootId;
@@ -135,16 +132,18 @@ pub trait SourceDatabaseExt: SourceDatabase {
135 #[salsa::input] 132 #[salsa::input]
136 fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; 133 fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
137 134
138 fn source_root_crates(&self, id: SourceRootId) -> Arc<Vec<CrateId>>; 135 fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>;
139} 136}
140 137
141fn source_root_crates( 138fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<FxHashSet<CrateId>> {
142 db: &(impl SourceDatabaseExt + SourceDatabase),
143 id: SourceRootId,
144) -> Arc<Vec<CrateId>> {
145 let root = db.source_root(id);
146 let graph = db.crate_graph(); 139 let graph = db.crate_graph();
147 let res = root.walk().filter_map(|it| graph.crate_id_for_crate_root(it)).collect::<Vec<_>>(); 140 let res = graph
141 .iter()
142 .filter(|&krate| {
143 let root_file = graph[krate].root_file_id;
144 db.file_source_root(root_file) == id
145 })
146 .collect::<FxHashSet<_>>();
148 Arc::new(res) 147 Arc::new(res)
149} 148}
150 149
@@ -155,33 +154,15 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
155 fn file_text(&self, file_id: FileId) -> Arc<String> { 154 fn file_text(&self, file_id: FileId) -> Arc<String> {
156 SourceDatabaseExt::file_text(self.0, file_id) 155 SourceDatabaseExt::file_text(self.0, file_id)
157 } 156 }
158 fn resolve_relative_path( 157 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
159 &self, 158 // FIXME: this *somehow* should be platform agnostic...
160 anchor: FileId,
161 relative_path: &RelativePath,
162 ) -> Option<FileId> {
163 let path = {
164 let mut path = self.0.file_relative_path(anchor);
165 assert!(path.pop());
166 path.push(relative_path);
167 path.normalize()
168 };
169 let source_root = self.0.file_source_root(anchor); 159 let source_root = self.0.file_source_root(anchor);
170 let source_root = self.0.source_root(source_root); 160 let source_root = self.0.source_root(source_root);
171 source_root.file_by_relative_path(&path) 161 source_root.file_set.resolve_path(anchor, path)
172 } 162 }
173 163
174 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 164 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
175 let source_root = self.0.file_source_root(file_id); 165 let source_root = self.0.file_source_root(file_id);
176 self.0.source_root_crates(source_root) 166 self.0.source_root_crates(source_root)
177 } 167 }
178
179 fn resolve_extern_path(
180 &self,
181 extern_id: ExternSourceId,
182 relative_path: &RelativePath,
183 ) -> Option<FileId> {
184 let source_root = self.0.source_root(SourceRootId(extern_id.0));
185 source_root.file_by_relative_path(&relative_path)
186 }
187} 168}
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs
deleted file mode 100644
index 041e38a9f..000000000
--- a/crates/ra_flycheck/src/lib.rs
+++ /dev/null
@@ -1,302 +0,0 @@
1//! cargo_check provides the functionality needed to run `cargo check` or
2//! another compatible command (f.x. clippy) in a background thread and provide
3//! LSP diagnostics based on the output of the command.
4
5use std::{
6 io::{self, BufReader},
7 path::PathBuf,
8 process::{Command, Stdio},
9 time::Instant,
10};
11
12use cargo_metadata::Message;
13use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
14
15pub use cargo_metadata::diagnostic::{
16 Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion,
17};
18
19#[derive(Clone, Debug, PartialEq, Eq)]
20pub enum FlycheckConfig {
21 CargoCommand { command: String, all_targets: bool, all_features: bool, extra_args: Vec<String> },
22 CustomCommand { command: String, args: Vec<String> },
23}
24
25/// Flycheck wraps the shared state and communication machinery used for
26/// running `cargo check` (or other compatible command) and providing
27/// diagnostics based on the output.
28/// The spawned thread is shut down when this struct is dropped.
29#[derive(Debug)]
30pub struct Flycheck {
31 // XXX: drop order is significant
32 cmd_send: Sender<CheckCommand>,
33 handle: jod_thread::JoinHandle<()>,
34 pub task_recv: Receiver<CheckTask>,
35}
36
37impl Flycheck {
38 pub fn new(config: FlycheckConfig, workspace_root: PathBuf) -> Flycheck {
39 let (task_send, task_recv) = unbounded::<CheckTask>();
40 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
41 let handle = jod_thread::spawn(move || {
42 FlycheckThread::new(config, workspace_root).run(&task_send, &cmd_recv);
43 });
44 Flycheck { task_recv, cmd_send, handle }
45 }
46
47 /// Schedule a re-start of the cargo check worker.
48 pub fn update(&self) {
49 self.cmd_send.send(CheckCommand::Update).unwrap();
50 }
51}
52
53#[derive(Debug)]
54pub enum CheckTask {
55 /// Request a clearing of all cached diagnostics from the check watcher
56 ClearDiagnostics,
57
58 /// Request adding a diagnostic with fixes included to a file
59 AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic },
60
61 /// Request check progress notification to client
62 Status(Status),
63}
64
65#[derive(Debug)]
66pub enum Status {
67 Being,
68 Progress(String),
69 End,
70}
71
72pub enum CheckCommand {
73 /// Request re-start of check thread
74 Update,
75}
76
77struct FlycheckThread {
78 config: FlycheckConfig,
79 workspace_root: PathBuf,
80 last_update_req: Option<Instant>,
81 // XXX: drop order is significant
82 message_recv: Receiver<CheckEvent>,
83 /// WatchThread exists to wrap around the communication needed to be able to
84 /// run `cargo check` without blocking. Currently the Rust standard library
85 /// doesn't provide a way to read sub-process output without blocking, so we
86 /// have to wrap sub-processes output handling in a thread and pass messages
87 /// back over a channel.
88 check_process: Option<jod_thread::JoinHandle<()>>,
89}
90
91impl FlycheckThread {
92 fn new(config: FlycheckConfig, workspace_root: PathBuf) -> FlycheckThread {
93 FlycheckThread {
94 config,
95 workspace_root,
96 last_update_req: None,
97 message_recv: never(),
98 check_process: None,
99 }
100 }
101
102 fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
103 // If we rerun the thread, we need to discard the previous check results first
104 self.clean_previous_results(task_send);
105
106 loop {
107 select! {
108 recv(&cmd_recv) -> cmd => match cmd {
109 Ok(cmd) => self.handle_command(cmd),
110 Err(RecvError) => {
111 // Command channel has closed, so shut down
112 break;
113 },
114 },
115 recv(self.message_recv) -> msg => match msg {
116 Ok(msg) => self.handle_message(msg, task_send),
117 Err(RecvError) => {
118 // Watcher finished, replace it with a never channel to
119 // avoid busy-waiting.
120 self.message_recv = never();
121 self.check_process = None;
122 },
123 }
124 };
125
126 if self.should_recheck() {
127 self.last_update_req = None;
128 task_send.send(CheckTask::ClearDiagnostics).unwrap();
129 self.restart_check_process();
130 }
131 }
132 }
133
134 fn clean_previous_results(&self, task_send: &Sender<CheckTask>) {
135 task_send.send(CheckTask::ClearDiagnostics).unwrap();
136 task_send.send(CheckTask::Status(Status::End)).unwrap();
137 }
138
139 fn should_recheck(&mut self) -> bool {
140 if let Some(_last_update_req) = &self.last_update_req {
141 // We currently only request an update on save, as we need up to
142 // date source on disk for cargo check to do it's magic, so we
143 // don't really need to debounce the requests at this point.
144 return true;
145 }
146 false
147 }
148
149 fn handle_command(&mut self, cmd: CheckCommand) {
150 match cmd {
151 CheckCommand::Update => self.last_update_req = Some(Instant::now()),
152 }
153 }
154
155 fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
156 match msg {
157 CheckEvent::Begin => {
158 task_send.send(CheckTask::Status(Status::Being)).unwrap();
159 }
160
161 CheckEvent::End => {
162 task_send.send(CheckTask::Status(Status::End)).unwrap();
163 }
164
165 CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
166 task_send.send(CheckTask::Status(Status::Progress(msg.target.name))).unwrap();
167 }
168
169 CheckEvent::Msg(Message::CompilerMessage(msg)) => {
170 task_send
171 .send(CheckTask::AddDiagnostic {
172 workspace_root: self.workspace_root.clone(),
173 diagnostic: msg.message,
174 })
175 .unwrap();
176 }
177
178 CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
179 CheckEvent::Msg(Message::BuildFinished(_)) => {}
180 CheckEvent::Msg(Message::TextLine(_)) => {}
181 CheckEvent::Msg(Message::Unknown) => {}
182 }
183 }
184
185 fn restart_check_process(&mut self) {
186 // First, clear and cancel the old thread
187 self.message_recv = never();
188 self.check_process = None;
189
190 let mut cmd = match &self.config {
191 FlycheckConfig::CargoCommand { command, all_targets, all_features, extra_args } => {
192 let mut cmd = Command::new(ra_toolchain::cargo());
193 cmd.arg(command);
194 cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
195 .arg(self.workspace_root.join("Cargo.toml"));
196 if *all_targets {
197 cmd.arg("--all-targets");
198 }
199 if *all_features {
200 cmd.arg("--all-features");
201 }
202 cmd.args(extra_args);
203 cmd
204 }
205 FlycheckConfig::CustomCommand { command, args } => {
206 let mut cmd = Command::new(command);
207 cmd.args(args);
208 cmd
209 }
210 };
211 cmd.current_dir(&self.workspace_root);
212
213 let (message_send, message_recv) = unbounded();
214 self.message_recv = message_recv;
215 self.check_process = Some(jod_thread::spawn(move || {
216 // If we trigger an error here, we will do so in the loop instead,
217 // which will break out of the loop, and continue the shutdown
218 let _ = message_send.send(CheckEvent::Begin);
219
220 let res = run_cargo(cmd, &mut |message| {
221 // Skip certain kinds of messages to only spend time on what's useful
222 match &message {
223 Message::CompilerArtifact(artifact) if artifact.fresh => return true,
224 Message::BuildScriptExecuted(_) => return true,
225 Message::Unknown => return true,
226 _ => {}
227 }
228
229 // if the send channel was closed, we want to shutdown
230 message_send.send(CheckEvent::Msg(message)).is_ok()
231 });
232
233 if let Err(err) = res {
234 // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
235 // to display user-caused misconfiguration errors instead of just logging them here
236 log::error!("Cargo watcher failed {:?}", err);
237 }
238
239 // We can ignore any error here, as we are already in the progress
240 // of shutting down.
241 let _ = message_send.send(CheckEvent::End);
242 }))
243 }
244}
245
246enum CheckEvent {
247 Begin,
248 Msg(cargo_metadata::Message),
249 End,
250}
251
252fn run_cargo(
253 mut command: Command,
254 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
255) -> io::Result<()> {
256 let mut child =
257 command.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()).spawn()?;
258
259 // We manually read a line at a time, instead of using serde's
260 // stream deserializers, because the deserializer cannot recover
261 // from an error, resulting in it getting stuck, because we try to
262 // be resillient against failures.
263 //
264 // Because cargo only outputs one JSON object per line, we can
265 // simply skip a line if it doesn't parse, which just ignores any
266 // erroneus output.
267 let stdout = BufReader::new(child.stdout.take().unwrap());
268 let mut read_at_least_one_message = false;
269 for message in cargo_metadata::Message::parse_stream(stdout) {
270 let message = match message {
271 Ok(message) => message,
272 Err(err) => {
273 log::error!("Invalid json from cargo check, ignoring ({})", err);
274 continue;
275 }
276 };
277
278 read_at_least_one_message = true;
279
280 if !on_message(message) {
281 break;
282 }
283 }
284
285 // It is okay to ignore the result, as it only errors if the process is already dead
286 let _ = child.kill();
287
288 let exit_status = child.wait()?;
289 if !exit_status.success() && !read_at_least_one_message {
290 // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
291 // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
292 return Err(io::Error::new(
293 io::ErrorKind::Other,
294 format!(
295 "the command produced no valid metadata (exit code: {:?}): {:?}",
296 exit_status, command
297 ),
298 ));
299 }
300
301 Ok(())
302}
diff --git a/crates/ra_fmt/Cargo.toml b/crates/ra_fmt/Cargo.toml
index e9d057afc..b4ef93f2b 100644
--- a/crates/ra_fmt/Cargo.toml
+++ b/crates/ra_fmt/Cargo.toml
@@ -4,6 +4,7 @@ name = "ra_fmt"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = false
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
index ba7b39a19..c260bb193 100644
--- a/crates/ra_hir/Cargo.toml
+++ b/crates/ra_hir/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_hir" 3name = "ra_hir"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
@@ -15,6 +16,7 @@ arrayvec = "0.5.1"
15 16
16itertools = "0.9.0" 17itertools = "0.9.0"
17 18
19stdx = { path = "../stdx" }
18ra_syntax = { path = "../ra_syntax" } 20ra_syntax = { path = "../ra_syntax" }
19ra_db = { path = "../ra_db" } 21ra_db = { path = "../ra_db" }
20ra_prof = { path = "../ra_prof" } 22ra_prof = { path = "../ra_prof" }
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index e40aeffbc..9891b0785 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -9,6 +9,7 @@ use hir_def::{
9 builtin_type::BuiltinType, 9 builtin_type::BuiltinType,
10 docs::Documentation, 10 docs::Documentation,
11 expr::{BindingAnnotation, Pat, PatId}, 11 expr::{BindingAnnotation, Pat, PatId},
12 import_map,
12 per_ns::PerNs, 13 per_ns::PerNs,
13 resolver::{HasResolver, Resolver}, 14 resolver::{HasResolver, Resolver},
14 type_ref::{Mutability, TypeRef}, 15 type_ref::{Mutability, TypeRef},
@@ -24,22 +25,22 @@ use hir_expand::{
24use hir_ty::{ 25use hir_ty::{
25 autoderef, 26 autoderef,
26 display::{HirDisplayError, HirFormatter}, 27 display::{HirDisplayError, HirFormatter},
27 expr::ExprValidator, 28 method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs,
28 method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, Ty, 29 TraitEnvironment, Ty, TyDefId, TypeCtor,
29 TyDefId, TypeCtor,
30}; 30};
31use ra_db::{CrateId, CrateName, Edition, FileId}; 31use ra_db::{CrateId, Edition, FileId};
32use ra_prof::profile; 32use ra_prof::profile;
33use ra_syntax::{ 33use ra_syntax::{
34 ast::{self, AttrsOwner, NameOwner}, 34 ast::{self, AttrsOwner, NameOwner},
35 AstNode, 35 AstNode,
36}; 36};
37use rustc_hash::FxHashSet; 37use rustc_hash::FxHashSet;
38use stdx::impl_from;
38 39
39use crate::{ 40use crate::{
40 db::{DefDatabase, HirDatabase}, 41 db::{DefDatabase, HirDatabase},
41 has_source::HasSource, 42 has_source::HasSource,
42 CallableDef, HirDisplay, InFile, Name, 43 CallableDefId, HirDisplay, InFile, Name,
43}; 44};
44 45
45/// hir::Crate describes a single crate. It's the main interface with which 46/// hir::Crate describes a single crate. It's the main interface with which
@@ -94,8 +95,25 @@ impl Crate {
94 db.crate_graph()[self.id].edition 95 db.crate_graph()[self.id].edition
95 } 96 }
96 97
97 pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateName> { 98 pub fn display_name(self, db: &dyn HirDatabase) -> Option<String> {
98 db.crate_graph()[self.id].display_name.as_ref().cloned() 99 db.crate_graph()[self.id].display_name.clone()
100 }
101
102 pub fn query_external_importables(
103 self,
104 db: &dyn DefDatabase,
105 query: &str,
106 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
107 import_map::search_dependencies(
108 db,
109 self.into(),
110 import_map::Query::new(query).anchor_end().case_sensitive().limit(40),
111 )
112 .into_iter()
113 .map(|item| match item {
114 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()),
115 ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()),
116 })
99 } 117 }
100 118
101 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { 119 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
@@ -122,8 +140,8 @@ pub enum ModuleDef {
122 TypeAlias(TypeAlias), 140 TypeAlias(TypeAlias),
123 BuiltinType(BuiltinType), 141 BuiltinType(BuiltinType),
124} 142}
125impl_froms!( 143impl_from!(
126 ModuleDef: Module, 144 Module,
127 Function, 145 Function,
128 Adt(Struct, Enum, Union), 146 Adt(Struct, Enum, Union),
129 EnumVariant, 147 EnumVariant,
@@ -132,6 +150,7 @@ impl_froms!(
132 Trait, 150 Trait,
133 TypeAlias, 151 TypeAlias,
134 BuiltinType 152 BuiltinType
153 for ModuleDef
135); 154);
136 155
137impl ModuleDef { 156impl ModuleDef {
@@ -168,10 +187,27 @@ impl ModuleDef {
168 187
169 module.visibility_of(db, self) 188 module.visibility_of(db, self)
170 } 189 }
190
191 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
192 match self {
193 ModuleDef::Adt(it) => Some(it.name(db)),
194 ModuleDef::Trait(it) => Some(it.name(db)),
195 ModuleDef::Function(it) => Some(it.name(db)),
196 ModuleDef::EnumVariant(it) => Some(it.name(db)),
197 ModuleDef::TypeAlias(it) => Some(it.name(db)),
198
199 ModuleDef::Module(it) => it.name(db),
200 ModuleDef::Const(it) => it.name(db),
201 ModuleDef::Static(it) => it.name(db),
202
203 ModuleDef::BuiltinType(it) => Some(it.as_name()),
204 }
205 }
171} 206}
172 207
173pub use hir_def::{ 208pub use hir_def::{
174 attr::Attrs, item_scope::ItemInNs, visibility::Visibility, AssocItemId, AssocItemLoc, 209 attr::Attrs, item_scope::ItemInNs, item_tree::ItemTreeNode, visibility::Visibility,
210 AssocItemId, AssocItemLoc,
175}; 211};
176 212
177impl Module { 213impl Module {
@@ -504,12 +540,12 @@ pub enum Adt {
504 Union(Union), 540 Union(Union),
505 Enum(Enum), 541 Enum(Enum),
506} 542}
507impl_froms!(Adt: Struct, Union, Enum); 543impl_from!(Struct, Union, Enum for Adt);
508 544
509impl Adt { 545impl Adt {
510 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { 546 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
511 let subst = db.generic_defaults(self.into()); 547 let subst = db.generic_defaults(self.into());
512 subst.iter().any(|ty| ty == &Ty::Unknown) 548 subst.iter().any(|ty| &ty.value == &Ty::Unknown)
513 } 549 }
514 550
515 /// Turns this ADT into a type. Any type parameters of the ADT will be 551 /// Turns this ADT into a type. Any type parameters of the ADT will be
@@ -547,7 +583,7 @@ pub enum VariantDef {
547 Union(Union), 583 Union(Union),
548 EnumVariant(EnumVariant), 584 EnumVariant(EnumVariant),
549} 585}
550impl_froms!(VariantDef: Struct, Union, EnumVariant); 586impl_from!(Struct, Union, EnumVariant for VariantDef);
551 587
552impl VariantDef { 588impl VariantDef {
553 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { 589 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
@@ -590,8 +626,7 @@ pub enum DefWithBody {
590 Static(Static), 626 Static(Static),
591 Const(Const), 627 Const(Const),
592} 628}
593 629impl_from!(Function, Const, Static for DefWithBody);
594impl_froms!(DefWithBody: Function, Const, Static);
595 630
596impl DefWithBody { 631impl DefWithBody {
597 pub fn module(self, db: &dyn HirDatabase) -> Module { 632 pub fn module(self, db: &dyn HirDatabase) -> Module {
@@ -637,12 +672,12 @@ impl Function {
637 db.function_data(self.id).params.clone() 672 db.function_data(self.id).params.clone()
638 } 673 }
639 674
675 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
676 db.function_data(self.id).is_unsafe
677 }
678
640 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 679 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
641 let _p = profile("Function::diagnostics"); 680 hir_ty::diagnostics::validate_body(db, self.id.into(), sink)
642 let infer = db.infer(self.id.into());
643 infer.add_diagnostics(db, self.id, sink);
644 let mut validator = ExprValidator::new(self.id, infer, sink);
645 validator.validate_body(db);
646 } 681 }
647} 682}
648 683
@@ -735,7 +770,7 @@ pub struct TypeAlias {
735impl TypeAlias { 770impl TypeAlias {
736 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { 771 pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
737 let subst = db.generic_defaults(self.id.into()); 772 let subst = db.generic_defaults(self.id.into());
738 subst.iter().any(|ty| ty == &Ty::Unknown) 773 subst.iter().any(|ty| &ty.value == &Ty::Unknown)
739 } 774 }
740 775
741 pub fn module(self, db: &dyn HirDatabase) -> Module { 776 pub fn module(self, db: &dyn HirDatabase) -> Module {
@@ -834,7 +869,7 @@ where
834 ID: Lookup<Data = AssocItemLoc<AST>>, 869 ID: Lookup<Data = AssocItemLoc<AST>>,
835 DEF: From<ID>, 870 DEF: From<ID>,
836 CTOR: FnOnce(DEF) -> AssocItem, 871 CTOR: FnOnce(DEF) -> AssocItem,
837 AST: AstNode, 872 AST: ItemTreeNode,
838{ 873{
839 match id.lookup(db.upcast()).container { 874 match id.lookup(db.upcast()).container {
840 AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))), 875 AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
@@ -887,14 +922,15 @@ pub enum GenericDef {
887 // consts can have type parameters from their parents (i.e. associated consts of traits) 922 // consts can have type parameters from their parents (i.e. associated consts of traits)
888 Const(Const), 923 Const(Const),
889} 924}
890impl_froms!( 925impl_from!(
891 GenericDef: Function, 926 Function,
892 Adt(Struct, Enum, Union), 927 Adt(Struct, Enum, Union),
893 Trait, 928 Trait,
894 TypeAlias, 929 TypeAlias,
895 ImplDef, 930 ImplDef,
896 EnumVariant, 931 EnumVariant,
897 Const 932 Const
933 for GenericDef
898); 934);
899 935
900impl GenericDef { 936impl GenericDef {
@@ -915,6 +951,16 @@ pub struct Local {
915} 951}
916 952
917impl Local { 953impl Local {
954 pub fn is_param(self, db: &dyn HirDatabase) -> bool {
955 let src = self.source(db);
956 match src.value {
957 Either::Left(bind_pat) => {
958 bind_pat.syntax().ancestors().any(|it| ast::Param::can_cast(it.kind()))
959 }
960 Either::Right(_self_param) => true,
961 }
962 }
963
918 // FIXME: why is this an option? It shouldn't be? 964 // FIXME: why is this an option? It shouldn't be?
919 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { 965 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
920 let body = db.body(self.parent.into()); 966 let body = db.body(self.parent.into());
@@ -995,7 +1041,10 @@ impl TypeParam {
995 let local_idx = hir_ty::param_idx(db, self.id)?; 1041 let local_idx = hir_ty::param_idx(db, self.id)?;
996 let resolver = self.id.parent.resolver(db.upcast()); 1042 let resolver = self.id.parent.resolver(db.upcast());
997 let environment = TraitEnvironment::lower(db, &resolver); 1043 let environment = TraitEnvironment::lower(db, &resolver);
998 params.get(local_idx).cloned().map(|ty| Type { 1044 let ty = params.get(local_idx)?.clone();
1045 let subst = Substs::type_params(db, self.id.parent);
1046 let ty = ty.subst(&subst.prefix(local_idx));
1047 Some(Type {
999 krate: self.id.parent.module(db.upcast()).krate, 1048 krate: self.id.parent.module(db.upcast()).krate,
1000 ty: InEnvironment { value: ty, environment }, 1049 ty: InEnvironment { value: ty, environment },
1001 }) 1050 })
@@ -1010,12 +1059,14 @@ pub struct ImplDef {
1010 1059
1011impl ImplDef { 1060impl ImplDef {
1012 pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<ImplDef> { 1061 pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<ImplDef> {
1013 let impls = db.impls_in_crate(krate.id); 1062 let inherent = db.inherent_impls_in_crate(krate.id);
1014 impls.all_impls().map(Self::from).collect() 1063 let trait_ = db.trait_impls_in_crate(krate.id);
1064
1065 inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect()
1015 } 1066 }
1016 pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<ImplDef> { 1067 pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<ImplDef> {
1017 let impls = db.impls_in_crate(krate.id); 1068 let impls = db.trait_impls_in_crate(krate.id);
1018 impls.lookup_impl_defs_for_trait(trait_.id).map(Self::from).collect() 1069 impls.for_trait(trait_.id).map(Self::from).collect()
1019 } 1070 }
1020 1071
1021 pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> { 1072 pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> {
@@ -1144,7 +1195,7 @@ impl Type {
1144 None => return false, 1195 None => return false,
1145 }; 1196 };
1146 1197
1147 let canonical_ty = Canonical { value: self.ty.value.clone(), num_vars: 0 }; 1198 let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1148 method_resolution::implements_trait( 1199 method_resolution::implements_trait(
1149 &canonical_ty, 1200 &canonical_ty,
1150 db, 1201 db,
@@ -1168,14 +1219,14 @@ impl Type {
1168 self.ty.environment.clone(), 1219 self.ty.environment.clone(),
1169 hir_ty::Obligation::Trait(trait_ref), 1220 hir_ty::Obligation::Trait(trait_ref),
1170 ), 1221 ),
1171 num_vars: 0, 1222 kinds: Arc::new([]),
1172 }; 1223 };
1173 1224
1174 db.trait_solve(self.krate, goal).is_some() 1225 db.trait_solve(self.krate, goal).is_some()
1175 } 1226 }
1176 1227
1177 // FIXME: this method is broken, as it doesn't take closures into account. 1228 // FIXME: this method is broken, as it doesn't take closures into account.
1178 pub fn as_callable(&self) -> Option<CallableDef> { 1229 pub fn as_callable(&self) -> Option<CallableDefId> {
1179 Some(self.ty.value.as_callable()?.0) 1230 Some(self.ty.value.as_callable()?.0)
1180 } 1231 }
1181 1232
@@ -1190,6 +1241,10 @@ impl Type {
1190 ) 1241 )
1191 } 1242 }
1192 1243
1244 pub fn is_raw_ptr(&self) -> bool {
1245 matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }))
1246 }
1247
1193 pub fn contains_unknown(&self) -> bool { 1248 pub fn contains_unknown(&self) -> bool {
1194 return go(&self.ty.value); 1249 return go(&self.ty.value);
1195 1250
@@ -1239,7 +1294,7 @@ impl Type {
1239 pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a { 1294 pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
1240 // There should be no inference vars in types passed here 1295 // There should be no inference vars in types passed here
1241 // FIXME check that? 1296 // FIXME check that?
1242 let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 }; 1297 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1243 let environment = self.ty.environment.clone(); 1298 let environment = self.ty.environment.clone();
1244 let ty = InEnvironment { value: canonical, environment }; 1299 let ty = InEnvironment { value: canonical, environment };
1245 autoderef(db, Some(self.krate), ty) 1300 autoderef(db, Some(self.krate), ty)
@@ -1256,10 +1311,10 @@ impl Type {
1256 mut callback: impl FnMut(AssocItem) -> Option<T>, 1311 mut callback: impl FnMut(AssocItem) -> Option<T>,
1257 ) -> Option<T> { 1312 ) -> Option<T> {
1258 for krate in self.ty.value.def_crates(db, krate.id)? { 1313 for krate in self.ty.value.def_crates(db, krate.id)? {
1259 let impls = db.impls_in_crate(krate); 1314 let impls = db.inherent_impls_in_crate(krate);
1260 1315
1261 for impl_def in impls.lookup_impl_defs(&self.ty.value) { 1316 for impl_def in impls.for_self_ty(&self.ty.value) {
1262 for &item in db.impl_data(impl_def).items.iter() { 1317 for &item in db.impl_data(*impl_def).items.iter() {
1263 if let Some(result) = callback(item.into()) { 1318 if let Some(result) = callback(item.into()) {
1264 return Some(result); 1319 return Some(result);
1265 } 1320 }
@@ -1280,7 +1335,7 @@ impl Type {
1280 // There should be no inference vars in types passed here 1335 // There should be no inference vars in types passed here
1281 // FIXME check that? 1336 // FIXME check that?
1282 // FIXME replace Unknown by bound vars here 1337 // FIXME replace Unknown by bound vars here
1283 let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 }; 1338 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1284 1339
1285 let env = self.ty.environment.clone(); 1340 let env = self.ty.environment.clone();
1286 let krate = krate.id; 1341 let krate = krate.id;
@@ -1311,7 +1366,7 @@ impl Type {
1311 // There should be no inference vars in types passed here 1366 // There should be no inference vars in types passed here
1312 // FIXME check that? 1367 // FIXME check that?
1313 // FIXME replace Unknown by bound vars here 1368 // FIXME replace Unknown by bound vars here
1314 let canonical = Canonical { value: self.ty.value.clone(), num_vars: 0 }; 1369 let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) };
1315 1370
1316 let env = self.ty.environment.clone(); 1371 let env = self.ty.environment.clone();
1317 let krate = krate.id; 1372 let krate = krate.id;
@@ -1333,6 +1388,27 @@ impl Type {
1333 Some(adt.into()) 1388 Some(adt.into())
1334 } 1389 }
1335 1390
1391 pub fn as_dyn_trait(&self) -> Option<Trait> {
1392 self.ty.value.dyn_trait().map(Into::into)
1393 }
1394
1395 pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> {
1396 self.ty.value.impl_trait_bounds(db).map(|it| {
1397 it.into_iter()
1398 .filter_map(|pred| match pred {
1399 hir_ty::GenericPredicate::Implemented(trait_ref) => {
1400 Some(Trait::from(trait_ref.trait_))
1401 }
1402 _ => None,
1403 })
1404 .collect()
1405 })
1406 }
1407
1408 pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> {
1409 self.ty.value.associated_type_parent_trait(db).map(Into::into)
1410 }
1411
1336 // FIXME: provide required accessors such that it becomes implementable from outside. 1412 // FIXME: provide required accessors such that it becomes implementable from outside.
1337 pub fn is_equal_for_find_impls(&self, other: &Type) -> bool { 1413 pub fn is_equal_for_find_impls(&self, other: &Type) -> bool {
1338 match (&self.ty.value, &other.ty.value) { 1414 match (&self.ty.value, &other.ty.value) {
@@ -1354,6 +1430,80 @@ impl Type {
1354 ty: InEnvironment { value: ty, environment: self.ty.environment.clone() }, 1430 ty: InEnvironment { value: ty, environment: self.ty.environment.clone() },
1355 } 1431 }
1356 } 1432 }
1433
1434 pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) {
1435 // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself.
1436 // We need a different order here.
1437
1438 fn walk_substs(
1439 db: &dyn HirDatabase,
1440 type_: &Type,
1441 substs: &Substs,
1442 cb: &mut impl FnMut(Type),
1443 ) {
1444 for ty in substs.iter() {
1445 walk_type(db, &type_.derived(ty.clone()), cb);
1446 }
1447 }
1448
1449 fn walk_bounds(
1450 db: &dyn HirDatabase,
1451 type_: &Type,
1452 bounds: &[GenericPredicate],
1453 cb: &mut impl FnMut(Type),
1454 ) {
1455 for pred in bounds {
1456 match pred {
1457 GenericPredicate::Implemented(trait_ref) => {
1458 cb(type_.clone());
1459 walk_substs(db, type_, &trait_ref.substs, cb);
1460 }
1461 _ => (),
1462 }
1463 }
1464 }
1465
1466 fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) {
1467 let ty = type_.ty.value.strip_references();
1468 match ty {
1469 Ty::Apply(ApplicationTy { ctor, parameters }) => {
1470 match ctor {
1471 TypeCtor::Adt(_) => {
1472 cb(type_.derived(ty.clone()));
1473 }
1474 TypeCtor::AssociatedType(_) => {
1475 if let Some(_) = ty.associated_type_parent_trait(db) {
1476 cb(type_.derived(ty.clone()));
1477 }
1478 }
1479 _ => (),
1480 }
1481
1482 // adt params, tuples, etc...
1483 walk_substs(db, type_, parameters, cb);
1484 }
1485 Ty::Opaque(opaque_ty) => {
1486 if let Some(bounds) = ty.impl_trait_bounds(db) {
1487 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1488 }
1489
1490 walk_substs(db, type_, &opaque_ty.parameters, cb);
1491 }
1492 Ty::Placeholder(_) => {
1493 if let Some(bounds) = ty.impl_trait_bounds(db) {
1494 walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb);
1495 }
1496 }
1497 Ty::Dyn(bounds) => {
1498 walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb);
1499 }
1500
1501 _ => (),
1502 }
1503 }
1504
1505 walk_type(db, self, &mut cb);
1506 }
1357} 1507}
1358 1508
1359impl HirDisplay for Type { 1509impl HirDisplay for Type {
@@ -1421,8 +1571,8 @@ pub enum AttrDef {
1421 MacroDef(MacroDef), 1571 MacroDef(MacroDef),
1422} 1572}
1423 1573
1424impl_froms!( 1574impl_from!(
1425 AttrDef: Module, 1575 Module,
1426 Field, 1576 Field,
1427 Adt(Struct, Enum, Union), 1577 Adt(Struct, Enum, Union),
1428 EnumVariant, 1578 EnumVariant,
@@ -1432,6 +1582,7 @@ impl_froms!(
1432 Trait, 1582 Trait,
1433 TypeAlias, 1583 TypeAlias,
1434 MacroDef 1584 MacroDef
1585 for AttrDef
1435); 1586);
1436 1587
1437pub trait HasAttrs { 1588pub trait HasAttrs {
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index ec931b34f..1ad92a1f8 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -3,11 +3,11 @@
3pub use hir_def::db::{ 3pub use hir_def::db::{
4 AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, 4 AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery,
5 CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, 5 CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery,
6 ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, InternConstQuery, 6 ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery,
7 InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery, 7 InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery,
8 InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery, 8 InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery,
9 LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, StructDataQuery, 9 InternUnionQuery, ItemTreeQuery, LangItemQuery, ModuleLangItemsQuery, StaticDataQuery,
10 TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, 10 StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
11}; 11};
12pub use hir_expand::db::{ 12pub use hir_expand::db::{
13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, 13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery,
@@ -16,10 +16,10 @@ pub use hir_expand::db::{
16pub use hir_ty::db::{ 16pub use hir_ty::db::{
17 AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, FieldTypesQuery, 17 AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, FieldTypesQuery,
18 GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, 18 GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase,
19 HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery, 19 HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, InferQueryQuery,
20 ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery, 20 InherentImplsInCrateQuery, InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery,
21 InternTypeCtorQuery, InternTypeParamIdQuery, StructDatumQuery, TraitDatumQuery, 21 TraitDatumQuery, TraitImplsInCrateQuery, TraitImplsInDepsQuery, TraitSolveQuery, TyQuery,
22 TraitSolveQuery, TyQuery, ValueTyQuery, 22 ValueTyQuery,
23}; 23};
24 24
25#[test] 25#[test]
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs
index c82883d0c..11a0ecb8b 100644
--- a/crates/ra_hir/src/diagnostics.rs
+++ b/crates/ra_hir/src/diagnostics.rs
@@ -1,4 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2pub use hir_def::diagnostics::UnresolvedModule; 2pub use hir_def::diagnostics::UnresolvedModule;
3pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; 3pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
4pub use hir_ty::diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField}; 4pub use hir_ty::diagnostics::{
5 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField,
6};
diff --git a/crates/ra_hir/src/has_source.rs b/crates/ra_hir/src/has_source.rs
index 63b8fd369..76c32fc17 100644
--- a/crates/ra_hir/src/has_source.rs
+++ b/crates/ra_hir/src/has_source.rs
@@ -2,7 +2,7 @@
2 2
3use either::Either; 3use either::Either;
4use hir_def::{ 4use hir_def::{
5 nameres::ModuleSource, 5 nameres::{ModuleOrigin, ModuleSource},
6 src::{HasChildSource, HasSource as _}, 6 src::{HasChildSource, HasSource as _},
7 Lookup, VariantId, 7 Lookup, VariantId,
8}; 8};
@@ -29,6 +29,14 @@ impl Module {
29 def_map[self.id.local_id].definition_source(db.upcast()) 29 def_map[self.id.local_id].definition_source(db.upcast())
30 } 30 }
31 31
32 pub fn is_mod_rs(self, db: &dyn HirDatabase) -> bool {
33 let def_map = db.crate_def_map(self.id.krate);
34 match def_map[self.id.local_id].origin {
35 ModuleOrigin::File { is_mod_rs, .. } => is_mod_rs,
36 _ => false,
37 }
38 }
39
32 /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`. 40 /// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
33 /// `None` for the crate root. 41 /// `None` for the crate root.
34 pub fn declaration_source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Module>> { 42 pub fn declaration_source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Module>> {
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 3364a822f..cf7134923 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -19,25 +19,6 @@
19 19
20#![recursion_limit = "512"] 20#![recursion_limit = "512"]
21 21
22macro_rules! impl_froms {
23 ($e:ident: $($v:ident $(($($sv:ident),*))?),*$(,)?) => {
24 $(
25 impl From<$v> for $e {
26 fn from(it: $v) -> $e {
27 $e::$v(it)
28 }
29 }
30 $($(
31 impl From<$sv> for $e {
32 fn from(it: $sv) -> $e {
33 $e::$v($v::$sv(it))
34 }
35 }
36 )*)?
37 )*
38 }
39}
40
41mod semantics; 22mod semantics;
42pub mod db; 23pub mod db;
43mod source_analyzer; 24mod source_analyzer;
@@ -74,4 +55,4 @@ pub use hir_expand::{
74 hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, 55 hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId,
75 MacroFile, Origin, 56 MacroFile, Origin,
76}; 57};
77pub use hir_ty::{display::HirDisplay, CallableDef}; 58pub use hir_ty::{display::HirDisplay, CallableDefId};
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 7c1f79f27..155b666d7 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -6,9 +6,9 @@ use std::{cell::RefCell, fmt, iter::successors};
6 6
7use hir_def::{ 7use hir_def::{
8 resolver::{self, HasResolver, Resolver}, 8 resolver::{self, HasResolver, Resolver},
9 AsMacroCall, TraitId, 9 AsMacroCall, TraitId, VariantId,
10}; 10};
11use hir_expand::{hygiene::Hygiene, ExpansionInfo}; 11use hir_expand::{diagnostics::AstDiagnostic, hygiene::Hygiene, ExpansionInfo};
12use hir_ty::associated_type_shorthand_candidates; 12use hir_ty::associated_type_shorthand_candidates;
13use itertools::Itertools; 13use itertools::Itertools;
14use ra_db::{FileId, FileRange}; 14use ra_db::{FileId, FileRange};
@@ -25,7 +25,7 @@ use crate::{
25 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, 25 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
26 source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer}, 26 source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer},
27 AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef, 27 AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef,
28 Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, 28 Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
29}; 29};
30use resolver::TypeNs; 30use resolver::TypeNs;
31 31
@@ -83,6 +83,11 @@ impl PathResolution {
83/// Primary API to get semantic information, like types, from syntax trees. 83/// Primary API to get semantic information, like types, from syntax trees.
84pub struct Semantics<'db, DB> { 84pub struct Semantics<'db, DB> {
85 pub db: &'db DB, 85 pub db: &'db DB,
86 imp: SemanticsImpl<'db>,
87}
88
89pub struct SemanticsImpl<'db> {
90 pub db: &'db dyn HirDatabase,
86 s2d_cache: RefCell<SourceToDefCache>, 91 s2d_cache: RefCell<SourceToDefCache>,
87 cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>, 92 cache: RefCell<FxHashMap<SyntaxNode, HirFileId>>,
88} 93}
@@ -95,16 +100,187 @@ impl<DB> fmt::Debug for Semantics<'_, DB> {
95 100
96impl<'db, DB: HirDatabase> Semantics<'db, DB> { 101impl<'db, DB: HirDatabase> Semantics<'db, DB> {
97 pub fn new(db: &DB) -> Semantics<DB> { 102 pub fn new(db: &DB) -> Semantics<DB> {
98 Semantics { db, s2d_cache: Default::default(), cache: Default::default() } 103 let impl_ = SemanticsImpl::new(db);
104 Semantics { db, imp: impl_ }
99 } 105 }
100 106
101 pub fn parse(&self, file_id: FileId) -> ast::SourceFile { 107 pub fn parse(&self, file_id: FileId) -> ast::SourceFile {
108 self.imp.parse(file_id)
109 }
110
111 pub fn ast<T: AstDiagnostic + Diagnostic>(&self, d: &T) -> <T as AstDiagnostic>::AST {
112 let file_id = d.source().file_id;
113 let root = self.db.parse_or_expand(file_id).unwrap();
114 self.imp.cache(root, file_id);
115 d.ast(self.db.upcast())
116 }
117
118 pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
119 self.imp.expand(macro_call)
120 }
121
122 pub fn expand_hypothetical(
123 &self,
124 actual_macro_call: &ast::MacroCall,
125 hypothetical_args: &ast::TokenTree,
126 token_to_map: SyntaxToken,
127 ) -> Option<(SyntaxNode, SyntaxToken)> {
128 self.imp.expand_hypothetical(actual_macro_call, hypothetical_args, token_to_map)
129 }
130
131 pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
132 self.imp.descend_into_macros(token)
133 }
134
135 pub fn descend_node_at_offset<N: ast::AstNode>(
136 &self,
137 node: &SyntaxNode,
138 offset: TextSize,
139 ) -> Option<N> {
140 self.imp.descend_node_at_offset(node, offset).find_map(N::cast)
141 }
142
143 pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
144 self.imp.original_range(node)
145 }
146
147 pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
148 self.imp.diagnostics_range(diagnostics)
149 }
150
151 pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
152 self.imp.ancestors_with_macros(node)
153 }
154
155 pub fn ancestors_at_offset_with_macros(
156 &self,
157 node: &SyntaxNode,
158 offset: TextSize,
159 ) -> impl Iterator<Item = SyntaxNode> + '_ {
160 self.imp.ancestors_at_offset_with_macros(node, offset)
161 }
162
163 /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*,
164 /// search up until it is of the target AstNode type
165 pub fn find_node_at_offset_with_macros<N: AstNode>(
166 &self,
167 node: &SyntaxNode,
168 offset: TextSize,
169 ) -> Option<N> {
170 self.imp.ancestors_at_offset_with_macros(node, offset).find_map(N::cast)
171 }
172
173 /// Find a AstNode by offset inside SyntaxNode, if it is inside *MacroCall*,
174 /// descend it and find again
175 pub fn find_node_at_offset_with_descend<N: AstNode>(
176 &self,
177 node: &SyntaxNode,
178 offset: TextSize,
179 ) -> Option<N> {
180 if let Some(it) = find_node_at_offset(&node, offset) {
181 return Some(it);
182 }
183
184 self.imp.descend_node_at_offset(node, offset).find_map(N::cast)
185 }
186
187 pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
188 self.imp.type_of_expr(expr)
189 }
190
191 pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
192 self.imp.type_of_pat(pat)
193 }
194
195 pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
196 self.imp.type_of_self(param)
197 }
198
199 pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
200 self.imp.resolve_method_call(call)
201 }
202
203 pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
204 self.imp.resolve_field(field)
205 }
206
207 pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<(Field, Option<Local>)> {
208 self.imp.resolve_record_field(field)
209 }
210
211 pub fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option<Field> {
212 self.imp.resolve_record_field_pat(field)
213 }
214
215 pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> {
216 self.imp.resolve_macro_call(macro_call)
217 }
218
219 pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
220 self.imp.resolve_path(path)
221 }
222
223 pub fn resolve_variant(&self, record_lit: ast::RecordLit) -> Option<VariantDef> {
224 self.imp.resolve_variant(record_lit).map(VariantDef::from)
225 }
226
227 pub fn lower_path(&self, path: &ast::Path) -> Option<Path> {
228 self.imp.lower_path(path)
229 }
230
231 pub fn resolve_bind_pat_to_const(&self, pat: &ast::BindPat) -> Option<ModuleDef> {
232 self.imp.resolve_bind_pat_to_const(pat)
233 }
234
235 // FIXME: use this instead?
236 // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>;
237
238 pub fn record_literal_missing_fields(&self, literal: &ast::RecordLit) -> Vec<(Field, Type)> {
239 self.imp.record_literal_missing_fields(literal)
240 }
241
242 pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> {
243 self.imp.record_pattern_missing_fields(pattern)
244 }
245
246 pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
247 let src = self.imp.find_file(src.syntax().clone()).with_value(src).cloned();
248 T::to_def(&self.imp, src)
249 }
250
251 pub fn to_module_def(&self, file: FileId) -> Option<Module> {
252 self.imp.to_module_def(file)
253 }
254
255 pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> {
256 self.imp.scope(node)
257 }
258
259 pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> {
260 self.imp.scope_at_offset(node, offset)
261 }
262
263 pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
264 self.imp.scope_for_def(def)
265 }
266
267 pub fn assert_contains_node(&self, node: &SyntaxNode) {
268 self.imp.assert_contains_node(node)
269 }
270}
271
272impl<'db> SemanticsImpl<'db> {
273 fn new(db: &'db dyn HirDatabase) -> Self {
274 Self { db, s2d_cache: Default::default(), cache: Default::default() }
275 }
276
277 fn parse(&self, file_id: FileId) -> ast::SourceFile {
102 let tree = self.db.parse(file_id).tree(); 278 let tree = self.db.parse(file_id).tree();
103 self.cache(tree.syntax().clone(), file_id.into()); 279 self.cache(tree.syntax().clone(), file_id.into());
104 tree 280 tree
105 } 281 }
106 282
107 pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { 283 fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
108 let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); 284 let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call);
109 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); 285 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None);
110 let file_id = sa.expand(self.db, macro_call)?; 286 let file_id = sa.expand(self.db, macro_call)?;
@@ -113,7 +289,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
113 Some(node) 289 Some(node)
114 } 290 }
115 291
116 pub fn expand_hypothetical( 292 fn expand_hypothetical(
117 &self, 293 &self,
118 actual_macro_call: &ast::MacroCall, 294 actual_macro_call: &ast::MacroCall,
119 hypothetical_args: &ast::TokenTree, 295 hypothetical_args: &ast::TokenTree,
@@ -122,12 +298,19 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
122 let macro_call = 298 let macro_call =
123 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); 299 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call);
124 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); 300 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None);
125 let macro_call_id = macro_call 301 let krate = sa.resolver.krate()?;
126 .as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; 302 let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| {
127 hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) 303 sa.resolver.resolve_path_as_macro(self.db.upcast(), &path)
128 } 304 })?;
129 305 hir_expand::db::expand_hypothetical(
130 pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { 306 self.db.upcast(),
307 macro_call_id,
308 hypothetical_args,
309 token_to_map,
310 )
311 }
312
313 fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken {
131 let parent = token.parent(); 314 let parent = token.parent();
132 let parent = self.find_file(parent); 315 let parent = self.find_file(parent);
133 let sa = self.analyze2(parent.as_ref(), None); 316 let sa = self.analyze2(parent.as_ref(), None);
@@ -139,7 +322,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
139 return None; 322 return None;
140 } 323 }
141 let file_id = sa.expand(self.db, token.with_value(&macro_call))?; 324 let file_id = sa.expand(self.db, token.with_value(&macro_call))?;
142 let token = file_id.expansion_info(self.db)?.map_token_down(token.as_ref())?; 325 let token = file_id.expansion_info(self.db.upcast())?.map_token_down(token.as_ref())?;
143 326
144 self.cache(find_root(&token.value.parent()), token.file_id); 327 self.cache(find_root(&token.value.parent()), token.file_id);
145 328
@@ -151,35 +334,36 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
151 token.value 334 token.value
152 } 335 }
153 336
154 pub fn descend_node_at_offset<N: ast::AstNode>( 337 fn descend_node_at_offset(
155 &self, 338 &self,
156 node: &SyntaxNode, 339 node: &SyntaxNode,
157 offset: TextSize, 340 offset: TextSize,
158 ) -> Option<N> { 341 ) -> impl Iterator<Item = SyntaxNode> + '_ {
159 // Handle macro token cases 342 // Handle macro token cases
160 node.token_at_offset(offset) 343 node.token_at_offset(offset)
161 .map(|token| self.descend_into_macros(token)) 344 .map(|token| self.descend_into_macros(token))
162 .find_map(|it| self.ancestors_with_macros(it.parent()).find_map(N::cast)) 345 .map(|it| self.ancestors_with_macros(it.parent()))
346 .flatten()
163 } 347 }
164 348
165 pub fn original_range(&self, node: &SyntaxNode) -> FileRange { 349 fn original_range(&self, node: &SyntaxNode) -> FileRange {
166 let node = self.find_file(node.clone()); 350 let node = self.find_file(node.clone());
167 original_range(self.db, node.as_ref()) 351 original_range(self.db, node.as_ref())
168 } 352 }
169 353
170 pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { 354 fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange {
171 let src = diagnostics.source(); 355 let src = diagnostics.source();
172 let root = self.db.parse_or_expand(src.file_id).unwrap(); 356 let root = self.db.parse_or_expand(src.file_id).unwrap();
173 let node = src.value.to_node(&root); 357 let node = src.value.to_node(&root);
174 original_range(self.db, src.with_value(&node)) 358 original_range(self.db, src.with_value(&node))
175 } 359 }
176 360
177 pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ { 361 fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
178 let node = self.find_file(node); 362 let node = self.find_file(node);
179 node.ancestors_with_macros(self.db).map(|it| it.value) 363 node.ancestors_with_macros(self.db.upcast()).map(|it| it.value)
180 } 364 }
181 365
182 pub fn ancestors_at_offset_with_macros( 366 fn ancestors_at_offset_with_macros(
183 &self, 367 &self,
184 node: &SyntaxNode, 368 node: &SyntaxNode,
185 offset: TextSize, 369 offset: TextSize,
@@ -189,116 +373,93 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
189 .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) 373 .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len())
190 } 374 }
191 375
192 /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*, 376 fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
193 /// search up until it is of the target AstNode type 377 self.analyze(expr.syntax()).type_of_expr(self.db, &expr)
194 pub fn find_node_at_offset_with_macros<N: AstNode>(
195 &self,
196 node: &SyntaxNode,
197 offset: TextSize,
198 ) -> Option<N> {
199 self.ancestors_at_offset_with_macros(node, offset).find_map(N::cast)
200 } 378 }
201 379
202 /// Find a AstNode by offset inside SyntaxNode, if it is inside *MacroCall*, 380 fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
203 /// descend it and find again 381 self.analyze(pat.syntax()).type_of_pat(self.db, &pat)
204 pub fn find_node_at_offset_with_descend<N: AstNode>(
205 &self,
206 node: &SyntaxNode,
207 offset: TextSize,
208 ) -> Option<N> {
209 if let Some(it) = find_node_at_offset(&node, offset) {
210 return Some(it);
211 }
212 self.descend_node_at_offset(&node, offset)
213 } 382 }
214 383
215 pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { 384 fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
216 self.analyze(expr.syntax()).type_of(self.db, &expr) 385 self.analyze(param.syntax()).type_of_self(self.db, &param)
217 } 386 }
218 387
219 pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> { 388 fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
220 self.analyze(pat.syntax()).type_of_pat(self.db, &pat)
221 }
222
223 pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
224 self.analyze(call.syntax()).resolve_method_call(self.db, call) 389 self.analyze(call.syntax()).resolve_method_call(self.db, call)
225 } 390 }
226 391
227 pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> { 392 fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
228 self.analyze(field.syntax()).resolve_field(self.db, field) 393 self.analyze(field.syntax()).resolve_field(self.db, field)
229 } 394 }
230 395
231 pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option<(Field, Option<Local>)> { 396 fn resolve_record_field(&self, field: &ast::RecordField) -> Option<(Field, Option<Local>)> {
232 self.analyze(field.syntax()).resolve_record_field(self.db, field) 397 self.analyze(field.syntax()).resolve_record_field(self.db, field)
233 } 398 }
234 399
235 pub fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option<Field> { 400 fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option<Field> {
236 self.analyze(field.syntax()).resolve_record_field_pat(self.db, field) 401 self.analyze(field.syntax()).resolve_record_field_pat(self.db, field)
237 } 402 }
238 403
239 pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> { 404 fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<MacroDef> {
240 let sa = self.analyze(macro_call.syntax()); 405 let sa = self.analyze(macro_call.syntax());
241 let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); 406 let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call);
242 sa.resolve_macro_call(self.db, macro_call) 407 sa.resolve_macro_call(self.db, macro_call)
243 } 408 }
244 409
245 pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> { 410 fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
246 self.analyze(path.syntax()).resolve_path(self.db, path) 411 self.analyze(path.syntax()).resolve_path(self.db, path)
247 } 412 }
248 413
249 pub fn lower_path(&self, path: &ast::Path) -> Option<Path> { 414 fn resolve_variant(&self, record_lit: ast::RecordLit) -> Option<VariantId> {
415 self.analyze(record_lit.syntax()).resolve_variant(self.db, record_lit)
416 }
417
418 fn lower_path(&self, path: &ast::Path) -> Option<Path> {
250 let src = self.find_file(path.syntax().clone()); 419 let src = self.find_file(path.syntax().clone());
251 Path::from_src(path.clone(), &Hygiene::new(self.db.upcast(), src.file_id.into())) 420 Path::from_src(path.clone(), &Hygiene::new(self.db.upcast(), src.file_id.into()))
252 } 421 }
253 422
254 pub fn resolve_bind_pat_to_const(&self, pat: &ast::BindPat) -> Option<ModuleDef> { 423 fn resolve_bind_pat_to_const(&self, pat: &ast::BindPat) -> Option<ModuleDef> {
255 self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat) 424 self.analyze(pat.syntax()).resolve_bind_pat_to_const(self.db, pat)
256 } 425 }
257 426
258 // FIXME: use this instead? 427 fn record_literal_missing_fields(&self, literal: &ast::RecordLit) -> Vec<(Field, Type)> {
259 // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option<???>;
260
261 pub fn record_literal_missing_fields(&self, literal: &ast::RecordLit) -> Vec<(Field, Type)> {
262 self.analyze(literal.syntax()) 428 self.analyze(literal.syntax())
263 .record_literal_missing_fields(self.db, literal) 429 .record_literal_missing_fields(self.db, literal)
264 .unwrap_or_default() 430 .unwrap_or_default()
265 } 431 }
266 432
267 pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> { 433 fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> {
268 self.analyze(pattern.syntax()) 434 self.analyze(pattern.syntax())
269 .record_pattern_missing_fields(self.db, pattern) 435 .record_pattern_missing_fields(self.db, pattern)
270 .unwrap_or_default() 436 .unwrap_or_default()
271 } 437 }
272 438
273 pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
274 let src = self.find_file(src.syntax().clone()).with_value(src).cloned();
275 T::to_def(self, src)
276 }
277
278 fn with_ctx<F: FnOnce(&mut SourceToDefCtx) -> T, T>(&self, f: F) -> T { 439 fn with_ctx<F: FnOnce(&mut SourceToDefCtx) -> T, T>(&self, f: F) -> T {
279 let mut cache = self.s2d_cache.borrow_mut(); 440 let mut cache = self.s2d_cache.borrow_mut();
280 let mut ctx = SourceToDefCtx { db: self.db, cache: &mut *cache }; 441 let mut ctx = SourceToDefCtx { db: self.db, cache: &mut *cache };
281 f(&mut ctx) 442 f(&mut ctx)
282 } 443 }
283 444
284 pub fn to_module_def(&self, file: FileId) -> Option<Module> { 445 fn to_module_def(&self, file: FileId) -> Option<Module> {
285 self.with_ctx(|ctx| ctx.file_to_def(file)).map(Module::from) 446 self.with_ctx(|ctx| ctx.file_to_def(file)).map(Module::from)
286 } 447 }
287 448
288 pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db, DB> { 449 fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db> {
289 let node = self.find_file(node.clone()); 450 let node = self.find_file(node.clone());
290 let resolver = self.analyze2(node.as_ref(), None).resolver; 451 let resolver = self.analyze2(node.as_ref(), None).resolver;
291 SemanticsScope { db: self.db, resolver } 452 SemanticsScope { db: self.db, resolver }
292 } 453 }
293 454
294 pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db, DB> { 455 fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> SemanticsScope<'db> {
295 let node = self.find_file(node.clone()); 456 let node = self.find_file(node.clone());
296 let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver; 457 let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver;
297 SemanticsScope { db: self.db, resolver } 458 SemanticsScope { db: self.db, resolver }
298 } 459 }
299 460
300 pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db, DB> { 461 fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
301 let resolver = def.id.resolver(self.db); 462 let resolver = def.id.resolver(self.db.upcast());
302 SemanticsScope { db: self.db, resolver } 463 SemanticsScope { db: self.db, resolver }
303 } 464 }
304 465
@@ -319,12 +480,13 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
319 ChildContainer::DefWithBodyId(def) => { 480 ChildContainer::DefWithBodyId(def) => {
320 return SourceAnalyzer::new_for_body(self.db, def, src, offset) 481 return SourceAnalyzer::new_for_body(self.db, def, src, offset)
321 } 482 }
322 ChildContainer::TraitId(it) => it.resolver(self.db), 483 ChildContainer::TraitId(it) => it.resolver(self.db.upcast()),
323 ChildContainer::ImplId(it) => it.resolver(self.db), 484 ChildContainer::ImplId(it) => it.resolver(self.db.upcast()),
324 ChildContainer::ModuleId(it) => it.resolver(self.db), 485 ChildContainer::ModuleId(it) => it.resolver(self.db.upcast()),
325 ChildContainer::EnumId(it) => it.resolver(self.db), 486 ChildContainer::EnumId(it) => it.resolver(self.db.upcast()),
326 ChildContainer::VariantId(it) => it.resolver(self.db), 487 ChildContainer::VariantId(it) => it.resolver(self.db.upcast()),
327 ChildContainer::GenericDefId(it) => it.resolver(self.db), 488 ChildContainer::TypeAliasId(it) => it.resolver(self.db.upcast()),
489 ChildContainer::GenericDefId(it) => it.resolver(self.db.upcast()),
328 }; 490 };
329 SourceAnalyzer::new_for_resolver(resolver, src) 491 SourceAnalyzer::new_for_resolver(resolver, src)
330 } 492 }
@@ -336,7 +498,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
336 assert!(prev == None || prev == Some(file_id)) 498 assert!(prev == None || prev == Some(file_id))
337 } 499 }
338 500
339 pub fn assert_contains_node(&self, node: &SyntaxNode) { 501 fn assert_contains_node(&self, node: &SyntaxNode) {
340 self.find_file(node.clone()); 502 self.find_file(node.clone());
341 } 503 }
342 504
@@ -370,14 +532,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
370pub trait ToDef: AstNode + Clone { 532pub trait ToDef: AstNode + Clone {
371 type Def; 533 type Def;
372 534
373 fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: InFile<Self>) -> Option<Self::Def>; 535 fn to_def(sema: &SemanticsImpl, src: InFile<Self>) -> Option<Self::Def>;
374} 536}
375 537
376macro_rules! to_def_impls { 538macro_rules! to_def_impls {
377 ($(($def:path, $ast:path, $meth:ident)),* ,) => {$( 539 ($(($def:path, $ast:path, $meth:ident)),* ,) => {$(
378 impl ToDef for $ast { 540 impl ToDef for $ast {
379 type Def = $def; 541 type Def = $def;
380 fn to_def<DB: HirDatabase>(sema: &Semantics<DB>, src: InFile<Self>) -> Option<Self::Def> { 542 fn to_def(sema: &SemanticsImpl, src: InFile<Self>) -> Option<Self::Def> {
381 sema.with_ctx(|ctx| ctx.$meth(src)).map(<$def>::from) 543 sema.with_ctx(|ctx| ctx.$meth(src)).map(<$def>::from)
382 } 544 }
383 } 545 }
@@ -407,12 +569,13 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode {
407 node.ancestors().last().unwrap() 569 node.ancestors().last().unwrap()
408} 570}
409 571
410pub struct SemanticsScope<'a, DB> { 572#[derive(Debug)]
411 pub db: &'a DB, 573pub struct SemanticsScope<'a> {
574 pub db: &'a dyn HirDatabase,
412 resolver: Resolver, 575 resolver: Resolver,
413} 576}
414 577
415impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> { 578impl<'a> SemanticsScope<'a> {
416 pub fn module(&self) -> Option<Module> { 579 pub fn module(&self) -> Option<Module> {
417 Some(Module { id: self.resolver.module()? }) 580 Some(Module { id: self.resolver.module()? })
418 } 581 }
@@ -421,13 +584,13 @@ impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> {
421 // FIXME: rename to visible_traits to not repeat scope? 584 // FIXME: rename to visible_traits to not repeat scope?
422 pub fn traits_in_scope(&self) -> FxHashSet<TraitId> { 585 pub fn traits_in_scope(&self) -> FxHashSet<TraitId> {
423 let resolver = &self.resolver; 586 let resolver = &self.resolver;
424 resolver.traits_in_scope(self.db) 587 resolver.traits_in_scope(self.db.upcast())
425 } 588 }
426 589
427 pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { 590 pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
428 let resolver = &self.resolver; 591 let resolver = &self.resolver;
429 592
430 resolver.process_all_names(self.db, &mut |name, def| { 593 resolver.process_all_names(self.db.upcast(), &mut |name, def| {
431 let def = match def { 594 let def = match def {
432 resolver::ScopeDef::PerNs(it) => { 595 resolver::ScopeDef::PerNs(it) => {
433 let items = ScopeDef::all_items(it); 596 let items = ScopeDef::all_items(it);
diff --git a/crates/ra_hir/src/semantics/source_to_def.rs b/crates/ra_hir/src/semantics/source_to_def.rs
index 8af64fdc1..42e5a1bdb 100644
--- a/crates/ra_hir/src/semantics/source_to_def.rs
+++ b/crates/ra_hir/src/semantics/source_to_def.rs
@@ -16,6 +16,7 @@ use ra_syntax::{
16 match_ast, AstNode, SyntaxNode, 16 match_ast, AstNode, SyntaxNode,
17}; 17};
18use rustc_hash::FxHashMap; 18use rustc_hash::FxHashMap;
19use stdx::impl_from;
19 20
20use crate::{db::HirDatabase, InFile, MacroDefId}; 21use crate::{db::HirDatabase, InFile, MacroDefId};
21 22
@@ -194,6 +195,10 @@ impl SourceToDefCtx<'_, '_> {
194 let def = self.const_to_def(container.with_value(it))?; 195 let def = self.const_to_def(container.with_value(it))?;
195 DefWithBodyId::from(def).into() 196 DefWithBodyId::from(def).into()
196 }, 197 },
198 ast::TypeAliasDef(it) => {
199 let def = self.type_alias_to_def(container.with_value(it))?;
200 def.into()
201 },
197 _ => continue, 202 _ => continue,
198 } 203 }
199 }; 204 };
@@ -246,19 +251,21 @@ pub(crate) enum ChildContainer {
246 ImplId(ImplId), 251 ImplId(ImplId),
247 EnumId(EnumId), 252 EnumId(EnumId),
248 VariantId(VariantId), 253 VariantId(VariantId),
254 TypeAliasId(TypeAliasId),
249 /// XXX: this might be the same def as, for example an `EnumId`. However, 255 /// XXX: this might be the same def as, for example an `EnumId`. However,
250 /// here the children generic parameters, and not, eg enum variants. 256 /// here the children generic parameters, and not, eg enum variants.
251 GenericDefId(GenericDefId), 257 GenericDefId(GenericDefId),
252} 258}
253impl_froms! { 259impl_from! {
254 ChildContainer:
255 DefWithBodyId, 260 DefWithBodyId,
256 ModuleId, 261 ModuleId,
257 TraitId, 262 TraitId,
258 ImplId, 263 ImplId,
259 EnumId, 264 EnumId,
260 VariantId, 265 VariantId,
266 TypeAliasId,
261 GenericDefId 267 GenericDefId
268 for ChildContainer
262} 269}
263 270
264impl ChildContainer { 271impl ChildContainer {
@@ -271,6 +278,7 @@ impl ChildContainer {
271 ChildContainer::ImplId(it) => it.child_by_source(db), 278 ChildContainer::ImplId(it) => it.child_by_source(db),
272 ChildContainer::EnumId(it) => it.child_by_source(db), 279 ChildContainer::EnumId(it) => it.child_by_source(db),
273 ChildContainer::VariantId(it) => it.child_by_source(db), 280 ChildContainer::VariantId(it) => it.child_by_source(db),
281 ChildContainer::TypeAliasId(_) => DynMap::default(),
274 ChildContainer::GenericDefId(it) => it.child_by_source(db), 282 ChildContainer::GenericDefId(it) => it.child_by_source(db),
275 } 283 }
276 } 284 }
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 4b509f07c..ecb54f653 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -18,7 +18,7 @@ use hir_def::{
18}; 18};
19use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; 19use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
20use hir_ty::{ 20use hir_ty::{
21 expr::{record_literal_missing_fields, record_pattern_missing_fields}, 21 diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
22 InferenceResult, Substs, Ty, 22 InferenceResult, Substs, Ty,
23}; 23};
24use ra_syntax::{ 24use ra_syntax::{
@@ -115,7 +115,7 @@ impl SourceAnalyzer {
115 Some(res) 115 Some(res)
116 } 116 }
117 117
118 pub(crate) fn type_of(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<Type> { 118 pub(crate) fn type_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<Type> {
119 let expr_id = self.expr_id(db, expr)?; 119 let expr_id = self.expr_id(db, expr)?;
120 let ty = self.infer.as_ref()?[expr_id].clone(); 120 let ty = self.infer.as_ref()?[expr_id].clone();
121 Type::new_with_resolver(db, &self.resolver, ty) 121 Type::new_with_resolver(db, &self.resolver, ty)
@@ -127,6 +127,17 @@ impl SourceAnalyzer {
127 Type::new_with_resolver(db, &self.resolver, ty) 127 Type::new_with_resolver(db, &self.resolver, ty)
128 } 128 }
129 129
130 pub(crate) fn type_of_self(
131 &self,
132 db: &dyn HirDatabase,
133 param: &ast::SelfParam,
134 ) -> Option<Type> {
135 let src = InFile { file_id: self.file_id, value: param };
136 let pat_id = self.body_source_map.as_ref()?.node_self_param(src)?;
137 let ty = self.infer.as_ref()?[pat_id].clone();
138 Type::new_with_resolver(db, &self.resolver, ty)
139 }
140
130 pub(crate) fn resolve_method_call( 141 pub(crate) fn resolve_method_call(
131 &self, 142 &self,
132 db: &dyn HirDatabase, 143 db: &dyn HirDatabase,
@@ -216,13 +227,43 @@ impl SourceAnalyzer {
216 if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { 227 if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) {
217 return Some(PathResolution::AssocItem(assoc.into())); 228 return Some(PathResolution::AssocItem(assoc.into()));
218 } 229 }
230 if let Some(VariantId::EnumVariantId(variant)) =
231 self.infer.as_ref()?.variant_resolution_for_expr(expr_id)
232 {
233 return Some(PathResolution::Def(ModuleDef::EnumVariant(variant.into())));
234 }
219 } 235 }
236
220 if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) { 237 if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) {
221 let pat_id = self.pat_id(&path_pat.into())?; 238 let pat_id = self.pat_id(&path_pat.into())?;
222 if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) { 239 if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) {
223 return Some(PathResolution::AssocItem(assoc.into())); 240 return Some(PathResolution::AssocItem(assoc.into()));
224 } 241 }
242 if let Some(VariantId::EnumVariantId(variant)) =
243 self.infer.as_ref()?.variant_resolution_for_pat(pat_id)
244 {
245 return Some(PathResolution::Def(ModuleDef::EnumVariant(variant.into())));
246 }
247 }
248
249 if let Some(rec_lit) = path.syntax().parent().and_then(ast::RecordLit::cast) {
250 let expr_id = self.expr_id(db, &rec_lit.into())?;
251 if let Some(VariantId::EnumVariantId(variant)) =
252 self.infer.as_ref()?.variant_resolution_for_expr(expr_id)
253 {
254 return Some(PathResolution::Def(ModuleDef::EnumVariant(variant.into())));
255 }
256 }
257
258 if let Some(rec_pat) = path.syntax().parent().and_then(ast::RecordPat::cast) {
259 let pat_id = self.pat_id(&rec_pat.into())?;
260 if let Some(VariantId::EnumVariantId(variant)) =
261 self.infer.as_ref()?.variant_resolution_for_pat(pat_id)
262 {
263 return Some(PathResolution::Def(ModuleDef::EnumVariant(variant.into())));
264 }
225 } 265 }
266
226 // This must be a normal source file rather than macro file. 267 // This must be a normal source file rather than macro file.
227 let hir_path = 268 let hir_path =
228 crate::Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?; 269 crate::Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?;
@@ -307,10 +348,21 @@ impl SourceAnalyzer {
307 db: &dyn HirDatabase, 348 db: &dyn HirDatabase,
308 macro_call: InFile<&ast::MacroCall>, 349 macro_call: InFile<&ast::MacroCall>,
309 ) -> Option<HirFileId> { 350 ) -> Option<HirFileId> {
310 let macro_call_id = macro_call.as_call_id(db.upcast(), |path| { 351 let krate = self.resolver.krate()?;
352 let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| {
311 self.resolver.resolve_path_as_macro(db.upcast(), &path) 353 self.resolver.resolve_path_as_macro(db.upcast(), &path)
312 })?; 354 })?;
313 Some(macro_call_id.as_file()) 355 Some(macro_call_id.as_file()).filter(|it| it.expansion_level(db.upcast()) < 64)
356 }
357
358 pub(crate) fn resolve_variant(
359 &self,
360 db: &dyn HirDatabase,
361 record_lit: ast::RecordLit,
362 ) -> Option<VariantId> {
363 let infer = self.infer.as_ref()?;
364 let expr_id = self.expr_id(db, &record_lit.into())?;
365 infer.variant_resolution_for_expr(expr_id)
314 } 366 }
315} 367}
316 368
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index b85358308..1ec619b42 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_hir_def" 3name = "ra_hir_def"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
@@ -14,6 +15,10 @@ rustc-hash = "1.1.0"
14either = "1.5.3" 15either = "1.5.3"
15anymap = "0.12.1" 16anymap = "0.12.1"
16drop_bomb = "0.1.4" 17drop_bomb = "0.1.4"
18fst = { version = "0.4", default-features = false }
19itertools = "0.9.0"
20indexmap = "1.4.0"
21smallvec = "1.4.0"
17 22
18stdx = { path = "../stdx" } 23stdx = { path = "../stdx" }
19 24
@@ -29,3 +34,4 @@ tt = { path = "../ra_tt", package = "ra_tt" }
29 34
30[dev-dependencies] 35[dev-dependencies]
31insta = "0.16.0" 36insta = "0.16.0"
37expect = { path = "../expect" }
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs
index 2bc34d449..4994a2125 100644
--- a/crates/ra_hir_def/src/adt.rs
+++ b/crates/ra_hir_def/src/adt.rs
@@ -8,12 +8,12 @@ use hir_expand::{
8 InFile, 8 InFile,
9}; 9};
10use ra_arena::{map::ArenaMap, Arena}; 10use ra_arena::{map::ArenaMap, Arena};
11use ra_prof::profile;
12use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner}; 11use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner};
13 12
14use crate::{ 13use crate::{
15 body::{CfgExpander, LowerCtx}, 14 body::{CfgExpander, LowerCtx},
16 db::DefDatabase, 15 db::DefDatabase,
16 item_tree::{Field, Fields, ItemTree},
17 src::HasChildSource, 17 src::HasChildSource,
18 src::HasSource, 18 src::HasSource,
19 trace::Trace, 19 trace::Trace,
@@ -22,6 +22,7 @@ use crate::{
22 EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, 22 EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId,
23 VariantId, 23 VariantId,
24}; 24};
25use ra_cfg::CfgOptions;
25 26
26/// Note that we use `StructData` for unions as well! 27/// Note that we use `StructData` for unions as well!
27#[derive(Debug, Clone, PartialEq, Eq)] 28#[derive(Debug, Clone, PartialEq, Eq)]
@@ -59,39 +60,48 @@ pub struct FieldData {
59 60
60impl StructData { 61impl StructData {
61 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { 62 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
62 let src = id.lookup(db).source(db); 63 let loc = id.lookup(db);
64 let item_tree = db.item_tree(loc.id.file_id);
65 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
63 66
64 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); 67 let strukt = &item_tree[loc.id.value];
65 let variant_data = 68 let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields);
66 VariantData::new(db, src.map(|s| s.kind()), id.lookup(db).container.module(db)); 69
67 let variant_data = Arc::new(variant_data); 70 Arc::new(StructData { name: strukt.name.clone(), variant_data: Arc::new(variant_data) })
68 Arc::new(StructData { name, variant_data })
69 } 71 }
70 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { 72 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
71 let src = id.lookup(db).source(db); 73 let loc = id.lookup(db);
72 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); 74 let item_tree = db.item_tree(loc.id.file_id);
73 let variant_data = VariantData::new( 75 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
74 db, 76
75 src.map(|s| { 77 let union = &item_tree[loc.id.value];
76 s.record_field_def_list() 78 let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields);
77 .map(ast::StructKind::Record) 79
78 .unwrap_or(ast::StructKind::Unit) 80 Arc::new(StructData { name: union.name.clone(), variant_data: Arc::new(variant_data) })
79 }),
80 id.lookup(db).container.module(db),
81 );
82 let variant_data = Arc::new(variant_data);
83 Arc::new(StructData { name, variant_data })
84 } 81 }
85} 82}
86 83
87impl EnumData { 84impl EnumData {
88 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { 85 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
89 let _p = profile("enum_data_query"); 86 let loc = e.lookup(db);
90 let src = e.lookup(db).source(db); 87 let item_tree = db.item_tree(loc.id.file_id);
91 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); 88 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone();
92 let mut trace = Trace::new_for_arena(); 89
93 lower_enum(db, &mut trace, &src, e.lookup(db).container.module(db)); 90 let enum_ = &item_tree[loc.id.value];
94 Arc::new(EnumData { name, variants: trace.into_arena() }) 91 let mut variants = Arena::new();
92 for var_id in enum_.variants.clone() {
93 if item_tree.attrs(var_id.into()).is_cfg_enabled(&cfg_options) {
94 let var = &item_tree[var_id];
95 let var_data = lower_fields(&item_tree, &cfg_options, &var.fields);
96
97 variants.alloc(EnumVariantData {
98 name: var.name.clone(),
99 variant_data: Arc::new(var_data),
100 });
101 }
102 }
103
104 Arc::new(EnumData { name: enum_.name.clone(), variants })
95 } 105 }
96 106
97 pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> { 107 pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
@@ -251,3 +261,35 @@ fn lower_struct(
251 ast::StructKind::Unit => StructKind::Unit, 261 ast::StructKind::Unit => StructKind::Unit,
252 } 262 }
253} 263}
264
265fn lower_fields(item_tree: &ItemTree, cfg_options: &CfgOptions, fields: &Fields) -> VariantData {
266 match fields {
267 Fields::Record(flds) => {
268 let mut arena = Arena::new();
269 for field_id in flds.clone() {
270 if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) {
271 arena.alloc(lower_field(item_tree, &item_tree[field_id]));
272 }
273 }
274 VariantData::Record(arena)
275 }
276 Fields::Tuple(flds) => {
277 let mut arena = Arena::new();
278 for field_id in flds.clone() {
279 if item_tree.attrs(field_id.into()).is_cfg_enabled(cfg_options) {
280 arena.alloc(lower_field(item_tree, &item_tree[field_id]));
281 }
282 }
283 VariantData::Tuple(arena)
284 }
285 Fields::Unit => VariantData::Unit,
286 }
287}
288
289fn lower_field(item_tree: &ItemTree, field: &Field) -> FieldData {
290 FieldData {
291 name: field.name.clone(),
292 type_ref: field.type_ref.clone(),
293 visibility: item_tree[field.visibility].clone(),
294 }
295}
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 8b6c0bede..e228e2145 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -13,7 +13,11 @@ use ra_syntax::{
13use tt::Subtree; 13use tt::Subtree;
14 14
15use crate::{ 15use crate::{
16 db::DefDatabase, nameres::ModuleSource, path::ModPath, src::HasChildSource, src::HasSource, 16 db::DefDatabase,
17 item_tree::{ItemTreeId, ItemTreeNode},
18 nameres::ModuleSource,
19 path::ModPath,
20 src::HasChildSource,
17 AdtId, AttrDefId, Lookup, 21 AdtId, AttrDefId, Lookup,
18}; 22};
19 23
@@ -34,6 +38,8 @@ impl ops::Deref for Attrs {
34} 38}
35 39
36impl Attrs { 40impl Attrs {
41 pub const EMPTY: Attrs = Attrs { entries: None };
42
37 pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs { 43 pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
38 match def { 44 match def {
39 AttrDefId::ModuleId(module) => { 45 AttrDefId::ModuleId(module) => {
@@ -65,19 +71,19 @@ impl Attrs {
65 Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner)) 71 Attrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner))
66 } 72 }
67 AttrDefId::AdtId(it) => match it { 73 AttrDefId::AdtId(it) => match it {
68 AdtId::StructId(it) => attrs_from_loc(it.lookup(db), db), 74 AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
69 AdtId::EnumId(it) => attrs_from_loc(it.lookup(db), db), 75 AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
70 AdtId::UnionId(it) => attrs_from_loc(it.lookup(db), db), 76 AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
71 }, 77 },
72 AttrDefId::TraitId(it) => attrs_from_loc(it.lookup(db), db), 78 AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
73 AttrDefId::MacroDefId(it) => { 79 AttrDefId::MacroDefId(it) => {
74 it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db)) 80 it.ast_id.map_or_else(Default::default, |ast_id| attrs_from_ast(ast_id, db))
75 } 81 }
76 AttrDefId::ImplId(it) => attrs_from_loc(it.lookup(db), db), 82 AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
77 AttrDefId::ConstId(it) => attrs_from_loc(it.lookup(db), db), 83 AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
78 AttrDefId::StaticId(it) => attrs_from_loc(it.lookup(db), db), 84 AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
79 AttrDefId::FunctionId(it) => attrs_from_loc(it.lookup(db), db), 85 AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
80 AttrDefId::TypeAliasId(it) => attrs_from_loc(it.lookup(db), db), 86 AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
81 } 87 }
82 } 88 }
83 89
@@ -87,16 +93,34 @@ impl Attrs {
87 } 93 }
88 94
89 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { 95 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
96 let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map(
97 |docs_text| Attr {
98 input: Some(AttrInput::Literal(SmolStr::new(docs_text))),
99 path: ModPath::from(hir_expand::name!(doc)),
100 },
101 );
90 let mut attrs = owner.attrs().peekable(); 102 let mut attrs = owner.attrs().peekable();
91 let entries = if attrs.peek().is_none() { 103 let entries = if attrs.peek().is_none() {
92 // Avoid heap allocation 104 // Avoid heap allocation
93 None 105 None
94 } else { 106 } else {
95 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect()) 107 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect())
96 }; 108 };
97 Attrs { entries } 109 Attrs { entries }
98 } 110 }
99 111
112 pub fn merge(&self, other: Attrs) -> Attrs {
113 match (&self.entries, &other.entries) {
114 (None, None) => Attrs { entries: None },
115 (Some(entries), None) | (None, Some(entries)) => {
116 Attrs { entries: Some(entries.clone()) }
117 }
118 (Some(a), Some(b)) => {
119 Attrs { entries: Some(a.iter().chain(b.iter()).cloned().collect()) }
120 }
121 }
122 }
123
100 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { 124 pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
101 AttrQuery { attrs: self, key } 125 AttrQuery { attrs: self, key }
102 } 126 }
@@ -181,11 +205,8 @@ where
181 Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner)) 205 Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
182} 206}
183 207
184fn attrs_from_loc<T>(node: T, db: &dyn DefDatabase) -> Attrs 208fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> Attrs {
185where 209 let tree = db.item_tree(id.file_id);
186 T: HasSource, 210 let mod_item = N::id_to_mod_item(id.value);
187 T::Value: ast::AttrsOwner, 211 tree.attrs(mod_item.into()).clone()
188{
189 let src = node.source(db);
190 Attrs::from_attrs_owner(db, src.as_ref().map(|it| it as &dyn AttrsOwner))
191} 212}
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 273036cee..2fe04db2b 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -14,6 +14,7 @@ use ra_db::CrateId;
14use ra_prof::profile; 14use ra_prof::profile;
15use ra_syntax::{ast, AstNode, AstPtr}; 15use ra_syntax::{ast, AstNode, AstPtr};
16use rustc_hash::FxHashMap; 16use rustc_hash::FxHashMap;
17use test_utils::mark;
17 18
18pub(crate) use lower::LowerCtx; 19pub(crate) use lower::LowerCtx;
19 20
@@ -42,9 +43,15 @@ pub(crate) struct Expander {
42 current_file_id: HirFileId, 43 current_file_id: HirFileId,
43 ast_id_map: Arc<AstIdMap>, 44 ast_id_map: Arc<AstIdMap>,
44 module: ModuleId, 45 module: ModuleId,
45 recursive_limit: usize, 46 recursion_limit: usize,
46} 47}
47 48
49#[cfg(test)]
50const EXPANSION_RECURSION_LIMIT: usize = 32;
51
52#[cfg(not(test))]
53const EXPANSION_RECURSION_LIMIT: usize = 128;
54
48impl CfgExpander { 55impl CfgExpander {
49 pub(crate) fn new( 56 pub(crate) fn new(
50 db: &dyn DefDatabase, 57 db: &dyn DefDatabase,
@@ -81,7 +88,7 @@ impl Expander {
81 current_file_id, 88 current_file_id,
82 ast_id_map, 89 ast_id_map,
83 module, 90 module,
84 recursive_limit: 0, 91 recursion_limit: 0,
85 } 92 }
86 } 93 }
87 94
@@ -91,13 +98,15 @@ impl Expander {
91 local_scope: Option<&ItemScope>, 98 local_scope: Option<&ItemScope>,
92 macro_call: ast::MacroCall, 99 macro_call: ast::MacroCall,
93 ) -> Option<(Mark, T)> { 100 ) -> Option<(Mark, T)> {
94 if self.recursive_limit > 1024 { 101 self.recursion_limit += 1;
102 if self.recursion_limit > EXPANSION_RECURSION_LIMIT {
103 mark::hit!(your_stack_belongs_to_me);
95 return None; 104 return None;
96 } 105 }
97 106
98 let macro_call = InFile::new(self.current_file_id, &macro_call); 107 let macro_call = InFile::new(self.current_file_id, &macro_call);
99 108
100 if let Some(call_id) = macro_call.as_call_id(db, |path| { 109 if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| {
101 if let Some(local_scope) = local_scope { 110 if let Some(local_scope) = local_scope {
102 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { 111 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
103 return Some(def); 112 return Some(def);
@@ -118,8 +127,6 @@ impl Expander {
118 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); 127 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
119 self.current_file_id = file_id; 128 self.current_file_id = file_id;
120 self.ast_id_map = db.ast_id_map(file_id); 129 self.ast_id_map = db.ast_id_map(file_id);
121 self.recursive_limit += 1;
122
123 return Some((mark, expr)); 130 return Some((mark, expr));
124 } 131 }
125 } 132 }
@@ -134,7 +141,7 @@ impl Expander {
134 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); 141 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
135 self.current_file_id = mark.file_id; 142 self.current_file_id = mark.file_id;
136 self.ast_id_map = mem::take(&mut mark.ast_id_map); 143 self.ast_id_map = mem::take(&mut mark.ast_id_map);
137 self.recursive_limit -= 1; 144 self.recursion_limit -= 1;
138 mark.bomb.defuse(); 145 mark.bomb.defuse();
139 } 146 }
140 147
@@ -302,7 +309,53 @@ impl BodySourceMap {
302 self.pat_map.get(&src).cloned() 309 self.pat_map.get(&src).cloned()
303 } 310 }
304 311
312 pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId> {
313 let src = node.map(|it| Either::Right(AstPtr::new(it)));
314 self.pat_map.get(&src).cloned()
315 }
316
305 pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordField>> { 317 pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordField>> {
306 self.field_map[&(expr, field)].clone() 318 self.field_map[&(expr, field)].clone()
307 } 319 }
308} 320}
321
322#[cfg(test)]
323mod tests {
324 use ra_db::{fixture::WithFixture, SourceDatabase};
325 use test_utils::mark;
326
327 use crate::ModuleDefId;
328
329 use super::*;
330
331 fn lower(ra_fixture: &str) -> Arc<Body> {
332 let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture);
333
334 let krate = db.crate_graph().iter().next().unwrap();
335 let def_map = db.crate_def_map(krate);
336 let module = def_map.modules_for_file(file_id).next().unwrap();
337 let module = &def_map[module];
338 let fn_def = match module.scope.declarations().next().unwrap() {
339 ModuleDefId::FunctionId(it) => it,
340 _ => panic!(),
341 };
342
343 db.body(fn_def.into())
344 }
345
346 #[test]
347 fn your_stack_belongs_to_me() {
348 mark::check!(your_stack_belongs_to_me);
349 lower(
350 "
351macro_rules! n_nuple {
352 ($e:tt) => ();
353 ($($rest:tt)*) => {{
354 (n_nuple!($($rest)*)None,)
355 }};
356}
357fn main() { n_nuple!(1,2,3); }
358",
359 );
360 }
361}
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index f159f80af..c6bc85e2f 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -27,6 +27,7 @@ use crate::{
27 LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, 27 LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
28 }, 28 },
29 item_scope::BuiltinShadowMode, 29 item_scope::BuiltinShadowMode,
30 item_tree::{ItemTree, ItemTreeId, ItemTreeNode},
30 path::{GenericArgs, Path}, 31 path::{GenericArgs, Path},
31 type_ref::{Mutability, Rawness, TypeRef}, 32 type_ref::{Mutability, Rawness, TypeRef},
32 AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, 33 AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
@@ -35,6 +36,8 @@ use crate::{
35 36
36use super::{ExprSource, PatSource}; 37use super::{ExprSource, PatSource};
37use ast::AstChildren; 38use ast::AstChildren;
39use rustc_hash::FxHashMap;
40use std::{any::type_name, sync::Arc};
38 41
39pub(crate) struct LowerCtx { 42pub(crate) struct LowerCtx {
40 hygiene: Hygiene, 43 hygiene: Hygiene,
@@ -60,10 +63,10 @@ pub(super) fn lower(
60 params: Option<ast::ParamList>, 63 params: Option<ast::ParamList>,
61 body: Option<ast::Expr>, 64 body: Option<ast::Expr>,
62) -> (Body, BodySourceMap) { 65) -> (Body, BodySourceMap) {
66 let item_tree = db.item_tree(expander.current_file_id);
63 ExprCollector { 67 ExprCollector {
64 db, 68 db,
65 def, 69 def,
66 expander,
67 source_map: BodySourceMap::default(), 70 source_map: BodySourceMap::default(),
68 body: Body { 71 body: Body {
69 exprs: Arena::default(), 72 exprs: Arena::default(),
@@ -72,6 +75,12 @@ pub(super) fn lower(
72 body_expr: dummy_expr_id(), 75 body_expr: dummy_expr_id(),
73 item_scope: Default::default(), 76 item_scope: Default::default(),
74 }, 77 },
78 item_trees: {
79 let mut map = FxHashMap::default();
80 map.insert(expander.current_file_id, item_tree);
81 map
82 },
83 expander,
75 } 84 }
76 .collect(params, body) 85 .collect(params, body)
77} 86}
@@ -82,6 +91,8 @@ struct ExprCollector<'a> {
82 expander: Expander, 91 expander: Expander,
83 body: Body, 92 body: Body,
84 source_map: BodySourceMap, 93 source_map: BodySourceMap,
94
95 item_trees: FxHashMap<HirFileId, Arc<ItemTree>>,
85} 96}
86 97
87impl ExprCollector<'_> { 98impl ExprCollector<'_> {
@@ -165,6 +176,7 @@ impl ExprCollector<'_> {
165 if !self.expander.is_cfg_enabled(&expr) { 176 if !self.expander.is_cfg_enabled(&expr) {
166 return self.missing_expr(); 177 return self.missing_expr();
167 } 178 }
179
168 match expr { 180 match expr {
169 ast::Expr::IfExpr(e) => { 181 ast::Expr::IfExpr(e) => {
170 let then_branch = self.collect_block_opt(e.then_branch()); 182 let then_branch = self.collect_block_opt(e.then_branch());
@@ -207,8 +219,12 @@ impl ExprCollector<'_> {
207 let body = self.collect_block_opt(e.block_expr()); 219 let body = self.collect_block_opt(e.block_expr());
208 self.alloc_expr(Expr::TryBlock { body }, syntax_ptr) 220 self.alloc_expr(Expr::TryBlock { body }, syntax_ptr)
209 } 221 }
222 ast::Effect::Unsafe(_) => {
223 let body = self.collect_block_opt(e.block_expr());
224 self.alloc_expr(Expr::Unsafe { body }, syntax_ptr)
225 }
210 // FIXME: we need to record these effects somewhere... 226 // FIXME: we need to record these effects somewhere...
211 ast::Effect::Async(_) | ast::Effect::Label(_) | ast::Effect::Unsafe(_) => { 227 ast::Effect::Async(_) | ast::Effect::Label(_) => {
212 self.collect_block_opt(e.block_expr()) 228 self.collect_block_opt(e.block_expr())
213 } 229 }
214 }, 230 },
@@ -434,7 +450,6 @@ impl ExprCollector<'_> {
434 Mutability::from_mutable(e.mut_token().is_some()) 450 Mutability::from_mutable(e.mut_token().is_some())
435 }; 451 };
436 let rawness = Rawness::from_raw(raw_tok); 452 let rawness = Rawness::from_raw(raw_tok);
437
438 self.alloc_expr(Expr::Ref { expr, rawness, mutability }, syntax_ptr) 453 self.alloc_expr(Expr::Ref { expr, rawness, mutability }, syntax_ptr)
439 } 454 }
440 ast::Expr::PrefixExpr(e) => { 455 ast::Expr::PrefixExpr(e) => {
@@ -533,6 +548,9 @@ impl ExprCollector<'_> {
533 self.source_map 548 self.source_map
534 .expansions 549 .expansions
535 .insert(macro_call, self.expander.current_file_id); 550 .insert(macro_call, self.expander.current_file_id);
551
552 let item_tree = self.db.item_tree(self.expander.current_file_id);
553 self.item_trees.insert(self.expander.current_file_id, item_tree);
536 let id = self.collect_expr(expansion); 554 let id = self.collect_expr(expansion);
537 self.expander.exit(self.db, mark); 555 self.expander.exit(self.db, mark);
538 id 556 id
@@ -547,6 +565,32 @@ impl ExprCollector<'_> {
547 } 565 }
548 } 566 }
549 567
568 fn find_inner_item<N: ItemTreeNode>(&self, ast: &N::Source) -> Option<ItemTreeId<N>> {
569 let id = self.expander.ast_id(ast);
570 let tree = &self.item_trees[&id.file_id];
571
572 // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes
573
574 // Root file (non-macro).
575 let item_tree_id = tree
576 .all_inner_items()
577 .chain(tree.top_level_items().iter().copied())
578 .filter_map(|mod_item| mod_item.downcast::<N>())
579 .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value.upcast())
580 .or_else(|| {
581 log::debug!(
582 "couldn't find inner {} item for {:?} (AST: `{}` - {:?})",
583 type_name::<N>(),
584 id,
585 ast.syntax(),
586 ast.syntax(),
587 );
588 None
589 })?;
590
591 Some(ItemTreeId::new(id.file_id, item_tree_id))
592 }
593
550 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { 594 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
551 if let Some(expr) = expr { 595 if let Some(expr) = expr {
552 self.collect_expr(expr) 596 self.collect_expr(expr)
@@ -578,56 +622,65 @@ impl ExprCollector<'_> {
578 622
579 fn collect_block_items(&mut self, block: &ast::BlockExpr) { 623 fn collect_block_items(&mut self, block: &ast::BlockExpr) {
580 let container = ContainerId::DefWithBodyId(self.def); 624 let container = ContainerId::DefWithBodyId(self.def);
581 for item in block.items() { 625
582 let (def, name): (ModuleDefId, Option<ast::Name>) = match item { 626 let items = block
583 ast::ModuleItem::FnDef(def) => { 627 .items()
584 let ast_id = self.expander.ast_id(&def); 628 .filter_map(|item| {
585 ( 629 let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
586 FunctionLoc { container: container.into(), ast_id }.intern(self.db).into(), 630 ast::ModuleItem::FnDef(def) => {
587 def.name(), 631 let id = self.find_inner_item(&def)?;
588 ) 632 (
589 } 633 FunctionLoc { container: container.into(), id }.intern(self.db).into(),
590 ast::ModuleItem::TypeAliasDef(def) => { 634 def.name(),
591 let ast_id = self.expander.ast_id(&def); 635 )
592 ( 636 }
593 TypeAliasLoc { container: container.into(), ast_id }.intern(self.db).into(), 637 ast::ModuleItem::TypeAliasDef(def) => {
594 def.name(), 638 let id = self.find_inner_item(&def)?;
595 ) 639 (
596 } 640 TypeAliasLoc { container: container.into(), id }.intern(self.db).into(),
597 ast::ModuleItem::ConstDef(def) => { 641 def.name(),
598 let ast_id = self.expander.ast_id(&def); 642 )
599 ( 643 }
600 ConstLoc { container: container.into(), ast_id }.intern(self.db).into(), 644 ast::ModuleItem::ConstDef(def) => {
601 def.name(), 645 let id = self.find_inner_item(&def)?;
602 ) 646 (
603 } 647 ConstLoc { container: container.into(), id }.intern(self.db).into(),
604 ast::ModuleItem::StaticDef(def) => { 648 def.name(),
605 let ast_id = self.expander.ast_id(&def); 649 )
606 (StaticLoc { container, ast_id }.intern(self.db).into(), def.name()) 650 }
607 } 651 ast::ModuleItem::StaticDef(def) => {
608 ast::ModuleItem::StructDef(def) => { 652 let id = self.find_inner_item(&def)?;
609 let ast_id = self.expander.ast_id(&def); 653 (StaticLoc { container, id }.intern(self.db).into(), def.name())
610 (StructLoc { container, ast_id }.intern(self.db).into(), def.name()) 654 }
611 } 655 ast::ModuleItem::StructDef(def) => {
612 ast::ModuleItem::EnumDef(def) => { 656 let id = self.find_inner_item(&def)?;
613 let ast_id = self.expander.ast_id(&def); 657 (StructLoc { container, id }.intern(self.db).into(), def.name())
614 (EnumLoc { container, ast_id }.intern(self.db).into(), def.name()) 658 }
615 } 659 ast::ModuleItem::EnumDef(def) => {
616 ast::ModuleItem::UnionDef(def) => { 660 let id = self.find_inner_item(&def)?;
617 let ast_id = self.expander.ast_id(&def); 661 (EnumLoc { container, id }.intern(self.db).into(), def.name())
618 (UnionLoc { container, ast_id }.intern(self.db).into(), def.name()) 662 }
619 } 663 ast::ModuleItem::UnionDef(def) => {
620 ast::ModuleItem::TraitDef(def) => { 664 let id = self.find_inner_item(&def)?;
621 let ast_id = self.expander.ast_id(&def); 665 (UnionLoc { container, id }.intern(self.db).into(), def.name())
622 (TraitLoc { container, ast_id }.intern(self.db).into(), def.name()) 666 }
623 } 667 ast::ModuleItem::TraitDef(def) => {
624 ast::ModuleItem::ExternBlock(_) => continue, // FIXME: collect from extern blocks 668 let id = self.find_inner_item(&def)?;
625 ast::ModuleItem::ImplDef(_) 669 (TraitLoc { container, id }.intern(self.db).into(), def.name())
626 | ast::ModuleItem::UseItem(_) 670 }
627 | ast::ModuleItem::ExternCrateItem(_) 671 ast::ModuleItem::ExternBlock(_) => return None, // FIXME: collect from extern blocks
628 | ast::ModuleItem::Module(_) 672 ast::ModuleItem::ImplDef(_)
629 | ast::ModuleItem::MacroCall(_) => continue, 673 | ast::ModuleItem::UseItem(_)
630 }; 674 | ast::ModuleItem::ExternCrateItem(_)
675 | ast::ModuleItem::Module(_)
676 | ast::ModuleItem::MacroCall(_) => return None,
677 };
678
679 Some((def, name))
680 })
681 .collect::<Vec<_>>();
682
683 for (def, name) in items {
631 self.body.item_scope.define_def(def); 684 self.body.item_scope.define_def(def);
632 if let Some(name) = name { 685 if let Some(name) = name {
633 let vis = crate::visibility::Visibility::Public; // FIXME determine correctly 686 let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs
index e48ff38f9..99e876683 100644
--- a/crates/ra_hir_def/src/body/scope.rs
+++ b/crates/ra_hir_def/src/body/scope.rs
@@ -87,15 +87,13 @@ impl ExprScopes {
87 } 87 }
88 88
89 fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { 89 fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
90 match &body[pat] { 90 let pattern = &body[pat];
91 Pat::Bind { name, .. } => { 91 if let Pat::Bind { name, .. } = pattern {
92 // bind can have a sub pattern, but it's actually not allowed 92 let entry = ScopeEntry { name: name.clone(), pat };
93 // to bind to things in there 93 self.scopes[scope].entries.push(entry);
94 let entry = ScopeEntry { name: name.clone(), pat };
95 self.scopes[scope].entries.push(entry)
96 }
97 p => p.walk_child_pats(|pat| self.add_bindings(body, scope, pat)),
98 } 94 }
95
96 pattern.walk_child_pats(|pat| self.add_bindings(body, scope, pat));
99 } 97 }
100 98
101 fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) { 99 fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) {
@@ -190,21 +188,23 @@ mod tests {
190 } 188 }
191 } 189 }
192 190
193 fn do_check(code: &str, expected: &[&str]) { 191 fn do_check(ra_fixture: &str, expected: &[&str]) {
194 let (off, code) = extract_offset(code); 192 let (offset, code) = extract_offset(ra_fixture);
195 let code = { 193 let code = {
196 let mut buf = String::new(); 194 let mut buf = String::new();
197 let off: usize = off.into(); 195 let off: usize = offset.into();
198 buf.push_str(&code[..off]); 196 buf.push_str(&code[..off]);
199 buf.push_str("marker"); 197 buf.push_str("<|>marker");
200 buf.push_str(&code[off..]); 198 buf.push_str(&code[off..]);
201 buf 199 buf
202 }; 200 };
203 201
204 let (db, file_id) = TestDB::with_single_file(&code); 202 let (db, position) = TestDB::with_position(&code);
203 let file_id = position.file_id;
204 let offset = position.offset;
205 205
206 let file_syntax = db.parse(file_id).syntax_node(); 206 let file_syntax = db.parse(file_id).syntax_node();
207 let marker: ast::PathExpr = find_node_at_offset(&file_syntax, off).unwrap(); 207 let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap();
208 let function = find_function(&db, file_id); 208 let function = find_function(&db, file_id);
209 209
210 let scopes = db.expr_scopes(function.into()); 210 let scopes = db.expr_scopes(function.into());
@@ -300,15 +300,65 @@ mod tests {
300 ); 300 );
301 } 301 }
302 302
303 fn do_check_local_name(code: &str, expected_offset: u32) { 303 #[test]
304 let (off, code) = extract_offset(code); 304 fn test_bindings_after_at() {
305 do_check(
306 r"
307fn foo() {
308 match Some(()) {
309 opt @ Some(unit) => {
310 <|>
311 }
312 _ => {}
313 }
314}
315",
316 &["opt", "unit"],
317 );
318 }
319
320 #[test]
321 fn macro_inner_item() {
322 do_check(
323 r"
324 macro_rules! mac {
325 () => {{
326 fn inner() {}
327 inner();
328 }};
329 }
330
331 fn foo() {
332 mac!();
333 <|>
334 }
335 ",
336 &[],
337 );
338 }
339
340 #[test]
341 fn broken_inner_item() {
342 do_check(
343 r"
344 fn foo() {
345 trait {}
346 <|>
347 }
348 ",
349 &[],
350 );
351 }
305 352
306 let (db, file_id) = TestDB::with_single_file(&code); 353 fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
354 let (db, position) = TestDB::with_position(ra_fixture);
355 let file_id = position.file_id;
356 let offset = position.offset;
307 357
308 let file = db.parse(file_id).ok().unwrap(); 358 let file = db.parse(file_id).ok().unwrap();
309 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()) 359 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
310 .expect("failed to find a name at the target offset"); 360 .expect("failed to find a name at the target offset");
311 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); 361 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset).unwrap();
312 362
313 let function = find_function(&db, file_id); 363 let function = find_function(&db, file_id);
314 364
@@ -336,15 +386,16 @@ mod tests {
336 fn test_resolve_local_name() { 386 fn test_resolve_local_name() {
337 do_check_local_name( 387 do_check_local_name(
338 r#" 388 r#"
339 fn foo(x: i32, y: u32) { 389fn foo(x: i32, y: u32) {
340 { 390 {
341 let z = x * 2; 391 let z = x * 2;
342 } 392 }
343 { 393 {
344 let t = x<|> * 3; 394 let t = x<|> * 3;
345 } 395 }
346 }"#, 396}
347 21, 397"#,
398 7,
348 ); 399 );
349 } 400 }
350 401
@@ -352,10 +403,11 @@ mod tests {
352 fn test_resolve_local_name_declaration() { 403 fn test_resolve_local_name_declaration() {
353 do_check_local_name( 404 do_check_local_name(
354 r#" 405 r#"
355 fn foo(x: String) { 406fn foo(x: String) {
356 let x : &str = &x<|>; 407 let x : &str = &x<|>;
357 }"#, 408}
358 21, 409"#,
410 7,
359 ); 411 );
360 } 412 }
361 413
@@ -363,12 +415,12 @@ mod tests {
363 fn test_resolve_local_name_shadow() { 415 fn test_resolve_local_name_shadow() {
364 do_check_local_name( 416 do_check_local_name(
365 r" 417 r"
366 fn foo(x: String) { 418fn foo(x: String) {
367 let x : &str = &x; 419 let x : &str = &x;
368 x<|> 420 x<|>
369 } 421}
370 ", 422",
371 53, 423 28,
372 ); 424 );
373 } 425 }
374 426
@@ -376,13 +428,13 @@ mod tests {
376 fn ref_patterns_contribute_bindings() { 428 fn ref_patterns_contribute_bindings() {
377 do_check_local_name( 429 do_check_local_name(
378 r" 430 r"
379 fn foo() { 431fn foo() {
380 if let Some(&from) = bar() { 432 if let Some(&from) = bar() {
381 from<|>; 433 from<|>;
382 } 434 }
383 } 435}
384 ", 436",
385 53, 437 28,
386 ); 438 );
387 } 439 }
388 440
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index e2130d931..88a8ef9bf 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -2,27 +2,19 @@
2 2
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_expand::{ 5use hir_expand::{name::Name, InFile};
6 hygiene::Hygiene,
7 name::{name, AsName, Name},
8 AstId, InFile,
9};
10use ra_prof::profile; 6use ra_prof::profile;
11use ra_syntax::ast::{ 7use ra_syntax::ast;
12 self, AssocItem, AstNode, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
13 VisibilityOwner,
14};
15 8
16use crate::{ 9use crate::{
17 attr::Attrs, 10 attr::Attrs,
18 body::LowerCtx, 11 body::Expander,
19 db::DefDatabase, 12 db::DefDatabase,
20 path::{path, AssociatedTypeBinding, GenericArgs, Path}, 13 item_tree::{AssocItem, ItemTreeId, ModItem},
21 src::HasSource, 14 type_ref::{TypeBound, TypeRef},
22 type_ref::{Mutability, TypeBound, TypeRef},
23 visibility::RawVisibility, 15 visibility::RawVisibility,
24 AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule, 16 AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
25 ImplId, Intern, Lookup, StaticId, TraitId, TypeAliasId, TypeAliasLoc, 17 Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
26}; 18};
27 19
28#[derive(Debug, Clone, PartialEq, Eq)] 20#[derive(Debug, Clone, PartialEq, Eq)]
@@ -34,85 +26,36 @@ pub struct FunctionData {
34 /// True if the first param is `self`. This is relevant to decide whether this 26 /// True if the first param is `self`. This is relevant to decide whether this
35 /// can be called as a method. 27 /// can be called as a method.
36 pub has_self_param: bool, 28 pub has_self_param: bool,
29 pub is_unsafe: bool,
30 pub is_varargs: bool,
37 pub visibility: RawVisibility, 31 pub visibility: RawVisibility,
38} 32}
39 33
40impl FunctionData { 34impl FunctionData {
41 pub(crate) fn fn_data_query(db: &impl DefDatabase, func: FunctionId) -> Arc<FunctionData> { 35 pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> {
42 let loc = func.lookup(db); 36 let loc = func.lookup(db);
43 let src = loc.source(db); 37 let item_tree = db.item_tree(loc.id.file_id);
44 let ctx = LowerCtx::new(db, src.file_id); 38 let func = &item_tree[loc.id.value];
45 let name = src.value.name().map(|n| n.as_name()).unwrap_or_else(Name::missing); 39
46 let mut params = Vec::new(); 40 Arc::new(FunctionData {
47 let mut has_self_param = false; 41 name: func.name.clone(),
48 if let Some(param_list) = src.value.param_list() { 42 params: func.params.to_vec(),
49 if let Some(self_param) = param_list.self_param() { 43 ret_type: func.ret_type.clone(),
50 let self_type = if let Some(type_ref) = self_param.ascribed_type() { 44 attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(),
51 TypeRef::from_ast(&ctx, type_ref) 45 has_self_param: func.has_self_param,
52 } else { 46 is_unsafe: func.is_unsafe,
53 let self_type = TypeRef::Path(name![Self].into()); 47 is_varargs: func.is_varargs,
54 match self_param.kind() { 48 visibility: item_tree[func.visibility].clone(),
55 ast::SelfParamKind::Owned => self_type, 49 })
56 ast::SelfParamKind::Ref => {
57 TypeRef::Reference(Box::new(self_type), Mutability::Shared)
58 }
59 ast::SelfParamKind::MutRef => {
60 TypeRef::Reference(Box::new(self_type), Mutability::Mut)
61 }
62 }
63 };
64 params.push(self_type);
65 has_self_param = true;
66 }
67 for param in param_list.params() {
68 let type_ref = TypeRef::from_ast_opt(&ctx, param.ascribed_type());
69 params.push(type_ref);
70 }
71 }
72 let attrs = Attrs::new(&src.value, &Hygiene::new(db.upcast(), src.file_id));
73
74 let ret_type = if let Some(type_ref) = src.value.ret_type().and_then(|rt| rt.type_ref()) {
75 TypeRef::from_ast(&ctx, type_ref)
76 } else {
77 TypeRef::unit()
78 };
79
80 let ret_type = if src.value.async_token().is_some() {
81 let future_impl = desugar_future_path(ret_type);
82 let ty_bound = TypeBound::Path(future_impl);
83 TypeRef::ImplTrait(vec![ty_bound])
84 } else {
85 ret_type
86 };
87
88 let vis_default = RawVisibility::default_for_container(loc.container);
89 let visibility =
90 RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility()));
91
92 let sig = FunctionData { name, params, ret_type, has_self_param, visibility, attrs };
93 Arc::new(sig)
94 } 50 }
95} 51}
96 52
97fn desugar_future_path(orig: TypeRef) -> Path {
98 let path = path![std::future::Future];
99 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
100 let mut last = GenericArgs::empty();
101 last.bindings.push(AssociatedTypeBinding {
102 name: name![Output],
103 type_ref: Some(orig),
104 bounds: Vec::new(),
105 });
106 generic_args.push(Some(Arc::new(last)));
107
108 Path::from_known_path(path, generic_args)
109}
110
111#[derive(Debug, Clone, PartialEq, Eq)] 53#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct TypeAliasData { 54pub struct TypeAliasData {
113 pub name: Name, 55 pub name: Name,
114 pub type_ref: Option<TypeRef>, 56 pub type_ref: Option<TypeRef>,
115 pub visibility: RawVisibility, 57 pub visibility: RawVisibility,
58 /// Bounds restricting the type alias itself (eg. `type Ty: Bound;` in a trait or impl).
116 pub bounds: Vec<TypeBound>, 59 pub bounds: Vec<TypeBound>,
117} 60}
118 61
@@ -122,22 +65,15 @@ impl TypeAliasData {
122 typ: TypeAliasId, 65 typ: TypeAliasId,
123 ) -> Arc<TypeAliasData> { 66 ) -> Arc<TypeAliasData> {
124 let loc = typ.lookup(db); 67 let loc = typ.lookup(db);
125 let node = loc.source(db); 68 let item_tree = db.item_tree(loc.id.file_id);
126 let name = node.value.name().map_or_else(Name::missing, |n| n.as_name()); 69 let typ = &item_tree[loc.id.value];
127 let lower_ctx = LowerCtx::new(db, node.file_id); 70
128 let type_ref = node.value.type_ref().map(|it| TypeRef::from_ast(&lower_ctx, it)); 71 Arc::new(TypeAliasData {
129 let vis_default = RawVisibility::default_for_container(loc.container); 72 name: typ.name.clone(),
130 let visibility = RawVisibility::from_ast_with_default( 73 type_ref: typ.type_ref.clone(),
131 db, 74 visibility: item_tree[typ.visibility].clone(),
132 vis_default, 75 bounds: typ.bounds.to_vec(),
133 node.as_ref().map(|n| n.visibility()), 76 })
134 );
135 let bounds = if let Some(bound_list) = node.value.type_bound_list() {
136 bound_list.bounds().map(|it| TypeBound::from_ast(&lower_ctx, it)).collect()
137 } else {
138 Vec::new()
139 };
140 Arc::new(TypeAliasData { name, type_ref, visibility, bounds })
141 } 77 }
142} 78}
143 79
@@ -151,30 +87,24 @@ pub struct TraitData {
151impl TraitData { 87impl TraitData {
152 pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> { 88 pub(crate) fn trait_data_query(db: &dyn DefDatabase, tr: TraitId) -> Arc<TraitData> {
153 let tr_loc = tr.lookup(db); 89 let tr_loc = tr.lookup(db);
154 let src = tr_loc.source(db); 90 let item_tree = db.item_tree(tr_loc.id.file_id);
155 let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); 91 let tr_def = &item_tree[tr_loc.id.value];
156 let auto = src.value.auto_token().is_some(); 92 let name = tr_def.name.clone();
93 let auto = tr_def.auto;
157 let module_id = tr_loc.container.module(db); 94 let module_id = tr_loc.container.module(db);
158
159 let container = AssocContainerId::TraitId(tr); 95 let container = AssocContainerId::TraitId(tr);
160 let mut items = Vec::new(); 96 let mut expander = Expander::new(db, tr_loc.id.file_id, module_id);
161 97
162 if let Some(item_list) = src.value.item_list() { 98 let items = collect_items(
163 let mut expander = Expander::new(db, tr_loc.ast_id.file_id, module_id); 99 db,
164 items.extend(collect_items( 100 module_id,
165 db, 101 &mut expander,
166 &mut expander, 102 tr_def.items.iter().copied(),
167 item_list.assoc_items(), 103 tr_loc.id.file_id,
168 src.file_id, 104 container,
169 container, 105 100,
170 )); 106 );
171 items.extend(collect_items_in_macros( 107
172 db,
173 &mut expander,
174 &src.with_value(item_list),
175 container,
176 ));
177 }
178 Arc::new(TraitData { name, items, auto }) 108 Arc::new(TraitData { name, items, auto })
179 } 109 }
180 110
@@ -205,33 +135,28 @@ impl ImplData {
205 pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> { 135 pub(crate) fn impl_data_query(db: &dyn DefDatabase, id: ImplId) -> Arc<ImplData> {
206 let _p = profile("impl_data_query"); 136 let _p = profile("impl_data_query");
207 let impl_loc = id.lookup(db); 137 let impl_loc = id.lookup(db);
208 let src = impl_loc.source(db);
209 let lower_ctx = LowerCtx::new(db, src.file_id);
210 138
211 let target_trait = src.value.target_trait().map(|it| TypeRef::from_ast(&lower_ctx, it)); 139 let item_tree = db.item_tree(impl_loc.id.file_id);
212 let target_type = TypeRef::from_ast_opt(&lower_ctx, src.value.target_type()); 140 let impl_def = &item_tree[impl_loc.id.value];
213 let is_negative = src.value.excl_token().is_some(); 141 let target_trait = impl_def.target_trait.clone();
142 let target_type = impl_def.target_type.clone();
143 let is_negative = impl_def.is_negative;
214 let module_id = impl_loc.container.module(db); 144 let module_id = impl_loc.container.module(db);
215 let container = AssocContainerId::ImplId(id); 145 let container = AssocContainerId::ImplId(id);
146 let mut expander = Expander::new(db, impl_loc.id.file_id, module_id);
216 147
217 let mut items: Vec<AssocItemId> = Vec::new(); 148 let items = collect_items(
218 149 db,
219 if let Some(item_list) = src.value.item_list() { 150 module_id,
220 let mut expander = Expander::new(db, impl_loc.ast_id.file_id, module_id); 151 &mut expander,
221 items.extend( 152 impl_def.items.iter().copied(),
222 collect_items(db, &mut expander, item_list.assoc_items(), src.file_id, container) 153 impl_loc.id.file_id,
223 .into_iter() 154 container,
224 .map(|(_, item)| item), 155 100,
225 ); 156 );
226 items.extend( 157 let items = items.into_iter().map(|(_, item)| item).collect();
227 collect_items_in_macros(db, &mut expander, &src.with_value(item_list), container)
228 .into_iter()
229 .map(|(_, item)| item),
230 );
231 }
232 158
233 let res = ImplData { target_trait, target_type, items, is_negative }; 159 Arc::new(ImplData { target_trait, target_type, items, is_negative })
234 Arc::new(res)
235 } 160 }
236} 161}
237 162
@@ -246,22 +171,14 @@ pub struct ConstData {
246impl ConstData { 171impl ConstData {
247 pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> { 172 pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc<ConstData> {
248 let loc = konst.lookup(db); 173 let loc = konst.lookup(db);
249 let node = loc.source(db); 174 let item_tree = db.item_tree(loc.id.file_id);
250 let vis_default = RawVisibility::default_for_container(loc.container); 175 let konst = &item_tree[loc.id.value];
251 Arc::new(ConstData::new(db, vis_default, node))
252 }
253 176
254 fn new<N: NameOwner + TypeAscriptionOwner + VisibilityOwner>( 177 Arc::new(ConstData {
255 db: &dyn DefDatabase, 178 name: konst.name.clone(),
256 vis_default: RawVisibility, 179 type_ref: konst.type_ref.clone(),
257 node: InFile<N>, 180 visibility: item_tree[konst.visibility].clone(),
258 ) -> ConstData { 181 })
259 let ctx = LowerCtx::new(db, node.file_id);
260 let name = node.value.name().map(|n| n.as_name());
261 let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type());
262 let visibility =
263 RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility()));
264 ConstData { name, type_ref, visibility }
265 } 182 }
266} 183}
267 184
@@ -275,44 +192,25 @@ pub struct StaticData {
275 192
276impl StaticData { 193impl StaticData {
277 pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> { 194 pub(crate) fn static_data_query(db: &dyn DefDatabase, konst: StaticId) -> Arc<StaticData> {
278 let node = konst.lookup(db).source(db); 195 let node = konst.lookup(db);
279 let ctx = LowerCtx::new(db, node.file_id); 196 let item_tree = db.item_tree(node.id.file_id);
280 197 let statik = &item_tree[node.id.value];
281 let name = node.value.name().map(|n| n.as_name()); 198
282 let type_ref = TypeRef::from_ast_opt(&ctx, node.value.ascribed_type()); 199 Arc::new(StaticData {
283 let mutable = node.value.mut_token().is_some(); 200 name: Some(statik.name.clone()),
284 let visibility = RawVisibility::from_ast_with_default( 201 type_ref: statik.type_ref.clone(),
285 db, 202 visibility: item_tree[statik.visibility].clone(),
286 RawVisibility::private(), 203 mutable: statik.mutable,
287 node.map(|n| n.visibility()), 204 })
288 );
289
290 Arc::new(StaticData { name, type_ref, visibility, mutable })
291 }
292}
293
294fn collect_items_in_macros(
295 db: &dyn DefDatabase,
296 expander: &mut Expander,
297 impl_def: &InFile<ast::ItemList>,
298 container: AssocContainerId,
299) -> Vec<(Name, AssocItemId)> {
300 let mut res = Vec::new();
301
302 // We set a limit to protect against infinite recursion
303 let limit = 100;
304
305 for m in impl_def.value.syntax().children().filter_map(ast::MacroCall::cast) {
306 res.extend(collect_items_in_macro(db, expander, m, container, limit))
307 } 205 }
308
309 res
310} 206}
311 207
312fn collect_items_in_macro( 208fn collect_items(
313 db: &dyn DefDatabase, 209 db: &dyn DefDatabase,
210 module: ModuleId,
314 expander: &mut Expander, 211 expander: &mut Expander,
315 m: ast::MacroCall, 212 assoc_items: impl Iterator<Item = AssocItem>,
213 file_id: crate::HirFileId,
316 container: AssocContainerId, 214 container: AssocContainerId,
317 limit: usize, 215 limit: usize,
318) -> Vec<(Name, AssocItemId)> { 216) -> Vec<(Name, AssocItemId)> {
@@ -320,62 +218,62 @@ fn collect_items_in_macro(
320 return Vec::new(); 218 return Vec::new();
321 } 219 }
322 220
323 if let Some((mark, items)) = expander.enter_expand(db, None, m) { 221 let item_tree = db.item_tree(file_id);
324 let items: InFile<ast::MacroItems> = expander.to_source(items); 222 let cfg_options = db.crate_graph()[module.krate].cfg_options.clone();
325 let mut res = collect_items( 223
326 db, 224 let mut items = Vec::new();
327 expander, 225 for item in assoc_items {
328 items.value.items().filter_map(|it| AssocItem::cast(it.syntax().clone())), 226 match item {
329 items.file_id, 227 AssocItem::Function(id) => {
330 container, 228 let item = &item_tree[id];
331 ); 229 let attrs = item_tree.attrs(ModItem::from(id).into());
332 230 if !attrs.is_cfg_enabled(&cfg_options) {
333 // Recursive collect macros 231 continue;
334 // Note that ast::ModuleItem do not include ast::MacroCall
335 // We cannot use ModuleItemOwner::items here
336 for it in items.value.syntax().children().filter_map(ast::MacroCall::cast) {
337 res.extend(collect_items_in_macro(db, expander, it, container, limit - 1))
338 }
339 expander.exit(db, mark);
340 res
341 } else {
342 Vec::new()
343 }
344}
345
346fn collect_items(
347 db: &dyn DefDatabase,
348 expander: &mut Expander,
349 assoc_items: impl Iterator<Item = AssocItem>,
350 file_id: crate::HirFileId,
351 container: AssocContainerId,
352) -> Vec<(Name, AssocItemId)> {
353 let items = db.ast_id_map(file_id);
354
355 assoc_items
356 .filter_map(|item_node| match item_node {
357 ast::AssocItem::FnDef(it) => {
358 let name = it.name().map_or_else(Name::missing, |it| it.as_name());
359 if !expander.is_cfg_enabled(&it) {
360 return None;
361 } 232 }
362 let def = FunctionLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } 233 let def = FunctionLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
363 .intern(db); 234 items.push((item.name.clone(), def.into()));
364 Some((name, def.into()))
365 } 235 }
366 ast::AssocItem::ConstDef(it) => { 236 // FIXME: cfg?
367 let name = it.name().map_or_else(Name::missing, |it| it.as_name()); 237 AssocItem::Const(id) => {
368 let def = ConstLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } 238 let item = &item_tree[id];
369 .intern(db); 239 let name = match item.name.clone() {
370 Some((name, def.into())) 240 Some(name) => name,
241 None => continue,
242 };
243 let def = ConstLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
244 items.push((name, def.into()));
371 } 245 }
372 ast::AssocItem::TypeAliasDef(it) => { 246 AssocItem::TypeAlias(id) => {
373 let name = it.name().map_or_else(Name::missing, |it| it.as_name()); 247 let item = &item_tree[id];
374 let def = 248 let def = TypeAliasLoc { container, id: ItemTreeId::new(file_id, id) }.intern(db);
375 TypeAliasLoc { container, ast_id: AstId::new(file_id, items.ast_id(&it)) } 249 items.push((item.name.clone(), def.into()));
376 .intern(db);
377 Some((name, def.into()))
378 } 250 }
379 }) 251 AssocItem::MacroCall(call) => {
380 .collect() 252 let call = &item_tree[call];
253 let ast_id_map = db.ast_id_map(file_id);
254 let root = db.parse_or_expand(file_id).unwrap();
255 let call = ast_id_map.get(call.ast_id).to_node(&root);
256
257 if let Some((mark, mac)) = expander.enter_expand(db, None, call) {
258 let src: InFile<ast::MacroItems> = expander.to_source(mac);
259 let item_tree = db.item_tree(src.file_id);
260 let iter =
261 item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
262 items.extend(collect_items(
263 db,
264 module,
265 expander,
266 iter,
267 src.file_id,
268 container,
269 limit - 1,
270 ));
271
272 expander.exit(db, mark);
273 }
274 }
275 }
276 }
277
278 items
381} 279}
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index 945a0025e..9c3ede2d7 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -1,7 +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 hir_expand::{db::AstDatabase, name::Name, HirFileId}; 4use hir_expand::{db::AstDatabase, HirFileId};
5use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; 5use ra_db::{salsa, CrateId, SourceDatabase, Upcast};
6use ra_prof::profile; 6use ra_prof::profile;
7use ra_syntax::SmolStr; 7use ra_syntax::SmolStr;
@@ -12,13 +12,11 @@ use crate::{
12 body::{scope::ExprScopes, Body, BodySourceMap}, 12 body::{scope::ExprScopes, Body, BodySourceMap},
13 data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, 13 data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData},
14 docs::Documentation, 14 docs::Documentation,
15 find_path,
16 generics::GenericParams, 15 generics::GenericParams,
17 item_scope::ItemInNs, 16 import_map::ImportMap,
17 item_tree::ItemTree,
18 lang_item::{LangItemTarget, LangItems}, 18 lang_item::{LangItemTarget, LangItems},
19 nameres::{raw::RawItems, CrateDefMap}, 19 nameres::CrateDefMap,
20 path::ModPath,
21 visibility::Visibility,
22 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, 20 AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
23 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, 21 GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
24 TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, 22 TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
@@ -48,8 +46,8 @@ pub trait InternDatabase: SourceDatabase {
48 46
49#[salsa::query_group(DefDatabaseStorage)] 47#[salsa::query_group(DefDatabaseStorage)]
50pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { 48pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
51 #[salsa::invoke(RawItems::raw_items_query)] 49 #[salsa::invoke(ItemTree::item_tree_query)]
52 fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>; 50 fn item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
53 51
54 #[salsa::invoke(crate_def_map_wait)] 52 #[salsa::invoke(crate_def_map_wait)]
55 #[salsa::transparent] 53 #[salsa::transparent]
@@ -113,15 +111,8 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
113 #[salsa::invoke(Documentation::documentation_query)] 111 #[salsa::invoke(Documentation::documentation_query)]
114 fn documentation(&self, def: AttrDefId) -> Option<Documentation>; 112 fn documentation(&self, def: AttrDefId) -> Option<Documentation>;
115 113
116 #[salsa::invoke(find_path::importable_locations_of_query)] 114 #[salsa::invoke(ImportMap::import_map_query)]
117 fn importable_locations_of( 115 fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
118 &self,
119 item: ItemInNs,
120 krate: CrateId,
121 ) -> Arc<[(ModuleId, Name, Visibility)]>;
122
123 #[salsa::invoke(find_path::find_path_inner_query)]
124 fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>;
125} 116}
126 117
127fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 118fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index 510c5e064..30db48f86 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -3,7 +3,6 @@
3use std::any::Any; 3use std::any::Any;
4 4
5use hir_expand::diagnostics::Diagnostic; 5use hir_expand::diagnostics::Diagnostic;
6use ra_db::RelativePathBuf;
7use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; 6use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
8 7
9use hir_expand::{HirFileId, InFile}; 8use hir_expand::{HirFileId, InFile};
@@ -12,7 +11,7 @@ use hir_expand::{HirFileId, InFile};
12pub struct UnresolvedModule { 11pub struct UnresolvedModule {
13 pub file: HirFileId, 12 pub file: HirFileId,
14 pub decl: AstPtr<ast::Module>, 13 pub decl: AstPtr<ast::Module>,
15 pub candidate: RelativePathBuf, 14 pub candidate: String,
16} 15}
17 16
18impl Diagnostic for UnresolvedModule { 17impl Diagnostic for UnresolvedModule {
diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs
index b221ae1ce..2630b3d89 100644
--- a/crates/ra_hir_def/src/docs.rs
+++ b/crates/ra_hir_def/src/docs.rs
@@ -29,6 +29,13 @@ impl Documentation {
29 Documentation(s.into()) 29 Documentation(s.into())
30 } 30 }
31 31
32 pub fn from_ast<N>(node: &N) -> Option<Documentation>
33 where
34 N: ast::DocCommentsOwner + ast::AttrsOwner,
35 {
36 docs_from_ast(node)
37 }
38
32 pub fn as_str(&self) -> &str { 39 pub fn as_str(&self) -> &str {
33 &*self.0 40 &*self.0
34 } 41 }
@@ -70,6 +77,45 @@ impl Documentation {
70 } 77 }
71} 78}
72 79
73pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> { 80pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation>
74 node.doc_comment_text().map(|it| Documentation::new(&it)) 81where
82 N: ast::DocCommentsOwner + ast::AttrsOwner,
83{
84 let doc_comment_text = node.doc_comment_text();
85 let doc_attr_text = expand_doc_attrs(node);
86 let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
87 docs.map(|it| Documentation::new(&it))
88}
89
90fn merge_doc_comments_and_attrs(
91 doc_comment_text: Option<String>,
92 doc_attr_text: Option<String>,
93) -> Option<String> {
94 match (doc_comment_text, doc_attr_text) {
95 (Some(mut comment_text), Some(attr_text)) => {
96 comment_text.push_str("\n\n");
97 comment_text.push_str(&attr_text);
98 Some(comment_text)
99 }
100 (Some(comment_text), None) => Some(comment_text),
101 (None, Some(attr_text)) => Some(attr_text),
102 (None, None) => None,
103 }
104}
105
106fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> {
107 let mut docs = String::new();
108 for attr in owner.attrs() {
109 if let Some(("doc", value)) =
110 attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str()))
111 {
112 docs.push_str(value);
113 docs.push_str("\n\n");
114 }
115 }
116 if docs.is_empty() {
117 None
118 } else {
119 Some(docs.trim_end_matches("\n\n").to_owned())
120 }
75} 121}
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs
index ca49b26d1..e41cfc16b 100644
--- a/crates/ra_hir_def/src/expr.rs
+++ b/crates/ra_hir_def/src/expr.rs
@@ -150,6 +150,9 @@ pub enum Expr {
150 Tuple { 150 Tuple {
151 exprs: Vec<ExprId>, 151 exprs: Vec<ExprId>,
152 }, 152 },
153 Unsafe {
154 body: ExprId,
155 },
153 Array(Array), 156 Array(Array),
154 Literal(Literal), 157 Literal(Literal),
155} 158}
@@ -247,7 +250,7 @@ impl Expr {
247 f(*expr); 250 f(*expr);
248 } 251 }
249 } 252 }
250 Expr::TryBlock { body } => f(*body), 253 Expr::TryBlock { body } | Expr::Unsafe { body } => f(*body),
251 Expr::Loop { body, .. } => f(*body), 254 Expr::Loop { body, .. } => f(*body),
252 Expr::While { condition, body, .. } => { 255 Expr::While { condition, body, .. } => {
253 f(*condition); 256 f(*condition);
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index 4db798473..06701a830 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -1,9 +1,8 @@
1//! An algorithm to find a path to refer to a certain item. 1//! An algorithm to find a path to refer to a certain item.
2 2
3use std::sync::Arc;
4
5use hir_expand::name::{known, AsName, Name}; 3use hir_expand::name::{known, AsName, Name};
6use ra_prof::profile; 4use ra_prof::profile;
5use rustc_hash::FxHashSet;
7use test_utils::mark; 6use test_utils::mark;
8 7
9use crate::{ 8use crate::{
@@ -11,7 +10,7 @@ use crate::{
11 item_scope::ItemInNs, 10 item_scope::ItemInNs,
12 path::{ModPath, PathKind}, 11 path::{ModPath, PathKind},
13 visibility::Visibility, 12 visibility::Visibility,
14 CrateId, ModuleDefId, ModuleId, 13 ModuleDefId, ModuleId,
15}; 14};
16 15
17// FIXME: handle local items 16// FIXME: handle local items
@@ -20,7 +19,7 @@ use crate::{
20/// *from where* you're referring to the item, hence the `from` parameter. 19/// *from where* you're referring to the item, hence the `from` parameter.
21pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { 20pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
22 let _p = profile("find_path"); 21 let _p = profile("find_path");
23 db.find_path_inner(item, from, MAX_PATH_LEN) 22 find_path_inner(db, item, from, MAX_PATH_LEN)
24} 23}
25 24
26const MAX_PATH_LEN: usize = 15; 25const MAX_PATH_LEN: usize = 15;
@@ -36,20 +35,9 @@ impl ModPath {
36 let first_segment = self.segments.first(); 35 let first_segment = self.segments.first();
37 first_segment == Some(&known::alloc) || first_segment == Some(&known::core) 36 first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
38 } 37 }
39
40 fn len(&self) -> usize {
41 self.segments.len()
42 + match self.kind {
43 PathKind::Plain => 0,
44 PathKind::Super(i) => i as usize,
45 PathKind::Crate => 1,
46 PathKind::Abs => 0,
47 PathKind::DollarCrate(_) => 1,
48 }
49 }
50} 38}
51 39
52pub(crate) fn find_path_inner_query( 40fn find_path_inner(
53 db: &dyn DefDatabase, 41 db: &dyn DefDatabase,
54 item: ItemInNs, 42 item: ItemInNs,
55 from: ModuleId, 43 from: ModuleId,
@@ -133,31 +121,67 @@ pub(crate) fn find_path_inner_query(
133 } 121 }
134 122
135 // - otherwise, look for modules containing (reexporting) it and import it from one of those 123 // - otherwise, look for modules containing (reexporting) it and import it from one of those
124
136 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; 125 let crate_root = ModuleId { local_id: def_map.root, krate: from.krate };
137 let crate_attrs = db.attrs(crate_root.into()); 126 let crate_attrs = db.attrs(crate_root.into());
138 let prefer_no_std = crate_attrs.by_key("no_std").exists(); 127 let prefer_no_std = crate_attrs.by_key("no_std").exists();
139 let importable_locations = find_importable_locations(db, item, from);
140 let mut best_path = None; 128 let mut best_path = None;
141 let mut best_path_len = max_len; 129 let mut best_path_len = max_len;
142 for (module_id, name) in importable_locations {
143 let mut path = match db.find_path_inner(
144 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
145 from,
146 best_path_len - 1,
147 ) {
148 None => continue,
149 Some(path) => path,
150 };
151 path.segments.push(name);
152 130
153 let new_path = if let Some(best_path) = best_path { 131 if item.krate(db) == Some(from.krate) {
154 select_best_path(best_path, path, prefer_no_std) 132 // Item was defined in the same crate that wants to import it. It cannot be found in any
155 } else { 133 // dependency in this case.
156 path 134
157 }; 135 let local_imports = find_local_import_locations(db, item, from);
158 best_path_len = new_path.len(); 136 for (module_id, name) in local_imports {
159 best_path = Some(new_path); 137 if let Some(mut path) = find_path_inner(
138 db,
139 ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
140 from,
141 best_path_len - 1,
142 ) {
143 path.segments.push(name);
144
145 let new_path = if let Some(best_path) = best_path {
146 select_best_path(best_path, path, prefer_no_std)
147 } else {
148 path
149 };
150 best_path_len = new_path.len();
151 best_path = Some(new_path);
152 }
153 }
154 } else {
155 // Item was defined in some upstream crate. This means that it must be exported from one,
156 // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
157 // that wants to import it here, but we always prefer to use the external path here.
158
159 let crate_graph = db.crate_graph();
160 let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
161 let import_map = db.import_map(dep.crate_id);
162 import_map.import_info_for(item).and_then(|info| {
163 // Determine best path for containing module and append last segment from `info`.
164 let mut path = find_path_inner(
165 db,
166 ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
167 from,
168 best_path_len - 1,
169 )?;
170 path.segments.push(info.path.segments.last().unwrap().clone());
171 Some(path)
172 })
173 });
174
175 for path in extern_paths {
176 let new_path = if let Some(best_path) = best_path {
177 select_best_path(best_path, path, prefer_no_std)
178 } else {
179 path
180 };
181 best_path = Some(new_path);
182 }
160 } 183 }
184
161 best_path 185 best_path
162} 186}
163 187
@@ -185,69 +209,86 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
185 } 209 }
186} 210}
187 211
188fn find_importable_locations( 212/// Finds locations in `from.krate` from which `item` can be imported by `from`.
213fn find_local_import_locations(
189 db: &dyn DefDatabase, 214 db: &dyn DefDatabase,
190 item: ItemInNs, 215 item: ItemInNs,
191 from: ModuleId, 216 from: ModuleId,
192) -> Vec<(ModuleId, Name)> { 217) -> Vec<(ModuleId, Name)> {
193 let crate_graph = db.crate_graph(); 218 let _p = profile("find_local_import_locations");
194 let mut result = Vec::new(); 219
195 // We only look in the crate from which we are importing, and the direct 220 // `from` can import anything below `from` with visibility of at least `from`, and anything
196 // dependencies. We cannot refer to names from transitive dependencies 221 // above `from` with any visibility. That means we do not need to descend into private siblings
197 // directly (only through reexports in direct dependencies). 222 // of `from` (and similar).
198 for krate in Some(from.krate) 223
199 .into_iter() 224 let def_map = db.crate_def_map(from.krate);
200 .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) 225
201 { 226 // Compute the initial worklist. We start with all direct child modules of `from` as well as all
202 result.extend( 227 // of its (recursive) parent modules.
203 db.importable_locations_of(item, krate) 228 let data = &def_map.modules[from.local_id];
204 .iter() 229 let mut worklist = data
205 .filter(|(_, _, vis)| vis.is_visible_from(db, from)) 230 .children
206 .map(|(m, n, _)| (*m, n.clone())), 231 .values()
207 ); 232 .map(|child| ModuleId { krate: from.krate, local_id: *child })
208 } 233 .collect::<Vec<_>>();
209 result 234 let mut parent = data.parent;
210} 235 while let Some(p) = parent {
236 worklist.push(ModuleId { krate: from.krate, local_id: p });
237 parent = def_map.modules[p].parent;
238 }
239
240 let mut seen: FxHashSet<_> = FxHashSet::default();
241
242 let mut locations = Vec::new();
243 while let Some(module) = worklist.pop() {
244 if !seen.insert(module) {
245 continue; // already processed this module
246 }
247
248 let ext_def_map;
249 let data = if module.krate == from.krate {
250 &def_map[module.local_id]
251 } else {
252 // The crate might reexport a module defined in another crate.
253 ext_def_map = db.crate_def_map(module.krate);
254 &ext_def_map[module.local_id]
255 };
211 256
212/// Collects all locations from which we might import the item in a particular
213/// crate. These include the original definition of the item, and any
214/// non-private `use`s.
215///
216/// Note that the crate doesn't need to be the one in which the item is defined;
217/// it might be re-exported in other crates.
218pub(crate) fn importable_locations_of_query(
219 db: &dyn DefDatabase,
220 item: ItemInNs,
221 krate: CrateId,
222) -> Arc<[(ModuleId, Name, Visibility)]> {
223 let _p = profile("importable_locations_of_query");
224 let def_map = db.crate_def_map(krate);
225 let mut result = Vec::new();
226 for (local_id, data) in def_map.modules.iter() {
227 if let Some((name, vis)) = data.scope.name_of(item) { 257 if let Some((name, vis)) = data.scope.name_of(item) {
228 let is_private = if let Visibility::Module(private_to) = vis { 258 if vis.is_visible_from(db, from) {
229 private_to.local_id == local_id 259 let is_private = if let Visibility::Module(private_to) = vis {
230 } else { 260 private_to.local_id == module.local_id
231 false 261 } else {
232 }; 262 false
233 let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { 263 };
234 data.scope.declarations().any(|it| it == module_def_id) 264 let is_original_def = if let Some(module_def_id) = item.as_module_def_id() {
235 } else { 265 data.scope.declarations().any(|it| it == module_def_id)
236 false 266 } else {
237 }; 267 false
238 if is_private && !is_original_def { 268 };
269
239 // Ignore private imports. these could be used if we are 270 // Ignore private imports. these could be used if we are
240 // in a submodule of this module, but that's usually not 271 // in a submodule of this module, but that's usually not
241 // what the user wants; and if this module can import 272 // what the user wants; and if this module can import
242 // the item and we're a submodule of it, so can we. 273 // the item and we're a submodule of it, so can we.
243 // Also this keeps the cached data smaller. 274 // Also this keeps the cached data smaller.
244 continue; 275 if !is_private || is_original_def {
276 locations.push((module, name.clone()));
277 }
278 }
279 }
280
281 // Descend into all modules visible from `from`.
282 for (_, per_ns) in data.scope.entries() {
283 if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() {
284 if vis.is_visible_from(db, from) {
285 worklist.push(module);
286 }
245 } 287 }
246 result.push((ModuleId { krate, local_id }, name.clone(), vis));
247 } 288 }
248 } 289 }
249 290
250 Arc::from(result) 291 locations
251} 292}
252 293
253#[cfg(test)] 294#[cfg(test)]
@@ -264,8 +305,8 @@ mod tests {
264 /// `code` needs to contain a cursor marker; checks that `find_path` for the 305 /// `code` needs to contain a cursor marker; checks that `find_path` for the
265 /// item the `path` refers to returns that same path when called from the 306 /// item the `path` refers to returns that same path when called from the
266 /// module the cursor is in. 307 /// module the cursor is in.
267 fn check_found_path(code: &str, path: &str) { 308 fn check_found_path(ra_fixture: &str, path: &str) {
268 let (db, pos) = TestDB::with_position(code); 309 let (db, pos) = TestDB::with_position(ra_fixture);
269 let module = db.module_for_file(pos.file_id); 310 let module = db.module_for_file(pos.file_id);
270 let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); 311 let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
271 let ast_path = parsed_path_file 312 let ast_path = parsed_path_file
@@ -396,6 +437,44 @@ mod tests {
396 } 437 }
397 438
398 #[test] 439 #[test]
440 fn partially_imported() {
441 // Tests that short paths are used even for external items, when parts of the path are
442 // already in scope.
443 check_found_path(
444 r#"
445 //- /main.rs crate:main deps:ra_syntax
446
447 use ra_syntax::ast;
448 <|>
449
450 //- /lib.rs crate:ra_syntax
451 pub mod ast {
452 pub enum ModuleItem {
453 A, B, C,
454 }
455 }
456 "#,
457 "ast::ModuleItem",
458 );
459
460 check_found_path(
461 r#"
462 //- /main.rs crate:main deps:ra_syntax
463
464 <|>
465
466 //- /lib.rs crate:ra_syntax
467 pub mod ast {
468 pub enum ModuleItem {
469 A, B, C,
470 }
471 }
472 "#,
473 "ra_syntax::ast::ModuleItem",
474 );
475 }
476
477 #[test]
399 fn same_crate_reexport() { 478 fn same_crate_reexport() {
400 let code = r#" 479 let code = r#"
401 //- /main.rs 480 //- /main.rs
diff --git a/crates/ra_hir_def/src/generics.rs b/crates/ra_hir_def/src/generics.rs
index 09a5241f7..6a0f493a7 100644
--- a/crates/ra_hir_def/src/generics.rs
+++ b/crates/ra_hir_def/src/generics.rs
@@ -42,7 +42,7 @@ pub enum TypeParamProvenance {
42} 42}
43 43
44/// Data about the generic parameters of a function, struct, impl, etc. 44/// Data about the generic parameters of a function, struct, impl, etc.
45#[derive(Clone, PartialEq, Eq, Debug)] 45#[derive(Clone, PartialEq, Eq, Debug, Default)]
46pub struct GenericParams { 46pub struct GenericParams {
47 pub types: Arena<TypeParamData>, 47 pub types: Arena<TypeParamData>,
48 // lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>, 48 // lifetimes: Arena<LocalLifetimeParamId, LifetimeParamData>,
@@ -74,8 +74,53 @@ impl GenericParams {
74 def: GenericDefId, 74 def: GenericDefId,
75 ) -> Arc<GenericParams> { 75 ) -> Arc<GenericParams> {
76 let _p = profile("generic_params_query"); 76 let _p = profile("generic_params_query");
77 let (params, _source_map) = GenericParams::new(db, def); 77
78 Arc::new(params) 78 let generics = match def {
79 GenericDefId::FunctionId(id) => {
80 let id = id.lookup(db).id;
81 let tree = db.item_tree(id.file_id);
82 let item = &tree[id.value];
83 tree[item.generic_params].clone()
84 }
85 GenericDefId::AdtId(AdtId::StructId(id)) => {
86 let id = id.lookup(db).id;
87 let tree = db.item_tree(id.file_id);
88 let item = &tree[id.value];
89 tree[item.generic_params].clone()
90 }
91 GenericDefId::AdtId(AdtId::EnumId(id)) => {
92 let id = id.lookup(db).id;
93 let tree = db.item_tree(id.file_id);
94 let item = &tree[id.value];
95 tree[item.generic_params].clone()
96 }
97 GenericDefId::AdtId(AdtId::UnionId(id)) => {
98 let id = id.lookup(db).id;
99 let tree = db.item_tree(id.file_id);
100 let item = &tree[id.value];
101 tree[item.generic_params].clone()
102 }
103 GenericDefId::TraitId(id) => {
104 let id = id.lookup(db).id;
105 let tree = db.item_tree(id.file_id);
106 let item = &tree[id.value];
107 tree[item.generic_params].clone()
108 }
109 GenericDefId::TypeAliasId(id) => {
110 let id = id.lookup(db).id;
111 let tree = db.item_tree(id.file_id);
112 let item = &tree[id.value];
113 tree[item.generic_params].clone()
114 }
115 GenericDefId::ImplId(id) => {
116 let id = id.lookup(db).id;
117 let tree = db.item_tree(id.file_id);
118 let item = &tree[id.value];
119 tree[item.generic_params].clone()
120 }
121 GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => GenericParams::default(),
122 };
123 Arc::new(generics)
79 } 124 }
80 125
81 fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) { 126 fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) {
@@ -156,7 +201,12 @@ impl GenericParams {
156 (generics, InFile::new(file_id, sm)) 201 (generics, InFile::new(file_id, sm))
157 } 202 }
158 203
159 fn fill(&mut self, lower_ctx: &LowerCtx, sm: &mut SourceMap, node: &dyn TypeParamsOwner) { 204 pub(crate) fn fill(
205 &mut self,
206 lower_ctx: &LowerCtx,
207 sm: &mut SourceMap,
208 node: &dyn TypeParamsOwner,
209 ) {
160 if let Some(params) = node.type_param_list() { 210 if let Some(params) = node.type_param_list() {
161 self.fill_params(lower_ctx, sm, params) 211 self.fill_params(lower_ctx, sm, params)
162 } 212 }
@@ -165,7 +215,7 @@ impl GenericParams {
165 } 215 }
166 } 216 }
167 217
168 fn fill_bounds( 218 pub(crate) fn fill_bounds(
169 &mut self, 219 &mut self,
170 lower_ctx: &LowerCtx, 220 lower_ctx: &LowerCtx,
171 node: &dyn ast::TypeBoundsOwner, 221 node: &dyn ast::TypeBoundsOwner,
@@ -229,7 +279,7 @@ impl GenericParams {
229 .push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound }); 279 .push(WherePredicate { target: WherePredicateTarget::TypeRef(type_ref), bound });
230 } 280 }
231 281
232 fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) { 282 pub(crate) fn fill_implicit_impl_trait_args(&mut self, type_ref: &TypeRef) {
233 type_ref.walk(&mut |type_ref| { 283 type_ref.walk(&mut |type_ref| {
234 if let TypeRef::ImplTrait(bounds) = type_ref { 284 if let TypeRef::ImplTrait(bounds) = type_ref {
235 let param = TypeParamData { 285 let param = TypeParamData {
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs
new file mode 100644
index 000000000..869f3ca5a
--- /dev/null
+++ b/crates/ra_hir_def/src/import_map.rs
@@ -0,0 +1,737 @@
1//! A map of all publicly exported items in a crate.
2
3use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
4
5use fst::{self, Streamer};
6use indexmap::{map::Entry, IndexMap};
7use ra_db::CrateId;
8use ra_syntax::SmolStr;
9use rustc_hash::{FxHashMap, FxHasher};
10use smallvec::SmallVec;
11
12use crate::{
13 db::DefDatabase,
14 item_scope::ItemInNs,
15 path::{ModPath, PathKind},
16 visibility::Visibility,
17 AssocItemId, ModuleDefId, ModuleId, TraitId,
18};
19
20type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
21
22/// Item import details stored in the `ImportMap`.
23#[derive(Debug, Clone, Eq, PartialEq)]
24pub struct ImportInfo {
25 /// A path that can be used to import the item, relative to the crate's root.
26 pub path: ModPath,
27 /// The module containing this item.
28 pub container: ModuleId,
29}
30
31/// A map from publicly exported items to the path needed to import/name them from a downstream
32/// crate.
33///
34/// Reexports of items are taken into account, ie. if something is exported under multiple
35/// names, the one with the shortest import path will be used.
36///
37/// Note that all paths are relative to the containing crate's root, so the crate name still needs
38/// to be prepended to the `ModPath` before the path is valid.
39#[derive(Default)]
40pub struct ImportMap {
41 map: FxIndexMap<ItemInNs, ImportInfo>,
42
43 /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
44 /// values returned by running `fst`.
45 ///
46 /// Since a path can refer to multiple items due to namespacing, we store all items with the
47 /// same path right after each other. This allows us to find all items after the FST gives us
48 /// the index of the first one.
49 importables: Vec<ItemInNs>,
50 fst: fst::Map<Vec<u8>>,
51
52 /// Maps names of associated items to the item's ID. Only includes items whose defining trait is
53 /// exported.
54 assoc_map: FxHashMap<SmolStr, SmallVec<[AssocItemId; 1]>>,
55}
56
57impl ImportMap {
58 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
59 let _p = ra_prof::profile("import_map_query");
60 let def_map = db.crate_def_map(krate);
61 let mut import_map = Self::default();
62
63 // We look only into modules that are public(ly reexported), starting with the crate root.
64 let empty = ModPath { kind: PathKind::Plain, segments: vec![] };
65 let root = ModuleId { krate, local_id: def_map.root };
66 let mut worklist = vec![(root, empty)];
67 while let Some((module, mod_path)) = worklist.pop() {
68 let ext_def_map;
69 let mod_data = if module.krate == krate {
70 &def_map[module.local_id]
71 } else {
72 // The crate might reexport a module defined in another crate.
73 ext_def_map = db.crate_def_map(module.krate);
74 &ext_def_map[module.local_id]
75 };
76
77 let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| {
78 let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public);
79 if per_ns.is_none() {
80 None
81 } else {
82 Some((name, per_ns))
83 }
84 });
85
86 for (name, per_ns) in visible_items {
87 let mk_path = || {
88 let mut path = mod_path.clone();
89 path.segments.push(name.clone());
90 path
91 };
92
93 for item in per_ns.iter_items() {
94 let path = mk_path();
95 match import_map.map.entry(item) {
96 Entry::Vacant(entry) => {
97 entry.insert(ImportInfo { path, container: module });
98 }
99 Entry::Occupied(mut entry) => {
100 // If the new path is shorter, prefer that one.
101 if path.len() < entry.get().path.len() {
102 *entry.get_mut() = ImportInfo { path, container: module };
103 } else {
104 continue;
105 }
106 }
107 }
108
109 // If we've just added a path to a module, descend into it. We might traverse
110 // modules multiple times, but only if the new path to it is shorter than the
111 // first (else we `continue` above).
112 if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() {
113 worklist.push((mod_id, mk_path()));
114 }
115
116 // If we've added a path to a trait, add the trait's methods to the method map.
117 if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() {
118 import_map.collect_trait_methods(db, tr);
119 }
120 }
121 }
122 }
123
124 let mut importables = import_map.map.iter().collect::<Vec<_>>();
125
126 importables.sort_by(cmp);
127
128 // Build the FST, taking care not to insert duplicate values.
129
130 let mut builder = fst::MapBuilder::memory();
131 let mut last_batch_start = 0;
132
133 for idx in 0..importables.len() {
134 if let Some(next_item) = importables.get(idx + 1) {
135 if cmp(&importables[last_batch_start], next_item) == Ordering::Equal {
136 continue;
137 }
138 }
139
140 let start = last_batch_start;
141 last_batch_start = idx + 1;
142
143 let key = fst_path(&importables[start].1.path);
144
145 builder.insert(key, start as u64).unwrap();
146 }
147
148 import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
149 import_map.importables = importables.iter().map(|(item, _)| **item).collect();
150
151 Arc::new(import_map)
152 }
153
154 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
155 pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> {
156 Some(&self.map.get(&item)?.path)
157 }
158
159 pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
160 self.map.get(&item)
161 }
162
163 fn collect_trait_methods(&mut self, db: &dyn DefDatabase, tr: TraitId) {
164 let data = db.trait_data(tr);
165 for (name, item) in data.items.iter() {
166 self.assoc_map.entry(name.to_string().into()).or_default().push(*item);
167 }
168 }
169}
170
171impl PartialEq for ImportMap {
172 fn eq(&self, other: &Self) -> bool {
173 // `fst` and `importables` are built from `map`, so we don't need to compare them.
174 self.map == other.map
175 }
176}
177
178impl Eq for ImportMap {}
179
180impl fmt::Debug for ImportMap {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 let mut importable_paths: Vec<_> = self
183 .map
184 .iter()
185 .map(|(item, info)| {
186 let ns = match item {
187 ItemInNs::Types(_) => "t",
188 ItemInNs::Values(_) => "v",
189 ItemInNs::Macros(_) => "m",
190 };
191 format!("- {} ({})", info.path, ns)
192 })
193 .collect();
194
195 importable_paths.sort();
196 f.write_str(&importable_paths.join("\n"))
197 }
198}
199
200fn fst_path(path: &ModPath) -> String {
201 let mut s = path.to_string();
202 s.make_ascii_lowercase();
203 s
204}
205
206fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering {
207 let lhs_str = fst_path(&lhs.path);
208 let rhs_str = fst_path(&rhs.path);
209 lhs_str.cmp(&rhs_str)
210}
211
212#[derive(Debug)]
213pub struct Query {
214 query: String,
215 lowercased: String,
216 anchor_end: bool,
217 case_sensitive: bool,
218 limit: usize,
219}
220
221impl Query {
222 pub fn new(query: &str) -> Self {
223 Self {
224 lowercased: query.to_lowercase(),
225 query: query.to_string(),
226 anchor_end: false,
227 case_sensitive: false,
228 limit: usize::max_value(),
229 }
230 }
231
232 /// Only returns items whose paths end with the (case-insensitive) query string as their last
233 /// segment.
234 pub fn anchor_end(self) -> Self {
235 Self { anchor_end: true, ..self }
236 }
237
238 /// Limits the returned number of items to `limit`.
239 pub fn limit(self, limit: usize) -> Self {
240 Self { limit, ..self }
241 }
242
243 /// Respect casing of the query string when matching.
244 pub fn case_sensitive(self) -> Self {
245 Self { case_sensitive: true, ..self }
246 }
247}
248
249/// Searches dependencies of `krate` for an importable path matching `query`.
250///
251/// This returns a list of items that could be imported from dependencies of `krate`.
252pub fn search_dependencies<'a>(
253 db: &'a dyn DefDatabase,
254 krate: CrateId,
255 query: Query,
256) -> Vec<ItemInNs> {
257 let _p = ra_prof::profile("search_dependencies").detail(|| format!("{:?}", query));
258
259 let graph = db.crate_graph();
260 let import_maps: Vec<_> =
261 graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
262
263 let automaton = fst::automaton::Subsequence::new(&query.lowercased);
264
265 let mut op = fst::map::OpBuilder::new();
266 for map in &import_maps {
267 op = op.add(map.fst.search(&automaton));
268 }
269
270 let mut stream = op.union();
271 let mut res = Vec::new();
272 while let Some((_, indexed_values)) = stream.next() {
273 for indexed_value in indexed_values {
274 let import_map = &import_maps[indexed_value.index];
275 let importables = &import_map.importables[indexed_value.value as usize..];
276
277 // Path shared by the importable items in this group.
278 let path = &import_map.map[&importables[0]].path;
279
280 if query.anchor_end {
281 // Last segment must match query.
282 let last = path.segments.last().unwrap().to_string();
283 if last.to_lowercase() != query.lowercased {
284 continue;
285 }
286 }
287
288 // Add the items from this `ModPath` group. Those are all subsequent items in
289 // `importables` whose paths match `path`.
290 let iter = importables.iter().copied().take_while(|item| {
291 let item_path = &import_map.map[item].path;
292 fst_path(item_path) == fst_path(path)
293 });
294
295 if query.case_sensitive {
296 // FIXME: This does not do a subsequence match.
297 res.extend(iter.filter(|item| {
298 let item_path = &import_map.map[item].path;
299 item_path.to_string().contains(&query.query)
300 }));
301 } else {
302 res.extend(iter);
303 }
304
305 if res.len() >= query.limit {
306 res.truncate(query.limit);
307 return res;
308 }
309 }
310 }
311
312 // Add all exported associated items whose names match the query (exactly).
313 for map in &import_maps {
314 if let Some(v) = map.assoc_map.get(&*query.query) {
315 res.extend(v.iter().map(|&assoc| {
316 ItemInNs::Types(match assoc {
317 AssocItemId::FunctionId(it) => it.into(),
318 AssocItemId::ConstId(it) => it.into(),
319 AssocItemId::TypeAliasId(it) => it.into(),
320 })
321 }));
322 }
323 }
324
325 res
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331 use crate::{test_db::TestDB, AssocContainerId, Lookup};
332 use insta::assert_snapshot;
333 use itertools::Itertools;
334 use ra_db::fixture::WithFixture;
335 use ra_db::{SourceDatabase, Upcast};
336
337 fn import_map(ra_fixture: &str) -> String {
338 let db = TestDB::with_files(ra_fixture);
339 let crate_graph = db.crate_graph();
340
341 let s = crate_graph
342 .iter()
343 .filter_map(|krate| {
344 let cdata = &crate_graph[krate];
345 let name = cdata.display_name.as_ref()?;
346
347 let map = db.import_map(krate);
348
349 Some(format!("{}:\n{:?}", name, map))
350 })
351 .join("\n");
352 s
353 }
354
355 fn search_dependencies_of(ra_fixture: &str, krate_name: &str, query: Query) -> String {
356 let db = TestDB::with_files(ra_fixture);
357 let crate_graph = db.crate_graph();
358 let krate = crate_graph
359 .iter()
360 .find(|krate| {
361 crate_graph[*krate].display_name.as_ref().map(|n| n.to_string())
362 == Some(krate_name.to_string())
363 })
364 .unwrap();
365
366 search_dependencies(db.upcast(), krate, query)
367 .into_iter()
368 .filter_map(|item| {
369 let mark = match item {
370 ItemInNs::Types(_) => "t",
371 ItemInNs::Values(_) => "v",
372 ItemInNs::Macros(_) => "m",
373 };
374 let item = assoc_to_trait(&db, item);
375 item.krate(db.upcast()).map(|krate| {
376 let map = db.import_map(krate);
377 let path = map.path_of(item).unwrap();
378 format!(
379 "{}::{} ({})",
380 crate_graph[krate].display_name.as_ref().unwrap(),
381 path,
382 mark
383 )
384 })
385 })
386 .join("\n")
387 }
388
389 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> ItemInNs {
390 let assoc: AssocItemId = match item {
391 ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
392 ModuleDefId::TypeAliasId(it) => it.into(),
393 ModuleDefId::FunctionId(it) => it.into(),
394 ModuleDefId::ConstId(it) => it.into(),
395 _ => return item,
396 },
397 _ => return item,
398 };
399
400 let container = match assoc {
401 AssocItemId::FunctionId(it) => it.lookup(db).container,
402 AssocItemId::ConstId(it) => it.lookup(db).container,
403 AssocItemId::TypeAliasId(it) => it.lookup(db).container,
404 };
405
406 match container {
407 AssocContainerId::TraitId(it) => ItemInNs::Types(it.into()),
408 _ => item,
409 }
410 }
411
412 #[test]
413 fn smoke() {
414 let map = import_map(
415 r"
416 //- /main.rs crate:main deps:lib
417
418 mod private {
419 pub use lib::Pub;
420 pub struct InPrivateModule;
421 }
422
423 pub mod publ1 {
424 use lib::Pub;
425 }
426
427 pub mod real_pub {
428 pub use lib::Pub;
429 }
430 pub mod real_pu2 { // same path length as above
431 pub use lib::Pub;
432 }
433
434 //- /lib.rs crate:lib
435 pub struct Pub {}
436 pub struct Pub2; // t + v
437 struct Priv;
438 ",
439 );
440
441 assert_snapshot!(map, @r###"
442 main:
443 - publ1 (t)
444 - real_pu2 (t)
445 - real_pub (t)
446 - real_pub::Pub (t)
447 lib:
448 - Pub (t)
449 - Pub2 (t)
450 - Pub2 (v)
451 "###);
452 }
453
454 #[test]
455 fn prefers_shortest_path() {
456 let map = import_map(
457 r"
458 //- /main.rs crate:main
459
460 pub mod sub {
461 pub mod subsub {
462 pub struct Def {}
463 }
464
465 pub use super::sub::subsub::Def;
466 }
467 ",
468 );
469
470 assert_snapshot!(map, @r###"
471 main:
472 - sub (t)
473 - sub::Def (t)
474 - sub::subsub (t)
475 "###);
476 }
477
478 #[test]
479 fn type_reexport_cross_crate() {
480 // Reexports need to be visible from a crate, even if the original crate exports the item
481 // at a shorter path.
482 let map = import_map(
483 r"
484 //- /main.rs crate:main deps:lib
485 pub mod m {
486 pub use lib::S;
487 }
488 //- /lib.rs crate:lib
489 pub struct S;
490 ",
491 );
492
493 assert_snapshot!(map, @r###"
494 main:
495 - m (t)
496 - m::S (t)
497 - m::S (v)
498 lib:
499 - S (t)
500 - S (v)
501 "###);
502 }
503
504 #[test]
505 fn macro_reexport() {
506 let map = import_map(
507 r"
508 //- /main.rs crate:main deps:lib
509 pub mod m {
510 pub use lib::pub_macro;
511 }
512 //- /lib.rs crate:lib
513 #[macro_export]
514 macro_rules! pub_macro {
515 () => {};
516 }
517 ",
518 );
519
520 assert_snapshot!(map, @r###"
521 main:
522 - m (t)
523 - m::pub_macro (m)
524 lib:
525 - pub_macro (m)
526 "###);
527 }
528
529 #[test]
530 fn module_reexport() {
531 // Reexporting modules from a dependency adds all contents to the import map.
532 let map = import_map(
533 r"
534 //- /main.rs crate:main deps:lib
535 pub use lib::module as reexported_module;
536 //- /lib.rs crate:lib
537 pub mod module {
538 pub struct S;
539 }
540 ",
541 );
542
543 assert_snapshot!(map, @r###"
544 main:
545 - reexported_module (t)
546 - reexported_module::S (t)
547 - reexported_module::S (v)
548 lib:
549 - module (t)
550 - module::S (t)
551 - module::S (v)
552 "###);
553 }
554
555 #[test]
556 fn cyclic_module_reexport() {
557 // A cyclic reexport does not hang.
558 let map = import_map(
559 r"
560 //- /lib.rs crate:lib
561 pub mod module {
562 pub struct S;
563 pub use super::sub::*;
564 }
565
566 pub mod sub {
567 pub use super::module;
568 }
569 ",
570 );
571
572 assert_snapshot!(map, @r###"
573 lib:
574 - module (t)
575 - module::S (t)
576 - module::S (v)
577 - sub (t)
578 "###);
579 }
580
581 #[test]
582 fn private_macro() {
583 let map = import_map(
584 r"
585 //- /lib.rs crate:lib
586 macro_rules! private_macro {
587 () => {};
588 }
589 ",
590 );
591
592 assert_snapshot!(map, @r###"
593 lib:
594 "###);
595 }
596
597 #[test]
598 fn namespacing() {
599 let map = import_map(
600 r"
601 //- /lib.rs crate:lib
602 pub struct Thing; // t + v
603 #[macro_export]
604 macro_rules! Thing { // m
605 () => {};
606 }
607 ",
608 );
609
610 assert_snapshot!(map, @r###"
611 lib:
612 - Thing (m)
613 - Thing (t)
614 - Thing (v)
615 "###);
616
617 let map = import_map(
618 r"
619 //- /lib.rs crate:lib
620 pub mod Thing {} // t
621 #[macro_export]
622 macro_rules! Thing { // m
623 () => {};
624 }
625 ",
626 );
627
628 assert_snapshot!(map, @r###"
629 lib:
630 - Thing (m)
631 - Thing (t)
632 "###);
633 }
634
635 #[test]
636 fn search() {
637 let ra_fixture = r#"
638 //- /main.rs crate:main deps:dep
639 //- /dep.rs crate:dep deps:tdep
640 use tdep::fmt as fmt_dep;
641 pub mod fmt {
642 pub trait Display {
643 fn fmt();
644 }
645 }
646 #[macro_export]
647 macro_rules! Fmt {
648 () => {};
649 }
650 pub struct Fmt;
651
652 pub fn format() {}
653 pub fn no() {}
654
655 //- /tdep.rs crate:tdep
656 pub mod fmt {
657 pub struct NotImportableFromMain;
658 }
659 "#;
660
661 let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt"));
662 assert_snapshot!(res, @r###"
663 dep::fmt (t)
664 dep::Fmt (t)
665 dep::Fmt (v)
666 dep::Fmt (m)
667 dep::fmt::Display (t)
668 dep::format (v)
669 dep::fmt::Display (t)
670 "###);
671
672 let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end());
673 assert_snapshot!(res, @r###"
674 dep::fmt (t)
675 dep::Fmt (t)
676 dep::Fmt (v)
677 dep::Fmt (m)
678 dep::fmt::Display (t)
679 "###);
680 }
681
682 #[test]
683 fn search_casing() {
684 let ra_fixture = r#"
685 //- /main.rs crate:main deps:dep
686 //- /dep.rs crate:dep
687
688 pub struct fmt;
689 pub struct FMT;
690 "#;
691
692 let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT"));
693
694 assert_snapshot!(res, @r###"
695 dep::fmt (t)
696 dep::fmt (v)
697 dep::FMT (t)
698 dep::FMT (v)
699 "###);
700
701 let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT").case_sensitive());
702
703 assert_snapshot!(res, @r###"
704 dep::FMT (t)
705 dep::FMT (v)
706 "###);
707 }
708
709 #[test]
710 fn search_limit() {
711 let res = search_dependencies_of(
712 r#"
713 //- /main.rs crate:main deps:dep
714 //- /dep.rs crate:dep
715 pub mod fmt {
716 pub trait Display {
717 fn fmt();
718 }
719 }
720 #[macro_export]
721 macro_rules! Fmt {
722 () => {};
723 }
724 pub struct Fmt;
725
726 pub fn format() {}
727 pub fn no() {}
728 "#,
729 "main",
730 Query::new("").limit(2),
731 );
732 assert_snapshot!(res, @r###"
733 dep::fmt (t)
734 dep::Fmt (t)
735 "###);
736 }
737}
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs
index fc15948ad..beeb98559 100644
--- a/crates/ra_hir_def/src/item_scope.rs
+++ b/crates/ra_hir_def/src/item_scope.rs
@@ -1,18 +1,39 @@
1//! Describes items defined or visible (ie, imported) in a certain scope. 1//! Describes items defined or visible (ie, imported) in a certain scope.
2//! This is shared between modules and blocks. 2//! This is shared between modules and blocks.
3 3
4use std::collections::hash_map::Entry;
5
4use hir_expand::name::Name; 6use hir_expand::name::Name;
5use once_cell::sync::Lazy; 7use once_cell::sync::Lazy;
6use rustc_hash::FxHashMap; 8use ra_db::CrateId;
9use rustc_hash::{FxHashMap, FxHashSet};
10use test_utils::mark;
7 11
8use crate::{ 12use crate::{
9 per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, 13 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
10 TraitId, 14 LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId,
11}; 15};
12 16
17#[derive(Copy, Clone)]
18pub(crate) enum ImportType {
19 Glob,
20 Named,
21}
22
23#[derive(Debug, Default)]
24pub struct PerNsGlobImports {
25 types: FxHashSet<(LocalModuleId, Name)>,
26 values: FxHashSet<(LocalModuleId, Name)>,
27 macros: FxHashSet<(LocalModuleId, Name)>,
28}
29
13#[derive(Debug, Default, PartialEq, Eq)] 30#[derive(Debug, Default, PartialEq, Eq)]
14pub struct ItemScope { 31pub struct ItemScope {
15 visible: FxHashMap<Name, PerNs>, 32 types: FxHashMap<Name, (ModuleDefId, Visibility)>,
33 values: FxHashMap<Name, (ModuleDefId, Visibility)>,
34 macros: FxHashMap<Name, (MacroDefId, Visibility)>,
35 unresolved: FxHashSet<Name>,
36
16 defs: Vec<ModuleDefId>, 37 defs: Vec<ModuleDefId>,
17 impls: Vec<ImplId>, 38 impls: Vec<ImplId>,
18 /// Macros visible in current module in legacy textual scope 39 /// Macros visible in current module in legacy textual scope
@@ -50,14 +71,16 @@ pub(crate) enum BuiltinShadowMode {
50/// Other methods will only resolve values, types and module scoped macros only. 71/// Other methods will only resolve values, types and module scoped macros only.
51impl ItemScope { 72impl ItemScope {
52 pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { 73 pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a {
53 //FIXME: shadowing 74 // FIXME: shadowing
54 self.visible.iter().map(|(n, def)| (n, *def)) 75 let keys: FxHashSet<_> = self
55 } 76 .types
77 .keys()
78 .chain(self.values.keys())
79 .chain(self.macros.keys())
80 .chain(self.unresolved.iter())
81 .collect();
56 82
57 pub fn entries_without_primitives<'a>( 83 keys.into_iter().map(move |name| (name, self.get(name)))
58 &'a self,
59 ) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a {
60 self.visible.iter().map(|(n, def)| (n, *def))
61 } 84 }
62 85
63 pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ { 86 pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
@@ -76,7 +99,7 @@ impl ItemScope {
76 99
77 /// Iterate over all module scoped macros 100 /// Iterate over all module scoped macros
78 pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { 101 pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
79 self.visible.iter().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) 102 self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_)))
80 } 103 }
81 104
82 /// Iterate over all legacy textual scoped macros visible at the end of the module 105 /// Iterate over all legacy textual scoped macros visible at the end of the module
@@ -86,12 +109,16 @@ impl ItemScope {
86 109
87 /// Get a name from current module scope, legacy macros are not included 110 /// Get a name from current module scope, legacy macros are not included
88 pub(crate) fn get(&self, name: &Name) -> PerNs { 111 pub(crate) fn get(&self, name: &Name) -> PerNs {
89 self.visible.get(name).copied().unwrap_or_else(PerNs::none) 112 PerNs {
113 types: self.types.get(name).copied(),
114 values: self.values.get(name).copied(),
115 macros: self.macros.get(name).copied(),
116 }
90 } 117 }
91 118
92 pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { 119 pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> {
93 for (name, per_ns) in &self.visible { 120 for (name, per_ns) in self.entries() {
94 if let Some(vis) = item.match_with(*per_ns) { 121 if let Some(vis) = item.match_with(per_ns) {
95 return Some((name, vis)); 122 return Some((name, vis));
96 } 123 }
97 } 124 }
@@ -99,8 +126,8 @@ impl ItemScope {
99 } 126 }
100 127
101 pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { 128 pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a {
102 self.visible.values().filter_map(|def| match def.take_types() { 129 self.types.values().filter_map(|(def, _)| match def {
103 Some(ModuleDefId::TraitId(t)) => Some(t), 130 ModuleDefId::TraitId(t) => Some(*t),
104 _ => None, 131 _ => None,
105 }) 132 })
106 } 133 }
@@ -123,26 +150,99 @@ impl ItemScope {
123 150
124 pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool { 151 pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool {
125 let mut changed = false; 152 let mut changed = false;
126 let existing = self.visible.entry(name).or_default();
127 153
128 if existing.types.is_none() && def.types.is_some() { 154 if let Some(types) = def.types {
129 existing.types = def.types; 155 self.types.entry(name.clone()).or_insert_with(|| {
130 changed = true; 156 changed = true;
157 types
158 });
131 } 159 }
132 if existing.values.is_none() && def.values.is_some() { 160 if let Some(values) = def.values {
133 existing.values = def.values; 161 self.values.entry(name.clone()).or_insert_with(|| {
134 changed = true; 162 changed = true;
163 values
164 });
165 }
166 if let Some(macros) = def.macros {
167 self.macros.entry(name.clone()).or_insert_with(|| {
168 changed = true;
169 macros
170 });
171 }
172
173 if def.is_none() {
174 if self.unresolved.insert(name) {
175 changed = true;
176 }
177 }
178
179 changed
180 }
181
182 pub(crate) fn push_res_with_import(
183 &mut self,
184 glob_imports: &mut PerNsGlobImports,
185 lookup: (LocalModuleId, Name),
186 def: PerNs,
187 def_import_type: ImportType,
188 ) -> bool {
189 let mut changed = false;
190
191 macro_rules! check_changed {
192 (
193 $changed:ident,
194 ( $this:ident / $def:ident ) . $field:ident,
195 $glob_imports:ident [ $lookup:ident ],
196 $def_import_type:ident
197 ) => {{
198 let existing = $this.$field.entry($lookup.1.clone());
199 match (existing, $def.$field) {
200 (Entry::Vacant(entry), Some(_)) => {
201 match $def_import_type {
202 ImportType::Glob => {
203 $glob_imports.$field.insert($lookup.clone());
204 }
205 ImportType::Named => {
206 $glob_imports.$field.remove(&$lookup);
207 }
208 }
209
210 if let Some(fld) = $def.$field {
211 entry.insert(fld);
212 }
213 $changed = true;
214 }
215 (Entry::Occupied(mut entry), Some(_))
216 if $glob_imports.$field.contains(&$lookup)
217 && matches!($def_import_type, ImportType::Named) =>
218 {
219 mark::hit!(import_shadowed);
220 $glob_imports.$field.remove(&$lookup);
221 if let Some(fld) = $def.$field {
222 entry.insert(fld);
223 }
224 $changed = true;
225 }
226 _ => {}
227 }
228 }};
135 } 229 }
136 if existing.macros.is_none() && def.macros.is_some() { 230
137 existing.macros = def.macros; 231 check_changed!(changed, (self / def).types, glob_imports[lookup], def_import_type);
138 changed = true; 232 check_changed!(changed, (self / def).values, glob_imports[lookup], def_import_type);
233 check_changed!(changed, (self / def).macros, glob_imports[lookup], def_import_type);
234
235 if def.is_none() {
236 if self.unresolved.insert(lookup.1) {
237 changed = true;
238 }
139 } 239 }
140 240
141 changed 241 changed
142 } 242 }
143 243
144 pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a { 244 pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a {
145 self.visible.iter().map(|(name, res)| (name.clone(), *res)) 245 self.entries().map(|(name, res)| (name.clone(), res))
146 } 246 }
147 247
148 pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { 248 pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> {
@@ -203,4 +303,22 @@ impl ItemInNs {
203 ItemInNs::Macros(_) => None, 303 ItemInNs::Macros(_) => None,
204 } 304 }
205 } 305 }
306
307 /// Returns the crate defining this item (or `None` if `self` is built-in).
308 pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> {
309 Some(match self {
310 ItemInNs::Types(did) | ItemInNs::Values(did) => match did {
311 ModuleDefId::ModuleId(id) => id.krate,
312 ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate,
313 ModuleDefId::AdtId(id) => id.module(db).krate,
314 ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate,
315 ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate,
316 ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate,
317 ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate,
318 ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate,
319 ModuleDefId::BuiltinType(_) => return None,
320 },
321 ItemInNs::Macros(id) => return id.krate,
322 })
323 }
206} 324}
diff --git a/crates/ra_hir_def/src/item_tree.rs b/crates/ra_hir_def/src/item_tree.rs
new file mode 100644
index 000000000..da79d8ffd
--- /dev/null
+++ b/crates/ra_hir_def/src/item_tree.rs
@@ -0,0 +1,754 @@
1//! A simplified AST that only contains items.
2
3mod lower;
4#[cfg(test)]
5mod tests;
6
7use std::{
8 any::type_name,
9 fmt::{self, Debug},
10 hash::{Hash, Hasher},
11 marker::PhantomData,
12 ops::{Index, Range},
13 sync::Arc,
14};
15
16use ast::{AstNode, AttrsOwner, NameOwner, StructKind, TypeAscriptionOwner};
17use either::Either;
18use hir_expand::{
19 ast_id_map::FileAstId,
20 hygiene::Hygiene,
21 name::{name, AsName, Name},
22 HirFileId, InFile,
23};
24use ra_arena::{Arena, Idx, RawId};
25use ra_syntax::{ast, match_ast};
26use rustc_hash::FxHashMap;
27use smallvec::SmallVec;
28use test_utils::mark;
29
30use crate::{
31 attr::Attrs,
32 db::DefDatabase,
33 generics::GenericParams,
34 path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
35 type_ref::{Mutability, TypeBound, TypeRef},
36 visibility::RawVisibility,
37};
38
39#[derive(Copy, Clone, Eq, PartialEq)]
40pub struct RawVisibilityId(u32);
41
42impl RawVisibilityId {
43 pub const PUB: Self = RawVisibilityId(u32::max_value());
44 pub const PRIV: Self = RawVisibilityId(u32::max_value() - 1);
45 pub const PUB_CRATE: Self = RawVisibilityId(u32::max_value() - 2);
46}
47
48impl fmt::Debug for RawVisibilityId {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 let mut f = f.debug_tuple("RawVisibilityId");
51 match *self {
52 Self::PUB => f.field(&"pub"),
53 Self::PRIV => f.field(&"pub(self)"),
54 Self::PUB_CRATE => f.field(&"pub(crate)"),
55 _ => f.field(&self.0),
56 };
57 f.finish()
58 }
59}
60
61#[derive(Debug, Copy, Clone, Eq, PartialEq)]
62pub struct GenericParamsId(u32);
63
64impl GenericParamsId {
65 pub const EMPTY: Self = GenericParamsId(u32::max_value());
66}
67
68/// The item tree of a source file.
69#[derive(Debug, Eq, PartialEq)]
70pub struct ItemTree {
71 top_level: SmallVec<[ModItem; 1]>,
72 attrs: FxHashMap<AttrOwner, Attrs>,
73 inner_items: FxHashMap<FileAstId<ast::ModuleItem>, SmallVec<[ModItem; 1]>>,
74
75 data: Option<Box<ItemTreeData>>,
76}
77
78impl ItemTree {
79 pub fn item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
80 let _p = ra_prof::profile("item_tree_query").detail(|| format!("{:?}", file_id));
81 let syntax = if let Some(node) = db.parse_or_expand(file_id) {
82 node
83 } else {
84 return Arc::new(Self::empty());
85 };
86
87 let hygiene = Hygiene::new(db.upcast(), file_id);
88 let ctx = lower::Ctx::new(db, hygiene.clone(), file_id);
89 let mut top_attrs = None;
90 let mut item_tree = match_ast! {
91 match syntax {
92 ast::SourceFile(file) => {
93 top_attrs = Some(Attrs::new(&file, &hygiene));
94 ctx.lower_module_items(&file)
95 },
96 ast::MacroItems(items) => {
97 ctx.lower_module_items(&items)
98 },
99 // Macros can expand to expressions. We return an empty item tree in this case, but
100 // still need to collect inner items.
101 ast::Expr(e) => {
102 ctx.lower_inner_items(e.syntax())
103 },
104 _ => {
105 panic!("cannot create item tree from {:?}", syntax);
106 },
107 }
108 };
109
110 if let Some(attrs) = top_attrs {
111 item_tree.attrs.insert(AttrOwner::TopLevel, attrs);
112 }
113 item_tree.shrink_to_fit();
114 Arc::new(item_tree)
115 }
116
117 fn empty() -> Self {
118 Self {
119 top_level: Default::default(),
120 attrs: Default::default(),
121 inner_items: Default::default(),
122 data: Default::default(),
123 }
124 }
125
126 fn shrink_to_fit(&mut self) {
127 if let Some(data) = &mut self.data {
128 let ItemTreeData {
129 imports,
130 extern_crates,
131 functions,
132 structs,
133 fields,
134 unions,
135 enums,
136 variants,
137 consts,
138 statics,
139 traits,
140 impls,
141 type_aliases,
142 mods,
143 macro_calls,
144 exprs,
145 vis,
146 generics,
147 } = &mut **data;
148
149 imports.shrink_to_fit();
150 extern_crates.shrink_to_fit();
151 functions.shrink_to_fit();
152 structs.shrink_to_fit();
153 fields.shrink_to_fit();
154 unions.shrink_to_fit();
155 enums.shrink_to_fit();
156 variants.shrink_to_fit();
157 consts.shrink_to_fit();
158 statics.shrink_to_fit();
159 traits.shrink_to_fit();
160 impls.shrink_to_fit();
161 type_aliases.shrink_to_fit();
162 mods.shrink_to_fit();
163 macro_calls.shrink_to_fit();
164 exprs.shrink_to_fit();
165
166 vis.arena.shrink_to_fit();
167 generics.arena.shrink_to_fit();
168 }
169 }
170
171 /// Returns an iterator over all items located at the top level of the `HirFileId` this
172 /// `ItemTree` was created from.
173 pub fn top_level_items(&self) -> &[ModItem] {
174 &self.top_level
175 }
176
177 /// Returns the inner attributes of the source file.
178 pub fn top_level_attrs(&self) -> &Attrs {
179 self.attrs.get(&AttrOwner::TopLevel).unwrap_or(&Attrs::EMPTY)
180 }
181
182 pub fn attrs(&self, of: AttrOwner) -> &Attrs {
183 self.attrs.get(&of).unwrap_or(&Attrs::EMPTY)
184 }
185
186 /// Returns the lowered inner items that `ast` corresponds to.
187 ///
188 /// Most AST items are lowered to a single `ModItem`, but some (eg. `use` items) may be lowered
189 /// to multiple items in the `ItemTree`.
190 pub fn inner_items(&self, ast: FileAstId<ast::ModuleItem>) -> &[ModItem] {
191 &self.inner_items[&ast]
192 }
193
194 pub fn all_inner_items(&self) -> impl Iterator<Item = ModItem> + '_ {
195 self.inner_items.values().flatten().copied()
196 }
197
198 pub fn source<S: ItemTreeNode>(&self, db: &dyn DefDatabase, of: ItemTreeId<S>) -> S::Source {
199 // This unwrap cannot fail, since it has either succeeded above, or resulted in an empty
200 // ItemTree (in which case there is no valid `FileItemTreeId` to call this method with).
201 let root =
202 db.parse_or_expand(of.file_id).expect("parse_or_expand failed on constructed ItemTree");
203
204 let id = self[of.value].ast_id();
205 let map = db.ast_id_map(of.file_id);
206 let ptr = map.get(id);
207 ptr.to_node(&root)
208 }
209
210 fn data(&self) -> &ItemTreeData {
211 self.data.as_ref().expect("attempted to access data of empty ItemTree")
212 }
213
214 fn data_mut(&mut self) -> &mut ItemTreeData {
215 self.data.get_or_insert_with(Box::default)
216 }
217}
218
219#[derive(Default, Debug, Eq, PartialEq)]
220struct ItemVisibilities {
221 arena: Arena<RawVisibility>,
222}
223
224impl ItemVisibilities {
225 fn alloc(&mut self, vis: RawVisibility) -> RawVisibilityId {
226 match &vis {
227 RawVisibility::Public => RawVisibilityId::PUB,
228 RawVisibility::Module(path) if path.segments.is_empty() => match &path.kind {
229 PathKind::Super(0) => RawVisibilityId::PRIV,
230 PathKind::Crate => RawVisibilityId::PUB_CRATE,
231 _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
232 },
233 _ => RawVisibilityId(self.arena.alloc(vis).into_raw().into()),
234 }
235 }
236}
237
238static VIS_PUB: RawVisibility = RawVisibility::Public;
239static VIS_PRIV: RawVisibility =
240 RawVisibility::Module(ModPath { kind: PathKind::Super(0), segments: Vec::new() });
241static VIS_PUB_CRATE: RawVisibility =
242 RawVisibility::Module(ModPath { kind: PathKind::Crate, segments: Vec::new() });
243
244#[derive(Default, Debug, Eq, PartialEq)]
245struct GenericParamsStorage {
246 arena: Arena<GenericParams>,
247}
248
249impl GenericParamsStorage {
250 fn alloc(&mut self, params: GenericParams) -> GenericParamsId {
251 if params.types.is_empty() && params.where_predicates.is_empty() {
252 return GenericParamsId::EMPTY;
253 }
254
255 GenericParamsId(self.arena.alloc(params).into_raw().into())
256 }
257}
258
259static EMPTY_GENERICS: GenericParams =
260 GenericParams { types: Arena::new(), where_predicates: Vec::new() };
261
262#[derive(Default, Debug, Eq, PartialEq)]
263struct ItemTreeData {
264 imports: Arena<Import>,
265 extern_crates: Arena<ExternCrate>,
266 functions: Arena<Function>,
267 structs: Arena<Struct>,
268 fields: Arena<Field>,
269 unions: Arena<Union>,
270 enums: Arena<Enum>,
271 variants: Arena<Variant>,
272 consts: Arena<Const>,
273 statics: Arena<Static>,
274 traits: Arena<Trait>,
275 impls: Arena<Impl>,
276 type_aliases: Arena<TypeAlias>,
277 mods: Arena<Mod>,
278 macro_calls: Arena<MacroCall>,
279 exprs: Arena<Expr>,
280
281 vis: ItemVisibilities,
282 generics: GenericParamsStorage,
283}
284
285#[derive(Debug, Eq, PartialEq, Hash)]
286pub enum AttrOwner {
287 /// Attributes on an item.
288 ModItem(ModItem),
289 /// Inner attributes of the source file.
290 TopLevel,
291
292 Variant(Idx<Variant>),
293 Field(Idx<Field>),
294 // FIXME: Store variant and field attrs, and stop reparsing them in `attrs_query`.
295}
296
297macro_rules! from_attrs {
298 ( $( $var:ident($t:ty) ),+ ) => {
299 $(
300 impl From<$t> for AttrOwner {
301 fn from(t: $t) -> AttrOwner {
302 AttrOwner::$var(t)
303 }
304 }
305 )+
306 };
307}
308
309from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>));
310
311/// Trait implemented by all item nodes in the item tree.
312pub trait ItemTreeNode: Clone {
313 type Source: AstNode + Into<ast::ModuleItem>;
314
315 fn ast_id(&self) -> FileAstId<Self::Source>;
316
317 /// Looks up an instance of `Self` in an item tree.
318 fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self;
319
320 /// Downcasts a `ModItem` to a `FileItemTreeId` specific to this type.
321 fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>>;
322
323 /// Upcasts a `FileItemTreeId` to a generic `ModItem`.
324 fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem;
325}
326
327pub struct FileItemTreeId<N: ItemTreeNode> {
328 index: Idx<N>,
329 _p: PhantomData<N>,
330}
331
332impl<N: ItemTreeNode> Clone for FileItemTreeId<N> {
333 fn clone(&self) -> Self {
334 Self { index: self.index, _p: PhantomData }
335 }
336}
337impl<N: ItemTreeNode> Copy for FileItemTreeId<N> {}
338
339impl<N: ItemTreeNode> PartialEq for FileItemTreeId<N> {
340 fn eq(&self, other: &FileItemTreeId<N>) -> bool {
341 self.index == other.index
342 }
343}
344impl<N: ItemTreeNode> Eq for FileItemTreeId<N> {}
345
346impl<N: ItemTreeNode> Hash for FileItemTreeId<N> {
347 fn hash<H: Hasher>(&self, state: &mut H) {
348 self.index.hash(state)
349 }
350}
351
352impl<N: ItemTreeNode> fmt::Debug for FileItemTreeId<N> {
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 self.index.fmt(f)
355 }
356}
357
358pub type ItemTreeId<N> = InFile<FileItemTreeId<N>>;
359
360macro_rules! mod_items {
361 ( $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => {
362 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
363 pub enum ModItem {
364 $(
365 $typ(FileItemTreeId<$typ>),
366 )+
367 }
368
369 $(
370 impl From<FileItemTreeId<$typ>> for ModItem {
371 fn from(id: FileItemTreeId<$typ>) -> ModItem {
372 ModItem::$typ(id)
373 }
374 }
375 )+
376
377 $(
378 impl ItemTreeNode for $typ {
379 type Source = $ast;
380
381 fn ast_id(&self) -> FileAstId<Self::Source> {
382 self.ast_id
383 }
384
385 fn lookup(tree: &ItemTree, index: Idx<Self>) -> &Self {
386 &tree.data().$fld[index]
387 }
388
389 fn id_from_mod_item(mod_item: ModItem) -> Option<FileItemTreeId<Self>> {
390 if let ModItem::$typ(id) = mod_item {
391 Some(id)
392 } else {
393 None
394 }
395 }
396
397 fn id_to_mod_item(id: FileItemTreeId<Self>) -> ModItem {
398 ModItem::$typ(id)
399 }
400 }
401
402 impl Index<Idx<$typ>> for ItemTree {
403 type Output = $typ;
404
405 fn index(&self, index: Idx<$typ>) -> &Self::Output {
406 &self.data().$fld[index]
407 }
408 }
409 )+
410 };
411}
412
413mod_items! {
414 Import in imports -> ast::UseItem,
415 ExternCrate in extern_crates -> ast::ExternCrateItem,
416 Function in functions -> ast::FnDef,
417 Struct in structs -> ast::StructDef,
418 Union in unions -> ast::UnionDef,
419 Enum in enums -> ast::EnumDef,
420 Const in consts -> ast::ConstDef,
421 Static in statics -> ast::StaticDef,
422 Trait in traits -> ast::TraitDef,
423 Impl in impls -> ast::ImplDef,
424 TypeAlias in type_aliases -> ast::TypeAliasDef,
425 Mod in mods -> ast::Module,
426 MacroCall in macro_calls -> ast::MacroCall,
427}
428
429macro_rules! impl_index {
430 ( $($fld:ident: $t:ty),+ $(,)? ) => {
431 $(
432 impl Index<Idx<$t>> for ItemTree {
433 type Output = $t;
434
435 fn index(&self, index: Idx<$t>) -> &Self::Output {
436 &self.data().$fld[index]
437 }
438 }
439 )+
440 };
441}
442
443impl_index!(fields: Field, variants: Variant, exprs: Expr);
444
445impl Index<RawVisibilityId> for ItemTree {
446 type Output = RawVisibility;
447 fn index(&self, index: RawVisibilityId) -> &Self::Output {
448 match index {
449 RawVisibilityId::PRIV => &VIS_PRIV,
450 RawVisibilityId::PUB => &VIS_PUB,
451 RawVisibilityId::PUB_CRATE => &VIS_PUB_CRATE,
452 _ => &self.data().vis.arena[Idx::from_raw(index.0.into())],
453 }
454 }
455}
456
457impl Index<GenericParamsId> for ItemTree {
458 type Output = GenericParams;
459
460 fn index(&self, index: GenericParamsId) -> &Self::Output {
461 match index {
462 GenericParamsId::EMPTY => &EMPTY_GENERICS,
463 _ => &self.data().generics.arena[Idx::from_raw(index.0.into())],
464 }
465 }
466}
467
468impl<N: ItemTreeNode> Index<FileItemTreeId<N>> for ItemTree {
469 type Output = N;
470 fn index(&self, id: FileItemTreeId<N>) -> &N {
471 N::lookup(self, id.index)
472 }
473}
474
475/// A desugared `use` import.
476#[derive(Debug, Clone, Eq, PartialEq)]
477pub struct Import {
478 pub path: ModPath,
479 pub alias: Option<ImportAlias>,
480 pub visibility: RawVisibilityId,
481 pub is_glob: bool,
482 pub is_prelude: bool,
483 /// AST ID of the `use` or `extern crate` item this import was derived from. Note that many
484 /// `Import`s can map to the same `use` item.
485 pub ast_id: FileAstId<ast::UseItem>,
486}
487
488#[derive(Debug, Clone, Eq, PartialEq)]
489pub struct ExternCrate {
490 pub path: ModPath,
491 pub alias: Option<ImportAlias>,
492 pub visibility: RawVisibilityId,
493 /// Whether this is a `#[macro_use] extern crate ...`.
494 pub is_macro_use: bool,
495 pub ast_id: FileAstId<ast::ExternCrateItem>,
496}
497
498#[derive(Debug, Clone, Eq, PartialEq)]
499pub struct Function {
500 pub name: Name,
501 pub visibility: RawVisibilityId,
502 pub generic_params: GenericParamsId,
503 pub has_self_param: bool,
504 pub is_unsafe: bool,
505 pub params: Box<[TypeRef]>,
506 pub is_varargs: bool,
507 pub ret_type: TypeRef,
508 pub ast_id: FileAstId<ast::FnDef>,
509}
510
511#[derive(Debug, Clone, Eq, PartialEq)]
512pub struct Struct {
513 pub name: Name,
514 pub visibility: RawVisibilityId,
515 pub generic_params: GenericParamsId,
516 pub fields: Fields,
517 pub ast_id: FileAstId<ast::StructDef>,
518 pub kind: StructDefKind,
519}
520
521#[derive(Debug, Clone, Eq, PartialEq)]
522pub enum StructDefKind {
523 /// `struct S { ... }` - type namespace only.
524 Record,
525 /// `struct S(...);`
526 Tuple,
527 /// `struct S;`
528 Unit,
529}
530
531#[derive(Debug, Clone, Eq, PartialEq)]
532pub struct Union {
533 pub name: Name,
534 pub visibility: RawVisibilityId,
535 pub generic_params: GenericParamsId,
536 pub fields: Fields,
537 pub ast_id: FileAstId<ast::UnionDef>,
538}
539
540#[derive(Debug, Clone, Eq, PartialEq)]
541pub struct Enum {
542 pub name: Name,
543 pub visibility: RawVisibilityId,
544 pub generic_params: GenericParamsId,
545 pub variants: IdRange<Variant>,
546 pub ast_id: FileAstId<ast::EnumDef>,
547}
548
549#[derive(Debug, Clone, Eq, PartialEq)]
550pub struct Const {
551 /// const _: () = ();
552 pub name: Option<Name>,
553 pub visibility: RawVisibilityId,
554 pub type_ref: TypeRef,
555 pub ast_id: FileAstId<ast::ConstDef>,
556}
557
558#[derive(Debug, Clone, Eq, PartialEq)]
559pub struct Static {
560 pub name: Name,
561 pub visibility: RawVisibilityId,
562 pub mutable: bool,
563 pub type_ref: TypeRef,
564 pub ast_id: FileAstId<ast::StaticDef>,
565}
566
567#[derive(Debug, Clone, Eq, PartialEq)]
568pub struct Trait {
569 pub name: Name,
570 pub visibility: RawVisibilityId,
571 pub generic_params: GenericParamsId,
572 pub auto: bool,
573 pub items: Box<[AssocItem]>,
574 pub ast_id: FileAstId<ast::TraitDef>,
575}
576
577#[derive(Debug, Clone, Eq, PartialEq)]
578pub struct Impl {
579 pub generic_params: GenericParamsId,
580 pub target_trait: Option<TypeRef>,
581 pub target_type: TypeRef,
582 pub is_negative: bool,
583 pub items: Box<[AssocItem]>,
584 pub ast_id: FileAstId<ast::ImplDef>,
585}
586
587#[derive(Debug, Clone, PartialEq, Eq)]
588pub struct TypeAlias {
589 pub name: Name,
590 pub visibility: RawVisibilityId,
591 /// Bounds on the type alias itself. Only valid in trait declarations, eg. `type Assoc: Copy;`.
592 pub bounds: Box<[TypeBound]>,
593 pub generic_params: GenericParamsId,
594 pub type_ref: Option<TypeRef>,
595 pub ast_id: FileAstId<ast::TypeAliasDef>,
596}
597
598#[derive(Debug, Clone, Eq, PartialEq)]
599pub struct Mod {
600 pub name: Name,
601 pub visibility: RawVisibilityId,
602 pub kind: ModKind,
603 pub ast_id: FileAstId<ast::Module>,
604}
605
606#[derive(Debug, Clone, Eq, PartialEq)]
607pub enum ModKind {
608 /// `mod m { ... }`
609 Inline { items: Box<[ModItem]> },
610
611 /// `mod m;`
612 Outline {},
613}
614
615#[derive(Debug, Clone, Eq, PartialEq)]
616pub struct MacroCall {
617 /// For `macro_rules!` declarations, this is the name of the declared macro.
618 pub name: Option<Name>,
619 /// Path to the called macro.
620 pub path: ModPath,
621 /// Has `#[macro_export]`.
622 pub is_export: bool,
623 /// Has `#[macro_export(local_inner_macros)]`.
624 pub is_local_inner: bool,
625 /// Has `#[rustc_builtin_macro]`.
626 pub is_builtin: bool,
627 pub ast_id: FileAstId<ast::MacroCall>,
628}
629
630// NB: There's no `FileAstId` for `Expr`. The only case where this would be useful is for array
631// lengths, but we don't do much with them yet.
632#[derive(Debug, Clone, Eq, PartialEq)]
633pub struct Expr;
634
635macro_rules! impl_froms {
636 ($e:ident { $($v:ident ($t:ty)),* $(,)? }) => {
637 $(
638 impl From<$t> for $e {
639 fn from(it: $t) -> $e {
640 $e::$v(it)
641 }
642 }
643 )*
644 }
645}
646
647impl ModItem {
648 pub fn as_assoc_item(&self) -> Option<AssocItem> {
649 match self {
650 ModItem::Import(_)
651 | ModItem::ExternCrate(_)
652 | ModItem::Struct(_)
653 | ModItem::Union(_)
654 | ModItem::Enum(_)
655 | ModItem::Static(_)
656 | ModItem::Trait(_)
657 | ModItem::Impl(_)
658 | ModItem::Mod(_) => None,
659 ModItem::MacroCall(call) => Some(AssocItem::MacroCall(*call)),
660 ModItem::Const(konst) => Some(AssocItem::Const(*konst)),
661 ModItem::TypeAlias(alias) => Some(AssocItem::TypeAlias(*alias)),
662 ModItem::Function(func) => Some(AssocItem::Function(*func)),
663 }
664 }
665
666 pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
667 N::id_from_mod_item(self)
668 }
669}
670
671#[derive(Debug, Copy, Clone, Eq, PartialEq)]
672pub enum AssocItem {
673 Function(FileItemTreeId<Function>),
674 TypeAlias(FileItemTreeId<TypeAlias>),
675 Const(FileItemTreeId<Const>),
676 MacroCall(FileItemTreeId<MacroCall>),
677}
678
679impl_froms!(AssocItem {
680 Function(FileItemTreeId<Function>),
681 TypeAlias(FileItemTreeId<TypeAlias>),
682 Const(FileItemTreeId<Const>),
683 MacroCall(FileItemTreeId<MacroCall>),
684});
685
686impl From<AssocItem> for ModItem {
687 fn from(item: AssocItem) -> Self {
688 match item {
689 AssocItem::Function(it) => it.into(),
690 AssocItem::TypeAlias(it) => it.into(),
691 AssocItem::Const(it) => it.into(),
692 AssocItem::MacroCall(it) => it.into(),
693 }
694 }
695}
696
697#[derive(Debug, Eq, PartialEq)]
698pub struct Variant {
699 pub name: Name,
700 pub fields: Fields,
701}
702
703pub struct IdRange<T> {
704 range: Range<u32>,
705 _p: PhantomData<T>,
706}
707
708impl<T> IdRange<T> {
709 fn new(range: Range<Idx<T>>) -> Self {
710 Self { range: range.start.into_raw().into()..range.end.into_raw().into(), _p: PhantomData }
711 }
712}
713
714impl<T> Iterator for IdRange<T> {
715 type Item = Idx<T>;
716 fn next(&mut self) -> Option<Self::Item> {
717 self.range.next().map(|raw| Idx::from_raw(raw.into()))
718 }
719}
720
721impl<T> fmt::Debug for IdRange<T> {
722 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
723 f.debug_tuple(&format!("IdRange::<{}>", type_name::<T>())).field(&self.range).finish()
724 }
725}
726
727impl<T> Clone for IdRange<T> {
728 fn clone(&self) -> Self {
729 Self { range: self.range.clone(), _p: PhantomData }
730 }
731}
732
733impl<T> PartialEq for IdRange<T> {
734 fn eq(&self, other: &Self) -> bool {
735 self.range == other.range
736 }
737}
738
739impl<T> Eq for IdRange<T> {}
740
741#[derive(Debug, Clone, PartialEq, Eq)]
742pub enum Fields {
743 Record(IdRange<Field>),
744 Tuple(IdRange<Field>),
745 Unit,
746}
747
748/// A single field of an enum variant or struct
749#[derive(Debug, Clone, PartialEq, Eq)]
750pub struct Field {
751 pub name: Name,
752 pub type_ref: TypeRef,
753 pub visibility: RawVisibilityId,
754}
diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs
new file mode 100644
index 000000000..f79b8fca3
--- /dev/null
+++ b/crates/ra_hir_def/src/item_tree/lower.rs
@@ -0,0 +1,708 @@
1//! AST -> `ItemTree` lowering code.
2
3use super::*;
4use crate::{
5 attr::Attrs,
6 generics::{GenericParams, TypeParamData, TypeParamProvenance},
7};
8use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
9use ra_arena::map::ArenaMap;
10use ra_syntax::{
11 ast::{self, ModuleItemOwner},
12 SyntaxNode,
13};
14use smallvec::SmallVec;
15use std::{collections::hash_map::Entry, mem, sync::Arc};
16
17fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
18 FileItemTreeId { index, _p: PhantomData }
19}
20
21struct ModItems(SmallVec<[ModItem; 1]>);
22
23impl<T> From<T> for ModItems
24where
25 T: Into<ModItem>,
26{
27 fn from(t: T) -> Self {
28 ModItems(SmallVec::from_buf([t.into(); 1]))
29 }
30}
31
32pub(super) struct Ctx {
33 tree: ItemTree,
34 hygiene: Hygiene,
35 file: HirFileId,
36 source_ast_id_map: Arc<AstIdMap>,
37 body_ctx: crate::body::LowerCtx,
38 inner_items: Vec<ModItem>,
39 forced_visibility: Option<RawVisibilityId>,
40}
41
42impl Ctx {
43 pub(super) fn new(db: &dyn DefDatabase, hygiene: Hygiene, file: HirFileId) -> Self {
44 Self {
45 tree: ItemTree::empty(),
46 hygiene,
47 file,
48 source_ast_id_map: db.ast_id_map(file),
49 body_ctx: crate::body::LowerCtx::new(db, file),
50 inner_items: Vec::new(),
51 forced_visibility: None,
52 }
53 }
54
55 pub(super) fn lower_module_items(mut self, item_owner: &dyn ModuleItemOwner) -> ItemTree {
56 self.tree.top_level = item_owner
57 .items()
58 .flat_map(|item| self.lower_mod_item(&item, false))
59 .flat_map(|items| items.0)
60 .collect();
61 self.tree
62 }
63
64 pub(super) fn lower_inner_items(mut self, within: &SyntaxNode) -> ItemTree {
65 self.collect_inner_items(within);
66 self.tree
67 }
68
69 fn data(&mut self) -> &mut ItemTreeData {
70 self.tree.data_mut()
71 }
72
73 fn lower_mod_item(&mut self, item: &ast::ModuleItem, inner: bool) -> Option<ModItems> {
74 assert!(inner || self.inner_items.is_empty());
75
76 // Collect inner items for 1-to-1-lowered items.
77 match item {
78 ast::ModuleItem::StructDef(_)
79 | ast::ModuleItem::UnionDef(_)
80 | ast::ModuleItem::EnumDef(_)
81 | ast::ModuleItem::FnDef(_)
82 | ast::ModuleItem::TypeAliasDef(_)
83 | ast::ModuleItem::ConstDef(_)
84 | ast::ModuleItem::StaticDef(_)
85 | ast::ModuleItem::MacroCall(_) => {
86 // Skip this if we're already collecting inner items. We'll descend into all nodes
87 // already.
88 if !inner {
89 self.collect_inner_items(item.syntax());
90 }
91 }
92
93 // These are handled in their respective `lower_X` method (since we can't just blindly
94 // walk them).
95 ast::ModuleItem::TraitDef(_)
96 | ast::ModuleItem::ImplDef(_)
97 | ast::ModuleItem::ExternBlock(_) => {}
98
99 // These don't have inner items.
100 ast::ModuleItem::Module(_)
101 | ast::ModuleItem::ExternCrateItem(_)
102 | ast::ModuleItem::UseItem(_) => {}
103 };
104
105 let attrs = Attrs::new(item, &self.hygiene);
106 let items = match item {
107 ast::ModuleItem::StructDef(ast) => self.lower_struct(ast).map(Into::into),
108 ast::ModuleItem::UnionDef(ast) => self.lower_union(ast).map(Into::into),
109 ast::ModuleItem::EnumDef(ast) => self.lower_enum(ast).map(Into::into),
110 ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into),
111 ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into),
112 ast::ModuleItem::StaticDef(ast) => self.lower_static(ast).map(Into::into),
113 ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()),
114 ast::ModuleItem::Module(ast) => self.lower_module(ast).map(Into::into),
115 ast::ModuleItem::TraitDef(ast) => self.lower_trait(ast).map(Into::into),
116 ast::ModuleItem::ImplDef(ast) => self.lower_impl(ast).map(Into::into),
117 ast::ModuleItem::UseItem(ast) => Some(ModItems(
118 self.lower_use(ast).into_iter().map(Into::into).collect::<SmallVec<_>>(),
119 )),
120 ast::ModuleItem::ExternCrateItem(ast) => self.lower_extern_crate(ast).map(Into::into),
121 ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
122 ast::ModuleItem::ExternBlock(ast) => {
123 Some(ModItems(self.lower_extern_block(ast).into_iter().collect::<SmallVec<_>>()))
124 }
125 };
126
127 if !attrs.is_empty() {
128 for item in items.iter().flat_map(|items| &items.0) {
129 self.add_attrs((*item).into(), attrs.clone());
130 }
131 }
132
133 items
134 }
135
136 fn add_attrs(&mut self, item: AttrOwner, attrs: Attrs) {
137 match self.tree.attrs.entry(item) {
138 Entry::Occupied(mut entry) => {
139 *entry.get_mut() = entry.get().merge(attrs);
140 }
141 Entry::Vacant(entry) => {
142 entry.insert(attrs);
143 }
144 }
145 }
146
147 fn collect_inner_items(&mut self, container: &SyntaxNode) {
148 let forced_vis = self.forced_visibility.take();
149 let mut inner_items = mem::take(&mut self.tree.inner_items);
150 inner_items.extend(
151 container.descendants().skip(1).filter_map(ast::ModuleItem::cast).filter_map(|item| {
152 let ast_id = self.source_ast_id_map.ast_id(&item);
153 Some((ast_id, self.lower_mod_item(&item, true)?.0))
154 }),
155 );
156 self.tree.inner_items = inner_items;
157 self.forced_visibility = forced_vis;
158 }
159
160 fn lower_assoc_item(&mut self, item: &ast::ModuleItem) -> Option<AssocItem> {
161 match item {
162 ast::ModuleItem::FnDef(ast) => self.lower_function(ast).map(Into::into),
163 ast::ModuleItem::TypeAliasDef(ast) => self.lower_type_alias(ast).map(Into::into),
164 ast::ModuleItem::ConstDef(ast) => Some(self.lower_const(ast).into()),
165 ast::ModuleItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
166 _ => None,
167 }
168 }
169
170 fn lower_struct(&mut self, strukt: &ast::StructDef) -> Option<FileItemTreeId<Struct>> {
171 let visibility = self.lower_visibility(strukt);
172 let name = strukt.name()?.as_name();
173 let generic_params = self.lower_generic_params(GenericsOwner::Struct, strukt);
174 let fields = self.lower_fields(&strukt.kind());
175 let ast_id = self.source_ast_id_map.ast_id(strukt);
176 let kind = match strukt.kind() {
177 ast::StructKind::Record(_) => StructDefKind::Record,
178 ast::StructKind::Tuple(_) => StructDefKind::Tuple,
179 ast::StructKind::Unit => StructDefKind::Unit,
180 };
181 let res = Struct { name, visibility, generic_params, fields, ast_id, kind };
182 Some(id(self.data().structs.alloc(res)))
183 }
184
185 fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields {
186 match strukt_kind {
187 ast::StructKind::Record(it) => {
188 let range = self.lower_record_fields(it);
189 Fields::Record(range)
190 }
191 ast::StructKind::Tuple(it) => {
192 let range = self.lower_tuple_fields(it);
193 Fields::Tuple(range)
194 }
195 ast::StructKind::Unit => Fields::Unit,
196 }
197 }
198
199 fn lower_record_fields(&mut self, fields: &ast::RecordFieldDefList) -> IdRange<Field> {
200 let start = self.next_field_idx();
201 for field in fields.fields() {
202 if let Some(data) = self.lower_record_field(&field) {
203 let idx = self.data().fields.alloc(data);
204 self.add_attrs(idx.into(), Attrs::new(&field, &self.hygiene));
205 }
206 }
207 let end = self.next_field_idx();
208 IdRange::new(start..end)
209 }
210
211 fn lower_record_field(&mut self, field: &ast::RecordFieldDef) -> Option<Field> {
212 let name = field.name()?.as_name();
213 let visibility = self.lower_visibility(field);
214 let type_ref = self.lower_type_ref_opt(field.ascribed_type());
215 let res = Field { name, type_ref, visibility };
216 Some(res)
217 }
218
219 fn lower_tuple_fields(&mut self, fields: &ast::TupleFieldDefList) -> IdRange<Field> {
220 let start = self.next_field_idx();
221 for (i, field) in fields.fields().enumerate() {
222 let data = self.lower_tuple_field(i, &field);
223 let idx = self.data().fields.alloc(data);
224 self.add_attrs(idx.into(), Attrs::new(&field, &self.hygiene));
225 }
226 let end = self.next_field_idx();
227 IdRange::new(start..end)
228 }
229
230 fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleFieldDef) -> Field {
231 let name = Name::new_tuple_field(idx);
232 let visibility = self.lower_visibility(field);
233 let type_ref = self.lower_type_ref_opt(field.type_ref());
234 let res = Field { name, type_ref, visibility };
235 res
236 }
237
238 fn lower_union(&mut self, union: &ast::UnionDef) -> Option<FileItemTreeId<Union>> {
239 let visibility = self.lower_visibility(union);
240 let name = union.name()?.as_name();
241 let generic_params = self.lower_generic_params(GenericsOwner::Union, union);
242 let fields = match union.record_field_def_list() {
243 Some(record_field_def_list) => {
244 self.lower_fields(&StructKind::Record(record_field_def_list))
245 }
246 None => Fields::Record(IdRange::new(self.next_field_idx()..self.next_field_idx())),
247 };
248 let ast_id = self.source_ast_id_map.ast_id(union);
249 let res = Union { name, visibility, generic_params, fields, ast_id };
250 Some(id(self.data().unions.alloc(res)))
251 }
252
253 fn lower_enum(&mut self, enum_: &ast::EnumDef) -> Option<FileItemTreeId<Enum>> {
254 let visibility = self.lower_visibility(enum_);
255 let name = enum_.name()?.as_name();
256 let generic_params = self.lower_generic_params(GenericsOwner::Enum, enum_);
257 let variants = match &enum_.variant_list() {
258 Some(variant_list) => self.lower_variants(variant_list),
259 None => IdRange::new(self.next_variant_idx()..self.next_variant_idx()),
260 };
261 let ast_id = self.source_ast_id_map.ast_id(enum_);
262 let res = Enum { name, visibility, generic_params, variants, ast_id };
263 Some(id(self.data().enums.alloc(res)))
264 }
265
266 fn lower_variants(&mut self, variants: &ast::EnumVariantList) -> IdRange<Variant> {
267 let start = self.next_variant_idx();
268 for variant in variants.variants() {
269 if let Some(data) = self.lower_variant(&variant) {
270 let idx = self.data().variants.alloc(data);
271 self.add_attrs(idx.into(), Attrs::new(&variant, &self.hygiene));
272 }
273 }
274 let end = self.next_variant_idx();
275 IdRange::new(start..end)
276 }
277
278 fn lower_variant(&mut self, variant: &ast::EnumVariant) -> Option<Variant> {
279 let name = variant.name()?.as_name();
280 let fields = self.lower_fields(&variant.kind());
281 let res = Variant { name, fields };
282 Some(res)
283 }
284
285 fn lower_function(&mut self, func: &ast::FnDef) -> Option<FileItemTreeId<Function>> {
286 let visibility = self.lower_visibility(func);
287 let name = func.name()?.as_name();
288
289 let mut params = Vec::new();
290 let mut has_self_param = false;
291 if let Some(param_list) = func.param_list() {
292 if let Some(self_param) = param_list.self_param() {
293 let self_type = match self_param.ascribed_type() {
294 Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
295 None => {
296 let self_type = TypeRef::Path(name![Self].into());
297 match self_param.kind() {
298 ast::SelfParamKind::Owned => self_type,
299 ast::SelfParamKind::Ref => {
300 TypeRef::Reference(Box::new(self_type), Mutability::Shared)
301 }
302 ast::SelfParamKind::MutRef => {
303 TypeRef::Reference(Box::new(self_type), Mutability::Mut)
304 }
305 }
306 }
307 };
308 params.push(self_type);
309 has_self_param = true;
310 }
311 for param in param_list.params() {
312 let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ascribed_type());
313 params.push(type_ref);
314 }
315 }
316
317 let mut is_varargs = false;
318 if let Some(params) = func.param_list() {
319 if let Some(last) = params.params().last() {
320 is_varargs = last.dotdotdot_token().is_some();
321 }
322 }
323
324 let ret_type = match func.ret_type().and_then(|rt| rt.type_ref()) {
325 Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
326 _ => TypeRef::unit(),
327 };
328
329 let ret_type = if func.async_token().is_some() {
330 let future_impl = desugar_future_path(ret_type);
331 let ty_bound = TypeBound::Path(future_impl);
332 TypeRef::ImplTrait(vec![ty_bound])
333 } else {
334 ret_type
335 };
336
337 let ast_id = self.source_ast_id_map.ast_id(func);
338 let mut res = Function {
339 name,
340 visibility,
341 generic_params: GenericParamsId::EMPTY,
342 has_self_param,
343 is_unsafe: func.unsafe_token().is_some(),
344 params: params.into_boxed_slice(),
345 is_varargs,
346 ret_type,
347 ast_id,
348 };
349 res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func);
350
351 Some(id(self.data().functions.alloc(res)))
352 }
353
354 fn lower_type_alias(
355 &mut self,
356 type_alias: &ast::TypeAliasDef,
357 ) -> Option<FileItemTreeId<TypeAlias>> {
358 let name = type_alias.name()?.as_name();
359 let type_ref = type_alias.type_ref().map(|it| self.lower_type_ref(&it));
360 let visibility = self.lower_visibility(type_alias);
361 let bounds = self.lower_type_bounds(type_alias);
362 let generic_params = self.lower_generic_params(GenericsOwner::TypeAlias, type_alias);
363 let ast_id = self.source_ast_id_map.ast_id(type_alias);
364 let res = TypeAlias {
365 name,
366 visibility,
367 bounds: bounds.into_boxed_slice(),
368 generic_params,
369 type_ref,
370 ast_id,
371 };
372 Some(id(self.data().type_aliases.alloc(res)))
373 }
374
375 fn lower_static(&mut self, static_: &ast::StaticDef) -> Option<FileItemTreeId<Static>> {
376 let name = static_.name()?.as_name();
377 let type_ref = self.lower_type_ref_opt(static_.ascribed_type());
378 let visibility = self.lower_visibility(static_);
379 let mutable = static_.mut_token().is_some();
380 let ast_id = self.source_ast_id_map.ast_id(static_);
381 let res = Static { name, visibility, mutable, type_ref, ast_id };
382 Some(id(self.data().statics.alloc(res)))
383 }
384
385 fn lower_const(&mut self, konst: &ast::ConstDef) -> FileItemTreeId<Const> {
386 let name = konst.name().map(|it| it.as_name());
387 let type_ref = self.lower_type_ref_opt(konst.ascribed_type());
388 let visibility = self.lower_visibility(konst);
389 let ast_id = self.source_ast_id_map.ast_id(konst);
390 let res = Const { name, visibility, type_ref, ast_id };
391 id(self.data().consts.alloc(res))
392 }
393
394 fn lower_module(&mut self, module: &ast::Module) -> Option<FileItemTreeId<Mod>> {
395 let name = module.name()?.as_name();
396 let visibility = self.lower_visibility(module);
397 let kind = if module.semicolon_token().is_some() {
398 ModKind::Outline {}
399 } else {
400 ModKind::Inline {
401 items: module
402 .item_list()
403 .map(|list| {
404 list.items()
405 .flat_map(|item| self.lower_mod_item(&item, false))
406 .flat_map(|items| items.0)
407 .collect()
408 })
409 .unwrap_or_else(|| {
410 mark::hit!(name_res_works_for_broken_modules);
411 Box::new([]) as Box<[_]>
412 }),
413 }
414 };
415 let ast_id = self.source_ast_id_map.ast_id(module);
416 let res = Mod { name, visibility, kind, ast_id };
417 Some(id(self.data().mods.alloc(res)))
418 }
419
420 fn lower_trait(&mut self, trait_def: &ast::TraitDef) -> Option<FileItemTreeId<Trait>> {
421 let name = trait_def.name()?.as_name();
422 let visibility = self.lower_visibility(trait_def);
423 let generic_params =
424 self.lower_generic_params_and_inner_items(GenericsOwner::Trait(trait_def), trait_def);
425 let auto = trait_def.auto_token().is_some();
426 let items = trait_def.item_list().map(|list| {
427 self.with_inherited_visibility(visibility, |this| {
428 list.items()
429 .filter_map(|item| {
430 let attrs = Attrs::new(&item, &this.hygiene);
431 this.collect_inner_items(item.syntax());
432 this.lower_assoc_item(&item).map(|item| {
433 this.add_attrs(ModItem::from(item).into(), attrs);
434 item
435 })
436 })
437 .collect()
438 })
439 });
440 let ast_id = self.source_ast_id_map.ast_id(trait_def);
441 let res = Trait {
442 name,
443 visibility,
444 generic_params,
445 auto,
446 items: items.unwrap_or_default(),
447 ast_id,
448 };
449 Some(id(self.data().traits.alloc(res)))
450 }
451
452 fn lower_impl(&mut self, impl_def: &ast::ImplDef) -> Option<FileItemTreeId<Impl>> {
453 let generic_params =
454 self.lower_generic_params_and_inner_items(GenericsOwner::Impl, impl_def);
455 let target_trait = impl_def.target_trait().map(|tr| self.lower_type_ref(&tr));
456 let target_type = self.lower_type_ref(&impl_def.target_type()?);
457 let is_negative = impl_def.excl_token().is_some();
458
459 // We cannot use `assoc_items()` here as that does not include macro calls.
460 let items = impl_def
461 .item_list()
462 .into_iter()
463 .flat_map(|it| it.items())
464 .filter_map(|item| {
465 self.collect_inner_items(item.syntax());
466 let assoc = self.lower_assoc_item(&item)?;
467 let attrs = Attrs::new(&item, &self.hygiene);
468 self.add_attrs(ModItem::from(assoc).into(), attrs);
469 Some(assoc)
470 })
471 .collect();
472 let ast_id = self.source_ast_id_map.ast_id(impl_def);
473 let res = Impl { generic_params, target_trait, target_type, is_negative, items, ast_id };
474 Some(id(self.data().impls.alloc(res)))
475 }
476
477 fn lower_use(&mut self, use_item: &ast::UseItem) -> Vec<FileItemTreeId<Import>> {
478 // FIXME: cfg_attr
479 let is_prelude = use_item.has_atom_attr("prelude_import");
480 let visibility = self.lower_visibility(use_item);
481 let ast_id = self.source_ast_id_map.ast_id(use_item);
482
483 // Every use item can expand to many `Import`s.
484 let mut imports = Vec::new();
485 let tree = self.tree.data_mut();
486 ModPath::expand_use_item(
487 InFile::new(self.file, use_item.clone()),
488 &self.hygiene,
489 |path, _tree, is_glob, alias| {
490 imports.push(id(tree.imports.alloc(Import {
491 path,
492 alias,
493 visibility,
494 is_glob,
495 is_prelude,
496 ast_id,
497 })));
498 },
499 );
500
501 imports
502 }
503
504 fn lower_extern_crate(
505 &mut self,
506 extern_crate: &ast::ExternCrateItem,
507 ) -> Option<FileItemTreeId<ExternCrate>> {
508 let path = ModPath::from_name_ref(&extern_crate.name_ref()?);
509 let alias = extern_crate.alias().map(|a| {
510 a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
511 });
512 let visibility = self.lower_visibility(extern_crate);
513 let ast_id = self.source_ast_id_map.ast_id(extern_crate);
514 // FIXME: cfg_attr
515 let is_macro_use = extern_crate.has_atom_attr("macro_use");
516
517 let res = ExternCrate { path, alias, visibility, is_macro_use, ast_id };
518 Some(id(self.data().extern_crates.alloc(res)))
519 }
520
521 fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
522 let name = m.name().map(|it| it.as_name());
523 let attrs = Attrs::new(m, &self.hygiene);
524 let path = ModPath::from_src(m.path()?, &self.hygiene)?;
525
526 let ast_id = self.source_ast_id_map.ast_id(m);
527
528 // FIXME: cfg_attr
529 let export_attr = attrs.by_key("macro_export");
530
531 let is_export = export_attr.exists();
532 let is_local_inner = if is_export {
533 export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
534 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
535 ident.text.contains("local_inner_macros")
536 }
537 _ => false,
538 })
539 } else {
540 false
541 };
542
543 let is_builtin = attrs.by_key("rustc_builtin_macro").exists();
544 let res = MacroCall { name, path, is_export, is_builtin, is_local_inner, ast_id };
545 Some(id(self.data().macro_calls.alloc(res)))
546 }
547
548 fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec<ModItem> {
549 block.extern_item_list().map_or(Vec::new(), |list| {
550 list.extern_items()
551 .filter_map(|item| {
552 self.collect_inner_items(item.syntax());
553 let attrs = Attrs::new(&item, &self.hygiene);
554 let id: ModItem = match item {
555 ast::ExternItem::FnDef(ast) => {
556 let func = self.lower_function(&ast)?;
557 self.data().functions[func.index].is_unsafe = true;
558 func.into()
559 }
560 ast::ExternItem::StaticDef(ast) => {
561 let statik = self.lower_static(&ast)?;
562 statik.into()
563 }
564 };
565 self.add_attrs(id.into(), attrs);
566 Some(id)
567 })
568 .collect()
569 })
570 }
571
572 /// Lowers generics defined on `node` and collects inner items defined within.
573 fn lower_generic_params_and_inner_items(
574 &mut self,
575 owner: GenericsOwner<'_>,
576 node: &impl ast::TypeParamsOwner,
577 ) -> GenericParamsId {
578 // Generics are part of item headers and may contain inner items we need to collect.
579 if let Some(params) = node.type_param_list() {
580 self.collect_inner_items(params.syntax());
581 }
582 if let Some(clause) = node.where_clause() {
583 self.collect_inner_items(clause.syntax());
584 }
585
586 self.lower_generic_params(owner, node)
587 }
588
589 fn lower_generic_params(
590 &mut self,
591 owner: GenericsOwner<'_>,
592 node: &impl ast::TypeParamsOwner,
593 ) -> GenericParamsId {
594 let mut sm = &mut ArenaMap::default();
595 let mut generics = GenericParams::default();
596 match owner {
597 GenericsOwner::Function(func) => {
598 generics.fill(&self.body_ctx, sm, node);
599 // lower `impl Trait` in arguments
600 for param in &*func.params {
601 generics.fill_implicit_impl_trait_args(param);
602 }
603 }
604 GenericsOwner::Struct
605 | GenericsOwner::Enum
606 | GenericsOwner::Union
607 | GenericsOwner::TypeAlias => {
608 generics.fill(&self.body_ctx, sm, node);
609 }
610 GenericsOwner::Trait(trait_def) => {
611 // traits get the Self type as an implicit first type parameter
612 let self_param_id = generics.types.alloc(TypeParamData {
613 name: Some(name![Self]),
614 default: None,
615 provenance: TypeParamProvenance::TraitSelf,
616 });
617 sm.insert(self_param_id, Either::Left(trait_def.clone()));
618 // add super traits as bounds on Self
619 // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
620 let self_param = TypeRef::Path(name![Self].into());
621 generics.fill_bounds(&self.body_ctx, trait_def, self_param);
622
623 generics.fill(&self.body_ctx, &mut sm, node);
624 }
625 GenericsOwner::Impl => {
626 // Note that we don't add `Self` here: in `impl`s, `Self` is not a
627 // type-parameter, but rather is a type-alias for impl's target
628 // type, so this is handled by the resolver.
629 generics.fill(&self.body_ctx, &mut sm, node);
630 }
631 }
632
633 self.data().generics.alloc(generics)
634 }
635
636 fn lower_type_bounds(&mut self, node: &impl ast::TypeBoundsOwner) -> Vec<TypeBound> {
637 match node.type_bound_list() {
638 Some(bound_list) => {
639 bound_list.bounds().map(|it| TypeBound::from_ast(&self.body_ctx, it)).collect()
640 }
641 None => Vec::new(),
642 }
643 }
644
645 fn lower_visibility(&mut self, item: &impl ast::VisibilityOwner) -> RawVisibilityId {
646 let vis = match self.forced_visibility {
647 Some(vis) => return vis,
648 None => RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene),
649 };
650
651 self.data().vis.alloc(vis)
652 }
653
654 fn lower_type_ref(&self, type_ref: &ast::TypeRef) -> TypeRef {
655 TypeRef::from_ast(&self.body_ctx, type_ref.clone())
656 }
657 fn lower_type_ref_opt(&self, type_ref: Option<ast::TypeRef>) -> TypeRef {
658 type_ref.map(|ty| self.lower_type_ref(&ty)).unwrap_or(TypeRef::Error)
659 }
660
661 /// Forces the visibility `vis` to be used for all items lowered during execution of `f`.
662 fn with_inherited_visibility<R>(
663 &mut self,
664 vis: RawVisibilityId,
665 f: impl FnOnce(&mut Self) -> R,
666 ) -> R {
667 let old = mem::replace(&mut self.forced_visibility, Some(vis));
668 let res = f(self);
669 self.forced_visibility = old;
670 res
671 }
672
673 fn next_field_idx(&self) -> Idx<Field> {
674 Idx::from_raw(RawId::from(
675 self.tree.data.as_ref().map_or(0, |data| data.fields.len() as u32),
676 ))
677 }
678 fn next_variant_idx(&self) -> Idx<Variant> {
679 Idx::from_raw(RawId::from(
680 self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32),
681 ))
682 }
683}
684
685fn desugar_future_path(orig: TypeRef) -> Path {
686 let path = path![core::future::Future];
687 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
688 let mut last = GenericArgs::empty();
689 let binding =
690 AssociatedTypeBinding { name: name![Output], type_ref: Some(orig), bounds: Vec::new() };
691 last.bindings.push(binding);
692 generic_args.push(Some(Arc::new(last)));
693
694 Path::from_known_path(path, generic_args)
695}
696
697enum GenericsOwner<'a> {
698 /// We need access to the partially-lowered `Function` for lowering `impl Trait` in argument
699 /// position.
700 Function(&'a Function),
701 Struct,
702 Enum,
703 Union,
704 /// The `TraitDef` is needed to fill the source map for the implicit `Self` parameter.
705 Trait(&'a ast::TraitDef),
706 TypeAlias,
707 Impl,
708}
diff --git a/crates/ra_hir_def/src/item_tree/tests.rs b/crates/ra_hir_def/src/item_tree/tests.rs
new file mode 100644
index 000000000..f26982985
--- /dev/null
+++ b/crates/ra_hir_def/src/item_tree/tests.rs
@@ -0,0 +1,439 @@
1use expect::{expect, Expect};
2use hir_expand::{db::AstDatabase, HirFileId, InFile};
3use ra_db::fixture::WithFixture;
4use ra_syntax::{ast, AstNode};
5use rustc_hash::FxHashSet;
6use std::sync::Arc;
7use stdx::format_to;
8
9use crate::{db::DefDatabase, test_db::TestDB};
10
11use super::{ItemTree, ModItem, ModKind};
12
13fn test_inner_items(ra_fixture: &str) {
14 let (db, file_id) = TestDB::with_single_file(ra_fixture);
15 let file_id = HirFileId::from(file_id);
16 let tree = db.item_tree(file_id);
17 let root = db.parse_or_expand(file_id).unwrap();
18 let ast_id_map = db.ast_id_map(file_id);
19
20 // Traverse the item tree and collect all module/impl/trait-level items as AST nodes.
21 let mut outer_items = FxHashSet::default();
22 let mut worklist = tree.top_level_items().to_vec();
23 while let Some(item) = worklist.pop() {
24 let node: ast::ModuleItem = match item {
25 ModItem::Import(it) => tree.source(&db, InFile::new(file_id, it)).into(),
26 ModItem::ExternCrate(it) => tree.source(&db, InFile::new(file_id, it)).into(),
27 ModItem::Function(it) => tree.source(&db, InFile::new(file_id, it)).into(),
28 ModItem::Struct(it) => tree.source(&db, InFile::new(file_id, it)).into(),
29 ModItem::Union(it) => tree.source(&db, InFile::new(file_id, it)).into(),
30 ModItem::Enum(it) => tree.source(&db, InFile::new(file_id, it)).into(),
31 ModItem::Const(it) => tree.source(&db, InFile::new(file_id, it)).into(),
32 ModItem::Static(it) => tree.source(&db, InFile::new(file_id, it)).into(),
33 ModItem::TypeAlias(it) => tree.source(&db, InFile::new(file_id, it)).into(),
34 ModItem::Mod(it) => {
35 if let ModKind::Inline { items } = &tree[it].kind {
36 worklist.extend(&**items);
37 }
38 tree.source(&db, InFile::new(file_id, it)).into()
39 }
40 ModItem::Trait(it) => {
41 worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item)));
42 tree.source(&db, InFile::new(file_id, it)).into()
43 }
44 ModItem::Impl(it) => {
45 worklist.extend(tree[it].items.iter().map(|item| ModItem::from(*item)));
46 tree.source(&db, InFile::new(file_id, it)).into()
47 }
48 ModItem::MacroCall(_) => continue,
49 };
50
51 outer_items.insert(node);
52 }
53
54 // Now descend the root node and check that all `ast::ModuleItem`s are either recorded above, or
55 // registered as inner items.
56 for item in root.descendants().skip(1).filter_map(ast::ModuleItem::cast) {
57 if outer_items.contains(&item) {
58 continue;
59 }
60
61 let ast_id = ast_id_map.ast_id(&item);
62 assert!(!tree.inner_items(ast_id).is_empty());
63 }
64}
65
66fn item_tree(ra_fixture: &str) -> Arc<ItemTree> {
67 let (db, file_id) = TestDB::with_single_file(ra_fixture);
68 db.item_tree(file_id.into())
69}
70
71fn print_item_tree(ra_fixture: &str) -> String {
72 let tree = item_tree(ra_fixture);
73 let mut out = String::new();
74
75 format_to!(out, "inner attrs: {:?}\n\n", tree.top_level_attrs());
76 format_to!(out, "top-level items:\n");
77 for item in tree.top_level_items() {
78 fmt_mod_item(&mut out, &tree, *item);
79 format_to!(out, "\n");
80 }
81
82 if !tree.inner_items.is_empty() {
83 format_to!(out, "\ninner items:\n\n");
84 for (ast_id, items) in &tree.inner_items {
85 format_to!(out, "for AST {:?}:\n", ast_id);
86 for inner in items {
87 fmt_mod_item(&mut out, &tree, *inner);
88 format_to!(out, "\n\n");
89 }
90 }
91 }
92
93 out
94}
95
96fn fmt_mod_item(out: &mut String, tree: &ItemTree, item: ModItem) {
97 let attrs = tree.attrs(item.into());
98 if !attrs.is_empty() {
99 format_to!(out, "#[{:?}]\n", attrs);
100 }
101
102 let mut children = String::new();
103 match item {
104 ModItem::ExternCrate(it) => {
105 format_to!(out, "{:?}", tree[it]);
106 }
107 ModItem::Import(it) => {
108 format_to!(out, "{:?}", tree[it]);
109 }
110 ModItem::Function(it) => {
111 format_to!(out, "{:?}", tree[it]);
112 }
113 ModItem::Struct(it) => {
114 format_to!(out, "{:?}", tree[it]);
115 }
116 ModItem::Union(it) => {
117 format_to!(out, "{:?}", tree[it]);
118 }
119 ModItem::Enum(it) => {
120 format_to!(out, "{:?}", tree[it]);
121 }
122 ModItem::Const(it) => {
123 format_to!(out, "{:?}", tree[it]);
124 }
125 ModItem::Static(it) => {
126 format_to!(out, "{:?}", tree[it]);
127 }
128 ModItem::Trait(it) => {
129 format_to!(out, "{:?}", tree[it]);
130 for item in &*tree[it].items {
131 fmt_mod_item(&mut children, tree, ModItem::from(*item));
132 format_to!(children, "\n");
133 }
134 }
135 ModItem::Impl(it) => {
136 format_to!(out, "{:?}", tree[it]);
137 for item in &*tree[it].items {
138 fmt_mod_item(&mut children, tree, ModItem::from(*item));
139 format_to!(children, "\n");
140 }
141 }
142 ModItem::TypeAlias(it) => {
143 format_to!(out, "{:?}", tree[it]);
144 }
145 ModItem::Mod(it) => {
146 format_to!(out, "{:?}", tree[it]);
147 match &tree[it].kind {
148 ModKind::Inline { items } => {
149 for item in &**items {
150 fmt_mod_item(&mut children, tree, *item);
151 format_to!(children, "\n");
152 }
153 }
154 ModKind::Outline {} => {}
155 }
156 }
157 ModItem::MacroCall(it) => {
158 format_to!(out, "{:?}", tree[it]);
159 }
160 }
161
162 for line in children.lines() {
163 format_to!(out, "\n> {}", line);
164 }
165}
166
167fn check(ra_fixture: &str, expect: Expect) {
168 let actual = print_item_tree(ra_fixture);
169 expect.assert_eq(&actual);
170}
171
172#[test]
173fn smoke() {
174 check(
175 r"
176 #![attr]
177
178 #[attr_on_use]
179 use {a, b::*};
180
181 #[ext_crate]
182 extern crate krate;
183
184 #[on_trait]
185 trait Tr<U> {
186 #[assoc_ty]
187 type AssocTy: Tr<()>;
188
189 #[assoc_const]
190 const CONST: u8;
191
192 #[assoc_method]
193 fn method(&self);
194
195 #[assoc_dfl_method]
196 fn dfl_method(&mut self) {}
197 }
198
199 #[struct0]
200 struct Struct0<T = ()>;
201
202 #[struct1]
203 struct Struct1<T>(#[struct1fld] u8);
204
205 #[struct2]
206 struct Struct2<T> {
207 #[struct2fld]
208 fld: (T, ),
209 }
210
211 #[en]
212 enum En {
213 #[enum_variant]
214 Variant {
215 #[enum_field]
216 field: u8,
217 },
218 }
219
220 #[un]
221 union Un {
222 #[union_fld]
223 fld: u16,
224 }
225 ",
226 expect![[r##"
227 inner attrs: Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr"))] }, input: None }]) }
228
229 top-level items:
230 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }]
231 Import { path: ModPath { kind: Plain, segments: [Name(Text("a"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: false, is_prelude: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UseItem>(0) }
232 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_on_use"))] }, input: None }]) }]
233 Import { path: ModPath { kind: Plain, segments: [Name(Text("b"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_glob: true, is_prelude: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UseItem>(0) }
234 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("ext_crate"))] }, input: None }]) }]
235 ExternCrate { path: ModPath { kind: Plain, segments: [Name(Text("krate"))] }, alias: None, visibility: RawVisibilityId("pub(self)"), is_macro_use: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ExternCrateItem>(1) }
236 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_trait"))] }, input: None }]) }]
237 Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [TypeAlias(Idx::<TypeAlias>(0)), Const(Idx::<Const>(0)), Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TraitDef>(2) }
238 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_ty"))] }, input: None }]) }]
239 > TypeAlias { name: Name(Text("AssocTy")), visibility: RawVisibilityId("pub(self)"), bounds: [Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Tr"))] }, generic_args: [Some(GenericArgs { args: [Type(Tuple([]))], has_self_type: false, bindings: [] })] })], generic_params: GenericParamsId(4294967295), type_ref: None, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TypeAliasDef>(8) }
240 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_const"))] }, input: None }]) }]
241 > Const { name: Some(Name(Text("CONST"))), visibility: RawVisibilityId("pub(self)"), type_ref: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("u8"))] }, generic_args: [None] }), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ConstDef>(9) }
242 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_method"))] }, input: None }]) }]
243 > Function { name: Name(Text("method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Shared)], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(10) }
244 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("assoc_dfl_method"))] }, input: None }]) }]
245 > Function { name: Name(Text("dfl_method")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: true, is_unsafe: false, params: [Reference(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Self"))] }, generic_args: [None] }), Mut)], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(11) }
246 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct0"))] }, input: None }]) }]
247 Struct { name: Name(Text("Struct0")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), fields: Unit, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(3), kind: Unit }
248 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct1"))] }, input: None }]) }]
249 Struct { name: Name(Text("Struct1")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(2), fields: Tuple(IdRange::<ra_hir_def::item_tree::Field>(0..1)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(4), kind: Tuple }
250 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("struct2"))] }, input: None }]) }]
251 Struct { name: Name(Text("Struct2")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(3), fields: Record(IdRange::<ra_hir_def::item_tree::Field>(1..2)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::StructDef>(5), kind: Record }
252 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("en"))] }, input: None }]) }]
253 Enum { name: Name(Text("En")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), variants: IdRange::<ra_hir_def::item_tree::Variant>(0..1), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::EnumDef>(6) }
254 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("un"))] }, input: None }]) }]
255 Union { name: Name(Text("Un")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), fields: Record(IdRange::<ra_hir_def::item_tree::Field>(3..4)), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::UnionDef>(7) }
256 "##]],
257 );
258}
259
260#[test]
261fn simple_inner_items() {
262 check(
263 r"
264 impl<T:A> D for Response<T> {
265 fn foo() {
266 end();
267 fn end<W: Write>() {
268 let _x: T = loop {};
269 }
270 }
271 }
272 ",
273 expect![[r#"
274 inner attrs: Attrs { entries: None }
275
276 top-level items:
277 Impl { generic_params: GenericParamsId(0), target_trait: Some(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("D"))] }, generic_args: [None] })), target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Response"))] }, generic_args: [Some(GenericArgs { args: [Type(Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("T"))] }, generic_args: [None] }))], has_self_type: false, bindings: [] })] }), is_negative: false, items: [Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
278 > Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
279
280 inner items:
281
282 for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(2):
283 Function { name: Name(Text("end")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(1), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
284
285 "#]],
286 );
287}
288
289#[test]
290fn extern_attrs() {
291 check(
292 r#"
293 #[block_attr]
294 extern "C" {
295 #[attr_a]
296 fn a() {}
297 #[attr_b]
298 fn b() {}
299 }
300 "#,
301 expect![[r##"
302 inner attrs: Attrs { entries: None }
303
304 top-level items:
305 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }]
306 Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: true, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
307 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }, Attr { path: ModPath { kind: Plain, segments: [Name(Text("block_attr"))] }, input: None }]) }]
308 Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: true, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
309 "##]],
310 );
311}
312
313#[test]
314fn trait_attrs() {
315 check(
316 r#"
317 #[trait_attr]
318 trait Tr {
319 #[attr_a]
320 fn a() {}
321 #[attr_b]
322 fn b() {}
323 }
324 "#,
325 expect![[r##"
326 inner attrs: Attrs { entries: None }
327
328 top-level items:
329 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("trait_attr"))] }, input: None }]) }]
330 Trait { name: Name(Text("Tr")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(0), auto: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::TraitDef>(0) }
331 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }]
332 > Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
333 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }]
334 > Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
335 "##]],
336 );
337}
338
339#[test]
340fn impl_attrs() {
341 check(
342 r#"
343 #[impl_attr]
344 impl Ty {
345 #[attr_a]
346 fn a() {}
347 #[attr_b]
348 fn b() {}
349 }
350 "#,
351 expect![[r##"
352 inner attrs: Attrs { entries: None }
353
354 top-level items:
355 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("impl_attr"))] }, input: None }]) }]
356 Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("Ty"))] }, generic_args: [None] }), is_negative: false, items: [Function(Idx::<Function>(0)), Function(Idx::<Function>(1))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
357 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_a"))] }, input: None }]) }]
358 > Function { name: Name(Text("a")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
359 > #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("attr_b"))] }, input: None }]) }]
360 > Function { name: Name(Text("b")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(2) }
361 "##]],
362 );
363}
364
365#[test]
366fn cursed_inner_items() {
367 test_inner_items(
368 r"
369 struct S<T: Trait = [u8; { fn f() {} 0 }]>(T);
370
371 enum En {
372 Var1 {
373 t: [(); { trait Inner {} 0 }],
374 },
375
376 Var2([u16; { enum Inner {} 0 }]),
377 }
378
379 type Ty = [En; { struct Inner; 0 }];
380
381 impl En {
382 fn assoc() {
383 trait InnerTrait<T = [u8; { fn f() {} }]> {}
384 struct InnerStruct<T = [u8; { fn f() {} }]> {}
385 impl<T = [u8; { fn f() {} }]> InnerTrait for InnerStruct {}
386 }
387 }
388
389 trait Tr<T = [u8; { fn f() {} }]> {
390 type AssocTy = [u8; { fn f() {} }];
391
392 const AssocConst: [u8; { fn f() {} }];
393 }
394 ",
395 );
396}
397
398#[test]
399fn inner_item_attrs() {
400 check(
401 r"
402 fn foo() {
403 #[on_inner]
404 fn inner() {}
405 }
406 ",
407 expect![[r##"
408 inner attrs: Attrs { entries: None }
409
410 top-level items:
411 Function { name: Name(Text("foo")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(0) }
412
413 inner items:
414
415 for AST FileAstId::<ra_syntax::ast::generated::nodes::ModuleItem>(1):
416 #[Attrs { entries: Some([Attr { path: ModPath { kind: Plain, segments: [Name(Text("on_inner"))] }, input: None }]) }]
417 Function { name: Name(Text("inner")), visibility: RawVisibilityId("pub(self)"), generic_params: GenericParamsId(4294967295), has_self_param: false, is_unsafe: false, params: [], is_varargs: false, ret_type: Tuple([]), ast_id: FileAstId::<ra_syntax::ast::generated::nodes::FnDef>(1) }
418
419 "##]],
420 );
421}
422
423#[test]
424fn assoc_item_macros() {
425 check(
426 r"
427 impl S {
428 items!();
429 }
430 ",
431 expect![[r#"
432 inner attrs: Attrs { entries: None }
433
434 top-level items:
435 Impl { generic_params: GenericParamsId(4294967295), target_trait: None, target_type: Path(Path { type_anchor: None, mod_path: ModPath { kind: Plain, segments: [Name(Text("S"))] }, generic_args: [None] }), is_negative: false, items: [MacroCall(Idx::<MacroCall>(0))], ast_id: FileAstId::<ra_syntax::ast::generated::nodes::ImplDef>(0) }
436 > MacroCall { name: None, path: ModPath { kind: Plain, segments: [Name(Text("items"))] }, is_export: false, is_local_inner: false, is_builtin: false, ast_id: FileAstId::<ra_syntax::ast::generated::nodes::MacroCall>(1) }
437 "#]],
438 );
439}
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index 5325a2760..87000fe98 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -25,6 +25,8 @@ pub mod item_scope;
25pub mod dyn_map; 25pub mod dyn_map;
26pub mod keys; 26pub mod keys;
27 27
28pub mod item_tree;
29
28pub mod adt; 30pub mod adt;
29pub mod data; 31pub mod data;
30pub mod generics; 32pub mod generics;
@@ -43,11 +45,12 @@ pub mod child_by_source;
43 45
44pub mod visibility; 46pub mod visibility;
45pub mod find_path; 47pub mod find_path;
48pub mod import_map;
46 49
47#[cfg(test)] 50#[cfg(test)]
48mod test_db; 51mod test_db;
49 52
50use std::hash::Hash; 53use std::hash::{Hash, Hasher};
51 54
52use hir_expand::{ 55use hir_expand::{
53 ast_id_map::FileAstId, eager::expand_eager_macro, hygiene::Hygiene, AstId, HirFileId, InFile, 56 ast_id_map::FileAstId, eager::expand_eager_macro, hygiene::Hygiene, AstId, HirFileId, InFile,
@@ -55,10 +58,14 @@ use hir_expand::{
55}; 58};
56use ra_arena::Idx; 59use ra_arena::Idx;
57use ra_db::{impl_intern_key, salsa, CrateId}; 60use ra_db::{impl_intern_key, salsa, CrateId};
58use ra_syntax::{ast, AstNode}; 61use ra_syntax::ast;
59 62
60use crate::body::Expander;
61use crate::builtin_type::BuiltinType; 63use crate::builtin_type::BuiltinType;
64use item_tree::{
65 Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
66 TypeAlias, Union,
67};
68use stdx::impl_from;
62 69
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
64pub struct ModuleId { 71pub struct ModuleId {
@@ -69,16 +76,62 @@ pub struct ModuleId {
69/// An ID of a module, **local** to a specific crate 76/// An ID of a module, **local** to a specific crate
70pub type LocalModuleId = Idx<nameres::ModuleData>; 77pub type LocalModuleId = Idx<nameres::ModuleData>;
71 78
72#[derive(Debug, Clone, PartialEq, Eq, Hash)] 79#[derive(Debug)]
73pub struct ItemLoc<N: AstNode> { 80pub struct ItemLoc<N: ItemTreeNode> {
74 pub container: ContainerId, 81 pub container: ContainerId,
75 pub ast_id: AstId<N>, 82 pub id: ItemTreeId<N>,
83}
84
85impl<N: ItemTreeNode> Clone for ItemLoc<N> {
86 fn clone(&self) -> Self {
87 Self { container: self.container, id: self.id }
88 }
89}
90
91impl<N: ItemTreeNode> Copy for ItemLoc<N> {}
92
93impl<N: ItemTreeNode> PartialEq for ItemLoc<N> {
94 fn eq(&self, other: &Self) -> bool {
95 self.container == other.container && self.id == other.id
96 }
97}
98
99impl<N: ItemTreeNode> Eq for ItemLoc<N> {}
100
101impl<N: ItemTreeNode> Hash for ItemLoc<N> {
102 fn hash<H: Hasher>(&self, state: &mut H) {
103 self.container.hash(state);
104 self.id.hash(state);
105 }
76} 106}
77 107
78#[derive(Debug, Clone, PartialEq, Eq, Hash)] 108#[derive(Debug)]
79pub struct AssocItemLoc<N: AstNode> { 109pub struct AssocItemLoc<N: ItemTreeNode> {
80 pub container: AssocContainerId, 110 pub container: AssocContainerId,
81 pub ast_id: AstId<N>, 111 pub id: ItemTreeId<N>,
112}
113
114impl<N: ItemTreeNode> Clone for AssocItemLoc<N> {
115 fn clone(&self) -> Self {
116 Self { container: self.container, id: self.id }
117 }
118}
119
120impl<N: ItemTreeNode> Copy for AssocItemLoc<N> {}
121
122impl<N: ItemTreeNode> PartialEq for AssocItemLoc<N> {
123 fn eq(&self, other: &Self) -> bool {
124 self.container == other.container && self.id == other.id
125 }
126}
127
128impl<N: ItemTreeNode> Eq for AssocItemLoc<N> {}
129
130impl<N: ItemTreeNode> Hash for AssocItemLoc<N> {
131 fn hash<H: Hasher>(&self, state: &mut H) {
132 self.container.hash(state);
133 self.id.hash(state);
134 }
82} 135}
83 136
84macro_rules! impl_intern { 137macro_rules! impl_intern {
@@ -103,22 +156,22 @@ macro_rules! impl_intern {
103 156
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
105pub struct FunctionId(salsa::InternId); 158pub struct FunctionId(salsa::InternId);
106type FunctionLoc = AssocItemLoc<ast::FnDef>; 159type FunctionLoc = AssocItemLoc<Function>;
107impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function); 160impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
108 161
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 162#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
110pub struct StructId(salsa::InternId); 163pub struct StructId(salsa::InternId);
111type StructLoc = ItemLoc<ast::StructDef>; 164type StructLoc = ItemLoc<Struct>;
112impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct); 165impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
113 166
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 167#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
115pub struct UnionId(salsa::InternId); 168pub struct UnionId(salsa::InternId);
116pub type UnionLoc = ItemLoc<ast::UnionDef>; 169pub type UnionLoc = ItemLoc<Union>;
117impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union); 170impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
118 171
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 172#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
120pub struct EnumId(salsa::InternId); 173pub struct EnumId(salsa::InternId);
121pub type EnumLoc = ItemLoc<ast::EnumDef>; 174pub type EnumLoc = ItemLoc<Enum>;
122impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum); 175impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
123 176
124// FIXME: rename to `VariantId`, only enums can ave variants 177// FIXME: rename to `VariantId`, only enums can ave variants
@@ -140,27 +193,27 @@ pub type LocalFieldId = Idx<adt::FieldData>;
140 193
141#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 194#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
142pub struct ConstId(salsa::InternId); 195pub struct ConstId(salsa::InternId);
143type ConstLoc = AssocItemLoc<ast::ConstDef>; 196type ConstLoc = AssocItemLoc<Const>;
144impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const); 197impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
145 198
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 199#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
147pub struct StaticId(salsa::InternId); 200pub struct StaticId(salsa::InternId);
148pub type StaticLoc = ItemLoc<ast::StaticDef>; 201pub type StaticLoc = ItemLoc<Static>;
149impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static); 202impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
150 203
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 204#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
152pub struct TraitId(salsa::InternId); 205pub struct TraitId(salsa::InternId);
153pub type TraitLoc = ItemLoc<ast::TraitDef>; 206pub type TraitLoc = ItemLoc<Trait>;
154impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait); 207impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
155 208
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 209#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
157pub struct TypeAliasId(salsa::InternId); 210pub struct TypeAliasId(salsa::InternId);
158type TypeAliasLoc = AssocItemLoc<ast::TypeAliasDef>; 211type TypeAliasLoc = AssocItemLoc<TypeAlias>;
159impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias); 212impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
160 213
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 214#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
162pub struct ImplId(salsa::InternId); 215pub struct ImplId(salsa::InternId);
163type ImplLoc = ItemLoc<ast::ImplDef>; 216type ImplLoc = ItemLoc<Impl>;
164impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); 217impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
165 218
166#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 219#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -171,25 +224,6 @@ pub struct TypeParamId {
171 224
172pub type LocalTypeParamId = Idx<generics::TypeParamData>; 225pub type LocalTypeParamId = Idx<generics::TypeParamData>;
173 226
174macro_rules! impl_froms {
175 ($e:ident: $($v:ident $(($($sv:ident),*))?),*) => {
176 $(
177 impl From<$v> for $e {
178 fn from(it: $v) -> $e {
179 $e::$v(it)
180 }
181 }
182 $($(
183 impl From<$sv> for $e {
184 fn from(it: $sv) -> $e {
185 $e::$v($v::$sv(it))
186 }
187 }
188 )*)?
189 )*
190 }
191}
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 227#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
194pub enum ContainerId { 228pub enum ContainerId {
195 ModuleId(ModuleId), 229 ModuleId(ModuleId),
@@ -202,16 +236,16 @@ pub enum AssocContainerId {
202 ImplId(ImplId), 236 ImplId(ImplId),
203 TraitId(TraitId), 237 TraitId(TraitId),
204} 238}
205impl_froms!(AssocContainerId: ContainerId); 239impl_from!(ContainerId for AssocContainerId);
206 240
207/// A Data Type 241/// A Data Type
208#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 242#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
209pub enum AdtId { 243pub enum AdtId {
210 StructId(StructId), 244 StructId(StructId),
211 UnionId(UnionId), 245 UnionId(UnionId),
212 EnumId(EnumId), 246 EnumId(EnumId),
213} 247}
214impl_froms!(AdtId: StructId, UnionId, EnumId); 248impl_from!(StructId, UnionId, EnumId for AdtId);
215 249
216/// The defs which can be visible in the module. 250/// The defs which can be visible in the module.
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 251#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -227,8 +261,8 @@ pub enum ModuleDefId {
227 TypeAliasId(TypeAliasId), 261 TypeAliasId(TypeAliasId),
228 BuiltinType(BuiltinType), 262 BuiltinType(BuiltinType),
229} 263}
230impl_froms!( 264impl_from!(
231 ModuleDefId: ModuleId, 265 ModuleId,
232 FunctionId, 266 FunctionId,
233 AdtId(StructId, EnumId, UnionId), 267 AdtId(StructId, EnumId, UnionId),
234 EnumVariantId, 268 EnumVariantId,
@@ -237,6 +271,7 @@ impl_froms!(
237 TraitId, 271 TraitId,
238 TypeAliasId, 272 TypeAliasId,
239 BuiltinType 273 BuiltinType
274 for ModuleDefId
240); 275);
241 276
242/// The defs which have a body. 277/// The defs which have a body.
@@ -247,7 +282,7 @@ pub enum DefWithBodyId {
247 ConstId(ConstId), 282 ConstId(ConstId),
248} 283}
249 284
250impl_froms!(DefWithBodyId: FunctionId, ConstId, StaticId); 285impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId);
251 286
252#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 287#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
253pub enum AssocItemId { 288pub enum AssocItemId {
@@ -259,7 +294,7 @@ pub enum AssocItemId {
259// sure that you can only turn actual assoc items into AssocItemIds. This would 294// sure that you can only turn actual assoc items into AssocItemIds. This would
260// require not implementing From, and instead having some checked way of 295// require not implementing From, and instead having some checked way of
261// casting them, and somehow making the constructors private, which would be annoying. 296// casting them, and somehow making the constructors private, which would be annoying.
262impl_froms!(AssocItemId: FunctionId, ConstId, TypeAliasId); 297impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId);
263 298
264#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] 299#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
265pub enum GenericDefId { 300pub enum GenericDefId {
@@ -274,14 +309,15 @@ pub enum GenericDefId {
274 // consts can have type parameters from their parents (i.e. associated consts of traits) 309 // consts can have type parameters from their parents (i.e. associated consts of traits)
275 ConstId(ConstId), 310 ConstId(ConstId),
276} 311}
277impl_froms!( 312impl_from!(
278 GenericDefId: FunctionId, 313 FunctionId,
279 AdtId(StructId, EnumId, UnionId), 314 AdtId(StructId, EnumId, UnionId),
280 TraitId, 315 TraitId,
281 TypeAliasId, 316 TypeAliasId,
282 ImplId, 317 ImplId,
283 EnumVariantId, 318 EnumVariantId,
284 ConstId 319 ConstId
320 for GenericDefId
285); 321);
286 322
287impl From<AssocItemId> for GenericDefId { 323impl From<AssocItemId> for GenericDefId {
@@ -309,8 +345,8 @@ pub enum AttrDefId {
309 ImplId(ImplId), 345 ImplId(ImplId),
310} 346}
311 347
312impl_froms!( 348impl_from!(
313 AttrDefId: ModuleId, 349 ModuleId,
314 FieldId, 350 FieldId,
315 AdtId(StructId, EnumId, UnionId), 351 AdtId(StructId, EnumId, UnionId),
316 EnumVariantId, 352 EnumVariantId,
@@ -321,6 +357,7 @@ impl_froms!(
321 TypeAliasId, 357 TypeAliasId,
322 MacroDefId, 358 MacroDefId,
323 ImplId 359 ImplId
360 for AttrDefId
324); 361);
325 362
326#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 363#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -329,7 +366,7 @@ pub enum VariantId {
329 StructId(StructId), 366 StructId(StructId),
330 UnionId(UnionId), 367 UnionId(UnionId),
331} 368}
332impl_froms!(VariantId: EnumVariantId, StructId, UnionId); 369impl_from!(EnumVariantId, StructId, UnionId for VariantId);
333 370
334trait Intern { 371trait Intern {
335 type ID; 372 type ID;
@@ -364,7 +401,7 @@ impl HasModule for AssocContainerId {
364 } 401 }
365} 402}
366 403
367impl<N: AstNode> HasModule for AssocItemLoc<N> { 404impl<N: ItemTreeNode> HasModule for AssocItemLoc<N> {
368 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { 405 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
369 self.container.module(db) 406 self.container.module(db)
370 } 407 }
@@ -391,6 +428,16 @@ impl HasModule for DefWithBodyId {
391 } 428 }
392} 429}
393 430
431impl DefWithBodyId {
432 pub fn as_mod_item(self, db: &dyn db::DefDatabase) -> ModItem {
433 match self {
434 DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
435 DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
436 DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
437 }
438 }
439}
440
394impl HasModule for GenericDefId { 441impl HasModule for GenericDefId {
395 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { 442 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
396 match self { 443 match self {
@@ -416,6 +463,7 @@ pub trait AsMacroCall {
416 fn as_call_id( 463 fn as_call_id(
417 &self, 464 &self,
418 db: &dyn db::DefDatabase, 465 db: &dyn db::DefDatabase,
466 krate: CrateId,
419 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 467 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
420 ) -> Option<MacroCallId>; 468 ) -> Option<MacroCallId>;
421} 469}
@@ -424,13 +472,14 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
424 fn as_call_id( 472 fn as_call_id(
425 &self, 473 &self,
426 db: &dyn db::DefDatabase, 474 db: &dyn db::DefDatabase,
475 krate: CrateId,
427 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 476 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
428 ) -> Option<MacroCallId> { 477 ) -> Option<MacroCallId> {
429 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); 478 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
430 let h = Hygiene::new(db.upcast(), self.file_id); 479 let h = Hygiene::new(db.upcast(), self.file_id);
431 let path = path::ModPath::from_src(self.value.path()?, &h)?; 480 let path = path::ModPath::from_src(self.value.path()?, &h)?;
432 481
433 AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, resolver) 482 AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver)
434 } 483 }
435} 484}
436 485
@@ -451,6 +500,7 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
451 fn as_call_id( 500 fn as_call_id(
452 &self, 501 &self,
453 db: &dyn db::DefDatabase, 502 db: &dyn db::DefDatabase,
503 krate: CrateId,
454 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 504 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
455 ) -> Option<MacroCallId> { 505 ) -> Option<MacroCallId> {
456 let def: MacroDefId = resolver(self.path.clone())?; 506 let def: MacroDefId = resolver(self.path.clone())?;
@@ -460,13 +510,13 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
460 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); 510 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id);
461 511
462 Some( 512 Some(
463 expand_eager_macro(db.upcast(), macro_call, def, &|path: ast::Path| { 513 expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| {
464 resolver(path::ModPath::from_src(path, &hygiene)?) 514 resolver(path::ModPath::from_src(path, &hygiene)?)
465 })? 515 })?
466 .into(), 516 .into(),
467 ) 517 )
468 } else { 518 } else {
469 Some(def.as_lazy_macro(db.upcast(), MacroCallKind::FnLike(self.ast_id)).into()) 519 Some(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(self.ast_id)).into())
470 } 520 }
471 } 521 }
472} 522}
@@ -475,12 +525,14 @@ impl AsMacroCall for AstIdWithPath<ast::ModuleItem> {
475 fn as_call_id( 525 fn as_call_id(
476 &self, 526 &self,
477 db: &dyn db::DefDatabase, 527 db: &dyn db::DefDatabase,
528 krate: CrateId,
478 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 529 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
479 ) -> Option<MacroCallId> { 530 ) -> Option<MacroCallId> {
480 let def = resolver(self.path.clone())?; 531 let def = resolver(self.path.clone())?;
481 Some( 532 Some(
482 def.as_lazy_macro( 533 def.as_lazy_macro(
483 db.upcast(), 534 db.upcast(),
535 krate,
484 MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()), 536 MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()),
485 ) 537 )
486 .into(), 538 .into(),
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index f279c2ad4..b279bdeef 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -47,7 +47,6 @@
47//! path and, upon success, we run macro expansion and "collect module" phase on 47//! path and, upon success, we run macro expansion and "collect module" phase on
48//! the result 48//! the result
49 49
50pub(crate) mod raw;
51mod collector; 50mod collector;
52mod mod_resolution; 51mod mod_resolution;
53mod path_resolution; 52mod path_resolution;
@@ -104,6 +103,7 @@ pub enum ModuleOrigin {
104 }, 103 },
105 /// Note that non-inline modules, by definition, live inside non-macro file. 104 /// Note that non-inline modules, by definition, live inside non-macro file.
106 File { 105 File {
106 is_mod_rs: bool,
107 declaration: AstId<ast::Module>, 107 declaration: AstId<ast::Module>,
108 definition: FileId, 108 definition: FileId,
109 }, 109 },
@@ -119,13 +119,6 @@ impl Default for ModuleOrigin {
119} 119}
120 120
121impl ModuleOrigin { 121impl ModuleOrigin {
122 pub(crate) fn not_sure_file(file: Option<FileId>, declaration: AstId<ast::Module>) -> Self {
123 match file {
124 None => ModuleOrigin::Inline { definition: declaration },
125 Some(definition) => ModuleOrigin::File { declaration, definition },
126 }
127 }
128
129 fn declaration(&self) -> Option<AstId<ast::Module>> { 122 fn declaration(&self) -> Option<AstId<ast::Module>> {
130 match self { 123 match self {
131 ModuleOrigin::File { declaration: module, .. } 124 ModuleOrigin::File { declaration: module, .. }
@@ -296,7 +289,6 @@ pub enum ModuleSource {
296 289
297mod diagnostics { 290mod diagnostics {
298 use hir_expand::diagnostics::DiagnosticSink; 291 use hir_expand::diagnostics::DiagnosticSink;
299 use ra_db::RelativePathBuf;
300 use ra_syntax::{ast, AstPtr}; 292 use ra_syntax::{ast, AstPtr};
301 293
302 use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; 294 use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId};
@@ -306,7 +298,7 @@ mod diagnostics {
306 UnresolvedModule { 298 UnresolvedModule {
307 module: LocalModuleId, 299 module: LocalModuleId,
308 declaration: AstId<ast::Module>, 300 declaration: AstId<ast::Module>,
309 candidate: RelativePathBuf, 301 candidate: String,
310 }, 302 },
311 } 303 }
312 304
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 353a31ad4..d85a86c0a 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -4,6 +4,7 @@
4//! resolves imports and expands macros. 4//! resolves imports and expands macros.
5 5
6use hir_expand::{ 6use hir_expand::{
7 ast_id_map::FileAstId,
7 builtin_derive::find_builtin_derive, 8 builtin_derive::find_builtin_derive,
8 builtin_macro::find_builtin_macro, 9 builtin_macro::find_builtin_macro,
9 name::{name, AsName, Name}, 10 name::{name, AsName, Name},
@@ -19,25 +20,33 @@ use test_utils::mark;
19use crate::{ 20use crate::{
20 attr::Attrs, 21 attr::Attrs,
21 db::DefDatabase, 22 db::DefDatabase,
23 item_scope::{ImportType, PerNsGlobImports},
24 item_tree::{
25 self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind,
26 },
22 nameres::{ 27 nameres::{
23 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, 28 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
24 raw, BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, 29 BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
25 }, 30 },
26 path::{ImportAlias, ModPath, PathKind}, 31 path::{ImportAlias, ModPath, PathKind},
27 per_ns::PerNs, 32 per_ns::PerNs,
28 visibility::Visibility, 33 visibility::{RawVisibility, Visibility},
29 AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, 34 AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId,
30 FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, 35 FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc,
31 TraitLoc, TypeAliasLoc, UnionLoc, 36 TraitLoc, TypeAliasLoc, UnionLoc,
32}; 37};
33 38
39const GLOB_RECURSION_LIMIT: usize = 100;
40const EXPANSION_DEPTH_LIMIT: usize = 128;
41const FIXED_POINT_LIMIT: usize = 8192;
42
34pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { 43pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap {
35 let crate_graph = db.crate_graph(); 44 let crate_graph = db.crate_graph();
36 45
37 // populate external prelude 46 // populate external prelude
38 for dep in &crate_graph[def_map.krate].dependencies { 47 for dep in &crate_graph[def_map.krate].dependencies {
39 let dep_def_map = db.crate_def_map(dep.crate_id);
40 log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); 48 log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
49 let dep_def_map = db.crate_def_map(dep.crate_id);
41 def_map.extern_prelude.insert( 50 def_map.extern_prelude.insert(
42 dep.as_name(), 51 dep.as_name(),
43 ModuleId { krate: dep.crate_id, local_id: dep_def_map.root }.into(), 52 ModuleId { krate: dep.crate_id, local_id: dep_def_map.root }.into(),
@@ -76,6 +85,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr
76 mod_dirs: FxHashMap::default(), 85 mod_dirs: FxHashMap::default(),
77 cfg_options, 86 cfg_options,
78 proc_macros, 87 proc_macros,
88 from_glob_import: Default::default(),
79 }; 89 };
80 collector.collect(); 90 collector.collect();
81 collector.finish() 91 collector.finish()
@@ -102,10 +112,50 @@ impl PartialResolvedImport {
102} 112}
103 113
104#[derive(Clone, Debug, Eq, PartialEq)] 114#[derive(Clone, Debug, Eq, PartialEq)]
115struct Import {
116 pub path: ModPath,
117 pub alias: Option<ImportAlias>,
118 pub visibility: RawVisibility,
119 pub is_glob: bool,
120 pub is_prelude: bool,
121 pub is_extern_crate: bool,
122 pub is_macro_use: bool,
123}
124
125impl Import {
126 fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self {
127 let it = &tree[id];
128 let visibility = &tree[it.visibility];
129 Self {
130 path: it.path.clone(),
131 alias: it.alias.clone(),
132 visibility: visibility.clone(),
133 is_glob: it.is_glob,
134 is_prelude: it.is_prelude,
135 is_extern_crate: false,
136 is_macro_use: false,
137 }
138 }
139
140 fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self {
141 let it = &tree[id];
142 let visibility = &tree[it.visibility];
143 Self {
144 path: it.path.clone(),
145 alias: it.alias.clone(),
146 visibility: visibility.clone(),
147 is_glob: false,
148 is_prelude: false,
149 is_extern_crate: true,
150 is_macro_use: it.is_macro_use,
151 }
152 }
153}
154
155#[derive(Clone, Debug, Eq, PartialEq)]
105struct ImportDirective { 156struct ImportDirective {
106 module_id: LocalModuleId, 157 module_id: LocalModuleId,
107 import_id: raw::Import, 158 import: Import,
108 import: raw::ImportData,
109 status: PartialResolvedImport, 159 status: PartialResolvedImport,
110} 160}
111 161
@@ -123,6 +173,13 @@ struct DeriveDirective {
123 ast_id: AstIdWithPath<ast::ModuleItem>, 173 ast_id: AstIdWithPath<ast::ModuleItem>,
124} 174}
125 175
176struct DefData<'a> {
177 id: ModuleDefId,
178 name: &'a Name,
179 visibility: &'a RawVisibility,
180 has_constructor: bool,
181}
182
126/// Walks the tree of module recursively 183/// Walks the tree of module recursively
127struct DefCollector<'a> { 184struct DefCollector<'a> {
128 db: &'a dyn DefDatabase, 185 db: &'a dyn DefDatabase,
@@ -135,12 +192,13 @@ struct DefCollector<'a> {
135 mod_dirs: FxHashMap<LocalModuleId, ModDir>, 192 mod_dirs: FxHashMap<LocalModuleId, ModDir>,
136 cfg_options: &'a CfgOptions, 193 cfg_options: &'a CfgOptions,
137 proc_macros: Vec<(Name, ProcMacroExpander)>, 194 proc_macros: Vec<(Name, ProcMacroExpander)>,
195 from_glob_import: PerNsGlobImports,
138} 196}
139 197
140impl DefCollector<'_> { 198impl DefCollector<'_> {
141 fn collect(&mut self) { 199 fn collect(&mut self) {
142 let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; 200 let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
143 let raw_items = self.db.raw_items(file_id.into()); 201 let item_tree = self.db.item_tree(file_id.into());
144 let module_id = self.def_map.root; 202 let module_id = self.def_map.root;
145 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; 203 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
146 ModCollector { 204 ModCollector {
@@ -148,10 +206,10 @@ impl DefCollector<'_> {
148 macro_depth: 0, 206 macro_depth: 0,
149 module_id, 207 module_id,
150 file_id: file_id.into(), 208 file_id: file_id.into(),
151 raw_items: &raw_items, 209 item_tree: &item_tree,
152 mod_dir: ModDir::root(), 210 mod_dir: ModDir::root(),
153 } 211 }
154 .collect(raw_items.items()); 212 .collect(item_tree.top_level_items());
155 213
156 // main name resolution fixed-point loop. 214 // main name resolution fixed-point loop.
157 let mut i = 0; 215 let mut i = 0;
@@ -163,7 +221,7 @@ impl DefCollector<'_> {
163 ReachedFixedPoint::Yes => break, 221 ReachedFixedPoint::Yes => break,
164 ReachedFixedPoint::No => i += 1, 222 ReachedFixedPoint::No => i += 1,
165 } 223 }
166 if i == 10000 { 224 if i == FIXED_POINT_LIMIT {
167 log::error!("name resolution is stuck"); 225 log::error!("name resolution is stuck");
168 break; 226 break;
169 } 227 }
@@ -254,6 +312,7 @@ impl DefCollector<'_> {
254 self.def_map.root, 312 self.def_map.root,
255 &[(name, PerNs::macros(macro_, Visibility::Public))], 313 &[(name, PerNs::macros(macro_, Visibility::Public))],
256 Visibility::Public, 314 Visibility::Public,
315 ImportType::Named,
257 ); 316 );
258 } 317 }
259 } 318 }
@@ -279,6 +338,7 @@ impl DefCollector<'_> {
279 self.def_map.root, 338 self.def_map.root,
280 &[(name, PerNs::macros(macro_, Visibility::Public))], 339 &[(name, PerNs::macros(macro_, Visibility::Public))],
281 Visibility::Public, 340 Visibility::Public,
341 ImportType::Named,
282 ); 342 );
283 } 343 }
284 344
@@ -286,7 +346,7 @@ impl DefCollector<'_> {
286 fn import_macros_from_extern_crate( 346 fn import_macros_from_extern_crate(
287 &mut self, 347 &mut self,
288 current_module_id: LocalModuleId, 348 current_module_id: LocalModuleId,
289 import: &raw::ImportData, 349 import: &item_tree::ExternCrate,
290 ) { 350 ) {
291 log::debug!( 351 log::debug!(
292 "importing macros from extern crate: {:?} ({:?})", 352 "importing macros from extern crate: {:?} ({:?})",
@@ -332,7 +392,6 @@ impl DefCollector<'_> {
332 let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); 392 let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
333 for mut directive in imports { 393 for mut directive in imports {
334 directive.status = self.resolve_import(directive.module_id, &directive.import); 394 directive.status = self.resolve_import(directive.module_id, &directive.import);
335
336 match directive.status { 395 match directive.status {
337 PartialResolvedImport::Indeterminate(_) => { 396 PartialResolvedImport::Indeterminate(_) => {
338 self.record_resolved_import(&directive); 397 self.record_resolved_import(&directive);
@@ -352,11 +411,7 @@ impl DefCollector<'_> {
352 } 411 }
353 } 412 }
354 413
355 fn resolve_import( 414 fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
356 &self,
357 module_id: LocalModuleId,
358 import: &raw::ImportData,
359 ) -> PartialResolvedImport {
360 log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); 415 log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
361 if import.is_extern_crate { 416 if import.is_extern_crate {
362 let res = self.def_map.resolve_name_in_extern_prelude( 417 let res = self.def_map.resolve_name_in_extern_prelude(
@@ -430,7 +485,7 @@ impl DefCollector<'_> {
430 .filter(|(_, res)| !res.is_none()) 485 .filter(|(_, res)| !res.is_none())
431 .collect::<Vec<_>>(); 486 .collect::<Vec<_>>();
432 487
433 self.update(module_id, &items, vis); 488 self.update(module_id, &items, vis, ImportType::Glob);
434 } else { 489 } else {
435 // glob import from same crate => we do an initial 490 // glob import from same crate => we do an initial
436 // import, and then need to propagate any further 491 // import, and then need to propagate any further
@@ -452,7 +507,7 @@ impl DefCollector<'_> {
452 .filter(|(_, res)| !res.is_none()) 507 .filter(|(_, res)| !res.is_none())
453 .collect::<Vec<_>>(); 508 .collect::<Vec<_>>();
454 509
455 self.update(module_id, &items, vis); 510 self.update(module_id, &items, vis, ImportType::Glob);
456 // record the glob import in case we add further items 511 // record the glob import in case we add further items
457 let glob = self.glob_imports.entry(m.local_id).or_default(); 512 let glob = self.glob_imports.entry(m.local_id).or_default();
458 if !glob.iter().any(|(mid, _)| *mid == module_id) { 513 if !glob.iter().any(|(mid, _)| *mid == module_id) {
@@ -482,7 +537,7 @@ impl DefCollector<'_> {
482 (name, res) 537 (name, res)
483 }) 538 })
484 .collect::<Vec<_>>(); 539 .collect::<Vec<_>>();
485 self.update(module_id, &resolutions, vis); 540 self.update(module_id, &resolutions, vis, ImportType::Glob);
486 } 541 }
487 Some(d) => { 542 Some(d) => {
488 log::debug!("glob import {:?} from non-module/enum {:?}", import, d); 543 log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
@@ -508,15 +563,22 @@ impl DefCollector<'_> {
508 } 563 }
509 } 564 }
510 565
511 self.update(module_id, &[(name, def)], vis); 566 self.update(module_id, &[(name, def)], vis, ImportType::Named);
512 } 567 }
513 None => mark::hit!(bogus_paths), 568 None => mark::hit!(bogus_paths),
514 } 569 }
515 } 570 }
516 } 571 }
517 572
518 fn update(&mut self, module_id: LocalModuleId, resolutions: &[(Name, PerNs)], vis: Visibility) { 573 fn update(
519 self.update_recursive(module_id, resolutions, vis, 0) 574 &mut self,
575 module_id: LocalModuleId,
576 resolutions: &[(Name, PerNs)],
577 vis: Visibility,
578 import_type: ImportType,
579 ) {
580 self.db.check_canceled();
581 self.update_recursive(module_id, resolutions, vis, import_type, 0)
520 } 582 }
521 583
522 fn update_recursive( 584 fn update_recursive(
@@ -526,16 +588,22 @@ impl DefCollector<'_> {
526 // All resolutions are imported with this visibility; the visibilies in 588 // All resolutions are imported with this visibility; the visibilies in
527 // the `PerNs` values are ignored and overwritten 589 // the `PerNs` values are ignored and overwritten
528 vis: Visibility, 590 vis: Visibility,
591 import_type: ImportType,
529 depth: usize, 592 depth: usize,
530 ) { 593 ) {
531 if depth > 100 { 594 if depth > GLOB_RECURSION_LIMIT {
532 // prevent stack overflows (but this shouldn't be possible) 595 // prevent stack overflows (but this shouldn't be possible)
533 panic!("infinite recursion in glob imports!"); 596 panic!("infinite recursion in glob imports!");
534 } 597 }
535 let scope = &mut self.def_map.modules[module_id].scope; 598 let scope = &mut self.def_map.modules[module_id].scope;
536 let mut changed = false; 599 let mut changed = false;
537 for (name, res) in resolutions { 600 for (name, res) in resolutions {
538 changed |= scope.push_res(name.clone(), res.with_visibility(vis)); 601 changed |= scope.push_res_with_import(
602 &mut self.from_glob_import,
603 (module_id, name.clone()),
604 res.with_visibility(vis),
605 import_type,
606 );
539 } 607 }
540 608
541 if !changed { 609 if !changed {
@@ -546,15 +614,22 @@ impl DefCollector<'_> {
546 .get(&module_id) 614 .get(&module_id)
547 .into_iter() 615 .into_iter()
548 .flat_map(|v| v.iter()) 616 .flat_map(|v| v.iter())
617 .filter(|(glob_importing_module, _)| {
618 // we know all resolutions have the same visibility (`vis`), so we
619 // just need to check that once
620 vis.is_visible_from_def_map(&self.def_map, *glob_importing_module)
621 })
549 .cloned() 622 .cloned()
550 .collect::<Vec<_>>(); 623 .collect::<Vec<_>>();
624
551 for (glob_importing_module, glob_import_vis) in glob_imports { 625 for (glob_importing_module, glob_import_vis) in glob_imports {
552 // we know all resolutions have the same visibility (`vis`), so we 626 self.update_recursive(
553 // just need to check that once 627 glob_importing_module,
554 if !vis.is_visible_from_def_map(&self.def_map, glob_importing_module) { 628 resolutions,
555 continue; 629 glob_import_vis,
556 } 630 ImportType::Glob,
557 self.update_recursive(glob_importing_module, resolutions, glob_import_vis, depth + 1); 631 depth + 1,
632 );
558 } 633 }
559 } 634 }
560 635
@@ -571,16 +646,18 @@ impl DefCollector<'_> {
571 return false; 646 return false;
572 } 647 }
573 648
574 if let Some(call_id) = directive.ast_id.as_call_id(self.db, |path| { 649 if let Some(call_id) =
575 let resolved_res = self.def_map.resolve_path_fp_with_macro( 650 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| {
576 self.db, 651 let resolved_res = self.def_map.resolve_path_fp_with_macro(
577 ResolveMode::Other, 652 self.db,
578 directive.module_id, 653 ResolveMode::Other,
579 &path, 654 directive.module_id,
580 BuiltinShadowMode::Module, 655 &path,
581 ); 656 BuiltinShadowMode::Module,
582 resolved_res.resolved_def.take_macros() 657 );
583 }) { 658 resolved_res.resolved_def.take_macros()
659 })
660 {
584 resolved.push((directive.module_id, call_id, directive.depth)); 661 resolved.push((directive.module_id, call_id, directive.depth));
585 res = ReachedFixedPoint::No; 662 res = ReachedFixedPoint::No;
586 return false; 663 return false;
@@ -589,9 +666,10 @@ impl DefCollector<'_> {
589 true 666 true
590 }); 667 });
591 attribute_macros.retain(|directive| { 668 attribute_macros.retain(|directive| {
592 if let Some(call_id) = directive 669 if let Some(call_id) =
593 .ast_id 670 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| {
594 .as_call_id(self.db, |path| self.resolve_attribute_macro(&directive, &path)) 671 self.resolve_attribute_macro(&directive, &path)
672 })
595 { 673 {
596 resolved.push((directive.module_id, call_id, 0)); 674 resolved.push((directive.module_id, call_id, 0));
597 res = ReachedFixedPoint::No; 675 res = ReachedFixedPoint::No;
@@ -605,10 +683,6 @@ impl DefCollector<'_> {
605 self.unexpanded_attribute_macros = attribute_macros; 683 self.unexpanded_attribute_macros = attribute_macros;
606 684
607 for (module_id, macro_call_id, depth) in resolved { 685 for (module_id, macro_call_id, depth) in resolved {
608 if depth > 1024 {
609 log::debug!("Max macro expansion depth reached");
610 continue;
611 }
612 self.collect_macro_expansion(module_id, macro_call_id, depth); 686 self.collect_macro_expansion(module_id, macro_call_id, depth);
613 } 687 }
614 688
@@ -645,18 +719,23 @@ impl DefCollector<'_> {
645 macro_call_id: MacroCallId, 719 macro_call_id: MacroCallId,
646 depth: usize, 720 depth: usize,
647 ) { 721 ) {
722 if depth > EXPANSION_DEPTH_LIMIT {
723 mark::hit!(macro_expansion_overflow);
724 log::warn!("macro expansion is too deep");
725 return;
726 }
648 let file_id: HirFileId = macro_call_id.as_file(); 727 let file_id: HirFileId = macro_call_id.as_file();
649 let raw_items = self.db.raw_items(file_id); 728 let item_tree = self.db.item_tree(file_id);
650 let mod_dir = self.mod_dirs[&module_id].clone(); 729 let mod_dir = self.mod_dirs[&module_id].clone();
651 ModCollector { 730 ModCollector {
652 def_collector: &mut *self, 731 def_collector: &mut *self,
653 macro_depth: depth, 732 macro_depth: depth,
654 file_id, 733 file_id,
655 module_id, 734 module_id,
656 raw_items: &raw_items, 735 item_tree: &item_tree,
657 mod_dir, 736 mod_dir,
658 } 737 }
659 .collect(raw_items.items()); 738 .collect(item_tree.top_level_items());
660 } 739 }
661 740
662 fn finish(self) -> CrateDefMap { 741 fn finish(self) -> CrateDefMap {
@@ -670,12 +749,12 @@ struct ModCollector<'a, 'b> {
670 macro_depth: usize, 749 macro_depth: usize,
671 module_id: LocalModuleId, 750 module_id: LocalModuleId,
672 file_id: HirFileId, 751 file_id: HirFileId,
673 raw_items: &'a raw::RawItems, 752 item_tree: &'a ItemTree,
674 mod_dir: ModDir, 753 mod_dir: ModDir,
675} 754}
676 755
677impl ModCollector<'_, '_> { 756impl ModCollector<'_, '_> {
678 fn collect(&mut self, items: &[raw::RawItem]) { 757 fn collect(&mut self, items: &[ModItem]) {
679 // Note: don't assert that inserted value is fresh: it's simply not true 758 // Note: don't assert that inserted value is fresh: it's simply not true
680 // for macros. 759 // for macros.
681 self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone()); 760 self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
@@ -692,64 +771,205 @@ impl ModCollector<'_, '_> {
692 // `#[macro_use] extern crate` is hoisted to imports macros before collecting 771 // `#[macro_use] extern crate` is hoisted to imports macros before collecting
693 // any other items. 772 // any other items.
694 for item in items { 773 for item in items {
695 if self.is_cfg_enabled(&item.attrs) { 774 if self.is_cfg_enabled(self.item_tree.attrs((*item).into())) {
696 if let raw::RawItemKind::Import(import_id) = item.kind { 775 if let ModItem::ExternCrate(id) = item {
697 let import = self.raw_items[import_id].clone(); 776 let import = self.item_tree[*id].clone();
698 if import.is_extern_crate && import.is_macro_use { 777 if import.is_macro_use {
699 self.def_collector.import_macros_from_extern_crate(self.module_id, &import); 778 self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
700 } 779 }
701 } 780 }
702 } 781 }
703 } 782 }
704 783
705 for item in items { 784 for &item in items {
706 if self.is_cfg_enabled(&item.attrs) { 785 let attrs = self.item_tree.attrs(item.into());
707 match item.kind { 786 if self.is_cfg_enabled(attrs) {
708 raw::RawItemKind::Module(m) => { 787 let module =
709 self.collect_module(&self.raw_items[m], &item.attrs) 788 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
710 } 789 let container = ContainerId::ModuleId(module);
711 raw::RawItemKind::Import(import_id) => { 790
791 let mut def = None;
792 match item {
793 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs),
794 ModItem::Import(import_id) => {
712 self.def_collector.unresolved_imports.push(ImportDirective { 795 self.def_collector.unresolved_imports.push(ImportDirective {
713 module_id: self.module_id, 796 module_id: self.module_id,
714 import_id, 797 import: Import::from_use(&self.item_tree, import_id),
715 import: self.raw_items[import_id].clone(),
716 status: PartialResolvedImport::Unresolved, 798 status: PartialResolvedImport::Unresolved,
717 }) 799 })
718 } 800 }
719 raw::RawItemKind::Def(def) => { 801 ModItem::ExternCrate(import_id) => {
720 self.define_def(&self.raw_items[def], &item.attrs) 802 self.def_collector.unresolved_imports.push(ImportDirective {
803 module_id: self.module_id,
804 import: Import::from_extern_crate(&self.item_tree, import_id),
805 status: PartialResolvedImport::Unresolved,
806 })
721 } 807 }
722 raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]), 808 ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]),
723 raw::RawItemKind::Impl(imp) => { 809 ModItem::Impl(imp) => {
724 let module = ModuleId { 810 let module = ModuleId {
725 krate: self.def_collector.def_map.krate, 811 krate: self.def_collector.def_map.krate,
726 local_id: self.module_id, 812 local_id: self.module_id,
727 }; 813 };
728 let container = ContainerId::ModuleId(module); 814 let container = ContainerId::ModuleId(module);
729 let ast_id = self.raw_items[imp].ast_id; 815 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) }
730 let impl_id = 816 .intern(self.def_collector.db);
731 ImplLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
732 .intern(self.def_collector.db);
733 self.def_collector.def_map.modules[self.module_id] 817 self.def_collector.def_map.modules[self.module_id]
734 .scope 818 .scope
735 .define_impl(impl_id) 819 .define_impl(impl_id)
736 } 820 }
821 ModItem::Function(id) => {
822 let func = &self.item_tree[id];
823 def = Some(DefData {
824 id: FunctionLoc {
825 container: container.into(),
826 id: ItemTreeId::new(self.file_id, id),
827 }
828 .intern(self.def_collector.db)
829 .into(),
830 name: &func.name,
831 visibility: &self.item_tree[func.visibility],
832 has_constructor: false,
833 });
834 }
835 ModItem::Struct(id) => {
836 let it = &self.item_tree[id];
837
838 // FIXME: check attrs to see if this is an attribute macro invocation;
839 // in which case we don't add the invocation, just a single attribute
840 // macro invocation
841 self.collect_derives(attrs, it.ast_id.upcast());
842
843 def = Some(DefData {
844 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
845 .intern(self.def_collector.db)
846 .into(),
847 name: &it.name,
848 visibility: &self.item_tree[it.visibility],
849 has_constructor: it.kind != StructDefKind::Record,
850 });
851 }
852 ModItem::Union(id) => {
853 let it = &self.item_tree[id];
854
855 // FIXME: check attrs to see if this is an attribute macro invocation;
856 // in which case we don't add the invocation, just a single attribute
857 // macro invocation
858 self.collect_derives(attrs, it.ast_id.upcast());
859
860 def = Some(DefData {
861 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) }
862 .intern(self.def_collector.db)
863 .into(),
864 name: &it.name,
865 visibility: &self.item_tree[it.visibility],
866 has_constructor: false,
867 });
868 }
869 ModItem::Enum(id) => {
870 let it = &self.item_tree[id];
871
872 // FIXME: check attrs to see if this is an attribute macro invocation;
873 // in which case we don't add the invocation, just a single attribute
874 // macro invocation
875 self.collect_derives(attrs, it.ast_id.upcast());
876
877 def = Some(DefData {
878 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) }
879 .intern(self.def_collector.db)
880 .into(),
881 name: &it.name,
882 visibility: &self.item_tree[it.visibility],
883 has_constructor: false,
884 });
885 }
886 ModItem::Const(id) => {
887 let it = &self.item_tree[id];
888
889 if let Some(name) = &it.name {
890 def = Some(DefData {
891 id: ConstLoc {
892 container: container.into(),
893 id: ItemTreeId::new(self.file_id, id),
894 }
895 .intern(self.def_collector.db)
896 .into(),
897 name,
898 visibility: &self.item_tree[it.visibility],
899 has_constructor: false,
900 });
901 }
902 }
903 ModItem::Static(id) => {
904 let it = &self.item_tree[id];
905
906 def = Some(DefData {
907 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
908 .intern(self.def_collector.db)
909 .into(),
910 name: &it.name,
911 visibility: &self.item_tree[it.visibility],
912 has_constructor: false,
913 });
914 }
915 ModItem::Trait(id) => {
916 let it = &self.item_tree[id];
917
918 def = Some(DefData {
919 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) }
920 .intern(self.def_collector.db)
921 .into(),
922 name: &it.name,
923 visibility: &self.item_tree[it.visibility],
924 has_constructor: false,
925 });
926 }
927 ModItem::TypeAlias(id) => {
928 let it = &self.item_tree[id];
929
930 def = Some(DefData {
931 id: TypeAliasLoc {
932 container: container.into(),
933 id: ItemTreeId::new(self.file_id, id),
934 }
935 .intern(self.def_collector.db)
936 .into(),
937 name: &it.name,
938 visibility: &self.item_tree[it.visibility],
939 has_constructor: false,
940 });
941 }
942 }
943
944 if let Some(DefData { id, name, visibility, has_constructor }) = def {
945 self.def_collector.def_map.modules[self.module_id].scope.define_def(id);
946 let vis = self
947 .def_collector
948 .def_map
949 .resolve_visibility(self.def_collector.db, self.module_id, visibility)
950 .unwrap_or(Visibility::Public);
951 self.def_collector.update(
952 self.module_id,
953 &[(name.clone(), PerNs::from_def(id, vis, has_constructor))],
954 vis,
955 ImportType::Named,
956 )
737 } 957 }
738 } 958 }
739 } 959 }
740 } 960 }
741 961
742 fn collect_module(&mut self, module: &raw::ModuleData, attrs: &Attrs) { 962 fn collect_module(&mut self, module: &Mod, attrs: &Attrs) {
743 let path_attr = attrs.by_key("path").string_value(); 963 let path_attr = attrs.by_key("path").string_value();
744 let is_macro_use = attrs.by_key("macro_use").exists(); 964 let is_macro_use = attrs.by_key("macro_use").exists();
745 match module { 965 match &module.kind {
746 // inline module, just recurse 966 // inline module, just recurse
747 raw::ModuleData::Definition { name, visibility, items, ast_id } => { 967 ModKind::Inline { items } => {
748 let module_id = self.push_child_module( 968 let module_id = self.push_child_module(
749 name.clone(), 969 module.name.clone(),
750 AstId::new(self.file_id, *ast_id), 970 AstId::new(self.file_id, module.ast_id),
751 None, 971 None,
752 &visibility, 972 &self.item_tree[module.visibility],
753 ); 973 );
754 974
755 ModCollector { 975 ModCollector {
@@ -757,8 +977,8 @@ impl ModCollector<'_, '_> {
757 macro_depth: self.macro_depth, 977 macro_depth: self.macro_depth,
758 module_id, 978 module_id,
759 file_id: self.file_id, 979 file_id: self.file_id,
760 raw_items: self.raw_items, 980 item_tree: self.item_tree,
761 mod_dir: self.mod_dir.descend_into_definition(name, path_attr), 981 mod_dir: self.mod_dir.descend_into_definition(&module.name, path_attr),
762 } 982 }
763 .collect(&*items); 983 .collect(&*items);
764 if is_macro_use { 984 if is_macro_use {
@@ -766,31 +986,31 @@ impl ModCollector<'_, '_> {
766 } 986 }
767 } 987 }
768 // out of line module, resolve, parse and recurse 988 // out of line module, resolve, parse and recurse
769 raw::ModuleData::Declaration { name, visibility, ast_id } => { 989 ModKind::Outline {} => {
770 let ast_id = AstId::new(self.file_id, *ast_id); 990 let ast_id = AstId::new(self.file_id, module.ast_id);
771 match self.mod_dir.resolve_declaration( 991 match self.mod_dir.resolve_declaration(
772 self.def_collector.db, 992 self.def_collector.db,
773 self.file_id, 993 self.file_id,
774 name, 994 &module.name,
775 path_attr, 995 path_attr,
776 ) { 996 ) {
777 Ok((file_id, mod_dir)) => { 997 Ok((file_id, is_mod_rs, mod_dir)) => {
778 let module_id = self.push_child_module( 998 let module_id = self.push_child_module(
779 name.clone(), 999 module.name.clone(),
780 ast_id, 1000 ast_id,
781 Some(file_id), 1001 Some((file_id, is_mod_rs)),
782 &visibility, 1002 &self.item_tree[module.visibility],
783 ); 1003 );
784 let raw_items = self.def_collector.db.raw_items(file_id.into()); 1004 let item_tree = self.def_collector.db.item_tree(file_id.into());
785 ModCollector { 1005 ModCollector {
786 def_collector: &mut *self.def_collector, 1006 def_collector: &mut *self.def_collector,
787 macro_depth: self.macro_depth, 1007 macro_depth: self.macro_depth,
788 module_id, 1008 module_id,
789 file_id: file_id.into(), 1009 file_id: file_id.into(),
790 raw_items: &raw_items, 1010 item_tree: &item_tree,
791 mod_dir, 1011 mod_dir,
792 } 1012 }
793 .collect(raw_items.items()); 1013 .collect(item_tree.top_level_items());
794 if is_macro_use { 1014 if is_macro_use {
795 self.import_all_legacy_macros(module_id); 1015 self.import_all_legacy_macros(module_id);
796 } 1016 }
@@ -811,7 +1031,7 @@ impl ModCollector<'_, '_> {
811 &mut self, 1031 &mut self,
812 name: Name, 1032 name: Name,
813 declaration: AstId<ast::Module>, 1033 declaration: AstId<ast::Module>,
814 definition: Option<FileId>, 1034 definition: Option<(FileId, bool)>,
815 visibility: &crate::visibility::RawVisibility, 1035 visibility: &crate::visibility::RawVisibility,
816 ) -> LocalModuleId { 1036 ) -> LocalModuleId {
817 let vis = self 1037 let vis = self
@@ -822,7 +1042,12 @@ impl ModCollector<'_, '_> {
822 let modules = &mut self.def_collector.def_map.modules; 1042 let modules = &mut self.def_collector.def_map.modules;
823 let res = modules.alloc(ModuleData::default()); 1043 let res = modules.alloc(ModuleData::default());
824 modules[res].parent = Some(self.module_id); 1044 modules[res].parent = Some(self.module_id);
825 modules[res].origin = ModuleOrigin::not_sure_file(definition, declaration); 1045 modules[res].origin = match definition {
1046 None => ModuleOrigin::Inline { definition: declaration },
1047 Some((definition, is_mod_rs)) => {
1048 ModuleOrigin::File { declaration, definition, is_mod_rs }
1049 }
1050 };
826 for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() { 1051 for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() {
827 modules[res].scope.define_legacy_macro(name, mac) 1052 modules[res].scope.define_legacy_macro(name, mac)
828 } 1053 }
@@ -830,81 +1055,16 @@ impl ModCollector<'_, '_> {
830 let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res }; 1055 let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res };
831 let def: ModuleDefId = module.into(); 1056 let def: ModuleDefId = module.into();
832 self.def_collector.def_map.modules[self.module_id].scope.define_def(def); 1057 self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
833 self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis, false))], vis);
834 res
835 }
836
837 fn define_def(&mut self, def: &raw::DefData, attrs: &Attrs) {
838 let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
839 // FIXME: check attrs to see if this is an attribute macro invocation;
840 // in which case we don't add the invocation, just a single attribute
841 // macro invocation
842 self.collect_derives(attrs, def);
843
844 let name = def.name.clone();
845 let container = ContainerId::ModuleId(module);
846 let vis = &def.visibility;
847 let mut has_constructor = false;
848
849 let def: ModuleDefId = match def.kind {
850 raw::DefKind::Function(ast_id) => FunctionLoc {
851 container: container.into(),
852 ast_id: AstId::new(self.file_id, ast_id),
853 }
854 .intern(self.def_collector.db)
855 .into(),
856 raw::DefKind::Struct(ast_id, mode) => {
857 has_constructor = mode != raw::StructDefKind::Record;
858 StructLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
859 .intern(self.def_collector.db)
860 .into()
861 }
862 raw::DefKind::Union(ast_id) => {
863 UnionLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
864 .intern(self.def_collector.db)
865 .into()
866 }
867 raw::DefKind::Enum(ast_id) => {
868 EnumLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
869 .intern(self.def_collector.db)
870 .into()
871 }
872 raw::DefKind::Const(ast_id) => {
873 ConstLoc { container: container.into(), ast_id: AstId::new(self.file_id, ast_id) }
874 .intern(self.def_collector.db)
875 .into()
876 }
877 raw::DefKind::Static(ast_id) => {
878 StaticLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
879 .intern(self.def_collector.db)
880 .into()
881 }
882 raw::DefKind::Trait(ast_id) => {
883 TraitLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
884 .intern(self.def_collector.db)
885 .into()
886 }
887 raw::DefKind::TypeAlias(ast_id) => TypeAliasLoc {
888 container: container.into(),
889 ast_id: AstId::new(self.file_id, ast_id),
890 }
891 .intern(self.def_collector.db)
892 .into(),
893 };
894 self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
895 let vis = self
896 .def_collector
897 .def_map
898 .resolve_visibility(self.def_collector.db, self.module_id, vis)
899 .unwrap_or(Visibility::Public);
900 self.def_collector.update( 1058 self.def_collector.update(
901 self.module_id, 1059 self.module_id,
902 &[(name, PerNs::from_def(def, vis, has_constructor))], 1060 &[(name, PerNs::from_def(def, vis, false))],
903 vis, 1061 vis,
904 ) 1062 ImportType::Named,
1063 );
1064 res
905 } 1065 }
906 1066
907 fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) { 1067 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::ModuleItem>) {
908 for derive_subtree in attrs.by_key("derive").tt_values() { 1068 for derive_subtree in attrs.by_key("derive").tt_values() {
909 // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree 1069 // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
910 for tt in &derive_subtree.token_trees { 1070 for tt in &derive_subtree.token_trees {
@@ -915,7 +1075,7 @@ impl ModCollector<'_, '_> {
915 }; 1075 };
916 let path = ModPath::from_tt_ident(ident); 1076 let path = ModPath::from_tt_ident(ident);
917 1077
918 let ast_id = AstIdWithPath::new(self.file_id, def.kind.ast_id(), path); 1078 let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
919 self.def_collector 1079 self.def_collector
920 .unexpanded_attribute_macros 1080 .unexpanded_attribute_macros
921 .push(DeriveDirective { module_id: self.module_id, ast_id }); 1081 .push(DeriveDirective { module_id: self.module_id, ast_id });
@@ -923,11 +1083,11 @@ impl ModCollector<'_, '_> {
923 } 1083 }
924 } 1084 }
925 1085
926 fn collect_macro(&mut self, mac: &raw::MacroData) { 1086 fn collect_macro(&mut self, mac: &MacroCall) {
927 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); 1087 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
928 1088
929 // Case 0: builtin macros 1089 // Case 0: builtin macros
930 if mac.builtin { 1090 if mac.is_builtin {
931 if let Some(name) = &mac.name { 1091 if let Some(name) = &mac.name {
932 let krate = self.def_collector.def_map.krate; 1092 let krate = self.def_collector.def_map.krate;
933 if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) { 1093 if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) {
@@ -935,7 +1095,7 @@ impl ModCollector<'_, '_> {
935 self.module_id, 1095 self.module_id,
936 name.clone(), 1096 name.clone(),
937 macro_id, 1097 macro_id,
938 mac.export, 1098 mac.is_export,
939 ); 1099 );
940 return; 1100 return;
941 } 1101 }
@@ -949,19 +1109,26 @@ impl ModCollector<'_, '_> {
949 ast_id: Some(ast_id.ast_id), 1109 ast_id: Some(ast_id.ast_id),
950 krate: Some(self.def_collector.def_map.krate), 1110 krate: Some(self.def_collector.def_map.krate),
951 kind: MacroDefKind::Declarative, 1111 kind: MacroDefKind::Declarative,
952 local_inner: mac.local_inner, 1112 local_inner: mac.is_local_inner,
953 }; 1113 };
954 self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export); 1114 self.def_collector.define_macro(
1115 self.module_id,
1116 name.clone(),
1117 macro_id,
1118 mac.is_export,
1119 );
955 } 1120 }
956 return; 1121 return;
957 } 1122 }
958 1123
959 // Case 2: try to resolve in legacy scope and expand macro_rules 1124 // Case 2: try to resolve in legacy scope and expand macro_rules
960 if let Some(macro_call_id) = ast_id.as_call_id(self.def_collector.db, |path| { 1125 if let Some(macro_call_id) =
961 path.as_ident().and_then(|name| { 1126 ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| {
962 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) 1127 path.as_ident().and_then(|name| {
1128 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
1129 })
963 }) 1130 })
964 }) { 1131 {
965 self.def_collector.unexpanded_macros.push(MacroDirective { 1132 self.def_collector.unexpanded_macros.push(MacroDirective {
966 module_id: self.module_id, 1133 module_id: self.module_id,
967 ast_id, 1134 ast_id,
@@ -1022,6 +1189,7 @@ mod tests {
1022 mod_dirs: FxHashMap::default(), 1189 mod_dirs: FxHashMap::default(),
1023 cfg_options: &CfgOptions::default(), 1190 cfg_options: &CfgOptions::default(),
1024 proc_macros: Default::default(), 1191 proc_macros: Default::default(),
1192 from_glob_import: Default::default(),
1025 }; 1193 };
1026 collector.collect(); 1194 collector.collect();
1027 collector.def_map 1195 collector.def_map
diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs
index 386c5cade..953961632 100644
--- a/crates/ra_hir_def/src/nameres/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs
@@ -1,23 +1,24 @@
1//! This module resolves `mod foo;` declaration to file. 1//! This module resolves `mod foo;` declaration to file.
2use hir_expand::name::Name; 2use hir_expand::name::Name;
3use ra_db::{FileId, RelativePathBuf}; 3use ra_db::FileId;
4use ra_syntax::SmolStr; 4use ra_syntax::SmolStr;
5 5
6use crate::{db::DefDatabase, HirFileId}; 6use crate::{db::DefDatabase, HirFileId};
7 7
8#[derive(Clone, Debug)] 8#[derive(Clone, Debug)]
9pub(super) struct ModDir { 9pub(super) struct ModDir {
10 /// `.` for `mod.rs`, `lib.rs` 10 /// `` for `mod.rs`, `lib.rs`
11 /// `./foo` for `foo.rs` 11 /// `foo/` for `foo.rs`
12 /// `./foo/bar` for `mod bar { mod x; }` nested in `foo.rs` 12 /// `foo/bar/` for `mod bar { mod x; }` nested in `foo.rs`
13 path: RelativePathBuf, 13 /// Invariant: path.is_empty() || path.ends_with('/')
14 dir_path: DirPath,
14 /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/` 15 /// inside `./foo.rs`, mods with `#[path]` should *not* be relative to `./foo/`
15 root_non_dir_owner: bool, 16 root_non_dir_owner: bool,
16} 17}
17 18
18impl ModDir { 19impl ModDir {
19 pub(super) fn root() -> ModDir { 20 pub(super) fn root() -> ModDir {
20 ModDir { path: RelativePathBuf::default(), root_non_dir_owner: false } 21 ModDir { dir_path: DirPath::empty(), root_non_dir_owner: false }
21 } 22 }
22 23
23 pub(super) fn descend_into_definition( 24 pub(super) fn descend_into_definition(
@@ -25,17 +26,21 @@ impl ModDir {
25 name: &Name, 26 name: &Name,
26 attr_path: Option<&SmolStr>, 27 attr_path: Option<&SmolStr>,
27 ) -> ModDir { 28 ) -> ModDir {
28 let mut path = self.path.clone(); 29 let path = match attr_path.map(|it| it.as_str()) {
29 match attr_to_path(attr_path) { 30 None => {
30 None => path.push(&name.to_string()), 31 let mut path = self.dir_path.clone();
32 path.push(&name.to_string());
33 path
34 }
31 Some(attr_path) => { 35 Some(attr_path) => {
32 if self.root_non_dir_owner { 36 let mut path = self.dir_path.join_attr(attr_path, self.root_non_dir_owner);
33 assert!(path.pop()); 37 if !(path.is_empty() || path.ends_with('/')) {
38 path.push('/')
34 } 39 }
35 path.push(attr_path); 40 DirPath::new(path)
36 } 41 }
37 } 42 };
38 ModDir { path, root_non_dir_owner: false } 43 ModDir { dir_path: path, root_non_dir_owner: false }
39 } 44 }
40 45
41 pub(super) fn resolve_declaration( 46 pub(super) fn resolve_declaration(
@@ -44,37 +49,91 @@ impl ModDir {
44 file_id: HirFileId, 49 file_id: HirFileId,
45 name: &Name, 50 name: &Name,
46 attr_path: Option<&SmolStr>, 51 attr_path: Option<&SmolStr>,
47 ) -> Result<(FileId, ModDir), RelativePathBuf> { 52 ) -> Result<(FileId, bool, ModDir), String> {
48 let file_id = file_id.original_file(db.upcast()); 53 let file_id = file_id.original_file(db.upcast());
49 54
50 let mut candidate_files = Vec::new(); 55 let mut candidate_files = Vec::new();
51 match attr_to_path(attr_path) { 56 match attr_path {
52 Some(attr_path) => { 57 Some(attr_path) => {
53 let base = 58 candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
54 if self.root_non_dir_owner { self.path.parent().unwrap() } else { &self.path };
55 candidate_files.push(base.join(attr_path))
56 } 59 }
57 None => { 60 None => {
58 candidate_files.push(self.path.join(&format!("{}.rs", name))); 61 candidate_files.push(format!("{}{}.rs", self.dir_path.0, name));
59 candidate_files.push(self.path.join(&format!("{}/mod.rs", name))); 62 candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name));
60 } 63 }
61 }; 64 };
62 65
63 for candidate in candidate_files.iter() { 66 for candidate in candidate_files.iter() {
64 if let Some(file_id) = db.resolve_relative_path(file_id, candidate) { 67 if let Some(file_id) = db.resolve_path(file_id, candidate.as_str()) {
65 let mut root_non_dir_owner = false; 68 let is_mod_rs = candidate.ends_with("mod.rs");
66 let mut mod_path = RelativePathBuf::new(); 69
67 if !(candidate.ends_with("mod.rs") || attr_path.is_some()) { 70 let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
68 root_non_dir_owner = true; 71 (DirPath::empty(), false)
69 mod_path.push(&name.to_string()); 72 } else {
70 } 73 (DirPath::new(format!("{}/", name)), true)
71 return Ok((file_id, ModDir { path: mod_path, root_non_dir_owner })); 74 };
75 return Ok((file_id, is_mod_rs, ModDir { dir_path, root_non_dir_owner }));
72 } 76 }
73 } 77 }
74 Err(candidate_files.remove(0)) 78 Err(candidate_files.remove(0))
75 } 79 }
76} 80}
77 81
78fn attr_to_path(attr: Option<&SmolStr>) -> Option<RelativePathBuf> { 82#[derive(Clone, Debug)]
79 attr.and_then(|it| RelativePathBuf::from_path(&it.replace("\\", "/")).ok()) 83struct DirPath(String);
84
85impl DirPath {
86 fn assert_invariant(&self) {
87 assert!(self.0.is_empty() || self.0.ends_with('/'));
88 }
89 fn new(repr: String) -> DirPath {
90 let res = DirPath(repr);
91 res.assert_invariant();
92 res
93 }
94 fn empty() -> DirPath {
95 DirPath::new(String::new())
96 }
97 fn push(&mut self, name: &str) {
98 self.0.push_str(name);
99 self.0.push('/');
100 self.assert_invariant();
101 }
102 fn parent(&self) -> Option<&str> {
103 if self.0.is_empty() {
104 return None;
105 };
106 let idx =
107 self.0[..self.0.len() - '/'.len_utf8()].rfind('/').map_or(0, |it| it + '/'.len_utf8());
108 Some(&self.0[..idx])
109 }
110 /// So this is the case which doesn't really work I think if we try to be
111 /// 100% platform agnostic:
112 ///
113 /// ```
114 /// mod a {
115 /// #[path="C://sad/face"]
116 /// mod b { mod c; }
117 /// }
118 /// ```
119 ///
120 /// Here, we need to join logical dir path to a string path from an
121 /// attribute. Ideally, we should somehow losslessly communicate the whole
122 /// construction to `FileLoader`.
123 fn join_attr(&self, mut attr: &str, relative_to_parent: bool) -> String {
124 let base = if relative_to_parent { self.parent().unwrap() } else { &self.0 };
125
126 if attr.starts_with("./") {
127 attr = &attr["./".len()..];
128 }
129 let tmp;
130 let attr = if attr.contains('\\') {
131 tmp = attr.replace('\\', "/");
132 &tmp
133 } else {
134 attr
135 };
136 let res = format!("{}{}", base, attr);
137 res
138 }
80} 139}
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
deleted file mode 100644
index f44baa579..000000000
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ /dev/null
@@ -1,482 +0,0 @@
1//! Lowers syntax tree of a rust file into a raw representation of containing
2//! items, *without* attaching them to a module structure.
3//!
4//! That is, raw items don't have semantics, just as syntax, but, unlike syntax,
5//! they don't change with trivial source code edits, making them a great tool
6//! for building salsa recomputation firewalls.
7
8use std::{ops::Index, sync::Arc};
9
10use hir_expand::{
11 ast_id_map::AstIdMap,
12 hygiene::Hygiene,
13 name::{AsName, Name},
14};
15use ra_arena::{Arena, Idx};
16use ra_prof::profile;
17use ra_syntax::{
18 ast::{self, AttrsOwner, NameOwner, VisibilityOwner},
19 AstNode,
20};
21use test_utils::mark;
22
23use crate::{
24 attr::Attrs,
25 db::DefDatabase,
26 path::{ImportAlias, ModPath},
27 visibility::RawVisibility,
28 FileAstId, HirFileId, InFile,
29};
30
31/// `RawItems` is a set of top-level items in a file (except for impls).
32///
33/// It is the input to name resolution algorithm. `RawItems` are not invalidated
34/// on most edits.
35#[derive(Debug, Default, PartialEq, Eq)]
36pub struct RawItems {
37 modules: Arena<ModuleData>,
38 imports: Arena<ImportData>,
39 defs: Arena<DefData>,
40 macros: Arena<MacroData>,
41 impls: Arena<ImplData>,
42 /// items for top-level module
43 items: Vec<RawItem>,
44}
45
46impl RawItems {
47 pub(crate) fn raw_items_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<RawItems> {
48 let _p = profile("raw_items_query");
49 let mut collector = RawItemsCollector {
50 raw_items: RawItems::default(),
51 source_ast_id_map: db.ast_id_map(file_id),
52 file_id,
53 hygiene: Hygiene::new(db.upcast(), file_id),
54 };
55 if let Some(node) = db.parse_or_expand(file_id) {
56 if let Some(source_file) = ast::SourceFile::cast(node.clone()) {
57 collector.process_module(None, source_file);
58 } else if let Some(item_list) = ast::MacroItems::cast(node) {
59 collector.process_module(None, item_list);
60 }
61 }
62 let raw_items = collector.raw_items;
63 Arc::new(raw_items)
64 }
65
66 pub(super) fn items(&self) -> &[RawItem] {
67 &self.items
68 }
69}
70
71impl Index<Idx<ModuleData>> for RawItems {
72 type Output = ModuleData;
73 fn index(&self, idx: Idx<ModuleData>) -> &ModuleData {
74 &self.modules[idx]
75 }
76}
77
78impl Index<Import> for RawItems {
79 type Output = ImportData;
80 fn index(&self, idx: Import) -> &ImportData {
81 &self.imports[idx]
82 }
83}
84
85impl Index<Idx<DefData>> for RawItems {
86 type Output = DefData;
87 fn index(&self, idx: Idx<DefData>) -> &DefData {
88 &self.defs[idx]
89 }
90}
91
92impl Index<Idx<MacroData>> for RawItems {
93 type Output = MacroData;
94 fn index(&self, idx: Idx<MacroData>) -> &MacroData {
95 &self.macros[idx]
96 }
97}
98
99impl Index<Idx<ImplData>> for RawItems {
100 type Output = ImplData;
101 fn index(&self, idx: Idx<ImplData>) -> &ImplData {
102 &self.impls[idx]
103 }
104}
105
106#[derive(Debug, PartialEq, Eq, Clone)]
107pub(super) struct RawItem {
108 pub(super) attrs: Attrs,
109 pub(super) kind: RawItemKind,
110}
111
112#[derive(Debug, PartialEq, Eq, Clone, Copy)]
113pub(super) enum RawItemKind {
114 Module(Idx<ModuleData>),
115 Import(Import),
116 Def(Idx<DefData>),
117 Macro(Idx<MacroData>),
118 Impl(Idx<ImplData>),
119}
120
121#[derive(Debug, PartialEq, Eq)]
122pub(super) enum ModuleData {
123 Declaration {
124 name: Name,
125 visibility: RawVisibility,
126 ast_id: FileAstId<ast::Module>,
127 },
128 Definition {
129 name: Name,
130 visibility: RawVisibility,
131 ast_id: FileAstId<ast::Module>,
132 items: Vec<RawItem>,
133 },
134}
135
136pub(crate) type Import = Idx<ImportData>;
137
138#[derive(Debug, Clone, PartialEq, Eq)]
139pub struct ImportData {
140 pub(super) path: ModPath,
141 pub(super) alias: Option<ImportAlias>,
142 pub(super) is_glob: bool,
143 pub(super) is_prelude: bool,
144 pub(super) is_extern_crate: bool,
145 pub(super) is_macro_use: bool,
146 pub(super) visibility: RawVisibility,
147}
148
149// type Def = Idx<DefData>;
150
151#[derive(Debug, PartialEq, Eq)]
152pub(super) struct DefData {
153 pub(super) name: Name,
154 pub(super) kind: DefKind,
155 pub(super) visibility: RawVisibility,
156}
157
158#[derive(Debug, PartialEq, Eq, Clone, Copy)]
159pub(super) enum StructDefKind {
160 Record,
161 Tuple,
162 Unit,
163}
164
165#[derive(Debug, PartialEq, Eq, Clone, Copy)]
166pub(super) enum DefKind {
167 Function(FileAstId<ast::FnDef>),
168 Struct(FileAstId<ast::StructDef>, StructDefKind),
169 Union(FileAstId<ast::UnionDef>),
170 Enum(FileAstId<ast::EnumDef>),
171 Const(FileAstId<ast::ConstDef>),
172 Static(FileAstId<ast::StaticDef>),
173 Trait(FileAstId<ast::TraitDef>),
174 TypeAlias(FileAstId<ast::TypeAliasDef>),
175}
176
177impl DefKind {
178 pub fn ast_id(self) -> FileAstId<ast::ModuleItem> {
179 match self {
180 DefKind::Function(it) => it.upcast(),
181 DefKind::Struct(it, _) => it.upcast(),
182 DefKind::Union(it) => it.upcast(),
183 DefKind::Enum(it) => it.upcast(),
184 DefKind::Const(it) => it.upcast(),
185 DefKind::Static(it) => it.upcast(),
186 DefKind::Trait(it) => it.upcast(),
187 DefKind::TypeAlias(it) => it.upcast(),
188 }
189 }
190}
191
192#[derive(Debug, PartialEq, Eq)]
193pub(super) struct MacroData {
194 pub(super) ast_id: FileAstId<ast::MacroCall>,
195 pub(super) path: ModPath,
196 pub(super) name: Option<Name>,
197 pub(super) export: bool,
198 pub(super) local_inner: bool,
199 pub(super) builtin: bool,
200}
201
202#[derive(Debug, PartialEq, Eq)]
203pub(super) struct ImplData {
204 pub(super) ast_id: FileAstId<ast::ImplDef>,
205}
206
207struct RawItemsCollector {
208 raw_items: RawItems,
209 source_ast_id_map: Arc<AstIdMap>,
210 file_id: HirFileId,
211 hygiene: Hygiene,
212}
213
214impl RawItemsCollector {
215 fn process_module(
216 &mut self,
217 current_module: Option<Idx<ModuleData>>,
218 body: impl ast::ModuleItemOwner,
219 ) {
220 for item in body.items() {
221 self.add_item(current_module, item)
222 }
223 }
224
225 fn add_item(&mut self, current_module: Option<Idx<ModuleData>>, item: ast::ModuleItem) {
226 let attrs = self.parse_attrs(&item);
227 let visibility = RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
228 let (kind, name) = match item {
229 ast::ModuleItem::Module(module) => {
230 self.add_module(current_module, module);
231 return;
232 }
233 ast::ModuleItem::UseItem(use_item) => {
234 self.add_use_item(current_module, use_item);
235 return;
236 }
237 ast::ModuleItem::ExternCrateItem(extern_crate) => {
238 self.add_extern_crate_item(current_module, extern_crate);
239 return;
240 }
241 ast::ModuleItem::ImplDef(it) => {
242 self.add_impl(current_module, it);
243 return;
244 }
245 ast::ModuleItem::StructDef(it) => {
246 let kind = match it.kind() {
247 ast::StructKind::Record(_) => StructDefKind::Record,
248 ast::StructKind::Tuple(_) => StructDefKind::Tuple,
249 ast::StructKind::Unit => StructDefKind::Unit,
250 };
251 let id = self.source_ast_id_map.ast_id(&it);
252 let name = it.name();
253 (DefKind::Struct(id, kind), name)
254 }
255 ast::ModuleItem::UnionDef(it) => {
256 let id = self.source_ast_id_map.ast_id(&it);
257 let name = it.name();
258 (DefKind::Union(id), name)
259 }
260 ast::ModuleItem::EnumDef(it) => {
261 (DefKind::Enum(self.source_ast_id_map.ast_id(&it)), it.name())
262 }
263 ast::ModuleItem::FnDef(it) => {
264 (DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
265 }
266 ast::ModuleItem::TraitDef(it) => {
267 (DefKind::Trait(self.source_ast_id_map.ast_id(&it)), it.name())
268 }
269 ast::ModuleItem::TypeAliasDef(it) => {
270 (DefKind::TypeAlias(self.source_ast_id_map.ast_id(&it)), it.name())
271 }
272 ast::ModuleItem::ConstDef(it) => {
273 (DefKind::Const(self.source_ast_id_map.ast_id(&it)), it.name())
274 }
275 ast::ModuleItem::StaticDef(it) => {
276 (DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
277 }
278 ast::ModuleItem::MacroCall(it) => {
279 self.add_macro(current_module, it);
280 return;
281 }
282 ast::ModuleItem::ExternBlock(it) => {
283 self.add_extern_block(current_module, it);
284 return;
285 }
286 };
287 if let Some(name) = name {
288 let name = name.as_name();
289 let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
290 self.push_item(current_module, attrs, RawItemKind::Def(def));
291 }
292 }
293
294 fn add_extern_block(
295 &mut self,
296 current_module: Option<Idx<ModuleData>>,
297 block: ast::ExternBlock,
298 ) {
299 if let Some(items) = block.extern_item_list() {
300 for item in items.extern_items() {
301 let attrs = self.parse_attrs(&item);
302 let visibility =
303 RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
304 let (kind, name) = match item {
305 ast::ExternItem::FnDef(it) => {
306 (DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
307 }
308 ast::ExternItem::StaticDef(it) => {
309 (DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
310 }
311 };
312
313 if let Some(name) = name {
314 let name = name.as_name();
315 let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
316 self.push_item(current_module, attrs, RawItemKind::Def(def));
317 }
318 }
319 }
320 }
321
322 fn add_module(&mut self, current_module: Option<Idx<ModuleData>>, module: ast::Module) {
323 let name = match module.name() {
324 Some(it) => it.as_name(),
325 None => return,
326 };
327 let attrs = self.parse_attrs(&module);
328 let visibility = RawVisibility::from_ast_with_hygiene(module.visibility(), &self.hygiene);
329
330 let ast_id = self.source_ast_id_map.ast_id(&module);
331 if module.semicolon_token().is_some() {
332 let item =
333 self.raw_items.modules.alloc(ModuleData::Declaration { name, visibility, ast_id });
334 self.push_item(current_module, attrs, RawItemKind::Module(item));
335 return;
336 }
337
338 if let Some(item_list) = module.item_list() {
339 let item = self.raw_items.modules.alloc(ModuleData::Definition {
340 name,
341 visibility,
342 ast_id,
343 items: Vec::new(),
344 });
345 self.process_module(Some(item), item_list);
346 self.push_item(current_module, attrs, RawItemKind::Module(item));
347 return;
348 }
349 mark::hit!(name_res_works_for_broken_modules);
350 }
351
352 fn add_use_item(&mut self, current_module: Option<Idx<ModuleData>>, use_item: ast::UseItem) {
353 // FIXME: cfg_attr
354 let is_prelude = use_item.has_atom_attr("prelude_import");
355 let attrs = self.parse_attrs(&use_item);
356 let visibility = RawVisibility::from_ast_with_hygiene(use_item.visibility(), &self.hygiene);
357
358 let mut buf = Vec::new();
359 ModPath::expand_use_item(
360 InFile { value: use_item, file_id: self.file_id },
361 &self.hygiene,
362 |path, _use_tree, is_glob, alias| {
363 let import_data = ImportData {
364 path,
365 alias,
366 is_glob,
367 is_prelude,
368 is_extern_crate: false,
369 is_macro_use: false,
370 visibility: visibility.clone(),
371 };
372 buf.push(import_data);
373 },
374 );
375 for import_data in buf {
376 self.push_import(current_module, attrs.clone(), import_data);
377 }
378 }
379
380 fn add_extern_crate_item(
381 &mut self,
382 current_module: Option<Idx<ModuleData>>,
383 extern_crate: ast::ExternCrateItem,
384 ) {
385 if let Some(name_ref) = extern_crate.name_ref() {
386 let path = ModPath::from_name_ref(&name_ref);
387 let visibility =
388 RawVisibility::from_ast_with_hygiene(extern_crate.visibility(), &self.hygiene);
389 let alias = extern_crate.alias().map(|a| {
390 a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
391 });
392 let attrs = self.parse_attrs(&extern_crate);
393 // FIXME: cfg_attr
394 let is_macro_use = extern_crate.has_atom_attr("macro_use");
395 let import_data = ImportData {
396 path,
397 alias,
398 is_glob: false,
399 is_prelude: false,
400 is_extern_crate: true,
401 is_macro_use,
402 visibility,
403 };
404 self.push_import(current_module, attrs, import_data);
405 }
406 }
407
408 fn add_macro(&mut self, current_module: Option<Idx<ModuleData>>, m: ast::MacroCall) {
409 let attrs = self.parse_attrs(&m);
410 let path = match m.path().and_then(|path| ModPath::from_src(path, &self.hygiene)) {
411 Some(it) => it,
412 _ => return,
413 };
414
415 let name = m.name().map(|it| it.as_name());
416 let ast_id = self.source_ast_id_map.ast_id(&m);
417
418 // FIXME: cfg_attr
419 let export_attr = attrs.by_key("macro_export");
420
421 let export = export_attr.exists();
422 let local_inner = if export {
423 export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
424 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
425 ident.text.contains("local_inner_macros")
426 }
427 _ => false,
428 })
429 } else {
430 false
431 };
432
433 let builtin = attrs.by_key("rustc_builtin_macro").exists();
434
435 let m = self.raw_items.macros.alloc(MacroData {
436 ast_id,
437 path,
438 name,
439 export,
440 local_inner,
441 builtin,
442 });
443 self.push_item(current_module, attrs, RawItemKind::Macro(m));
444 }
445
446 fn add_impl(&mut self, current_module: Option<Idx<ModuleData>>, imp: ast::ImplDef) {
447 let attrs = self.parse_attrs(&imp);
448 let ast_id = self.source_ast_id_map.ast_id(&imp);
449 let imp = self.raw_items.impls.alloc(ImplData { ast_id });
450 self.push_item(current_module, attrs, RawItemKind::Impl(imp))
451 }
452
453 fn push_import(
454 &mut self,
455 current_module: Option<Idx<ModuleData>>,
456 attrs: Attrs,
457 data: ImportData,
458 ) {
459 let import = self.raw_items.imports.alloc(data);
460 self.push_item(current_module, attrs, RawItemKind::Import(import))
461 }
462
463 fn push_item(
464 &mut self,
465 current_module: Option<Idx<ModuleData>>,
466 attrs: Attrs,
467 kind: RawItemKind,
468 ) {
469 match current_module {
470 Some(module) => match &mut self.raw_items.modules[module] {
471 ModuleData::Definition { items, .. } => items,
472 ModuleData::Declaration { .. } => unreachable!(),
473 },
474 None => &mut self.raw_items.items,
475 }
476 .push(RawItem { attrs, kind })
477 }
478
479 fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs {
480 Attrs::new(item, &self.hygiene)
481 }
482}
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs
index 05cd0297d..503099fb7 100644
--- a/crates/ra_hir_def/src/nameres/tests.rs
+++ b/crates/ra_hir_def/src/nameres/tests.rs
@@ -424,31 +424,6 @@ fn extern_crate_rename_2015_edition() {
424} 424}
425 425
426#[test] 426#[test]
427fn import_across_source_roots() {
428 let map = def_map(
429 "
430 //- /main.rs crate:main deps:test_crate
431 use test_crate::a::b::C;
432
433 //- root /test_crate/
434
435 //- /test_crate/lib.rs crate:test_crate
436 pub mod a {
437 pub mod b {
438 pub struct C;
439 }
440 }
441
442 ",
443 );
444
445 assert_snapshot!(map, @r###"
446 ⋮crate
447 ⋮C: t v
448 "###);
449}
450
451#[test]
452fn reexport_across_crates() { 427fn reexport_across_crates() {
453 let map = def_map( 428 let map = def_map(
454 " 429 "
diff --git a/crates/ra_hir_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs
index 2b12c0daa..7f3d7509c 100644
--- a/crates/ra_hir_def/src/nameres/tests/globs.rs
+++ b/crates/ra_hir_def/src/nameres/tests/globs.rs
@@ -229,3 +229,140 @@ fn glob_enum_group() {
229 "### 229 "###
230 ); 230 );
231} 231}
232
233#[test]
234fn glob_shadowed_def() {
235 mark::check!(import_shadowed);
236 let map = def_map(
237 r###"
238 //- /lib.rs
239 mod foo;
240 mod bar;
241
242 use foo::*;
243 use bar::baz;
244
245 use baz::Bar;
246
247 //- /foo.rs
248 pub mod baz {
249 pub struct Foo;
250 }
251
252 //- /bar.rs
253 pub mod baz {
254 pub struct Bar;
255 }
256 "###,
257 );
258 assert_snapshot!(map, @r###"
259 ⋮crate
260 ⋮Bar: t v
261 ⋮bar: t
262 ⋮baz: t
263 ⋮foo: t
264
265 ⋮crate::bar
266 ⋮baz: t
267
268 ⋮crate::bar::baz
269 ⋮Bar: t v
270
271 ⋮crate::foo
272 ⋮baz: t
273
274 ⋮crate::foo::baz
275 ⋮Foo: t v
276 "###
277 );
278}
279
280#[test]
281fn glob_shadowed_def_reversed() {
282 let map = def_map(
283 r###"
284 //- /lib.rs
285 mod foo;
286 mod bar;
287
288 use bar::baz;
289 use foo::*;
290
291 use baz::Bar;
292
293 //- /foo.rs
294 pub mod baz {
295 pub struct Foo;
296 }
297
298 //- /bar.rs
299 pub mod baz {
300 pub struct Bar;
301 }
302 "###,
303 );
304 assert_snapshot!(map, @r###"
305 ⋮crate
306 ⋮Bar: t v
307 ⋮bar: t
308 ⋮baz: t
309 ⋮foo: t
310
311 ⋮crate::bar
312 ⋮baz: t
313
314 ⋮crate::bar::baz
315 ⋮Bar: t v
316
317 ⋮crate::foo
318 ⋮baz: t
319
320 ⋮crate::foo::baz
321 ⋮Foo: t v
322 "###
323 );
324}
325
326#[test]
327fn glob_shadowed_def_dependencies() {
328 let map = def_map(
329 r###"
330 //- /lib.rs
331 mod a { pub mod foo { pub struct X; } }
332 mod b { pub use super::a::foo; }
333 mod c { pub mod foo { pub struct Y; } }
334 mod d {
335 use super::c::foo;
336 use super::b::*;
337 use foo::Y;
338 }
339 "###,
340 );
341 assert_snapshot!(map, @r###"
342 ⋮crate
343 ⋮a: t
344 ⋮b: t
345 ⋮c: t
346 ⋮d: t
347
348 ⋮crate::d
349 ⋮Y: t v
350 ⋮foo: t
351
352 ⋮crate::c
353 ⋮foo: t
354
355 ⋮crate::c::foo
356 ⋮Y: t v
357
358 ⋮crate::b
359 ⋮foo: t
360
361 ⋮crate::a
362 ⋮foo: t
363
364 ⋮crate::a::foo
365 ⋮X: t v
366 "###
367 );
368}
diff --git a/crates/ra_hir_def/src/nameres/tests/incremental.rs b/crates/ra_hir_def/src/nameres/tests/incremental.rs
index 87165ac33..0c288a108 100644
--- a/crates/ra_hir_def/src/nameres/tests/incremental.rs
+++ b/crates/ra_hir_def/src/nameres/tests/incremental.rs
@@ -58,44 +58,6 @@ fn typing_inside_a_function_should_not_invalidate_def_map() {
58} 58}
59 59
60#[test] 60#[test]
61fn adding_inner_items_should_not_invalidate_def_map() {
62 check_def_map_is_not_recomputed(
63 r"
64 //- /lib.rs
65 struct S { a: i32}
66 enum E { A }
67 trait T {
68 fn a() {}
69 }
70 mod foo;<|>
71 impl S {
72 fn a() {}
73 }
74 use crate::foo::bar::Baz;
75 //- /foo/mod.rs
76 pub mod bar;
77
78 //- /foo/bar.rs
79 pub struct Baz;
80 ",
81 r"
82 struct S { a: i32, b: () }
83 enum E { A, B }
84 trait T {
85 fn a() {}
86 fn b() {}
87 }
88 mod foo;<|>
89 impl S {
90 fn a() {}
91 fn b() {}
92 }
93 use crate::foo::bar::Baz;
94 ",
95 );
96}
97
98#[test]
99fn typing_inside_a_macro_should_not_invalidate_def_map() { 61fn typing_inside_a_macro_should_not_invalidate_def_map() {
100 let (mut db, pos) = TestDB::with_position( 62 let (mut db, pos) = TestDB::with_position(
101 r" 63 r"
diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs
index 84480d9f6..c52341a07 100644
--- a/crates/ra_hir_def/src/nameres/tests/macros.rs
+++ b/crates/ra_hir_def/src/nameres/tests/macros.rs
@@ -660,3 +660,27 @@ fn expand_multiple_derive() {
660 ); 660 );
661 assert_eq!(map.modules[map.root].scope.impls().len(), 2); 661 assert_eq!(map.modules[map.root].scope.impls().len(), 2);
662} 662}
663
664#[test]
665fn macro_expansion_overflow() {
666 mark::check!(macro_expansion_overflow);
667 compute_crate_def_map(
668 "
669macro_rules! a {
670 ($e:expr; $($t:tt)*) => {
671 b!($($t)*);
672 };
673 () => {};
674}
675
676macro_rules! b {
677 (static = $e:expr; $($t:tt)*) => {
678 a!($e; $($t)*);
679 };
680 () => {};
681}
682
683b! { static = #[] (); }
684",
685 );
686}
diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
index b43b294ca..753684201 100644
--- a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
@@ -20,8 +20,11 @@ fn name_res_works_for_broken_modules() {
20 ", 20 ",
21 ); 21 );
22 assert_snapshot!(map, @r###" 22 assert_snapshot!(map, @r###"
23 ⋮crate 23crate
24 ⋮Baz: _ 24Baz: _
25foo: t
26
27crate::foo
25 "###); 28 "###);
26} 29}
27 30
@@ -332,6 +335,22 @@ fn module_resolution_relative_path_2() {
332} 335}
333 336
334#[test] 337#[test]
338fn module_resolution_relative_path_outside_root() {
339 let map = def_map(
340 r###"
341 //- /main.rs
342
343 #[path="../../../../../outside.rs"]
344 mod foo;
345 "###,
346 );
347
348 assert_snapshot!(map, @r###"
349 ⋮crate
350 "###);
351}
352
353#[test]
335fn module_resolution_explicit_path_mod_rs_2() { 354fn module_resolution_explicit_path_mod_rs_2() {
336 let map = def_map( 355 let map = def_map(
337 r###" 356 r###"
@@ -719,10 +738,7 @@ fn unresolved_module_diagnostics() {
719 ), 738 ),
720 ), 739 ),
721 ), 740 ),
722 value: FileAstId { 741 value: FileAstId::<ra_syntax::ast::generated::nodes::Module>(1),
723 raw: Idx::<SyntaxNodePtr>(1),
724 _ty: PhantomData,
725 },
726 }, 742 },
727 candidate: "bar.rs", 743 candidate: "bar.rs",
728 }, 744 },
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index e84efe2ab..190d6d98d 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -76,6 +76,19 @@ impl ModPath {
76 } 76 }
77 } 77 }
78 78
79 /// Returns the number of segments in the path (counting special segments like `$crate` and
80 /// `super`).
81 pub fn len(&self) -> usize {
82 self.segments.len()
83 + match self.kind {
84 PathKind::Plain => 0,
85 PathKind::Super(i) => i as usize,
86 PathKind::Crate => 1,
87 PathKind::Abs => 0,
88 PathKind::DollarCrate(_) => 1,
89 }
90 }
91
79 pub fn is_ident(&self) -> bool { 92 pub fn is_ident(&self) -> bool {
80 self.kind == PathKind::Plain && self.segments.len() == 1 93 self.kind == PathKind::Plain && self.segments.len() == 1
81 } 94 }
@@ -141,7 +154,7 @@ pub enum GenericArg {
141 154
142impl Path { 155impl Path {
143 /// Converts an `ast::Path` to `Path`. Works with use trees. 156 /// Converts an `ast::Path` to `Path`. Works with use trees.
144 /// DEPRECATED: It does not handle `$crate` from macro call. 157 #[deprecated = "Doesn't handle hygiene, don't add new calls, remove old ones"]
145 pub fn from_ast(path: ast::Path) -> Option<Path> { 158 pub fn from_ast(path: ast::Path) -> Option<Path> {
146 lower::lower_path(path, &Hygiene::new_unhygienic()) 159 lower::lower_path(path, &Hygiene::new_unhygienic())
147 } 160 }
@@ -273,7 +286,7 @@ impl From<Name> for ModPath {
273impl Display for ModPath { 286impl Display for ModPath {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 let mut first_segment = true; 288 let mut first_segment = true;
276 let mut add_segment = |s| { 289 let mut add_segment = |s| -> fmt::Result {
277 if !first_segment { 290 if !first_segment {
278 f.write_str("::")?; 291 f.write_str("::")?;
279 } 292 }
@@ -310,16 +323,16 @@ pub use hir_expand::name as __name;
310 323
311#[macro_export] 324#[macro_export]
312macro_rules! __known_path { 325macro_rules! __known_path {
313 (std::iter::IntoIterator) => {}; 326 (core::iter::IntoIterator) => {};
314 (std::result::Result) => {}; 327 (core::result::Result) => {};
315 (std::ops::Range) => {}; 328 (core::ops::Range) => {};
316 (std::ops::RangeFrom) => {}; 329 (core::ops::RangeFrom) => {};
317 (std::ops::RangeFull) => {}; 330 (core::ops::RangeFull) => {};
318 (std::ops::RangeTo) => {}; 331 (core::ops::RangeTo) => {};
319 (std::ops::RangeToInclusive) => {}; 332 (core::ops::RangeToInclusive) => {};
320 (std::ops::RangeInclusive) => {}; 333 (core::ops::RangeInclusive) => {};
321 (std::future::Future) => {}; 334 (core::future::Future) => {};
322 (std::ops::Try) => {}; 335 (core::ops::Try) => {};
323 ($path:path) => { 336 ($path:path) => {
324 compile_error!("Please register your known path in the path module") 337 compile_error!("Please register your known path in the path module")
325 }; 338 };
diff --git a/crates/ra_hir_def/src/per_ns.rs b/crates/ra_hir_def/src/per_ns.rs
index 6e435c8c1..74665c588 100644
--- a/crates/ra_hir_def/src/per_ns.rs
+++ b/crates/ra_hir_def/src/per_ns.rs
@@ -5,7 +5,7 @@
5 5
6use hir_expand::MacroDefId; 6use hir_expand::MacroDefId;
7 7
8use crate::{visibility::Visibility, ModuleDefId}; 8use crate::{item_scope::ItemInNs, visibility::Visibility, ModuleDefId};
9 9
10#[derive(Debug, Copy, Clone, PartialEq, Eq)] 10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
11pub struct PerNs { 11pub struct PerNs {
@@ -84,4 +84,12 @@ impl PerNs {
84 macros: self.macros.or(other.macros), 84 macros: self.macros.or(other.macros),
85 } 85 }
86 } 86 }
87
88 pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> {
89 self.types
90 .map(|it| ItemInNs::Types(it.0))
91 .into_iter()
92 .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter())
93 .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter())
94 }
87} 95}
diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs
index 15fdd9019..0bf51eb7b 100644
--- a/crates/ra_hir_def/src/resolver.rs
+++ b/crates/ra_hir_def/src/resolver.rs
@@ -511,11 +511,9 @@ impl Scope {
511 }); 511 });
512 } 512 }
513 } 513 }
514 Scope::LocalItemsScope(body) => { 514 Scope::LocalItemsScope(body) => body.item_scope.entries().for_each(|(name, def)| {
515 body.item_scope.entries_without_primitives().for_each(|(name, def)| { 515 f(name.clone(), ScopeDef::PerNs(def));
516 f(name.clone(), ScopeDef::PerNs(def)); 516 }),
517 })
518 }
519 Scope::GenericParams { params, def } => { 517 Scope::GenericParams { params, def } => {
520 for (local_id, param) in params.types.iter() { 518 for (local_id, param) in params.types.iter() {
521 if let Some(name) = &param.name { 519 if let Some(name) = &param.name {
diff --git a/crates/ra_hir_def/src/src.rs b/crates/ra_hir_def/src/src.rs
index 46e90da70..043b93fad 100644
--- a/crates/ra_hir_def/src/src.rs
+++ b/crates/ra_hir_def/src/src.rs
@@ -2,30 +2,37 @@
2 2
3use hir_expand::InFile; 3use hir_expand::InFile;
4use ra_arena::map::ArenaMap; 4use ra_arena::map::ArenaMap;
5use ra_syntax::AstNode;
6 5
7use crate::{db::DefDatabase, AssocItemLoc, ItemLoc}; 6use crate::{db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc};
8 7
9pub trait HasSource { 8pub trait HasSource {
10 type Value; 9 type Value;
11 fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>; 10 fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value>;
12} 11}
13 12
14impl<N: AstNode> HasSource for AssocItemLoc<N> { 13impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> {
15 type Value = N; 14 type Value = N::Source;
16 15
17 fn source(&self, db: &dyn DefDatabase) -> InFile<N> { 16 fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
18 let node = self.ast_id.to_node(db.upcast()); 17 let tree = db.item_tree(self.id.file_id);
19 InFile::new(self.ast_id.file_id, node) 18 let ast_id_map = db.ast_id_map(self.id.file_id);
19 let root = db.parse_or_expand(self.id.file_id).unwrap();
20 let node = &tree[self.id.value];
21
22 InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root))
20 } 23 }
21} 24}
22 25
23impl<N: AstNode> HasSource for ItemLoc<N> { 26impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
24 type Value = N; 27 type Value = N::Source;
28
29 fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
30 let tree = db.item_tree(self.id.file_id);
31 let ast_id_map = db.ast_id_map(self.id.file_id);
32 let root = db.parse_or_expand(self.id.file_id).unwrap();
33 let node = &tree[self.id.value];
25 34
26 fn source(&self, db: &dyn DefDatabase) -> InFile<N> { 35 InFile::new(self.id.file_id, ast_id_map.get(node.ast_id()).to_node(&root))
27 let node = self.ast_id.to_node(db.upcast());
28 InFile::new(self.ast_id.file_id, node)
29 } 36 }
30} 37}
31 38
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs
index eb83dee79..339f819b8 100644
--- a/crates/ra_hir_def/src/test_db.rs
+++ b/crates/ra_hir_def/src/test_db.rs
@@ -1,14 +1,13 @@
1//! Database used for testing `hir_def`. 1//! Database used for testing `hir_def`.
2 2
3use std::{ 3use std::{
4 panic, 4 fmt, panic,
5 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
6}; 6};
7 7
8use hir_expand::db::AstDatabase; 8use hir_expand::db::AstDatabase;
9use ra_db::{ 9use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
10 salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath, Upcast, 10use rustc_hash::FxHashSet;
11};
12 11
13use crate::db::DefDatabase; 12use crate::db::DefDatabase;
14 13
@@ -19,10 +18,10 @@ use crate::db::DefDatabase;
19 crate::db::InternDatabaseStorage, 18 crate::db::InternDatabaseStorage,
20 crate::db::DefDatabaseStorage 19 crate::db::DefDatabaseStorage
21)] 20)]
22#[derive(Debug, Default)] 21#[derive(Default)]
23pub struct TestDB { 22pub struct TestDB {
24 runtime: salsa::Runtime<TestDB>, 23 storage: salsa::Storage<TestDB>,
25 events: Mutex<Option<Vec<salsa::Event<TestDB>>>>, 24 events: Mutex<Option<Vec<salsa::Event>>>,
26} 25}
27 26
28impl Upcast<dyn AstDatabase> for TestDB { 27impl Upcast<dyn AstDatabase> for TestDB {
@@ -38,44 +37,32 @@ impl Upcast<dyn DefDatabase> for TestDB {
38} 37}
39 38
40impl salsa::Database for TestDB { 39impl salsa::Database for TestDB {
41 fn salsa_runtime(&self) -> &salsa::Runtime<Self> { 40 fn salsa_event(&self, event: salsa::Event) {
42 &self.runtime
43 }
44 fn salsa_runtime_mut(&mut self) -> &mut salsa::Runtime<Self> {
45 &mut self.runtime
46 }
47 fn salsa_event(&self, event: impl Fn() -> salsa::Event<TestDB>) {
48 let mut events = self.events.lock().unwrap(); 41 let mut events = self.events.lock().unwrap();
49 if let Some(events) = &mut *events { 42 if let Some(events) = &mut *events {
50 events.push(event()); 43 events.push(event);
51 } 44 }
52 } 45 }
53} 46}
54 47
48impl fmt::Debug for TestDB {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 f.debug_struct("TestDB").finish()
51 }
52}
53
55impl panic::RefUnwindSafe for TestDB {} 54impl panic::RefUnwindSafe for TestDB {}
56 55
57impl FileLoader for TestDB { 56impl FileLoader for TestDB {
58 fn file_text(&self, file_id: FileId) -> Arc<String> { 57 fn file_text(&self, file_id: FileId) -> Arc<String> {
59 FileLoaderDelegate(self).file_text(file_id) 58 FileLoaderDelegate(self).file_text(file_id)
60 } 59 }
61 fn resolve_relative_path( 60 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
62 &self, 61 FileLoaderDelegate(self).resolve_path(anchor, path)
63 anchor: FileId,
64 relative_path: &RelativePath,
65 ) -> Option<FileId> {
66 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
67 } 62 }
68 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 63 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
69 FileLoaderDelegate(self).relevant_crates(file_id) 64 FileLoaderDelegate(self).relevant_crates(file_id)
70 } 65 }
71
72 fn resolve_extern_path(
73 &self,
74 extern_id: ExternSourceId,
75 relative_path: &RelativePath,
76 ) -> Option<FileId> {
77 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
78 }
79} 66}
80 67
81impl TestDB { 68impl TestDB {
@@ -91,7 +78,7 @@ impl TestDB {
91 panic!("Can't find module for file") 78 panic!("Can't find module for file")
92 } 79 }
93 80
94 pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<TestDB>> { 81 pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event> {
95 *self.events.lock().unwrap() = Some(Vec::new()); 82 *self.events.lock().unwrap() = Some(Vec::new());
96 f(); 83 f();
97 self.events.lock().unwrap().take().unwrap() 84 self.events.lock().unwrap().take().unwrap()
@@ -105,7 +92,7 @@ impl TestDB {
105 // This pretty horrible, but `Debug` is the only way to inspect 92 // This pretty horrible, but `Debug` is the only way to inspect
106 // QueryDescriptor at the moment. 93 // QueryDescriptor at the moment.
107 salsa::EventKind::WillExecute { database_key } => { 94 salsa::EventKind::WillExecute { database_key } => {
108 Some(format!("{:?}", database_key)) 95 Some(format!("{:?}", database_key.debug(self)))
109 } 96 }
110 _ => None, 97 _ => None,
111 }) 98 })
diff --git a/crates/ra_hir_def/src/type_ref.rs b/crates/ra_hir_def/src/type_ref.rs
index 86a77b704..e90b2a0b9 100644
--- a/crates/ra_hir_def/src/type_ref.rs
+++ b/crates/ra_hir_def/src/type_ref.rs
@@ -63,7 +63,7 @@ pub enum TypeRef {
63 Array(Box<TypeRef> /*, Expr*/), 63 Array(Box<TypeRef> /*, Expr*/),
64 Slice(Box<TypeRef>), 64 Slice(Box<TypeRef>),
65 /// A fn pointer. Last element of the vector is the return type. 65 /// A fn pointer. Last element of the vector is the return type.
66 Fn(Vec<TypeRef>), 66 Fn(Vec<TypeRef>, bool /*varargs*/),
67 // For 67 // For
68 ImplTrait(Vec<TypeBound>), 68 ImplTrait(Vec<TypeBound>),
69 DynTrait(Vec<TypeBound>), 69 DynTrait(Vec<TypeBound>),
@@ -118,7 +118,12 @@ impl TypeRef {
118 .and_then(|rt| rt.type_ref()) 118 .and_then(|rt| rt.type_ref())
119 .map(|it| TypeRef::from_ast(ctx, it)) 119 .map(|it| TypeRef::from_ast(ctx, it))
120 .unwrap_or_else(|| TypeRef::Tuple(Vec::new())); 120 .unwrap_or_else(|| TypeRef::Tuple(Vec::new()));
121 let mut is_varargs = false;
121 let mut params = if let Some(pl) = inner.param_list() { 122 let mut params = if let Some(pl) = inner.param_list() {
123 if let Some(param) = pl.params().last() {
124 is_varargs = param.dotdotdot_token().is_some();
125 }
126
122 pl.params() 127 pl.params()
123 .map(|p| p.ascribed_type()) 128 .map(|p| p.ascribed_type())
124 .map(|it| TypeRef::from_ast_opt(&ctx, it)) 129 .map(|it| TypeRef::from_ast_opt(&ctx, it))
@@ -127,7 +132,7 @@ impl TypeRef {
127 Vec::new() 132 Vec::new()
128 }; 133 };
129 params.push(ret_ty); 134 params.push(ret_ty);
130 TypeRef::Fn(params) 135 TypeRef::Fn(params, is_varargs)
131 } 136 }
132 // for types are close enough for our purposes to the inner type for now... 137 // for types are close enough for our purposes to the inner type for now...
133 ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.type_ref()), 138 ast::TypeRef::ForType(inner) => TypeRef::from_ast_opt(&ctx, inner.type_ref()),
@@ -158,7 +163,9 @@ impl TypeRef {
158 fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) { 163 fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
159 f(type_ref); 164 f(type_ref);
160 match type_ref { 165 match type_ref {
161 TypeRef::Fn(types) | TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)), 166 TypeRef::Fn(types, _) | TypeRef::Tuple(types) => {
167 types.iter().for_each(|t| go(t, f))
168 }
162 TypeRef::RawPtr(type_ref, _) 169 TypeRef::RawPtr(type_ref, _)
163 | TypeRef::Reference(type_ref, _) 170 | TypeRef::Reference(type_ref, _)
164 | TypeRef::Array(type_ref) 171 | TypeRef::Array(type_ref)
diff --git a/crates/ra_hir_def/src/visibility.rs b/crates/ra_hir_def/src/visibility.rs
index 1482d3be0..8136cb50c 100644
--- a/crates/ra_hir_def/src/visibility.rs
+++ b/crates/ra_hir_def/src/visibility.rs
@@ -6,7 +6,7 @@ use ra_syntax::ast;
6use crate::{ 6use crate::{
7 db::DefDatabase, 7 db::DefDatabase,
8 path::{ModPath, PathKind}, 8 path::{ModPath, PathKind},
9 AssocContainerId, ModuleId, 9 ModuleId,
10}; 10};
11 11
12/// Visibility of an item, not yet resolved. 12/// Visibility of an item, not yet resolved.
@@ -25,25 +25,6 @@ impl RawVisibility {
25 RawVisibility::Module(path) 25 RawVisibility::Module(path)
26 } 26 }
27 27
28 pub(crate) fn default_for_container(container_id: AssocContainerId) -> Self {
29 match container_id {
30 AssocContainerId::TraitId(_) => RawVisibility::Public,
31 _ => RawVisibility::private(),
32 }
33 }
34
35 pub(crate) fn from_ast_with_default(
36 db: &dyn DefDatabase,
37 default: RawVisibility,
38 node: InFile<Option<ast::Visibility>>,
39 ) -> RawVisibility {
40 Self::from_ast_with_hygiene_and_default(
41 node.value,
42 default,
43 &Hygiene::new(db.upcast(), node.file_id),
44 )
45 }
46
47 pub(crate) fn from_ast( 28 pub(crate) fn from_ast(
48 db: &dyn DefDatabase, 29 db: &dyn DefDatabase,
49 node: InFile<Option<ast::Visibility>>, 30 node: InFile<Option<ast::Visibility>>,
diff --git a/crates/ra_hir_expand/Cargo.toml b/crates/ra_hir_expand/Cargo.toml
index 2cd522766..6da0e2a16 100644
--- a/crates/ra_hir_expand/Cargo.toml
+++ b/crates/ra_hir_expand/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_hir_expand" 3name = "ra_hir_expand"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
@@ -10,6 +11,7 @@ doctest = false
10[dependencies] 11[dependencies]
11log = "0.4.8" 12log = "0.4.8"
12either = "1.5.3" 13either = "1.5.3"
14rustc-hash = "1.0.0"
13 15
14ra_arena = { path = "../ra_arena" } 16ra_arena = { path = "../ra_arena" }
15ra_db = { path = "../ra_db" } 17ra_db = { path = "../ra_db" }
diff --git a/crates/ra_hir_expand/src/ast_id_map.rs b/crates/ra_hir_expand/src/ast_id_map.rs
index d19569245..f4d31526a 100644
--- a/crates/ra_hir_expand/src/ast_id_map.rs
+++ b/crates/ra_hir_expand/src/ast_id_map.rs
@@ -6,6 +6,8 @@
6//! changes. 6//! changes.
7 7
8use std::{ 8use std::{
9 any::type_name,
10 fmt,
9 hash::{Hash, Hasher}, 11 hash::{Hash, Hasher},
10 marker::PhantomData, 12 marker::PhantomData,
11}; 13};
@@ -14,7 +16,6 @@ use ra_arena::{Arena, Idx};
14use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; 16use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
15 17
16/// `AstId` points to an AST node in a specific file. 18/// `AstId` points to an AST node in a specific file.
17#[derive(Debug)]
18pub struct FileAstId<N: AstNode> { 19pub struct FileAstId<N: AstNode> {
19 raw: ErasedFileAstId, 20 raw: ErasedFileAstId,
20 _ty: PhantomData<fn() -> N>, 21 _ty: PhantomData<fn() -> N>,
@@ -39,11 +40,17 @@ impl<N: AstNode> Hash for FileAstId<N> {
39 } 40 }
40} 41}
41 42
43impl<N: AstNode> fmt::Debug for FileAstId<N> {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "FileAstId::<{}>({})", type_name::<N>(), self.raw.into_raw())
46 }
47}
48
42impl<N: AstNode> FileAstId<N> { 49impl<N: AstNode> FileAstId<N> {
43 // Can't make this a From implementation because of coherence 50 // Can't make this a From implementation because of coherence
44 pub fn upcast<M: AstNode>(self) -> FileAstId<M> 51 pub fn upcast<M: AstNode>(self) -> FileAstId<M>
45 where 52 where
46 M: From<N>, 53 N: Into<M>,
47 { 54 {
48 FileAstId { raw: self.raw, _ty: PhantomData } 55 FileAstId { raw: self.raw, _ty: PhantomData }
49 } 56 }
@@ -89,7 +96,7 @@ impl AstIdMap {
89 } 96 }
90 } 97 }
91 98
92 pub(crate) fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> { 99 pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
93 self.arena[id.raw].clone().cast::<N>().unwrap() 100 self.arena[id.raw].clone().cast::<N>().unwrap()
94 } 101 }
95 102
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
index 1dc9cac66..f2d664863 100644
--- a/crates/ra_hir_expand/src/builtin_derive.rs
+++ b/crates/ra_hir_expand/src/builtin_derive.rs
@@ -8,8 +8,7 @@ use ra_syntax::{
8 match_ast, 8 match_ast,
9}; 9};
10 10
11use crate::db::AstDatabase; 11use crate::{db::AstDatabase, name, quote, LazyMacroId, MacroDefId, MacroDefKind};
12use crate::{name, quote, LazyMacroId, MacroCallId, MacroDefId, MacroDefKind};
13 12
14macro_rules! register_builtin { 13macro_rules! register_builtin {
15 ( $($trait:ident => $expand:ident),* ) => { 14 ( $($trait:ident => $expand:ident),* ) => {
@@ -156,23 +155,13 @@ fn expand_simple_derive(
156fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree { 155fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree {
157 // FIXME: make hygiene works for builtin derive macro 156 // FIXME: make hygiene works for builtin derive macro
158 // such that $crate can be used here. 157 // such that $crate can be used here.
159
160 let m: MacroCallId = id.into();
161 let file_id = m.as_file().original_file(db);
162 let cg = db.crate_graph(); 158 let cg = db.crate_graph();
163 let krates = db.relevant_crates(file_id); 159 let krate = db.lookup_intern_macro(id).krate;
164 let krate = match krates.get(0) {
165 Some(krate) => krate,
166 None => {
167 let tt = quote! { core };
168 return tt.token_trees[0].clone();
169 }
170 };
171 160
172 // XXX 161 // XXX
173 // All crates except core itself should have a dependency on core, 162 // All crates except core itself should have a dependency on core,
174 // We detect `core` by seeing whether it doesn't have such a dependency. 163 // We detect `core` by seeing whether it doesn't have such a dependency.
175 let tt = if cg[*krate].dependencies.iter().any(|dep| dep.name == "core") { 164 let tt = if cg[krate].dependencies.iter().any(|dep| &*dep.name == "core") {
176 quote! { core } 165 quote! { core }
177 } else { 166 } else {
178 quote! { crate } 167 quote! { crate }
@@ -264,10 +253,12 @@ fn partial_ord_expand(
264 253
265#[cfg(test)] 254#[cfg(test)]
266mod tests { 255mod tests {
267 use super::*;
268 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
269 use name::{known, Name}; 256 use name::{known, Name};
270 use ra_db::{fixture::WithFixture, SourceDatabase}; 257 use ra_db::{fixture::WithFixture, CrateId, SourceDatabase};
258
259 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
260
261 use super::*;
271 262
272 fn expand_builtin_derive(s: &str, name: Name) -> String { 263 fn expand_builtin_derive(s: &str, name: Name) -> String {
273 let def = find_builtin_derive(&name).unwrap(); 264 let def = find_builtin_derive(&name).unwrap();
@@ -291,7 +282,11 @@ mod tests {
291 282
292 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0])); 283 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]));
293 284
294 let loc = MacroCallLoc { def, kind: MacroCallKind::Attr(attr_id, name.to_string()) }; 285 let loc = MacroCallLoc {
286 def,
287 krate: CrateId(0),
288 kind: MacroCallKind::Attr(attr_id, name.to_string()),
289 };
295 290
296 let id: MacroCallId = db.intern_macro(loc).into(); 291 let id: MacroCallId = db.intern_macro(loc).into();
297 let parsed = db.parse_or_expand(id.as_file()).unwrap(); 292 let parsed = db.parse_or_expand(id.as_file()).unwrap();
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index 3bce8f673..9f50569dc 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -1,15 +1,14 @@
1//! Builtin macro 1//! Builtin macro
2use crate::db::AstDatabase;
3use crate::{ 2use crate::{
4 ast::{self, AstToken, HasStringValue}, 3 db::AstDatabase, name, quote, AstId, CrateId, EagerMacroId, LazyMacroId, MacroCallId,
5 name, AstId, CrateId, MacroDefId, MacroDefKind, TextSize, 4 MacroDefId, MacroDefKind, TextSize,
6}; 5};
7 6
8use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId};
9use either::Either; 7use either::Either;
10use mbe::parse_to_token_tree; 8use mbe::parse_to_token_tree;
11use ra_db::{FileId, RelativePath}; 9use ra_db::FileId;
12use ra_parser::FragmentKind; 10use ra_parser::FragmentKind;
11use ra_syntax::ast::{self, AstToken, HasStringValue};
13 12
14macro_rules! register_builtin { 13macro_rules! register_builtin {
15 ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { 14 ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
@@ -100,6 +99,8 @@ register_builtin! {
100 EAGER: 99 EAGER:
101 (concat, Concat) => concat_expand, 100 (concat, Concat) => concat_expand,
102 (include, Include) => include_expand, 101 (include, Include) => include_expand,
102 (include_bytes, IncludeBytes) => include_bytes_expand,
103 (include_str, IncludeStr) => include_str_expand,
103 (env, Env) => env_expand, 104 (env, Env) => env_expand,
104 (option_env, OptionEnv) => option_env_expand 105 (option_env, OptionEnv) => option_env_expand
105} 106}
@@ -271,7 +272,7 @@ fn format_args_expand(
271fn unquote_str(lit: &tt::Literal) -> Option<String> { 272fn unquote_str(lit: &tt::Literal) -> Option<String> {
272 let lit = ast::make::tokens::literal(&lit.to_string()); 273 let lit = ast::make::tokens::literal(&lit.to_string());
273 let token = ast::String::cast(lit)?; 274 let token = ast::String::cast(lit)?;
274 token.value() 275 token.value().map(|it| it.into_owned())
275} 276}
276 277
277fn concat_expand( 278fn concat_expand(
@@ -293,21 +294,20 @@ fn concat_expand(
293 Ok((quote!(#text), FragmentKind::Expr)) 294 Ok((quote!(#text), FragmentKind::Expr))
294} 295}
295 296
296fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> { 297fn relative_file(
298 db: &dyn AstDatabase,
299 call_id: MacroCallId,
300 path: &str,
301 allow_recursion: bool,
302) -> Option<FileId> {
297 let call_site = call_id.as_file().original_file(db); 303 let call_site = call_id.as_file().original_file(db);
298 304 let res = db.resolve_path(call_site, path)?;
299 // Handle trivial case 305 // Prevent include itself
300 if let Some(res) = db.resolve_relative_path(call_site, &RelativePath::new(&path)) { 306 if res == call_site && !allow_recursion {
301 // Prevent include itself 307 None
302 return if res == call_site { None } else { Some(res) }; 308 } else {
309 Some(res)
303 } 310 }
304
305 // Extern paths ?
306 let krate = *db.relevant_crates(call_site).get(0)?;
307 let (extern_source_id, relative_file) =
308 db.crate_graph()[krate].extern_source.extern_path(path)?;
309
310 db.resolve_extern_path(extern_source_id, &relative_file)
311} 311}
312 312
313fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> { 313fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> {
@@ -326,8 +326,8 @@ fn include_expand(
326 tt: &tt::Subtree, 326 tt: &tt::Subtree,
327) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { 327) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
328 let path = parse_string(tt)?; 328 let path = parse_string(tt)?;
329 let file_id = 329 let file_id = relative_file(db, arg_id.into(), &path, false)
330 relative_file(db, arg_id.into(), &path).ok_or_else(|| mbe::ExpandError::ConversionError)?; 330 .ok_or_else(|| mbe::ExpandError::ConversionError)?;
331 331
332 // FIXME: 332 // FIXME:
333 // Handle include as expression 333 // Handle include as expression
@@ -338,11 +338,50 @@ fn include_expand(
338 Ok((res, FragmentKind::Items)) 338 Ok((res, FragmentKind::Items))
339} 339}
340 340
341fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> { 341fn include_bytes_expand(
342 let call_id: MacroCallId = arg_id.into(); 342 _db: &dyn AstDatabase,
343 let original_file = call_id.as_file().original_file(db); 343 _arg_id: EagerMacroId,
344 tt: &tt::Subtree,
345) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
346 let _path = parse_string(tt)?;
347
348 // FIXME: actually read the file here if the user asked for macro expansion
349 let res = tt::Subtree {
350 delimiter: None,
351 token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
352 text: r#"b"""#.into(),
353 id: tt::TokenId::unspecified(),
354 }))],
355 };
356 Ok((res, FragmentKind::Expr))
357}
358
359fn include_str_expand(
360 db: &dyn AstDatabase,
361 arg_id: EagerMacroId,
362 tt: &tt::Subtree,
363) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
364 let path = parse_string(tt)?;
365
366 // FIXME: we're not able to read excluded files (which is most of them because
367 // it's unusual to `include_str!` a Rust file), but we can return an empty string.
368 // Ideally, we'd be able to offer a precise expansion if the user asks for macro
369 // expansion.
370 let file_id = match relative_file(db, arg_id.into(), &path, true) {
371 Some(file_id) => file_id,
372 None => {
373 return Ok((quote!(""), FragmentKind::Expr));
374 }
375 };
376
377 let text = db.file_text(file_id);
378 let text = &*text;
344 379
345 let krate = *db.relevant_crates(original_file).get(0)?; 380 Ok((quote!(#text), FragmentKind::Expr))
381}
382
383fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> {
384 let krate = db.lookup_intern_eager_expansion(arg_id).krate;
346 db.crate_graph()[krate].env.get(key) 385 db.crate_graph()[krate].env.get(key)
347} 386}
348 387
@@ -401,6 +440,7 @@ mod tests {
401 440
402 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap(); 441 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap();
403 442
443 let krate = CrateId(0);
404 let file_id = match expander { 444 let file_id = match expander {
405 Either::Left(expander) => { 445 Either::Left(expander) => {
406 // the first one should be a macro_rules 446 // the first one should be a macro_rules
@@ -413,6 +453,7 @@ mod tests {
413 453
414 let loc = MacroCallLoc { 454 let loc = MacroCallLoc {
415 def, 455 def,
456 krate,
416 kind: MacroCallKind::FnLike(AstId::new( 457 kind: MacroCallKind::FnLike(AstId::new(
417 file_id.into(), 458 file_id.into(),
418 ast_id_map.ast_id(&macro_calls[1]), 459 ast_id_map.ast_id(&macro_calls[1]),
@@ -425,7 +466,7 @@ mod tests {
425 Either::Right(expander) => { 466 Either::Right(expander) => {
426 // the first one should be a macro_rules 467 // the first one should be a macro_rules
427 let def = MacroDefId { 468 let def = MacroDefId {
428 krate: Some(CrateId(0)), 469 krate: Some(krate),
429 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))), 470 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
430 kind: MacroDefKind::BuiltInEager(expander), 471 kind: MacroDefKind::BuiltInEager(expander),
431 local_inner: false, 472 local_inner: false,
@@ -439,6 +480,7 @@ mod tests {
439 def, 480 def,
440 fragment: FragmentKind::Expr, 481 fragment: FragmentKind::Expr,
441 subtree: Arc::new(parsed_args.clone()), 482 subtree: Arc::new(parsed_args.clone()),
483 krate,
442 file_id: file_id.into(), 484 file_id: file_id.into(),
443 } 485 }
444 }); 486 });
@@ -448,6 +490,7 @@ mod tests {
448 def, 490 def,
449 fragment, 491 fragment,
450 subtree: Arc::new(subtree), 492 subtree: Arc::new(subtree),
493 krate,
451 file_id: file_id.into(), 494 file_id: file_id.into(),
452 }; 495 };
453 496
@@ -587,4 +630,20 @@ mod tests {
587 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"# 630 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"#
588 ); 631 );
589 } 632 }
633
634 #[test]
635 fn test_include_bytes_expand() {
636 let expanded = expand_builtin_macro(
637 r#"
638 #[rustc_builtin_macro]
639 macro_rules! include_bytes {
640 ($file:expr) => {{ /* compiler built-in */ }};
641 ($file:expr,) => {{ /* compiler built-in */ }};
642 }
643 include_bytes("foo");
644 "#,
645 );
646
647 assert_eq!(expanded, r#"b"""#);
648 }
590} 649}
diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs
index 99209c6e8..545cff9bd 100644
--- a/crates/ra_hir_expand/src/diagnostics.rs
+++ b/crates/ra_hir_expand/src/diagnostics.rs
@@ -28,7 +28,7 @@ pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static {
28 28
29pub trait AstDiagnostic { 29pub trait AstDiagnostic {
30 type AST; 30 type AST;
31 fn ast(&self, db: &impl AstDatabase) -> Self::AST; 31 fn ast(&self, db: &dyn AstDatabase) -> Self::AST;
32} 32}
33 33
34impl dyn Diagnostic { 34impl dyn Diagnostic {
diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs
index 932f47c30..302d2b3e0 100644
--- a/crates/ra_hir_expand/src/eager.rs
+++ b/crates/ra_hir_expand/src/eager.rs
@@ -25,12 +25,14 @@ use crate::{
25 EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 25 EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
26}; 26};
27 27
28use ra_db::CrateId;
28use ra_parser::FragmentKind; 29use ra_parser::FragmentKind;
29use ra_syntax::{algo::SyntaxRewriter, SyntaxNode}; 30use ra_syntax::{algo::SyntaxRewriter, SyntaxNode};
30use std::sync::Arc; 31use std::sync::Arc;
31 32
32pub fn expand_eager_macro( 33pub fn expand_eager_macro(
33 db: &dyn AstDatabase, 34 db: &dyn AstDatabase,
35 krate: CrateId,
34 macro_call: InFile<ast::MacroCall>, 36 macro_call: InFile<ast::MacroCall>,
35 def: MacroDefId, 37 def: MacroDefId,
36 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 38 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
@@ -47,6 +49,7 @@ pub fn expand_eager_macro(
47 def, 49 def,
48 fragment: FragmentKind::Expr, 50 fragment: FragmentKind::Expr,
49 subtree: Arc::new(parsed_args.clone()), 51 subtree: Arc::new(parsed_args.clone()),
52 krate,
50 file_id: macro_call.file_id, 53 file_id: macro_call.file_id,
51 } 54 }
52 }); 55 });
@@ -56,14 +59,20 @@ pub fn expand_eager_macro(
56 let result = eager_macro_recur( 59 let result = eager_macro_recur(
57 db, 60 db,
58 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()), 61 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
62 krate,
59 resolver, 63 resolver,
60 )?; 64 )?;
61 let subtree = to_subtree(&result)?; 65 let subtree = to_subtree(&result)?;
62 66
63 if let MacroDefKind::BuiltInEager(eager) = def.kind { 67 if let MacroDefKind::BuiltInEager(eager) = def.kind {
64 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?; 68 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?;
65 let eager = 69 let eager = EagerCallLoc {
66 EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id }; 70 def,
71 fragment,
72 subtree: Arc::new(subtree),
73 krate,
74 file_id: macro_call.file_id,
75 };
67 76
68 Some(db.intern_eager_expansion(eager)) 77 Some(db.intern_eager_expansion(eager))
69 } else { 78 } else {
@@ -81,11 +90,12 @@ fn lazy_expand(
81 db: &dyn AstDatabase, 90 db: &dyn AstDatabase,
82 def: &MacroDefId, 91 def: &MacroDefId,
83 macro_call: InFile<ast::MacroCall>, 92 macro_call: InFile<ast::MacroCall>,
93 krate: CrateId,
84) -> Option<InFile<SyntaxNode>> { 94) -> Option<InFile<SyntaxNode>> {
85 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); 95 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
86 96
87 let id: MacroCallId = 97 let id: MacroCallId =
88 def.as_lazy_macro(db, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); 98 def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
89 99
90 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)) 100 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node))
91} 101}
@@ -93,6 +103,7 @@ fn lazy_expand(
93fn eager_macro_recur( 103fn eager_macro_recur(
94 db: &dyn AstDatabase, 104 db: &dyn AstDatabase,
95 curr: InFile<SyntaxNode>, 105 curr: InFile<SyntaxNode>,
106 krate: CrateId,
96 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 107 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
97) -> Option<SyntaxNode> { 108) -> Option<SyntaxNode> {
98 let original = curr.value.clone(); 109 let original = curr.value.clone();
@@ -105,18 +116,23 @@ fn eager_macro_recur(
105 let def: MacroDefId = macro_resolver(child.path()?)?; 116 let def: MacroDefId = macro_resolver(child.path()?)?;
106 let insert = match def.kind { 117 let insert = match def.kind {
107 MacroDefKind::BuiltInEager(_) => { 118 MacroDefKind::BuiltInEager(_) => {
108 let id: MacroCallId = 119 let id: MacroCallId = expand_eager_macro(
109 expand_eager_macro(db, curr.with_value(child.clone()), def, macro_resolver)? 120 db,
110 .into(); 121 krate,
122 curr.with_value(child.clone()),
123 def,
124 macro_resolver,
125 )?
126 .into();
111 db.parse_or_expand(id.as_file())? 127 db.parse_or_expand(id.as_file())?
112 } 128 }
113 MacroDefKind::Declarative 129 MacroDefKind::Declarative
114 | MacroDefKind::BuiltIn(_) 130 | MacroDefKind::BuiltIn(_)
115 | MacroDefKind::BuiltInDerive(_) 131 | MacroDefKind::BuiltInDerive(_)
116 | MacroDefKind::CustomDerive(_) => { 132 | MacroDefKind::CustomDerive(_) => {
117 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?; 133 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?;
118 // replace macro inside 134 // replace macro inside
119 eager_macro_recur(db, expanded, macro_resolver)? 135 eager_macro_recur(db, expanded, krate, macro_resolver)?
120 } 136 }
121 }; 137 };
122 138
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index f440c073b..1cf6c1ba9 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -88,6 +88,25 @@ impl HirFileId {
88 } 88 }
89 } 89 }
90 90
91 pub fn expansion_level(self, db: &dyn db::AstDatabase) -> u32 {
92 let mut level = 0;
93 let mut curr = self;
94 while let HirFileIdRepr::MacroFile(macro_file) = curr.0 {
95 level += 1;
96 curr = match macro_file.macro_call_id {
97 MacroCallId::LazyMacro(id) => {
98 let loc = db.lookup_intern_macro(id);
99 loc.kind.file_id()
100 }
101 MacroCallId::EagerMacro(id) => {
102 let loc = db.lookup_intern_eager_expansion(id);
103 loc.file_id
104 }
105 };
106 }
107 level
108 }
109
91 /// If this is a macro call, returns the syntax node of the call. 110 /// If this is a macro call, returns the syntax node of the call.
92 pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> { 111 pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
93 match self.0 { 112 match self.0 {
@@ -209,8 +228,13 @@ pub struct MacroDefId {
209} 228}
210 229
211impl MacroDefId { 230impl MacroDefId {
212 pub fn as_lazy_macro(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> LazyMacroId { 231 pub fn as_lazy_macro(
213 db.intern_macro(MacroCallLoc { def: self, kind }) 232 self,
233 db: &dyn db::AstDatabase,
234 krate: CrateId,
235 kind: MacroCallKind,
236 ) -> LazyMacroId {
237 db.intern_macro(MacroCallLoc { def: self, krate, kind })
214 } 238 }
215} 239}
216 240
@@ -227,6 +251,7 @@ pub enum MacroDefKind {
227#[derive(Debug, Clone, PartialEq, Eq, Hash)] 251#[derive(Debug, Clone, PartialEq, Eq, Hash)]
228pub struct MacroCallLoc { 252pub struct MacroCallLoc {
229 pub(crate) def: MacroDefId, 253 pub(crate) def: MacroDefId,
254 pub(crate) krate: CrateId,
230 pub(crate) kind: MacroCallKind, 255 pub(crate) kind: MacroCallKind,
231} 256}
232 257
@@ -274,6 +299,7 @@ pub struct EagerCallLoc {
274 pub(crate) def: MacroDefId, 299 pub(crate) def: MacroDefId,
275 pub(crate) fragment: FragmentKind, 300 pub(crate) fragment: FragmentKind,
276 pub(crate) subtree: Arc<tt::Subtree>, 301 pub(crate) subtree: Arc<tt::Subtree>,
302 pub(crate) krate: CrateId,
277 pub(crate) file_id: HirFileId, 303 pub(crate) file_id: HirFileId,
278} 304}
279 305
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index ea495cb11..969a2e5b8 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -117,7 +117,7 @@ impl AsName for ast::FieldKind {
117 117
118impl AsName for ra_db::Dependency { 118impl AsName for ra_db::Dependency {
119 fn as_name(&self) -> Name { 119 fn as_name(&self) -> Name {
120 Name::new_text(self.name.clone()) 120 Name::new_text(SmolStr::new(&*self.name))
121 } 121 }
122} 122}
123 123
@@ -153,6 +153,7 @@ pub mod known {
153 str, 153 str,
154 // Special names 154 // Special names
155 macro_rules, 155 macro_rules,
156 doc,
156 // Components of known path (value or mod name) 157 // Components of known path (value or mod name)
157 std, 158 std,
158 core, 159 core,
@@ -190,6 +191,8 @@ pub mod known {
190 stringify, 191 stringify,
191 concat, 192 concat,
192 include, 193 include,
194 include_bytes,
195 include_str,
193 format_args, 196 format_args,
194 format_args_nl, 197 format_args_nl,
195 env, 198 env,
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs
index c1fb762de..332fa556f 100644
--- a/crates/ra_hir_expand/src/test_db.rs
+++ b/crates/ra_hir_expand/src/test_db.rs
@@ -1,36 +1,35 @@
1//! Database used for testing `hir_expand`. 1//! Database used for testing `hir_expand`.
2 2
3use std::{ 3use std::{
4 panic, 4 fmt, panic,
5 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
6}; 6};
7 7
8use ra_db::{salsa, CrateId, ExternSourceId, FileId, FileLoader, FileLoaderDelegate, RelativePath}; 8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate};
9use rustc_hash::FxHashSet;
9 10
10#[salsa::database( 11#[salsa::database(
11 ra_db::SourceDatabaseExtStorage, 12 ra_db::SourceDatabaseExtStorage,
12 ra_db::SourceDatabaseStorage, 13 ra_db::SourceDatabaseStorage,
13 crate::db::AstDatabaseStorage 14 crate::db::AstDatabaseStorage
14)] 15)]
15#[derive(Debug, Default)] 16#[derive(Default)]
16pub struct TestDB { 17pub struct TestDB {
17 runtime: salsa::Runtime<TestDB>, 18 storage: salsa::Storage<TestDB>,
18 events: Mutex<Option<Vec<salsa::Event<TestDB>>>>, 19 events: Mutex<Option<Vec<salsa::Event>>>,
19} 20}
20 21
21impl salsa::Database for TestDB { 22impl fmt::Debug for TestDB {
22 fn salsa_runtime(&self) -> &salsa::Runtime<Self> { 23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 &self.runtime 24 f.debug_struct("TestDB").finish()
24 }
25
26 fn salsa_runtime_mut(&mut self) -> &mut salsa::Runtime<Self> {
27 &mut self.runtime
28 } 25 }
26}
29 27
30 fn salsa_event(&self, event: impl Fn() -> salsa::Event<TestDB>) { 28impl salsa::Database for TestDB {
29 fn salsa_event(&self, event: salsa::Event) {
31 let mut events = self.events.lock().unwrap(); 30 let mut events = self.events.lock().unwrap();
32 if let Some(events) = &mut *events { 31 if let Some(events) = &mut *events {
33 events.push(event()); 32 events.push(event);
34 } 33 }
35 } 34 }
36} 35}
@@ -41,21 +40,10 @@ impl FileLoader for TestDB {
41 fn file_text(&self, file_id: FileId) -> Arc<String> { 40 fn file_text(&self, file_id: FileId) -> Arc<String> {
42 FileLoaderDelegate(self).file_text(file_id) 41 FileLoaderDelegate(self).file_text(file_id)
43 } 42 }
44 fn resolve_relative_path( 43 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
45 &self, 44 FileLoaderDelegate(self).resolve_path(anchor, path)
46 anchor: FileId,
47 relative_path: &RelativePath,
48 ) -> Option<FileId> {
49 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
50 } 45 }
51 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 46 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
52 FileLoaderDelegate(self).relevant_crates(file_id) 47 FileLoaderDelegate(self).relevant_crates(file_id)
53 } 48 }
54 fn resolve_extern_path(
55 &self,
56 anchor: ExternSourceId,
57 relative_path: &RelativePath,
58 ) -> Option<FileId> {
59 FileLoaderDelegate(self).resolve_extern_path(anchor, relative_path)
60 }
61} 49}
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 4b8dcdc07..78f5e55bb 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_hir_ty" 3name = "ra_hir_ty"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
@@ -27,8 +28,14 @@ test_utils = { path = "../test_utils" }
27 28
28scoped-tls = "1" 29scoped-tls = "1"
29 30
30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" } 31chalk-solve = { version = "0.17.0" }
31chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" } 32chalk-ir = { version = "0.17.0" }
33chalk-recursive = { version = "0.17.0" }
32 34
33[dev-dependencies] 35[dev-dependencies]
34insta = "0.16.0" 36insta = "0.16.0"
37expect = { path = "../expect" }
38
39tracing = "0.1"
40tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
41tracing-tree = { version = "0.1.3" }
diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs
deleted file mode 100644
index 3e6e1e333..000000000
--- a/crates/ra_hir_ty/src/_match.rs
+++ /dev/null
@@ -1,2085 +0,0 @@
1//! This module implements match statement exhaustiveness checking and usefulness checking
2//! for match arms.
3//!
4//! It is modeled on the rustc module `librustc_mir_build::hair::pattern::_match`, which
5//! contains very detailed documentation about the algorithms used here. I've duplicated
6//! most of that documentation below.
7//!
8//! This file includes the logic for exhaustiveness and usefulness checking for
9//! pattern-matching. Specifically, given a list of patterns for a type, we can
10//! tell whether:
11//! (a) the patterns cover every possible constructor for the type [exhaustiveness]
12//! (b) each pattern is necessary [usefulness]
13//!
14//! The algorithm implemented here is a modified version of the one described in:
15//! http://moscova.inria.fr/~maranget/papers/warn/index.html
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
18//! (without being so rigorous).
19//!
20//! The core of the algorithm revolves about a "usefulness" check. In particular, we
21//! are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as
22//! a matrix). `U(P, p)` represents whether, given an existing list of patterns
23//! `P_1 ..= P_m`, adding a new pattern `p` will be "useful" (that is, cover previously-
24//! uncovered values of the type).
25//!
26//! If we have this predicate, then we can easily compute both exhaustiveness of an
27//! entire set of patterns and the individual usefulness of each one.
28//! (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e., adding a wildcard
29//! match doesn't increase the number of values we're matching)
30//! (b) a pattern `P_i` is not useful if `U(P[0..=(i-1), P_i)` is false (i.e., adding a
31//! pattern to those that have come before it doesn't increase the number of values
32//! we're matching).
33//!
34//! During the course of the algorithm, the rows of the matrix won't just be individual patterns,
35//! but rather partially-deconstructed patterns in the form of a list of patterns. The paper
36//! calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the
37//! new pattern `p`.
38//!
39//! For example, say we have the following:
40//! ```
41//! // x: (Option<bool>, Result<()>)
42//! match x {
43//! (Some(true), _) => {}
44//! (None, Err(())) => {}
45//! (None, Err(_)) => {}
46//! }
47//! ```
48//! Here, the matrix `P` starts as:
49//! [
50//! [(Some(true), _)],
51//! [(None, Err(()))],
52//! [(None, Err(_))],
53//! ]
54//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering
55//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because
56//! all the values it covers are already covered by row 2.
57//!
58//! A list of patterns can be thought of as a stack, because we are mainly interested in the top of
59//! the stack at any given point, and we can pop or apply constructors to get new pattern-stacks.
60//! To match the paper, the top of the stack is at the beginning / on the left.
61//!
62//! There are two important operations on pattern-stacks necessary to understand the algorithm:
63//! 1. We can pop a given constructor off the top of a stack. This operation is called
64//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or
65//! `None`) and `p` a pattern-stack.
66//! If the pattern on top of the stack can cover `c`, this removes the constructor and
67//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns.
68//! Otherwise the pattern-stack is discarded.
69//! This essentially filters those pattern-stacks whose top covers the constructor `c` and
70//! discards the others.
71//!
72//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we
73//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the
74//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get
75//! nothing back.
76//!
77//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1`
78//! on top of the stack, and we have four cases:
79//! 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We
80//! push onto the stack the arguments of this constructor, and return the result:
81//! r_1, .., r_a, p_2, .., p_n
82//! 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and
83//! return nothing.
84//! 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has
85//! arguments (its arity), and return the resulting stack:
86//! _, .., _, p_2, .., p_n
87//! 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
88//! stack:
89//! S(c, (r_1, p_2, .., p_n))
90//! S(c, (r_2, p_2, .., p_n))
91//!
92//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is
93//! a pattern-stack.
94//! This is used when we know there are missing constructor cases, but there might be
95//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check
96//! all its *other* components.
97//!
98//! It is computed as follows. We look at the pattern `p_1` on top of the stack,
99//! and we have three cases:
100//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
101//! 1.2. `p_1 = _`. We return the rest of the stack:
102//! p_2, .., p_n
103//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
104//! stack.
105//! D((r_1, p_2, .., p_n))
106//! D((r_2, p_2, .., p_n))
107//!
108//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the
109//! exhaustive integer matching rules, so they're written here for posterity.
110//!
111//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by
112//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with
113//! the given constructor, and popping a wildcard keeps those rows that start with a wildcard.
114//!
115//!
116//! The algorithm for computing `U`
117//! -------------------------------
118//! The algorithm is inductive (on the number of columns: i.e., components of tuple patterns).
119//! That means we're going to check the components from left-to-right, so the algorithm
120//! operates principally on the first component of the matrix and new pattern-stack `p`.
121//! This algorithm is realised in the `is_useful` function.
122//!
123//! Base case. (`n = 0`, i.e., an empty tuple pattern)
124//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`),
125//! then `U(P, p)` is false.
126//! - Otherwise, `P` must be empty, so `U(P, p)` is true.
127//!
128//! Inductive step. (`n > 0`, i.e., whether there's at least one column
129//! [which may then be expanded into further columns later])
130//! We're going to match on the top of the new pattern-stack, `p_1`.
131//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern.
132//! Then, the usefulness of `p_1` can be reduced to whether it is useful when
133//! we ignore all the patterns in the first column of `P` that involve other constructors.
134//! This is where `S(c, P)` comes in:
135//! `U(P, p) := U(S(c, P), S(c, p))`
136//! This special case is handled in `is_useful_specialized`.
137//!
138//! For example, if `P` is:
139//! [
140//! [Some(true), _],
141//! [None, 0],
142//! ]
143//! and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only
144//! matches values that row 2 doesn't. For row 1 however, we need to dig into the
145//! arguments of `Some` to know whether some new value is covered. So we compute
146//! `U([[true, _]], [false, 0])`.
147//!
148//! - If `p_1 == _`, then we look at the list of constructors that appear in the first
149//! component of the rows of `P`:
150//! + If there are some constructors that aren't present, then we might think that the
151//! wildcard `_` is useful, since it covers those constructors that weren't covered
152//! before.
153//! That's almost correct, but only works if there were no wildcards in those first
154//! components. So we need to check that `p` is useful with respect to the rows that
155//! start with a wildcard, if there are any. This is where `D` comes in:
156//! `U(P, p) := U(D(P), D(p))`
157//!
158//! For example, if `P` is:
159//! [
160//! [_, true, _],
161//! [None, false, 1],
162//! ]
163//! and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we
164//! only had row 2, we'd know that `p` is useful. However row 1 starts with a
165//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`.
166//!
167//! + Otherwise, all possible constructors (for the relevant type) are present. In this
168//! case we must check whether the wildcard pattern covers any unmatched value. For
169//! that, we can think of the `_` pattern as a big OR-pattern that covers all
170//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for
171//! example. The wildcard pattern is useful in this case if it is useful when
172//! specialized to one of the possible constructors. So we compute:
173//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))`
174//!
175//! For example, if `P` is:
176//! [
177//! [Some(true), _],
178//! [None, false],
179//! ]
180//! and `p` is [_, false], both `None` and `Some` constructors appear in the first
181//! components of `P`. We will therefore try popping both constructors in turn: we
182//! compute U([[true, _]], [_, false]) for the `Some` constructor, and U([[false]],
183//! [false]) for the `None` constructor. The first case returns true, so we know that
184//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched
185//! before.
186//!
187//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately:
188//! `U(P, p) := U(P, (r_1, p_2, .., p_n))
189//! || U(P, (r_2, p_2, .., p_n))`
190use std::sync::Arc;
191
192use smallvec::{smallvec, SmallVec};
193
194use crate::{
195 db::HirDatabase,
196 expr::{Body, Expr, Literal, Pat, PatId},
197 ApplicationTy, InferenceResult, Ty, TypeCtor,
198};
199use hir_def::{adt::VariantData, AdtId, EnumVariantId, VariantId};
200use ra_arena::Idx;
201
202#[derive(Debug, Clone, Copy)]
203/// Either a pattern from the source code being analyzed, represented as
204/// as `PatId`, or a `Wild` pattern which is created as an intermediate
205/// step in the match checking algorithm and thus is not backed by a
206/// real `PatId`.
207///
208/// Note that it is totally valid for the `PatId` variant to contain
209/// a `PatId` which resolves to a `Wild` pattern, if that wild pattern
210/// exists in the source code being analyzed.
211enum PatIdOrWild {
212 PatId(PatId),
213 Wild,
214}
215
216impl PatIdOrWild {
217 fn as_pat(self, cx: &MatchCheckCtx) -> Pat {
218 match self {
219 PatIdOrWild::PatId(id) => cx.body.pats[id].clone(),
220 PatIdOrWild::Wild => Pat::Wild,
221 }
222 }
223
224 fn as_id(self) -> Option<PatId> {
225 match self {
226 PatIdOrWild::PatId(id) => Some(id),
227 PatIdOrWild::Wild => None,
228 }
229 }
230}
231
232impl From<PatId> for PatIdOrWild {
233 fn from(pat_id: PatId) -> Self {
234 Self::PatId(pat_id)
235 }
236}
237
238impl From<&PatId> for PatIdOrWild {
239 fn from(pat_id: &PatId) -> Self {
240 Self::PatId(*pat_id)
241 }
242}
243
244#[derive(Debug, Clone, Copy, PartialEq)]
245pub enum MatchCheckErr {
246 NotImplemented,
247 MalformedMatchArm,
248 /// Used when type inference cannot resolve the type of
249 /// a pattern or expression.
250 Unknown,
251}
252
253/// The return type of `is_useful` is either an indication of usefulness
254/// of the match arm, or an error in the case the match statement
255/// is made up of types for which exhaustiveness checking is currently
256/// not completely implemented.
257///
258/// The `std::result::Result` type is used here rather than a custom enum
259/// to allow the use of `?`.
260pub type MatchCheckResult<T> = Result<T, MatchCheckErr>;
261
262#[derive(Debug)]
263/// A row in a Matrix.
264///
265/// This type is modeled from the struct of the same name in `rustc`.
266pub(crate) struct PatStack(PatStackInner);
267type PatStackInner = SmallVec<[PatIdOrWild; 2]>;
268
269impl PatStack {
270 pub(crate) fn from_pattern(pat_id: PatId) -> PatStack {
271 Self(smallvec!(pat_id.into()))
272 }
273
274 pub(crate) fn from_wild() -> PatStack {
275 Self(smallvec!(PatIdOrWild::Wild))
276 }
277
278 fn from_slice(slice: &[PatIdOrWild]) -> PatStack {
279 Self(SmallVec::from_slice(slice))
280 }
281
282 fn from_vec(v: PatStackInner) -> PatStack {
283 Self(v)
284 }
285
286 fn is_empty(&self) -> bool {
287 self.0.is_empty()
288 }
289
290 fn head(&self) -> PatIdOrWild {
291 self.0[0]
292 }
293
294 fn get_head(&self) -> Option<PatIdOrWild> {
295 self.0.first().copied()
296 }
297
298 fn to_tail(&self) -> PatStack {
299 Self::from_slice(&self.0[1..])
300 }
301
302 fn replace_head_with<I, T>(&self, pats: I) -> PatStack
303 where
304 I: Iterator<Item = T>,
305 T: Into<PatIdOrWild>,
306 {
307 let mut patterns: PatStackInner = smallvec![];
308 for pat in pats {
309 patterns.push(pat.into());
310 }
311 for pat in &self.0[1..] {
312 patterns.push(*pat);
313 }
314 PatStack::from_vec(patterns)
315 }
316
317 /// Computes `D(self)`.
318 ///
319 /// See the module docs and the associated documentation in rustc for details.
320 fn specialize_wildcard(&self, cx: &MatchCheckCtx) -> Option<PatStack> {
321 if matches!(self.head().as_pat(cx), Pat::Wild) {
322 Some(self.to_tail())
323 } else {
324 None
325 }
326 }
327
328 /// Computes `S(constructor, self)`.
329 ///
330 /// See the module docs and the associated documentation in rustc for details.
331 fn specialize_constructor(
332 &self,
333 cx: &MatchCheckCtx,
334 constructor: &Constructor,
335 ) -> MatchCheckResult<Option<PatStack>> {
336 let result = match (self.head().as_pat(cx), constructor) {
337 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
338 if ellipsis.is_some() {
339 // If there are ellipsis here, we should add the correct number of
340 // Pat::Wild patterns to `pat_ids`. We should be able to use the
341 // constructors arity for this, but at the time of writing we aren't
342 // correctly calculating this arity when ellipsis are present.
343 return Err(MatchCheckErr::NotImplemented);
344 }
345
346 Some(self.replace_head_with(pat_ids.iter()))
347 }
348 (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => {
349 match cx.body.exprs[lit_expr] {
350 Expr::Literal(Literal::Bool(pat_val)) if *constructor_val == pat_val => {
351 Some(self.to_tail())
352 }
353 // it was a bool but the value doesn't match
354 Expr::Literal(Literal::Bool(_)) => None,
355 // perhaps this is actually unreachable given we have
356 // already checked that these match arms have the appropriate type?
357 _ => return Err(MatchCheckErr::NotImplemented),
358 }
359 }
360 (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?),
361 (Pat::Path(_), Constructor::Enum(constructor)) => {
362 // unit enum variants become `Pat::Path`
363 let pat_id = self.head().as_id().expect("we know this isn't a wild");
364 if !enum_variant_matches(cx, pat_id, *constructor) {
365 None
366 } else {
367 Some(self.to_tail())
368 }
369 }
370 (
371 Pat::TupleStruct { args: ref pat_ids, ellipsis, .. },
372 Constructor::Enum(enum_constructor),
373 ) => {
374 let pat_id = self.head().as_id().expect("we know this isn't a wild");
375 if !enum_variant_matches(cx, pat_id, *enum_constructor) {
376 None
377 } else {
378 let constructor_arity = constructor.arity(cx)?;
379 if let Some(ellipsis_position) = ellipsis {
380 // If there are ellipsis in the pattern, the ellipsis must take the place
381 // of at least one sub-pattern, so `pat_ids` should be smaller than the
382 // constructor arity.
383 if pat_ids.len() < constructor_arity {
384 let mut new_patterns: Vec<PatIdOrWild> = vec![];
385
386 for pat_id in &pat_ids[0..ellipsis_position] {
387 new_patterns.push((*pat_id).into());
388 }
389
390 for _ in 0..(constructor_arity - pat_ids.len()) {
391 new_patterns.push(PatIdOrWild::Wild);
392 }
393
394 for pat_id in &pat_ids[ellipsis_position..pat_ids.len()] {
395 new_patterns.push((*pat_id).into());
396 }
397
398 Some(self.replace_head_with(new_patterns.into_iter()))
399 } else {
400 return Err(MatchCheckErr::MalformedMatchArm);
401 }
402 } else {
403 // If there is no ellipsis in the tuple pattern, the number
404 // of patterns must equal the constructor arity.
405 if pat_ids.len() == constructor_arity {
406 Some(self.replace_head_with(pat_ids.into_iter()))
407 } else {
408 return Err(MatchCheckErr::MalformedMatchArm);
409 }
410 }
411 }
412 }
413 (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => {
414 let pat_id = self.head().as_id().expect("we know this isn't a wild");
415 if !enum_variant_matches(cx, pat_id, *e) {
416 None
417 } else {
418 match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() {
419 VariantData::Record(struct_field_arena) => {
420 // Here we treat any missing fields in the record as the wild pattern, as
421 // if the record has ellipsis. We want to do this here even if the
422 // record does not contain ellipsis, because it allows us to continue
423 // enforcing exhaustiveness for the rest of the match statement.
424 //
425 // Creating the diagnostic for the missing field in the pattern
426 // should be done in a different diagnostic.
427 let patterns = struct_field_arena.iter().map(|(_, struct_field)| {
428 arg_patterns
429 .iter()
430 .find(|pat| pat.name == struct_field.name)
431 .map(|pat| PatIdOrWild::from(pat.pat))
432 .unwrap_or(PatIdOrWild::Wild)
433 });
434
435 Some(self.replace_head_with(patterns))
436 }
437 _ => return Err(MatchCheckErr::Unknown),
438 }
439 }
440 }
441 (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented),
442 (_, _) => return Err(MatchCheckErr::NotImplemented),
443 };
444
445 Ok(result)
446 }
447
448 /// A special case of `specialize_constructor` where the head of the pattern stack
449 /// is a Wild pattern.
450 ///
451 /// Replaces the Wild pattern at the head of the pattern stack with N Wild patterns
452 /// (N >= 0), where N is the arity of the given constructor.
453 fn expand_wildcard(
454 &self,
455 cx: &MatchCheckCtx,
456 constructor: &Constructor,
457 ) -> MatchCheckResult<PatStack> {
458 assert_eq!(
459 Pat::Wild,
460 self.head().as_pat(cx),
461 "expand_wildcard must only be called on PatStack with wild at head",
462 );
463
464 let mut patterns: PatStackInner = smallvec![];
465
466 for _ in 0..constructor.arity(cx)? {
467 patterns.push(PatIdOrWild::Wild);
468 }
469
470 for pat in &self.0[1..] {
471 patterns.push(*pat);
472 }
473
474 Ok(PatStack::from_vec(patterns))
475 }
476}
477
478#[derive(Debug)]
479/// A collection of PatStack.
480///
481/// This type is modeled from the struct of the same name in `rustc`.
482pub(crate) struct Matrix(Vec<PatStack>);
483
484impl Matrix {
485 pub(crate) fn empty() -> Self {
486 Self(vec![])
487 }
488
489 pub(crate) fn push(&mut self, cx: &MatchCheckCtx, row: PatStack) {
490 if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) {
491 // Or patterns are expanded here
492 for pat_id in pat_ids {
493 self.0.push(PatStack::from_pattern(pat_id));
494 }
495 } else {
496 self.0.push(row);
497 }
498 }
499
500 fn is_empty(&self) -> bool {
501 self.0.is_empty()
502 }
503
504 fn heads(&self) -> Vec<PatIdOrWild> {
505 self.0.iter().map(|p| p.head()).collect()
506 }
507
508 /// Computes `D(self)` for each contained PatStack.
509 ///
510 /// See the module docs and the associated documentation in rustc for details.
511 fn specialize_wildcard(&self, cx: &MatchCheckCtx) -> Self {
512 Self::collect(cx, self.0.iter().filter_map(|r| r.specialize_wildcard(cx)))
513 }
514
515 /// Computes `S(constructor, self)` for each contained PatStack.
516 ///
517 /// See the module docs and the associated documentation in rustc for details.
518 fn specialize_constructor(
519 &self,
520 cx: &MatchCheckCtx,
521 constructor: &Constructor,
522 ) -> MatchCheckResult<Self> {
523 let mut new_matrix = Matrix::empty();
524 for pat in &self.0 {
525 if let Some(pat) = pat.specialize_constructor(cx, constructor)? {
526 new_matrix.push(cx, pat);
527 }
528 }
529
530 Ok(new_matrix)
531 }
532
533 fn collect<T: IntoIterator<Item = PatStack>>(cx: &MatchCheckCtx, iter: T) -> Self {
534 let mut matrix = Matrix::empty();
535
536 for pat in iter {
537 // using push ensures we expand or-patterns
538 matrix.push(cx, pat);
539 }
540
541 matrix
542 }
543}
544
545#[derive(Clone, Debug, PartialEq)]
546/// An indication of the usefulness of a given match arm, where
547/// usefulness is defined as matching some patterns which were
548/// not matched by an prior match arms.
549///
550/// We may eventually need an `Unknown` variant here.
551pub enum Usefulness {
552 Useful,
553 NotUseful,
554}
555
556pub struct MatchCheckCtx<'a> {
557 pub match_expr: Idx<Expr>,
558 pub body: Arc<Body>,
559 pub infer: Arc<InferenceResult>,
560 pub db: &'a dyn HirDatabase,
561}
562
563/// Given a set of patterns `matrix`, and pattern to consider `v`, determines
564/// whether `v` is useful. A pattern is useful if it covers cases which were
565/// not previously covered.
566///
567/// When calling this function externally (that is, not the recursive calls) it
568/// expected that you have already type checked the match arms. All patterns in
569/// matrix should be the same type as v, as well as they should all be the same
570/// type as the match expression.
571pub(crate) fn is_useful(
572 cx: &MatchCheckCtx,
573 matrix: &Matrix,
574 v: &PatStack,
575) -> MatchCheckResult<Usefulness> {
576 // Handle two special cases:
577 // - enum with no variants
578 // - `!` type
579 // In those cases, no match arm is useful.
580 match cx.infer[cx.match_expr].strip_references() {
581 Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(AdtId::EnumId(enum_id)), .. }) => {
582 if cx.db.enum_data(*enum_id).variants.is_empty() {
583 return Ok(Usefulness::NotUseful);
584 }
585 }
586 Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) => {
587 return Ok(Usefulness::NotUseful);
588 }
589 _ => (),
590 }
591
592 if v.is_empty() {
593 let result = if matrix.is_empty() { Usefulness::Useful } else { Usefulness::NotUseful };
594
595 return Ok(result);
596 }
597
598 if let Pat::Or(pat_ids) = v.head().as_pat(cx) {
599 let mut found_unimplemented = false;
600 let any_useful = pat_ids.iter().any(|&pat_id| {
601 let v = PatStack::from_pattern(pat_id);
602
603 match is_useful(cx, matrix, &v) {
604 Ok(Usefulness::Useful) => true,
605 Ok(Usefulness::NotUseful) => false,
606 _ => {
607 found_unimplemented = true;
608 false
609 }
610 }
611 });
612
613 return if any_useful {
614 Ok(Usefulness::Useful)
615 } else if found_unimplemented {
616 Err(MatchCheckErr::NotImplemented)
617 } else {
618 Ok(Usefulness::NotUseful)
619 };
620 }
621
622 if let Some(constructor) = pat_constructor(cx, v.head())? {
623 let matrix = matrix.specialize_constructor(&cx, &constructor)?;
624 let v = v
625 .specialize_constructor(&cx, &constructor)?
626 .expect("we know this can't fail because we get the constructor from `v.head()` above");
627
628 is_useful(&cx, &matrix, &v)
629 } else {
630 // expanding wildcard
631 let mut used_constructors: Vec<Constructor> = vec![];
632 for pat in matrix.heads() {
633 if let Some(constructor) = pat_constructor(cx, pat)? {
634 used_constructors.push(constructor);
635 }
636 }
637
638 // We assume here that the first constructor is the "correct" type. Since we
639 // only care about the "type" of the constructor (i.e. if it is a bool we
640 // don't care about the value), this assumption should be valid as long as
641 // the match statement is well formed. We currently uphold this invariant by
642 // filtering match arms before calling `is_useful`, only passing in match arms
643 // whose type matches the type of the match expression.
644 match &used_constructors.first() {
645 Some(constructor) if all_constructors_covered(&cx, constructor, &used_constructors) => {
646 // If all constructors are covered, then we need to consider whether
647 // any values are covered by this wildcard.
648 //
649 // For example, with matrix '[[Some(true)], [None]]', all
650 // constructors are covered (`Some`/`None`), so we need
651 // to perform specialization to see that our wildcard will cover
652 // the `Some(false)` case.
653 //
654 // Here we create a constructor for each variant and then check
655 // usefulness after specializing for that constructor.
656 let mut found_unimplemented = false;
657 for constructor in constructor.all_constructors(cx) {
658 let matrix = matrix.specialize_constructor(&cx, &constructor)?;
659 let v = v.expand_wildcard(&cx, &constructor)?;
660
661 match is_useful(&cx, &matrix, &v) {
662 Ok(Usefulness::Useful) => return Ok(Usefulness::Useful),
663 Ok(Usefulness::NotUseful) => continue,
664 _ => found_unimplemented = true,
665 };
666 }
667
668 if found_unimplemented {
669 Err(MatchCheckErr::NotImplemented)
670 } else {
671 Ok(Usefulness::NotUseful)
672 }
673 }
674 _ => {
675 // Either not all constructors are covered, or the only other arms
676 // are wildcards. Either way, this pattern is useful if it is useful
677 // when compared to those arms with wildcards.
678 let matrix = matrix.specialize_wildcard(&cx);
679 let v = v.to_tail();
680
681 is_useful(&cx, &matrix, &v)
682 }
683 }
684 }
685}
686
687#[derive(Debug, Clone, Copy)]
688/// Similar to TypeCtor, but includes additional information about the specific
689/// value being instantiated. For example, TypeCtor::Bool doesn't contain the
690/// boolean value.
691enum Constructor {
692 Bool(bool),
693 Tuple { arity: usize },
694 Enum(EnumVariantId),
695}
696
697impl Constructor {
698 fn arity(&self, cx: &MatchCheckCtx) -> MatchCheckResult<usize> {
699 let arity = match self {
700 Constructor::Bool(_) => 0,
701 Constructor::Tuple { arity } => *arity,
702 Constructor::Enum(e) => {
703 match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() {
704 VariantData::Tuple(struct_field_data) => struct_field_data.len(),
705 VariantData::Record(struct_field_data) => struct_field_data.len(),
706 VariantData::Unit => 0,
707 }
708 }
709 };
710
711 Ok(arity)
712 }
713
714 fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> {
715 match self {
716 Constructor::Bool(_) => vec![Constructor::Bool(true), Constructor::Bool(false)],
717 Constructor::Tuple { .. } => vec![*self],
718 Constructor::Enum(e) => cx
719 .db
720 .enum_data(e.parent)
721 .variants
722 .iter()
723 .map(|(local_id, _)| {
724 Constructor::Enum(EnumVariantId { parent: e.parent, local_id })
725 })
726 .collect(),
727 }
728 }
729}
730
731/// Returns the constructor for the given pattern. Should only return None
732/// in the case of a Wild pattern.
733fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> {
734 let res = match pat.as_pat(cx) {
735 Pat::Wild => None,
736 // FIXME somehow create the Tuple constructor with the proper arity. If there are
737 // ellipsis, the arity is not equal to the number of patterns.
738 Pat::Tuple { args: pats, ellipsis } if ellipsis.is_none() => {
739 Some(Constructor::Tuple { arity: pats.len() })
740 }
741 Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] {
742 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)),
743 _ => return Err(MatchCheckErr::NotImplemented),
744 },
745 Pat::TupleStruct { .. } | Pat::Path(_) | Pat::Record { .. } => {
746 let pat_id = pat.as_id().expect("we already know this pattern is not a wild");
747 let variant_id =
748 cx.infer.variant_resolution_for_pat(pat_id).ok_or(MatchCheckErr::Unknown)?;
749 match variant_id {
750 VariantId::EnumVariantId(enum_variant_id) => {
751 Some(Constructor::Enum(enum_variant_id))
752 }
753 _ => return Err(MatchCheckErr::NotImplemented),
754 }
755 }
756 _ => return Err(MatchCheckErr::NotImplemented),
757 };
758
759 Ok(res)
760}
761
762fn all_constructors_covered(
763 cx: &MatchCheckCtx,
764 constructor: &Constructor,
765 used_constructors: &[Constructor],
766) -> bool {
767 match constructor {
768 Constructor::Tuple { arity } => {
769 used_constructors.iter().any(|constructor| match constructor {
770 Constructor::Tuple { arity: used_arity } => arity == used_arity,
771 _ => false,
772 })
773 }
774 Constructor::Bool(_) => {
775 if used_constructors.is_empty() {
776 return false;
777 }
778
779 let covers_true =
780 used_constructors.iter().any(|c| matches!(c, Constructor::Bool(true)));
781 let covers_false =
782 used_constructors.iter().any(|c| matches!(c, Constructor::Bool(false)));
783
784 covers_true && covers_false
785 }
786 Constructor::Enum(e) => cx.db.enum_data(e.parent).variants.iter().all(|(id, _)| {
787 for constructor in used_constructors {
788 if let Constructor::Enum(e) = constructor {
789 if id == e.local_id {
790 return true;
791 }
792 }
793 }
794
795 false
796 }),
797 }
798}
799
800fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: EnumVariantId) -> bool {
801 Some(enum_variant_id.into()) == cx.infer.variant_resolution_for_pat(pat_id)
802}
803
804#[cfg(test)]
805mod tests {
806 pub(super) use insta::assert_snapshot;
807 pub(super) use ra_db::fixture::WithFixture;
808
809 pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB};
810
811 pub(super) fn check_diagnostic_message(content: &str) -> String {
812 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().0
813 }
814
815 pub(super) fn check_diagnostic(content: &str) {
816 let diagnostic_count =
817 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().1;
818
819 assert_eq!(1, diagnostic_count, "no diagnostic reported");
820 }
821
822 pub(super) fn check_no_diagnostic(content: &str) {
823 let diagnostic_count =
824 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().1;
825
826 assert_eq!(0, diagnostic_count, "expected no diagnostic, found one");
827 }
828
829 #[test]
830 fn empty_tuple_no_arms_diagnostic_message() {
831 let content = r"
832 fn test_fn() {
833 match () {
834 }
835 }
836 ";
837
838 assert_snapshot!(
839 check_diagnostic_message(content),
840 @"\"()\": Missing match arm\n"
841 );
842 }
843
844 #[test]
845 fn empty_tuple_no_arms() {
846 let content = r"
847 fn test_fn() {
848 match () {
849 }
850 }
851 ";
852
853 check_diagnostic(content);
854 }
855
856 #[test]
857 fn empty_tuple_wild() {
858 let content = r"
859 fn test_fn() {
860 match () {
861 _ => {}
862 }
863 }
864 ";
865
866 check_no_diagnostic(content);
867 }
868
869 #[test]
870 fn empty_tuple_no_diagnostic() {
871 let content = r"
872 fn test_fn() {
873 match () {
874 () => {}
875 }
876 }
877 ";
878
879 check_no_diagnostic(content);
880 }
881
882 #[test]
883 fn tuple_of_empty_tuple_no_arms() {
884 let content = r"
885 fn test_fn() {
886 match (()) {
887 }
888 }
889 ";
890
891 check_diagnostic(content);
892 }
893
894 #[test]
895 fn tuple_of_empty_tuple_no_diagnostic() {
896 let content = r"
897 fn test_fn() {
898 match (()) {
899 (()) => {}
900 }
901 }
902 ";
903
904 check_no_diagnostic(content);
905 }
906
907 #[test]
908 fn tuple_of_two_empty_tuple_no_arms() {
909 let content = r"
910 fn test_fn() {
911 match ((), ()) {
912 }
913 }
914 ";
915
916 check_diagnostic(content);
917 }
918
919 #[test]
920 fn tuple_of_two_empty_tuple_no_diagnostic() {
921 let content = r"
922 fn test_fn() {
923 match ((), ()) {
924 ((), ()) => {}
925 }
926 }
927 ";
928
929 check_no_diagnostic(content);
930 }
931
932 #[test]
933 fn bool_no_arms() {
934 let content = r"
935 fn test_fn() {
936 match false {
937 }
938 }
939 ";
940
941 check_diagnostic(content);
942 }
943
944 #[test]
945 fn bool_missing_arm() {
946 let content = r"
947 fn test_fn() {
948 match false {
949 true => {}
950 }
951 }
952 ";
953
954 check_diagnostic(content);
955 }
956
957 #[test]
958 fn bool_no_diagnostic() {
959 let content = r"
960 fn test_fn() {
961 match false {
962 true => {}
963 false => {}
964 }
965 }
966 ";
967
968 check_no_diagnostic(content);
969 }
970
971 #[test]
972 fn tuple_of_bools_no_arms() {
973 let content = r"
974 fn test_fn() {
975 match (false, true) {
976 }
977 }
978 ";
979
980 check_diagnostic(content);
981 }
982
983 #[test]
984 fn tuple_of_bools_missing_arms() {
985 let content = r"
986 fn test_fn() {
987 match (false, true) {
988 (true, true) => {},
989 }
990 }
991 ";
992
993 check_diagnostic(content);
994 }
995
996 #[test]
997 fn tuple_of_bools_missing_arm() {
998 let content = r"
999 fn test_fn() {
1000 match (false, true) {
1001 (false, true) => {},
1002 (false, false) => {},
1003 (true, false) => {},
1004 }
1005 }
1006 ";
1007
1008 check_diagnostic(content);
1009 }
1010
1011 #[test]
1012 fn tuple_of_bools_with_wilds() {
1013 let content = r"
1014 fn test_fn() {
1015 match (false, true) {
1016 (false, _) => {},
1017 (true, false) => {},
1018 (_, true) => {},
1019 }
1020 }
1021 ";
1022
1023 check_no_diagnostic(content);
1024 }
1025
1026 #[test]
1027 fn tuple_of_bools_no_diagnostic() {
1028 let content = r"
1029 fn test_fn() {
1030 match (false, true) {
1031 (true, true) => {},
1032 (true, false) => {},
1033 (false, true) => {},
1034 (false, false) => {},
1035 }
1036 }
1037 ";
1038
1039 check_no_diagnostic(content);
1040 }
1041
1042 #[test]
1043 fn tuple_of_bools_binding_missing_arms() {
1044 let content = r"
1045 fn test_fn() {
1046 match (false, true) {
1047 (true, _x) => {},
1048 }
1049 }
1050 ";
1051
1052 check_diagnostic(content);
1053 }
1054
1055 #[test]
1056 fn tuple_of_bools_binding_no_diagnostic() {
1057 let content = r"
1058 fn test_fn() {
1059 match (false, true) {
1060 (true, _x) => {},
1061 (false, true) => {},
1062 (false, false) => {},
1063 }
1064 }
1065 ";
1066
1067 check_no_diagnostic(content);
1068 }
1069
1070 #[test]
1071 fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() {
1072 let content = r"
1073 fn test_fn() {
1074 match (false, true, false) {
1075 (false, ..) => {},
1076 (true, ..) => {},
1077 }
1078 }
1079 ";
1080
1081 check_no_diagnostic(content);
1082 }
1083
1084 #[test]
1085 fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() {
1086 let content = r"
1087 fn test_fn() {
1088 match (false, true, false) {
1089 (.., false) => {},
1090 (.., true) => {},
1091 }
1092 }
1093 ";
1094
1095 check_no_diagnostic(content);
1096 }
1097
1098 #[test]
1099 fn tuple_of_bools_with_ellipsis_no_diagnostic() {
1100 let content = r"
1101 fn test_fn() {
1102 match (false, true, false) {
1103 (..) => {},
1104 }
1105 }
1106 ";
1107
1108 check_no_diagnostic(content);
1109 }
1110
1111 #[test]
1112 fn tuple_of_tuple_and_bools_no_arms() {
1113 let content = r"
1114 fn test_fn() {
1115 match (false, ((), false)) {
1116 }
1117 }
1118 ";
1119
1120 check_diagnostic(content);
1121 }
1122
1123 #[test]
1124 fn tuple_of_tuple_and_bools_missing_arms() {
1125 let content = r"
1126 fn test_fn() {
1127 match (false, ((), false)) {
1128 (true, ((), true)) => {},
1129 }
1130 }
1131 ";
1132
1133 check_diagnostic(content);
1134 }
1135
1136 #[test]
1137 fn tuple_of_tuple_and_bools_no_diagnostic() {
1138 let content = r"
1139 fn test_fn() {
1140 match (false, ((), false)) {
1141 (true, ((), true)) => {},
1142 (true, ((), false)) => {},
1143 (false, ((), true)) => {},
1144 (false, ((), false)) => {},
1145 }
1146 }
1147 ";
1148
1149 check_no_diagnostic(content);
1150 }
1151
1152 #[test]
1153 fn tuple_of_tuple_and_bools_wildcard_missing_arms() {
1154 let content = r"
1155 fn test_fn() {
1156 match (false, ((), false)) {
1157 (true, _) => {},
1158 }
1159 }
1160 ";
1161
1162 check_diagnostic(content);
1163 }
1164
1165 #[test]
1166 fn tuple_of_tuple_and_bools_wildcard_no_diagnostic() {
1167 let content = r"
1168 fn test_fn() {
1169 match (false, ((), false)) {
1170 (true, ((), true)) => {},
1171 (true, ((), false)) => {},
1172 (false, _) => {},
1173 }
1174 }
1175 ";
1176
1177 check_no_diagnostic(content);
1178 }
1179
1180 #[test]
1181 fn enum_no_arms() {
1182 let content = r"
1183 enum Either {
1184 A,
1185 B,
1186 }
1187 fn test_fn() {
1188 match Either::A {
1189 }
1190 }
1191 ";
1192
1193 check_diagnostic(content);
1194 }
1195
1196 #[test]
1197 fn enum_missing_arms() {
1198 let content = r"
1199 enum Either {
1200 A,
1201 B,
1202 }
1203 fn test_fn() {
1204 match Either::B {
1205 Either::A => {},
1206 }
1207 }
1208 ";
1209
1210 check_diagnostic(content);
1211 }
1212
1213 #[test]
1214 fn enum_no_diagnostic() {
1215 let content = r"
1216 enum Either {
1217 A,
1218 B,
1219 }
1220 fn test_fn() {
1221 match Either::B {
1222 Either::A => {},
1223 Either::B => {},
1224 }
1225 }
1226 ";
1227
1228 check_no_diagnostic(content);
1229 }
1230
1231 #[test]
1232 fn enum_ref_missing_arms() {
1233 let content = r"
1234 enum Either {
1235 A,
1236 B,
1237 }
1238 fn test_fn() {
1239 match &Either::B {
1240 Either::A => {},
1241 }
1242 }
1243 ";
1244
1245 check_diagnostic(content);
1246 }
1247
1248 #[test]
1249 fn enum_ref_no_diagnostic() {
1250 let content = r"
1251 enum Either {
1252 A,
1253 B,
1254 }
1255 fn test_fn() {
1256 match &Either::B {
1257 Either::A => {},
1258 Either::B => {},
1259 }
1260 }
1261 ";
1262
1263 check_no_diagnostic(content);
1264 }
1265
1266 #[test]
1267 fn enum_containing_bool_no_arms() {
1268 let content = r"
1269 enum Either {
1270 A(bool),
1271 B,
1272 }
1273 fn test_fn() {
1274 match Either::B {
1275 }
1276 }
1277 ";
1278
1279 check_diagnostic(content);
1280 }
1281
1282 #[test]
1283 fn enum_containing_bool_missing_arms() {
1284 let content = r"
1285 enum Either {
1286 A(bool),
1287 B,
1288 }
1289 fn test_fn() {
1290 match Either::B {
1291 Either::A(true) => (),
1292 Either::B => (),
1293 }
1294 }
1295 ";
1296
1297 check_diagnostic(content);
1298 }
1299
1300 #[test]
1301 fn enum_containing_bool_no_diagnostic() {
1302 let content = r"
1303 enum Either {
1304 A(bool),
1305 B,
1306 }
1307 fn test_fn() {
1308 match Either::B {
1309 Either::A(true) => (),
1310 Either::A(false) => (),
1311 Either::B => (),
1312 }
1313 }
1314 ";
1315
1316 check_no_diagnostic(content);
1317 }
1318
1319 #[test]
1320 fn enum_containing_bool_with_wild_no_diagnostic() {
1321 let content = r"
1322 enum Either {
1323 A(bool),
1324 B,
1325 }
1326 fn test_fn() {
1327 match Either::B {
1328 Either::B => (),
1329 _ => (),
1330 }
1331 }
1332 ";
1333
1334 check_no_diagnostic(content);
1335 }
1336
1337 #[test]
1338 fn enum_containing_bool_with_wild_2_no_diagnostic() {
1339 let content = r"
1340 enum Either {
1341 A(bool),
1342 B,
1343 }
1344 fn test_fn() {
1345 match Either::B {
1346 Either::A(_) => (),
1347 Either::B => (),
1348 }
1349 }
1350 ";
1351
1352 check_no_diagnostic(content);
1353 }
1354
1355 #[test]
1356 fn enum_different_sizes_missing_arms() {
1357 let content = r"
1358 enum Either {
1359 A(bool),
1360 B(bool, bool),
1361 }
1362 fn test_fn() {
1363 match Either::A(false) {
1364 Either::A(_) => (),
1365 Either::B(false, _) => (),
1366 }
1367 }
1368 ";
1369
1370 check_diagnostic(content);
1371 }
1372
1373 #[test]
1374 fn enum_different_sizes_no_diagnostic() {
1375 let content = r"
1376 enum Either {
1377 A(bool),
1378 B(bool, bool),
1379 }
1380 fn test_fn() {
1381 match Either::A(false) {
1382 Either::A(_) => (),
1383 Either::B(true, _) => (),
1384 Either::B(false, _) => (),
1385 }
1386 }
1387 ";
1388
1389 check_no_diagnostic(content);
1390 }
1391
1392 #[test]
1393 fn or_no_diagnostic() {
1394 let content = r"
1395 enum Either {
1396 A(bool),
1397 B(bool, bool),
1398 }
1399 fn test_fn() {
1400 match Either::A(false) {
1401 Either::A(true) | Either::A(false) => (),
1402 Either::B(true, _) => (),
1403 Either::B(false, _) => (),
1404 }
1405 }
1406 ";
1407
1408 check_no_diagnostic(content);
1409 }
1410
1411 #[test]
1412 fn tuple_of_enum_no_diagnostic() {
1413 let content = r"
1414 enum Either {
1415 A(bool),
1416 B(bool, bool),
1417 }
1418 enum Either2 {
1419 C,
1420 D,
1421 }
1422 fn test_fn() {
1423 match (Either::A(false), Either2::C) {
1424 (Either::A(true), _) | (Either::A(false), _) => (),
1425 (Either::B(true, _), Either2::C) => (),
1426 (Either::B(false, _), Either2::C) => (),
1427 (Either::B(_, _), Either2::D) => (),
1428 }
1429 }
1430 ";
1431
1432 check_no_diagnostic(content);
1433 }
1434
1435 #[test]
1436 fn mismatched_types() {
1437 let content = r"
1438 enum Either {
1439 A,
1440 B,
1441 }
1442 enum Either2 {
1443 C,
1444 D,
1445 }
1446 fn test_fn() {
1447 match Either::A {
1448 Either2::C => (),
1449 Either2::D => (),
1450 }
1451 }
1452 ";
1453
1454 // Match statements with arms that don't match the
1455 // expression pattern do not fire this diagnostic.
1456 check_no_diagnostic(content);
1457 }
1458
1459 #[test]
1460 fn mismatched_types_with_different_arity() {
1461 let content = r"
1462 fn test_fn() {
1463 match (true, false) {
1464 (true, false, true) => (),
1465 (true) => (),
1466 }
1467 }
1468 ";
1469
1470 // Match statements with arms that don't match the
1471 // expression pattern do not fire this diagnostic.
1472 check_no_diagnostic(content);
1473 }
1474
1475 #[test]
1476 fn malformed_match_arm_tuple_missing_pattern() {
1477 let content = r"
1478 fn test_fn() {
1479 match (0) {
1480 () => (),
1481 }
1482 }
1483 ";
1484
1485 // Match statements with arms that don't match the
1486 // expression pattern do not fire this diagnostic.
1487 check_no_diagnostic(content);
1488 }
1489
1490 #[test]
1491 fn malformed_match_arm_tuple_enum_missing_pattern() {
1492 let content = r"
1493 enum Either {
1494 A,
1495 B(u32),
1496 }
1497 fn test_fn() {
1498 match Either::A {
1499 Either::A => (),
1500 Either::B() => (),
1501 }
1502 }
1503 ";
1504
1505 // We are testing to be sure we don't panic here when the match
1506 // arm `Either::B` is missing its pattern.
1507 check_no_diagnostic(content);
1508 }
1509
1510 #[test]
1511 fn enum_not_in_scope() {
1512 let content = r"
1513 fn test_fn() {
1514 match Foo::Bar {
1515 Foo::Baz => (),
1516 }
1517 }
1518 ";
1519
1520 // The enum is not in scope so we don't perform exhaustiveness
1521 // checking, but we want to be sure we don't panic here (and
1522 // we don't create a diagnostic).
1523 check_no_diagnostic(content);
1524 }
1525
1526 #[test]
1527 fn expr_diverges() {
1528 let content = r"
1529 enum Either {
1530 A,
1531 B,
1532 }
1533 fn test_fn() {
1534 match loop {} {
1535 Either::A => (),
1536 Either::B => (),
1537 }
1538 }
1539 ";
1540
1541 check_no_diagnostic(content);
1542 }
1543
1544 #[test]
1545 fn expr_loop_with_break() {
1546 let content = r"
1547 enum Either {
1548 A,
1549 B,
1550 }
1551 fn test_fn() {
1552 match loop { break Foo::A } {
1553 Either::A => (),
1554 Either::B => (),
1555 }
1556 }
1557 ";
1558
1559 check_no_diagnostic(content);
1560 }
1561
1562 #[test]
1563 fn expr_partially_diverges() {
1564 let content = r"
1565 enum Either<T> {
1566 A(T),
1567 B,
1568 }
1569 fn foo() -> Either<!> {
1570 Either::B
1571 }
1572 fn test_fn() -> u32 {
1573 match foo() {
1574 Either::A(val) => val,
1575 Either::B => 0,
1576 }
1577 }
1578 ";
1579
1580 check_no_diagnostic(content);
1581 }
1582
1583 #[test]
1584 fn enum_record_no_arms() {
1585 let content = r"
1586 enum Either {
1587 A { foo: bool },
1588 B,
1589 }
1590 fn test_fn() {
1591 let a = Either::A { foo: true };
1592 match a {
1593 }
1594 }
1595 ";
1596
1597 check_diagnostic(content);
1598 }
1599
1600 #[test]
1601 fn enum_record_missing_arms() {
1602 let content = r"
1603 enum Either {
1604 A { foo: bool },
1605 B,
1606 }
1607 fn test_fn() {
1608 let a = Either::A { foo: true };
1609 match a {
1610 Either::A { foo: true } => (),
1611 }
1612 }
1613 ";
1614
1615 check_diagnostic(content);
1616 }
1617
1618 #[test]
1619 fn enum_record_no_diagnostic() {
1620 let content = r"
1621 enum Either {
1622 A { foo: bool },
1623 B,
1624 }
1625 fn test_fn() {
1626 let a = Either::A { foo: true };
1627 match a {
1628 Either::A { foo: true } => (),
1629 Either::A { foo: false } => (),
1630 Either::B => (),
1631 }
1632 }
1633 ";
1634
1635 check_no_diagnostic(content);
1636 }
1637
1638 #[test]
1639 fn enum_record_missing_field_no_diagnostic() {
1640 let content = r"
1641 enum Either {
1642 A { foo: bool },
1643 B,
1644 }
1645 fn test_fn() {
1646 let a = Either::B;
1647 match a {
1648 Either::A { } => (),
1649 Either::B => (),
1650 }
1651 }
1652 ";
1653
1654 // When `Either::A` is missing a struct member, we don't want
1655 // to fire the missing match arm diagnostic. This should fire
1656 // some other diagnostic.
1657 check_no_diagnostic(content);
1658 }
1659
1660 #[test]
1661 fn enum_record_missing_field_missing_match_arm() {
1662 let content = r"
1663 enum Either {
1664 A { foo: bool },
1665 B,
1666 }
1667 fn test_fn() {
1668 let a = Either::B;
1669 match a {
1670 Either::A { } => (),
1671 }
1672 }
1673 ";
1674
1675 // Even though `Either::A` is missing fields, we still want to fire
1676 // the missing arm diagnostic here, since we know `Either::B` is missing.
1677 check_diagnostic(content);
1678 }
1679
1680 #[test]
1681 fn enum_record_no_diagnostic_wild() {
1682 let content = r"
1683 enum Either {
1684 A { foo: bool },
1685 B,
1686 }
1687 fn test_fn() {
1688 let a = Either::A { foo: true };
1689 match a {
1690 Either::A { foo: _ } => (),
1691 Either::B => (),
1692 }
1693 }
1694 ";
1695
1696 check_no_diagnostic(content);
1697 }
1698
1699 #[test]
1700 fn enum_record_fields_out_of_order_missing_arm() {
1701 let content = r"
1702 enum Either {
1703 A { foo: bool, bar: () },
1704 B,
1705 }
1706 fn test_fn() {
1707 let a = Either::A { foo: true };
1708 match a {
1709 Either::A { bar: (), foo: false } => (),
1710 Either::A { foo: true, bar: () } => (),
1711 }
1712 }
1713 ";
1714
1715 check_diagnostic(content);
1716 }
1717
1718 #[test]
1719 fn enum_record_fields_out_of_order_no_diagnostic() {
1720 let content = r"
1721 enum Either {
1722 A { foo: bool, bar: () },
1723 B,
1724 }
1725 fn test_fn() {
1726 let a = Either::A { foo: true };
1727 match a {
1728 Either::A { bar: (), foo: false } => (),
1729 Either::A { foo: true, bar: () } => (),
1730 Either::B => (),
1731 }
1732 }
1733 ";
1734
1735 check_no_diagnostic(content);
1736 }
1737
1738 #[test]
1739 fn enum_record_ellipsis_missing_arm() {
1740 let content = r"
1741 enum Either {
1742 A { foo: bool, bar: bool },
1743 B,
1744 }
1745 fn test_fn() {
1746 match Either::B {
1747 Either::A { foo: true, .. } => (),
1748 Either::B => (),
1749 }
1750 }
1751 ";
1752
1753 check_diagnostic(content);
1754 }
1755
1756 #[test]
1757 fn enum_record_ellipsis_no_diagnostic() {
1758 let content = r"
1759 enum Either {
1760 A { foo: bool, bar: bool },
1761 B,
1762 }
1763 fn test_fn() {
1764 let a = Either::A { foo: true };
1765 match a {
1766 Either::A { foo: true, .. } => (),
1767 Either::A { foo: false, .. } => (),
1768 Either::B => (),
1769 }
1770 }
1771 ";
1772
1773 check_no_diagnostic(content);
1774 }
1775
1776 #[test]
1777 fn enum_record_ellipsis_all_fields_missing_arm() {
1778 let content = r"
1779 enum Either {
1780 A { foo: bool, bar: bool },
1781 B,
1782 }
1783 fn test_fn() {
1784 let a = Either::B;
1785 match a {
1786 Either::A { .. } => (),
1787 }
1788 }
1789 ";
1790
1791 check_diagnostic(content);
1792 }
1793
1794 #[test]
1795 fn enum_record_ellipsis_all_fields_no_diagnostic() {
1796 let content = r"
1797 enum Either {
1798 A { foo: bool, bar: bool },
1799 B,
1800 }
1801 fn test_fn() {
1802 let a = Either::B;
1803 match a {
1804 Either::A { .. } => (),
1805 Either::B => (),
1806 }
1807 }
1808 ";
1809
1810 check_no_diagnostic(content);
1811 }
1812
1813 #[test]
1814 fn enum_tuple_partial_ellipsis_no_diagnostic() {
1815 let content = r"
1816 enum Either {
1817 A(bool, bool, bool, bool),
1818 B,
1819 }
1820 fn test_fn() {
1821 match Either::B {
1822 Either::A(true, .., true) => {},
1823 Either::A(true, .., false) => {},
1824 Either::A(false, .., true) => {},
1825 Either::A(false, .., false) => {},
1826 Either::B => {},
1827 }
1828 }
1829 ";
1830
1831 check_no_diagnostic(content);
1832 }
1833
1834 #[test]
1835 fn enum_tuple_partial_ellipsis_2_no_diagnostic() {
1836 let content = r"
1837 enum Either {
1838 A(bool, bool, bool, bool),
1839 B,
1840 }
1841 fn test_fn() {
1842 match Either::B {
1843 Either::A(true, .., true) => {},
1844 Either::A(true, .., false) => {},
1845 Either::A(.., true) => {},
1846 Either::A(.., false) => {},
1847 Either::B => {},
1848 }
1849 }
1850 ";
1851
1852 check_no_diagnostic(content);
1853 }
1854
1855 #[test]
1856 fn enum_tuple_partial_ellipsis_missing_arm() {
1857 let content = r"
1858 enum Either {
1859 A(bool, bool, bool, bool),
1860 B,
1861 }
1862 fn test_fn() {
1863 match Either::B {
1864 Either::A(true, .., true) => {},
1865 Either::A(true, .., false) => {},
1866 Either::A(false, .., false) => {},
1867 Either::B => {},
1868 }
1869 }
1870 ";
1871
1872 check_diagnostic(content);
1873 }
1874
1875 #[test]
1876 fn enum_tuple_partial_ellipsis_2_missing_arm() {
1877 let content = r"
1878 enum Either {
1879 A(bool, bool, bool, bool),
1880 B,
1881 }
1882 fn test_fn() {
1883 match Either::B {
1884 Either::A(true, .., true) => {},
1885 Either::A(true, .., false) => {},
1886 Either::A(.., true) => {},
1887 Either::B => {},
1888 }
1889 }
1890 ";
1891
1892 check_diagnostic(content);
1893 }
1894
1895 #[test]
1896 fn enum_tuple_ellipsis_no_diagnostic() {
1897 let content = r"
1898 enum Either {
1899 A(bool, bool, bool, bool),
1900 B,
1901 }
1902 fn test_fn() {
1903 match Either::B {
1904 Either::A(..) => {},
1905 Either::B => {},
1906 }
1907 }
1908 ";
1909
1910 check_no_diagnostic(content);
1911 }
1912
1913 #[test]
1914 fn enum_never() {
1915 let content = r"
1916 enum Never {}
1917
1918 fn test_fn(never: Never) {
1919 match never {}
1920 }
1921 ";
1922
1923 check_no_diagnostic(content);
1924 }
1925
1926 #[test]
1927 fn type_never() {
1928 let content = r"
1929 fn test_fn(never: !) {
1930 match never {}
1931 }
1932 ";
1933
1934 check_no_diagnostic(content);
1935 }
1936
1937 #[test]
1938 fn enum_never_ref() {
1939 let content = r"
1940 enum Never {}
1941
1942 fn test_fn(never: &Never) {
1943 match never {}
1944 }
1945 ";
1946
1947 check_no_diagnostic(content);
1948 }
1949
1950 #[test]
1951 fn expr_diverges_missing_arm() {
1952 let content = r"
1953 enum Either {
1954 A,
1955 B,
1956 }
1957 fn test_fn() {
1958 match loop {} {
1959 Either::A => (),
1960 }
1961 }
1962 ";
1963
1964 check_no_diagnostic(content);
1965 }
1966}
1967
1968#[cfg(test)]
1969mod false_negatives {
1970 //! The implementation of match checking here is a work in progress. As we roll this out, we
1971 //! prefer false negatives to false positives (ideally there would be no false positives). This
1972 //! test module should document known false negatives. Eventually we will have a complete
1973 //! implementation of match checking and this module will be empty.
1974 //!
1975 //! The reasons for documenting known false negatives:
1976 //!
1977 //! 1. It acts as a backlog of work that can be done to improve the behavior of the system.
1978 //! 2. It ensures the code doesn't panic when handling these cases.
1979
1980 use super::tests::*;
1981
1982 #[test]
1983 fn integers() {
1984 let content = r"
1985 fn test_fn() {
1986 match 5 {
1987 10 => (),
1988 11..20 => (),
1989 }
1990 }
1991 ";
1992
1993 // This is a false negative.
1994 // We don't currently check integer exhaustiveness.
1995 check_no_diagnostic(content);
1996 }
1997
1998 #[test]
1999 fn internal_or() {
2000 let content = r"
2001 fn test_fn() {
2002 enum Either {
2003 A(bool),
2004 B,
2005 }
2006 match Either::B {
2007 Either::A(true | false) => (),
2008 }
2009 }
2010 ";
2011
2012 // This is a false negative.
2013 // We do not currently handle patterns with internal `or`s.
2014 check_no_diagnostic(content);
2015 }
2016
2017 #[test]
2018 fn expr_loop_missing_arm() {
2019 let content = r"
2020 enum Either {
2021 A,
2022 B,
2023 }
2024 fn test_fn() {
2025 match loop { break Foo::A } {
2026 Either::A => (),
2027 }
2028 }
2029 ";
2030
2031 // This is a false negative.
2032 // We currently infer the type of `loop { break Foo::A }` to `!`, which
2033 // causes us to skip the diagnostic since `Either::A` doesn't type check
2034 // with `!`.
2035 check_diagnostic(content);
2036 }
2037
2038 #[test]
2039 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
2040 let content = r"
2041 fn test_fn() {
2042 match (false, true, false) {
2043 (false, ..) => {},
2044 }
2045 }
2046 ";
2047
2048 // This is a false negative.
2049 // We don't currently handle tuple patterns with ellipsis.
2050 check_no_diagnostic(content);
2051 }
2052
2053 #[test]
2054 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
2055 let content = r"
2056 fn test_fn() {
2057 match (false, true, false) {
2058 (.., false) => {},
2059 }
2060 }
2061 ";
2062
2063 // This is a false negative.
2064 // We don't currently handle tuple patterns with ellipsis.
2065 check_no_diagnostic(content);
2066 }
2067
2068 #[test]
2069 fn struct_missing_arm() {
2070 let content = r"
2071 struct Foo {
2072 a: bool,
2073 }
2074 fn test_fn(f: Foo) {
2075 match f {
2076 Foo { a: true } => {},
2077 }
2078 }
2079 ";
2080
2081 // This is a false negative.
2082 // We don't currently handle structs.
2083 check_no_diagnostic(content);
2084 }
2085}
diff --git a/crates/ra_hir_ty/src/autoderef.rs b/crates/ra_hir_ty/src/autoderef.rs
index 1b0f84c5c..c727012c6 100644
--- a/crates/ra_hir_ty/src/autoderef.rs
+++ b/crates/ra_hir_ty/src/autoderef.rs
@@ -37,7 +37,7 @@ pub(crate) fn deref(
37 ty: InEnvironment<&Canonical<Ty>>, 37 ty: InEnvironment<&Canonical<Ty>>,
38) -> Option<Canonical<Ty>> { 38) -> Option<Canonical<Ty>> {
39 if let Some(derefed) = ty.value.value.builtin_deref() { 39 if let Some(derefed) = ty.value.value.builtin_deref() {
40 Some(Canonical { value: derefed, num_vars: ty.value.num_vars }) 40 Some(Canonical { value: derefed, kinds: ty.value.kinds.clone() })
41 } else { 41 } else {
42 deref_by_trait(db, krate, ty) 42 deref_by_trait(db, krate, ty)
43 } 43 }
@@ -68,8 +68,8 @@ fn deref_by_trait(
68 68
69 // Check that the type implements Deref at all 69 // Check that the type implements Deref at all
70 let trait_ref = TraitRef { trait_: deref_trait, substs: parameters.clone() }; 70 let trait_ref = TraitRef { trait_: deref_trait, substs: parameters.clone() };
71 let implements_goal = super::Canonical { 71 let implements_goal = Canonical {
72 num_vars: ty.value.num_vars, 72 kinds: ty.value.kinds.clone(),
73 value: InEnvironment { 73 value: InEnvironment {
74 value: Obligation::Trait(trait_ref), 74 value: Obligation::Trait(trait_ref),
75 environment: ty.environment.clone(), 75 environment: ty.environment.clone(),
@@ -81,7 +81,7 @@ fn deref_by_trait(
81 81
82 // Now do the assoc type projection 82 // Now do the assoc type projection
83 let projection = super::traits::ProjectionPredicate { 83 let projection = super::traits::ProjectionPredicate {
84 ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.num_vars)), 84 ty: Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, ty.value.kinds.len())),
85 projection_ty: super::ProjectionTy { associated_ty: target, parameters }, 85 projection_ty: super::ProjectionTy { associated_ty: target, parameters },
86 }; 86 };
87 87
@@ -89,7 +89,8 @@ fn deref_by_trait(
89 89
90 let in_env = InEnvironment { value: obligation, environment: ty.environment }; 90 let in_env = InEnvironment { value: obligation, environment: ty.environment };
91 91
92 let canonical = super::Canonical { num_vars: 1 + ty.value.num_vars, value: in_env }; 92 let canonical =
93 Canonical::new(in_env, ty.value.kinds.iter().copied().chain(Some(super::TyKind::General)));
93 94
94 let solution = db.trait_solve(krate, canonical)?; 95 let solution = db.trait_solve(krate, canonical)?;
95 96
@@ -110,7 +111,7 @@ fn deref_by_trait(
110 // assumptions will be broken. We would need to properly introduce 111 // assumptions will be broken. We would need to properly introduce
111 // new variables in that case 112 // new variables in that case
112 113
113 for i in 1..vars.0.num_vars { 114 for i in 1..vars.0.kinds.len() {
114 if vars.0.value[i - 1] != Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, i - 1)) 115 if vars.0.value[i - 1] != Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, i - 1))
115 { 116 {
116 warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.value, solution); 117 warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.value, solution);
@@ -119,7 +120,7 @@ fn deref_by_trait(
119 } 120 }
120 Some(Canonical { 121 Some(Canonical {
121 value: vars.0.value[vars.0.value.len() - 1].clone(), 122 value: vars.0.value[vars.0.value.len() - 1].clone(),
122 num_vars: vars.0.num_vars, 123 kinds: vars.0.kinds.clone(),
123 }) 124 })
124 } 125 }
125 Solution::Ambig(_) => { 126 Solution::Ambig(_) => {
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index 0a8bb24ac..c773adc67 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -3,23 +3,22 @@
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::{ 5use hir_def::{
6 db::DefDatabase, DefWithBodyId, GenericDefId, ImplId, LocalFieldId, TraitId, TypeParamId, 6 db::DefDatabase, expr::ExprId, DefWithBodyId, FunctionId, GenericDefId, ImplId, LocalFieldId,
7 VariantId, 7 TypeParamId, VariantId,
8}; 8};
9use ra_arena::map::ArenaMap; 9use ra_arena::map::ArenaMap;
10use ra_db::{impl_intern_key, salsa, CrateId, Upcast}; 10use ra_db::{impl_intern_key, salsa, CrateId, Upcast};
11use ra_prof::profile; 11use ra_prof::profile;
12 12
13use crate::{ 13use crate::{
14 method_resolution::{CrateImplDefs, TyFingerprint}, 14 method_resolution::{InherentImpls, TraitImpls},
15 traits::{chalk, AssocTyValue, Impl}, 15 traits::chalk,
16 Binders, CallableDef, GenericPredicate, InferenceResult, PolyFnSig, Substs, TraitRef, Ty, 16 Binders, CallableDefId, GenericPredicate, InferenceResult, OpaqueTyId, PolyFnSig,
17 TyDefId, TypeCtor, ValueTyDefId, 17 ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId,
18}; 18};
19use hir_expand::name::Name; 19use hir_expand::name::Name;
20 20
21#[salsa::query_group(HirDatabaseStorage)] 21#[salsa::query_group(HirDatabaseStorage)]
22#[salsa::requires(salsa::Database)]
23pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { 22pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
24 #[salsa::invoke(infer_wait)] 23 #[salsa::invoke(infer_wait)]
25 #[salsa::transparent] 24 #[salsa::transparent]
@@ -46,7 +45,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
46 fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>; 45 fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>;
47 46
48 #[salsa::invoke(crate::callable_item_sig)] 47 #[salsa::invoke(crate::callable_item_sig)]
49 fn callable_item_signature(&self, def: CallableDef) -> PolyFnSig; 48 fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
49
50 #[salsa::invoke(crate::lower::return_type_impl_traits)]
51 fn return_type_impl_traits(
52 &self,
53 def: FunctionId,
54 ) -> Option<Arc<Binders<ReturnTypeImplTraits>>>;
50 55
51 #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] 56 #[salsa::invoke(crate::lower::generic_predicates_for_param_query)]
52 #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] 57 #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)]
@@ -59,30 +64,26 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
59 fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders<GenericPredicate>]>; 64 fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders<GenericPredicate>]>;
60 65
61 #[salsa::invoke(crate::lower::generic_defaults_query)] 66 #[salsa::invoke(crate::lower::generic_defaults_query)]
62 fn generic_defaults(&self, def: GenericDefId) -> Substs; 67 fn generic_defaults(&self, def: GenericDefId) -> Arc<[Binders<Ty>]>;
63 68
64 #[salsa::invoke(crate::method_resolution::CrateImplDefs::impls_in_crate_query)] 69 #[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)]
65 fn impls_in_crate(&self, krate: CrateId) -> Arc<CrateImplDefs>; 70 fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>;
66 71
67 #[salsa::invoke(crate::traits::impls_for_trait_query)] 72 #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
68 fn impls_for_trait( 73 fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
69 &self, 74
70 krate: CrateId, 75 #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
71 trait_: TraitId, 76 fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>;
72 self_ty_fp: Option<TyFingerprint>,
73 ) -> Arc<[ImplId]>;
74 77
75 // Interned IDs for Chalk integration 78 // Interned IDs for Chalk integration
76 #[salsa::interned] 79 #[salsa::interned]
77 fn intern_type_ctor(&self, type_ctor: TypeCtor) -> crate::TypeCtorId; 80 fn intern_callable_def(&self, callable_def: CallableDefId) -> InternedCallableDefId;
78 #[salsa::interned]
79 fn intern_callable_def(&self, callable_def: CallableDef) -> crate::CallableDefId;
80 #[salsa::interned] 81 #[salsa::interned]
81 fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; 82 fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId;
82 #[salsa::interned] 83 #[salsa::interned]
83 fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; 84 fn intern_impl_trait_id(&self, id: OpaqueTyId) -> InternedOpaqueTyId;
84 #[salsa::interned] 85 #[salsa::interned]
85 fn intern_assoc_ty_value(&self, assoc_ty_value: AssocTyValue) -> crate::traits::AssocTyValueId; 86 fn intern_closure(&self, id: (DefWithBodyId, ExprId)) -> ClosureId;
86 87
87 #[salsa::invoke(chalk::associated_ty_data_query)] 88 #[salsa::invoke(chalk::associated_ty_data_query)]
88 fn associated_ty_data(&self, id: chalk::AssocTypeId) -> Arc<chalk::AssociatedTyDatum>; 89 fn associated_ty_data(&self, id: chalk::AssocTypeId) -> Arc<chalk::AssociatedTyDatum>;
@@ -142,3 +143,17 @@ fn hir_database_is_object_safe() {
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 143#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
143pub struct GlobalTypeParamId(salsa::InternId); 144pub struct GlobalTypeParamId(salsa::InternId);
144impl_intern_key!(GlobalTypeParamId); 145impl_intern_key!(GlobalTypeParamId);
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
148pub struct InternedOpaqueTyId(salsa::InternId);
149impl_intern_key!(InternedOpaqueTyId);
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
152pub struct ClosureId(salsa::InternId);
153impl_intern_key!(ClosureId);
154
155/// This exists just for Chalk, because Chalk just has a single `FnDefId` where
156/// we have different IDs for struct and enum variant constructors.
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
158pub struct InternedCallableDefId(salsa::InternId);
159impl_intern_key!(InternedCallableDefId);
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 2c7298714..d3ee9cf55 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -1,13 +1,30 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2mod expr;
3mod match_check;
4mod unsafe_check;
2 5
3use std::any::Any; 6use std::any::Any;
4 7
8use hir_def::DefWithBodyId;
9use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
5use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; 10use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
11use ra_prof::profile;
6use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; 12use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
7use stdx::format_to; 13use stdx::format_to;
8 14
9pub use hir_def::{diagnostics::UnresolvedModule, expr::MatchArm}; 15use crate::db::HirDatabase;
10pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; 16
17pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields};
18
19pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) {
20 let _p = profile("validate_body");
21 let infer = db.infer(owner);
22 infer.add_diagnostics(db, owner, sink);
23 let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
24 validator.validate_body(db);
25 let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink);
26 validator.validate_body(db);
27}
11 28
12#[derive(Debug)] 29#[derive(Debug)]
13pub struct NoSuchField { 30pub struct NoSuchField {
@@ -29,6 +46,16 @@ impl Diagnostic for NoSuchField {
29 } 46 }
30} 47}
31 48
49impl AstDiagnostic for NoSuchField {
50 type AST = ast::RecordField;
51
52 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
53 let root = db.parse_or_expand(self.source().file_id).unwrap();
54 let node = self.source().value.to_node(&root);
55 ast::RecordField::cast(node).unwrap()
56 }
57}
58
32#[derive(Debug)] 59#[derive(Debug)]
33pub struct MissingFields { 60pub struct MissingFields {
34 pub file: HirFileId, 61 pub file: HirFileId,
@@ -55,7 +82,7 @@ impl Diagnostic for MissingFields {
55impl AstDiagnostic for MissingFields { 82impl AstDiagnostic for MissingFields {
56 type AST = ast::RecordFieldList; 83 type AST = ast::RecordFieldList;
57 84
58 fn ast(&self, db: &impl AstDatabase) -> Self::AST { 85 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
59 let root = db.parse_or_expand(self.source().file_id).unwrap(); 86 let root = db.parse_or_expand(self.source().file_id).unwrap();
60 let node = self.source().value.to_node(&root); 87 let node = self.source().value.to_node(&root);
61 ast::RecordFieldList::cast(node).unwrap() 88 ast::RecordFieldList::cast(node).unwrap()
@@ -125,7 +152,7 @@ impl Diagnostic for MissingOkInTailExpr {
125impl AstDiagnostic for MissingOkInTailExpr { 152impl AstDiagnostic for MissingOkInTailExpr {
126 type AST = ast::Expr; 153 type AST = ast::Expr;
127 154
128 fn ast(&self, db: &impl AstDatabase) -> Self::AST { 155 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
129 let root = db.parse_or_expand(self.file).unwrap(); 156 let root = db.parse_or_expand(self.file).unwrap();
130 let node = self.source().value.to_node(&root); 157 let node = self.source().value.to_node(&root);
131 ast::Expr::cast(node).unwrap() 158 ast::Expr::cast(node).unwrap()
@@ -153,9 +180,299 @@ impl Diagnostic for BreakOutsideOfLoop {
153impl AstDiagnostic for BreakOutsideOfLoop { 180impl AstDiagnostic for BreakOutsideOfLoop {
154 type AST = ast::Expr; 181 type AST = ast::Expr;
155 182
156 fn ast(&self, db: &impl AstDatabase) -> Self::AST { 183 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
157 let root = db.parse_or_expand(self.file).unwrap(); 184 let root = db.parse_or_expand(self.file).unwrap();
158 let node = self.source().value.to_node(&root); 185 let node = self.source().value.to_node(&root);
159 ast::Expr::cast(node).unwrap() 186 ast::Expr::cast(node).unwrap()
160 } 187 }
161} 188}
189
190#[derive(Debug)]
191pub struct MissingUnsafe {
192 pub file: HirFileId,
193 pub expr: AstPtr<ast::Expr>,
194}
195
196impl Diagnostic for MissingUnsafe {
197 fn message(&self) -> String {
198 format!("This operation is unsafe and requires an unsafe function or block")
199 }
200 fn source(&self) -> InFile<SyntaxNodePtr> {
201 InFile { file_id: self.file, value: self.expr.clone().into() }
202 }
203 fn as_any(&self) -> &(dyn Any + Send + 'static) {
204 self
205 }
206}
207
208impl AstDiagnostic for MissingUnsafe {
209 type AST = ast::Expr;
210
211 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
212 let root = db.parse_or_expand(self.source().file_id).unwrap();
213 let node = self.source().value.to_node(&root);
214 ast::Expr::cast(node).unwrap()
215 }
216}
217
218#[derive(Debug)]
219pub struct MismatchedArgCount {
220 pub file: HirFileId,
221 pub call_expr: AstPtr<ast::Expr>,
222 pub expected: usize,
223 pub found: usize,
224}
225
226impl Diagnostic for MismatchedArgCount {
227 fn message(&self) -> String {
228 let s = if self.expected == 1 { "" } else { "s" };
229 format!("Expected {} argument{}, found {}", self.expected, s, self.found)
230 }
231 fn source(&self) -> InFile<SyntaxNodePtr> {
232 InFile { file_id: self.file, value: self.call_expr.clone().into() }
233 }
234 fn as_any(&self) -> &(dyn Any + Send + 'static) {
235 self
236 }
237}
238
239impl AstDiagnostic for MismatchedArgCount {
240 type AST = ast::CallExpr;
241 fn ast(&self, db: &dyn AstDatabase) -> Self::AST {
242 let root = db.parse_or_expand(self.source().file_id).unwrap();
243 let node = self.source().value.to_node(&root);
244 ast::CallExpr::cast(node).unwrap()
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId};
251 use hir_expand::diagnostics::{Diagnostic, DiagnosticSink};
252 use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
253 use ra_syntax::{TextRange, TextSize};
254 use rustc_hash::FxHashMap;
255
256 use crate::{diagnostics::validate_body, test_db::TestDB};
257
258 impl TestDB {
259 fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
260 let crate_graph = self.crate_graph();
261 for krate in crate_graph.iter() {
262 let crate_def_map = self.crate_def_map(krate);
263
264 let mut fns = Vec::new();
265 for (module_id, _) in crate_def_map.modules.iter() {
266 for decl in crate_def_map[module_id].scope.declarations() {
267 if let ModuleDefId::FunctionId(f) = decl {
268 fns.push(f)
269 }
270 }
271
272 for impl_id in crate_def_map[module_id].scope.impls() {
273 let impl_data = self.impl_data(impl_id);
274 for item in impl_data.items.iter() {
275 if let AssocItemId::FunctionId(f) = item {
276 fns.push(*f)
277 }
278 }
279 }
280 }
281
282 for f in fns {
283 let mut sink = DiagnosticSink::new(&mut cb);
284 validate_body(self, f.into(), &mut sink);
285 }
286 }
287 }
288 }
289
290 pub(crate) fn check_diagnostics(ra_fixture: &str) {
291 let db = TestDB::with_files(ra_fixture);
292 let annotations = db.extract_annotations();
293
294 let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
295 db.diagnostics(|d| {
296 // FXIME: macros...
297 let file_id = d.source().file_id.original_file(&db);
298 let range = d.syntax_node(&db).text_range();
299 let message = d.message().to_owned();
300 actual.entry(file_id).or_default().push((range, message));
301 });
302
303 for (file_id, diags) in actual.iter_mut() {
304 diags.sort_by_key(|it| it.0.start());
305 let text = db.file_text(*file_id);
306 // For multiline spans, place them on line start
307 for (range, content) in diags {
308 if text[*range].contains('\n') {
309 *range = TextRange::new(range.start(), range.start() + TextSize::from(1));
310 *content = format!("... {}", content);
311 }
312 }
313 }
314
315 assert_eq!(annotations, actual);
316 }
317
318 #[test]
319 fn no_such_field_diagnostics() {
320 check_diagnostics(
321 r#"
322struct S { foo: i32, bar: () }
323impl S {
324 fn new() -> S {
325 S {
326 //^... Missing structure fields:
327 //| - bar
328 foo: 92,
329 baz: 62,
330 //^^^^^^^ no such field
331 }
332 }
333}
334"#,
335 );
336 }
337 #[test]
338 fn no_such_field_with_feature_flag_diagnostics() {
339 check_diagnostics(
340 r#"
341//- /lib.rs crate:foo cfg:feature=foo
342struct MyStruct {
343 my_val: usize,
344 #[cfg(feature = "foo")]
345 bar: bool,
346}
347
348impl MyStruct {
349 #[cfg(feature = "foo")]
350 pub(crate) fn new(my_val: usize, bar: bool) -> Self {
351 Self { my_val, bar }
352 }
353 #[cfg(not(feature = "foo"))]
354 pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
355 Self { my_val }
356 }
357}
358"#,
359 );
360 }
361
362 #[test]
363 fn no_such_field_enum_with_feature_flag_diagnostics() {
364 check_diagnostics(
365 r#"
366//- /lib.rs crate:foo cfg:feature=foo
367enum Foo {
368 #[cfg(not(feature = "foo"))]
369 Buz,
370 #[cfg(feature = "foo")]
371 Bar,
372 Baz
373}
374
375fn test_fn(f: Foo) {
376 match f {
377 Foo::Bar => {},
378 Foo::Baz => {},
379 }
380}
381"#,
382 );
383 }
384
385 #[test]
386 fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
387 check_diagnostics(
388 r#"
389//- /lib.rs crate:foo cfg:feature=foo
390struct S {
391 #[cfg(feature = "foo")]
392 foo: u32,
393 #[cfg(not(feature = "foo"))]
394 bar: u32,
395}
396
397impl S {
398 #[cfg(feature = "foo")]
399 fn new(foo: u32) -> Self {
400 Self { foo }
401 }
402 #[cfg(not(feature = "foo"))]
403 fn new(bar: u32) -> Self {
404 Self { bar }
405 }
406 fn new2(bar: u32) -> Self {
407 #[cfg(feature = "foo")]
408 { Self { foo: bar } }
409 #[cfg(not(feature = "foo"))]
410 { Self { bar } }
411 }
412 fn new2(val: u32) -> Self {
413 Self {
414 #[cfg(feature = "foo")]
415 foo: val,
416 #[cfg(not(feature = "foo"))]
417 bar: val,
418 }
419 }
420}
421"#,
422 );
423 }
424
425 #[test]
426 fn no_such_field_with_type_macro() {
427 check_diagnostics(
428 r#"
429macro_rules! Type { () => { u32 }; }
430struct Foo { bar: Type![] }
431
432impl Foo {
433 fn new() -> Self {
434 Foo { bar: 0 }
435 }
436}
437"#,
438 );
439 }
440
441 #[test]
442 fn missing_record_pat_field_diagnostic() {
443 check_diagnostics(
444 r#"
445struct S { foo: i32, bar: () }
446fn baz(s: S) {
447 let S { foo: _ } = s;
448 //^^^^^^^^^^ Missing structure fields:
449 // | - bar
450}
451"#,
452 );
453 }
454
455 #[test]
456 fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
457 check_diagnostics(
458 r"
459struct S { foo: i32, bar: () }
460fn baz(s: S) -> i32 {
461 match s {
462 S { foo, .. } => foo,
463 }
464}
465",
466 )
467 }
468
469 #[test]
470 fn break_outside_of_loop() {
471 check_diagnostics(
472 r#"
473fn foo() { break; }
474 //^^^^^ break outside of loop
475"#,
476 );
477 }
478}
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/diagnostics/expr.rs
index f04968e14..557d01cdc 100644
--- a/crates/ra_hir_ty/src/expr.rs
+++ b/crates/ra_hir_ty/src/diagnostics/expr.rs
@@ -2,17 +2,19 @@
2 2
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId}; 5use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId};
6use hir_expand::diagnostics::DiagnosticSink; 6use hir_expand::diagnostics::DiagnosticSink;
7use ra_syntax::{ast, AstPtr}; 7use ra_syntax::{ast, AstPtr};
8use rustc_hash::FxHashSet; 8use rustc_hash::FxHashSet;
9 9
10use crate::{ 10use crate::{
11 db::HirDatabase, 11 db::HirDatabase,
12 diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields}, 12 diagnostics::{
13 match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
14 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields,
15 },
13 utils::variant_data, 16 utils::variant_data,
14 ApplicationTy, InferenceResult, Ty, TypeCtor, 17 ApplicationTy, InferenceResult, Ty, TypeCtor,
15 _match::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness},
16}; 18};
17 19
18pub use hir_def::{ 20pub use hir_def::{
@@ -24,26 +26,27 @@ pub use hir_def::{
24 ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp, 26 ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp,
25 MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, UnaryOp, 27 MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, UnaryOp,
26 }, 28 },
27 LocalFieldId, VariantId, 29 src::HasSource,
30 LocalFieldId, Lookup, VariantId,
28}; 31};
29 32
30pub struct ExprValidator<'a, 'b: 'a> { 33pub(super) struct ExprValidator<'a, 'b: 'a> {
31 func: FunctionId, 34 owner: DefWithBodyId,
32 infer: Arc<InferenceResult>, 35 infer: Arc<InferenceResult>,
33 sink: &'a mut DiagnosticSink<'b>, 36 sink: &'a mut DiagnosticSink<'b>,
34} 37}
35 38
36impl<'a, 'b> ExprValidator<'a, 'b> { 39impl<'a, 'b> ExprValidator<'a, 'b> {
37 pub fn new( 40 pub(super) fn new(
38 func: FunctionId, 41 owner: DefWithBodyId,
39 infer: Arc<InferenceResult>, 42 infer: Arc<InferenceResult>,
40 sink: &'a mut DiagnosticSink<'b>, 43 sink: &'a mut DiagnosticSink<'b>,
41 ) -> ExprValidator<'a, 'b> { 44 ) -> ExprValidator<'a, 'b> {
42 ExprValidator { func, infer, sink } 45 ExprValidator { owner, infer, sink }
43 } 46 }
44 47
45 pub fn validate_body(&mut self, db: &dyn HirDatabase) { 48 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) {
46 let body = db.body(self.func.into()); 49 let body = db.body(self.owner.into());
47 50
48 for (id, expr) in body.exprs.iter() { 51 for (id, expr) in body.exprs.iter() {
49 if let Some((variant_def, missed_fields, true)) = 52 if let Some((variant_def, missed_fields, true)) =
@@ -56,8 +59,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
56 missed_fields, 59 missed_fields,
57 ); 60 );
58 } 61 }
59 if let Expr::Match { expr, arms } = expr { 62
60 self.validate_match(id, *expr, arms, db, self.infer.clone()); 63 match expr {
64 Expr::Match { expr, arms } => {
65 self.validate_match(id, *expr, arms, db, self.infer.clone());
66 }
67 Expr::Call { .. } | Expr::MethodCall { .. } => {
68 self.validate_call(db, id, expr);
69 }
70 _ => {}
61 } 71 }
62 } 72 }
63 for (id, pat) in body.pats.iter() { 73 for (id, pat) in body.pats.iter() {
@@ -86,7 +96,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
86 missed_fields: Vec<LocalFieldId>, 96 missed_fields: Vec<LocalFieldId>,
87 ) { 97 ) {
88 // XXX: only look at source_map if we do have missing fields 98 // XXX: only look at source_map if we do have missing fields
89 let (_, source_map) = db.body_with_source_map(self.func.into()); 99 let (_, source_map) = db.body_with_source_map(self.owner.into());
90 100
91 if let Ok(source_ptr) = source_map.expr_syntax(id) { 101 if let Ok(source_ptr) = source_map.expr_syntax(id) {
92 let root = source_ptr.file_syntax(db.upcast()); 102 let root = source_ptr.file_syntax(db.upcast());
@@ -115,7 +125,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
115 missed_fields: Vec<LocalFieldId>, 125 missed_fields: Vec<LocalFieldId>,
116 ) { 126 ) {
117 // XXX: only look at source_map if we do have missing fields 127 // XXX: only look at source_map if we do have missing fields
118 let (_, source_map) = db.body_with_source_map(self.func.into()); 128 let (_, source_map) = db.body_with_source_map(self.owner.into());
119 129
120 if let Ok(source_ptr) = source_map.pat_syntax(id) { 130 if let Ok(source_ptr) = source_map.pat_syntax(id) {
121 if let Some(expr) = source_ptr.value.as_ref().left() { 131 if let Some(expr) = source_ptr.value.as_ref().left() {
@@ -138,6 +148,61 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
138 } 148 }
139 } 149 }
140 150
151 fn validate_call(&mut self, db: &dyn HirDatabase, call_id: ExprId, expr: &Expr) -> Option<()> {
152 // Check that the number of arguments matches the number of parameters.
153
154 // FIXME: Due to shortcomings in the current type system implementation, only emit this
155 // diagnostic if there are no type mismatches in the containing function.
156 if self.infer.type_mismatches.iter().next().is_some() {
157 return Some(());
158 }
159
160 let is_method_call = matches!(expr, Expr::MethodCall { .. });
161 let (callee, args) = match expr {
162 Expr::Call { callee, args } => {
163 let callee = &self.infer.type_of_expr[*callee];
164 let (callable, _) = callee.as_callable()?;
165
166 (callable, args.clone())
167 }
168 Expr::MethodCall { receiver, args, .. } => {
169 let callee = self.infer.method_resolution(call_id)?;
170 let mut args = args.clone();
171 args.insert(0, *receiver);
172 (callee.into(), args)
173 }
174 _ => return None,
175 };
176
177 let sig = db.callable_item_signature(callee);
178 if sig.value.is_varargs {
179 return None;
180 }
181
182 let params = sig.value.params();
183
184 let mut param_count = params.len();
185 let mut arg_count = args.len();
186
187 if arg_count != param_count {
188 let (_, source_map) = db.body_with_source_map(self.owner.into());
189 if let Ok(source_ptr) = source_map.expr_syntax(call_id) {
190 if is_method_call {
191 param_count -= 1;
192 arg_count -= 1;
193 }
194 self.sink.push(MismatchedArgCount {
195 file: source_ptr.file_id,
196 call_expr: source_ptr.value,
197 expected: param_count,
198 found: arg_count,
199 });
200 }
201 }
202
203 None
204 }
205
141 fn validate_match( 206 fn validate_match(
142 &mut self, 207 &mut self,
143 id: ExprId, 208 id: ExprId,
@@ -147,7 +212,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
147 infer: Arc<InferenceResult>, 212 infer: Arc<InferenceResult>,
148 ) { 213 ) {
149 let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) = 214 let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) =
150 db.body_with_source_map(self.func.into()); 215 db.body_with_source_map(self.owner.into());
151 216
152 let match_expr_ty = match infer.type_of_expr.get(match_expr) { 217 let match_expr_ty = match infer.type_of_expr.get(match_expr) {
153 Some(ty) => ty, 218 Some(ty) => ty,
@@ -226,22 +291,24 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
226 None => return, 291 None => return,
227 }; 292 };
228 293
229 let std_result_path = path![std::result::Result]; 294 let core_result_path = path![core::result::Result];
230 295
231 let resolver = self.func.resolver(db.upcast()); 296 let resolver = self.owner.resolver(db.upcast());
232 let std_result_enum = match resolver.resolve_known_enum(db.upcast(), &std_result_path) { 297 let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) {
233 Some(it) => it, 298 Some(it) => it,
234 _ => return, 299 _ => return,
235 }; 300 };
236 301
237 let std_result_ctor = TypeCtor::Adt(AdtId::EnumId(std_result_enum)); 302 let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum));
238 let params = match &mismatch.expected { 303 let params = match &mismatch.expected {
239 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters, 304 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => {
305 parameters
306 }
240 _ => return, 307 _ => return,
241 }; 308 };
242 309
243 if params.len() == 2 && params[0] == mismatch.actual { 310 if params.len() == 2 && params[0] == mismatch.actual {
244 let (_, source_map) = db.body_with_source_map(self.func.into()); 311 let (_, source_map) = db.body_with_source_map(self.owner.into());
245 312
246 if let Ok(source_ptr) = source_map.expr_syntax(id) { 313 if let Ok(source_ptr) = source_map.expr_syntax(id) {
247 self.sink 314 self.sink
@@ -310,3 +377,169 @@ pub fn record_pattern_missing_fields(
310 } 377 }
311 Some((variant_def, missed_fields, exhaustive)) 378 Some((variant_def, missed_fields, exhaustive))
312} 379}
380
381#[cfg(test)]
382mod tests {
383 use crate::diagnostics::tests::check_diagnostics;
384
385 #[test]
386 fn simple_free_fn_zero() {
387 check_diagnostics(
388 r#"
389fn zero() {}
390fn f() { zero(1); }
391 //^^^^^^^ Expected 0 arguments, found 1
392"#,
393 );
394
395 check_diagnostics(
396 r#"
397fn zero() {}
398fn f() { zero(); }
399"#,
400 );
401 }
402
403 #[test]
404 fn simple_free_fn_one() {
405 check_diagnostics(
406 r#"
407fn one(arg: u8) {}
408fn f() { one(); }
409 //^^^^^ Expected 1 argument, found 0
410"#,
411 );
412
413 check_diagnostics(
414 r#"
415fn one(arg: u8) {}
416fn f() { one(1); }
417"#,
418 );
419 }
420
421 #[test]
422 fn method_as_fn() {
423 check_diagnostics(
424 r#"
425struct S;
426impl S { fn method(&self) {} }
427
428fn f() {
429 S::method();
430} //^^^^^^^^^^^ Expected 1 argument, found 0
431"#,
432 );
433
434 check_diagnostics(
435 r#"
436struct S;
437impl S { fn method(&self) {} }
438
439fn f() {
440 S::method(&S);
441 S.method();
442}
443"#,
444 );
445 }
446
447 #[test]
448 fn method_with_arg() {
449 check_diagnostics(
450 r#"
451struct S;
452impl S { fn method(&self, arg: u8) {} }
453
454 fn f() {
455 S.method();
456 } //^^^^^^^^^^ Expected 1 argument, found 0
457 "#,
458 );
459
460 check_diagnostics(
461 r#"
462struct S;
463impl S { fn method(&self, arg: u8) {} }
464
465fn f() {
466 S::method(&S, 0);
467 S.method(1);
468}
469"#,
470 );
471 }
472
473 #[test]
474 fn tuple_struct() {
475 check_diagnostics(
476 r#"
477struct Tup(u8, u16);
478fn f() {
479 Tup(0);
480} //^^^^^^ Expected 2 arguments, found 1
481"#,
482 )
483 }
484
485 #[test]
486 fn enum_variant() {
487 check_diagnostics(
488 r#"
489enum En { Variant(u8, u16), }
490fn f() {
491 En::Variant(0);
492} //^^^^^^^^^^^^^^ Expected 2 arguments, found 1
493"#,
494 )
495 }
496
497 #[test]
498 fn enum_variant_type_macro() {
499 check_diagnostics(
500 r#"
501macro_rules! Type {
502 () => { u32 };
503}
504enum Foo {
505 Bar(Type![])
506}
507impl Foo {
508 fn new() {
509 Foo::Bar(0);
510 Foo::Bar(0, 1);
511 //^^^^^^^^^^^^^^ Expected 1 argument, found 2
512 Foo::Bar();
513 //^^^^^^^^^^ Expected 1 argument, found 0
514 }
515}
516 "#,
517 );
518 }
519
520 #[test]
521 fn varargs() {
522 check_diagnostics(
523 r#"
524extern "C" {
525 fn fixed(fixed: u8);
526 fn varargs(fixed: u8, ...);
527 fn varargs2(...);
528}
529
530fn f() {
531 unsafe {
532 fixed(0);
533 fixed(0, 1);
534 //^^^^^^^^^^^ Expected 1 argument, found 2
535 varargs(0);
536 varargs(0, 1);
537 varargs2();
538 varargs2(0);
539 varargs2(0, 1);
540 }
541}
542 "#,
543 )
544 }
545}
diff --git a/crates/ra_hir_ty/src/diagnostics/match_check.rs b/crates/ra_hir_ty/src/diagnostics/match_check.rs
new file mode 100644
index 000000000..507edcb7d
--- /dev/null
+++ b/crates/ra_hir_ty/src/diagnostics/match_check.rs
@@ -0,0 +1,1421 @@
1//! This module implements match statement exhaustiveness checking and usefulness checking
2//! for match arms.
3//!
4//! It is modeled on the rustc module `librustc_mir_build::hair::pattern::_match`, which
5//! contains very detailed documentation about the algorithms used here. I've duplicated
6//! most of that documentation below.
7//!
8//! This file includes the logic for exhaustiveness and usefulness checking for
9//! pattern-matching. Specifically, given a list of patterns for a type, we can
10//! tell whether:
11//! - (a) the patterns cover every possible constructor for the type (exhaustiveness).
12//! - (b) each pattern is necessary (usefulness).
13//!
14//! The algorithm implemented here is a modified version of the one described in
15//! <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
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
18//! (without being so rigorous).
19//!
20//! The core of the algorithm revolves about a "usefulness" check. In particular, we
21//! are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as
22//! a matrix). `U(P, p)` represents whether, given an existing list of patterns
23//! `P_1 ..= P_m`, adding a new pattern `p` will be "useful" (that is, cover previously-
24//! uncovered values of the type).
25//!
26//! If we have this predicate, then we can easily compute both exhaustiveness of an
27//! entire set of patterns and the individual usefulness of each one.
28//! (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e., adding a wildcard
29//! match doesn't increase the number of values we're matching)
30//! (b) a pattern `P_i` is not useful if `U(P[0..=(i-1), P_i)` is false (i.e., adding a
31//! pattern to those that have come before it doesn't increase the number of values
32//! we're matching).
33//!
34//! During the course of the algorithm, the rows of the matrix won't just be individual patterns,
35//! but rather partially-deconstructed patterns in the form of a list of patterns. The paper
36//! calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the
37//! new pattern `p`.
38//!
39//! For example, say we have the following:
40//!
41//! ```ignore
42//! // x: (Option<bool>, Result<()>)
43//! match x {
44//! (Some(true), _) => (),
45//! (None, Err(())) => (),
46//! (None, Err(_)) => (),
47//! }
48//! ```
49//!
50//! Here, the matrix `P` starts as:
51//!
52//! ```text
53//! [
54//! [(Some(true), _)],
55//! [(None, Err(()))],
56//! [(None, Err(_))],
57//! ]
58//! ```
59//!
60//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering
61//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because
62//! all the values it covers are already covered by row 2.
63//!
64//! A list of patterns can be thought of as a stack, because we are mainly interested in the top of
65//! the stack at any given point, and we can pop or apply constructors to get new pattern-stacks.
66//! To match the paper, the top of the stack is at the beginning / on the left.
67//!
68//! There are two important operations on pattern-stacks necessary to understand the algorithm:
69//!
70//! 1. We can pop a given constructor off the top of a stack. This operation is called
71//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or
72//! `None`) and `p` a pattern-stack.
73//! If the pattern on top of the stack can cover `c`, this removes the constructor and
74//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns.
75//! Otherwise the pattern-stack is discarded.
76//! This essentially filters those pattern-stacks whose top covers the constructor `c` and
77//! discards the others.
78//!
79//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we
80//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the
81//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get
82//! nothing back.
83//!
84//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1`
85//! on top of the stack, and we have four cases:
86//!
87//! * 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We push onto
88//! the stack the arguments of this constructor, and return the result:
89//!
90//! r_1, .., r_a, p_2, .., p_n
91//!
92//! * 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and return
93//! nothing.
94//! * 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has
95//! arguments (its arity), and return the resulting stack:
96//!
97//! _, .., _, p_2, .., p_n
98//!
99//! * 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting stack:
100//!
101//! S(c, (r_1, p_2, .., p_n))
102//! S(c, (r_2, p_2, .., p_n))
103//!
104//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is
105//! a pattern-stack.
106//! This is used when we know there are missing constructor cases, but there might be
107//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check
108//! all its *other* components.
109//!
110//! It is computed as follows. We look at the pattern `p_1` on top of the stack,
111//! and we have three cases:
112//! * 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
113//! * 1.2. `p_1 = _`. We return the rest of the stack:
114//!
115//! p_2, .., p_n
116//!
117//! * 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting stack:
118//!
119//! D((r_1, p_2, .., p_n))
120//! D((r_2, p_2, .., p_n))
121//!
122//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the
123//! exhaustive integer matching rules, so they're written here for posterity.
124//!
125//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by
126//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with
127//! the given constructor, and popping a wildcard keeps those rows that start with a wildcard.
128//!
129//!
130//! The algorithm for computing `U`
131//! -------------------------------
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
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.
136//!
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
139//! `U(P, p)` is false.
140//! - Otherwise, `P` must be empty, so `U(P, p)` is true.
141//!
142//! Inductive step (`n > 0`, i.e., whether there's at least one column [which may then be expanded
143//! into further columns later]). We're going to match on the top of the new pattern-stack, `p_1`:
144//!
145//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern.
146//! Then, the usefulness of `p_1` can be reduced to whether it is useful when
147//! we ignore all the patterns in the first column of `P` that involve other constructors.
148//! This is where `S(c, P)` comes in:
149//!
150//! ```text
151//! U(P, p) := U(S(c, P), S(c, p))
152//! ```
153//!
154//! This special case is handled in `is_useful_specialized`.
155//!
156//! For example, if `P` is:
157//!
158//! ```text
159//! [
160//! [Some(true), _],
161//! [None, 0],
162//! ]
163//! ```
164//!
165//! and `p` is `[Some(false), 0]`, then we don't care about row 2 since we know `p` only
166//! matches values that row 2 doesn't. For row 1 however, we need to dig into the
167//! arguments of `Some` to know whether some new value is covered. So we compute
168//! `U([[true, _]], [false, 0])`.
169//!
170//! - If `p_1 == _`, then we look at the list of constructors that appear in the first component of
171//! the rows of `P`:
172//! - If there are some constructors that aren't present, then we might think that the
173//! wildcard `_` is useful, since it covers those constructors that weren't covered
174//! before.
175//! That's almost correct, but only works if there were no wildcards in those first
176//! components. So we need to check that `p` is useful with respect to the rows that
177//! start with a wildcard, if there are any. This is where `D` comes in:
178//! `U(P, p) := U(D(P), D(p))`
179//!
180//! For example, if `P` is:
181//! ```text
182//! [
183//! [_, true, _],
184//! [None, false, 1],
185//! ]
186//! ```
187//! and `p` is `[_, false, _]`, the `Some` constructor doesn't appear in `P`. So if we
188//! only had row 2, we'd know that `p` is useful. However row 1 starts with a
189//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`.
190//!
191//! - Otherwise, all possible constructors (for the relevant type) are present. In this
192//! case we must check whether the wildcard pattern covers any unmatched value. For
193//! that, we can think of the `_` pattern as a big OR-pattern that covers all
194//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for
195//! example. The wildcard pattern is useful in this case if it is useful when
196//! specialized to one of the possible constructors. So we compute:
197//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))`
198//!
199//! For example, if `P` is:
200//! ```text
201//! [
202//! [Some(true), _],
203//! [None, false],
204//! ]
205//! ```
206//! and `p` is `[_, false]`, both `None` and `Some` constructors appear in the first
207//! components of `P`. We will therefore try popping both constructors in turn: we
208//! compute `U([[true, _]], [_, false])` for the `Some` constructor, and `U([[false]],
209//! [false])` for the `None` constructor. The first case returns true, so we know that
210//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched
211//! before.
212//!
213//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately:
214//!
215//! ```text
216//! U(P, p) := U(P, (r_1, p_2, .., p_n))
217//! || U(P, (r_2, p_2, .., p_n))
218//! ```
219use std::sync::Arc;
220
221use hir_def::{
222 adt::VariantData,
223 body::Body,
224 expr::{Expr, Literal, Pat, PatId},
225 AdtId, EnumVariantId, VariantId,
226};
227use ra_arena::Idx;
228use smallvec::{smallvec, SmallVec};
229
230use crate::{db::HirDatabase, ApplicationTy, InferenceResult, Ty, TypeCtor};
231
232#[derive(Debug, Clone, Copy)]
233/// Either a pattern from the source code being analyzed, represented as
234/// as `PatId`, or a `Wild` pattern which is created as an intermediate
235/// step in the match checking algorithm and thus is not backed by a
236/// real `PatId`.
237///
238/// Note that it is totally valid for the `PatId` variant to contain
239/// a `PatId` which resolves to a `Wild` pattern, if that wild pattern
240/// exists in the source code being analyzed.
241enum PatIdOrWild {
242 PatId(PatId),
243 Wild,
244}
245
246impl PatIdOrWild {
247 fn as_pat(self, cx: &MatchCheckCtx) -> Pat {
248 match self {
249 PatIdOrWild::PatId(id) => cx.body.pats[id].clone(),
250 PatIdOrWild::Wild => Pat::Wild,
251 }
252 }
253
254 fn as_id(self) -> Option<PatId> {
255 match self {
256 PatIdOrWild::PatId(id) => Some(id),
257 PatIdOrWild::Wild => None,
258 }
259 }
260}
261
262impl From<PatId> for PatIdOrWild {
263 fn from(pat_id: PatId) -> Self {
264 Self::PatId(pat_id)
265 }
266}
267
268impl From<&PatId> for PatIdOrWild {
269 fn from(pat_id: &PatId) -> Self {
270 Self::PatId(*pat_id)
271 }
272}
273
274#[derive(Debug, Clone, Copy, PartialEq)]
275pub(super) enum MatchCheckErr {
276 NotImplemented,
277 MalformedMatchArm,
278 /// Used when type inference cannot resolve the type of
279 /// a pattern or expression.
280 Unknown,
281}
282
283/// The return type of `is_useful` is either an indication of usefulness
284/// of the match arm, or an error in the case the match statement
285/// is made up of types for which exhaustiveness checking is currently
286/// not completely implemented.
287///
288/// The `std::result::Result` type is used here rather than a custom enum
289/// to allow the use of `?`.
290pub(super) type MatchCheckResult<T> = Result<T, MatchCheckErr>;
291
292#[derive(Debug)]
293/// A row in a Matrix.
294///
295/// This type is modeled from the struct of the same name in `rustc`.
296pub(super) struct PatStack(PatStackInner);
297type PatStackInner = SmallVec<[PatIdOrWild; 2]>;
298
299impl PatStack {
300 pub(super) fn from_pattern(pat_id: PatId) -> PatStack {
301 Self(smallvec!(pat_id.into()))
302 }
303
304 pub(super) fn from_wild() -> PatStack {
305 Self(smallvec!(PatIdOrWild::Wild))
306 }
307
308 fn from_slice(slice: &[PatIdOrWild]) -> PatStack {
309 Self(SmallVec::from_slice(slice))
310 }
311
312 fn from_vec(v: PatStackInner) -> PatStack {
313 Self(v)
314 }
315
316 fn get_head(&self) -> Option<PatIdOrWild> {
317 self.0.first().copied()
318 }
319
320 fn tail(&self) -> &[PatIdOrWild] {
321 self.0.get(1..).unwrap_or(&[])
322 }
323
324 fn to_tail(&self) -> PatStack {
325 Self::from_slice(self.tail())
326 }
327
328 fn replace_head_with<I, T>(&self, pats: I) -> PatStack
329 where
330 I: Iterator<Item = T>,
331 T: Into<PatIdOrWild>,
332 {
333 let mut patterns: PatStackInner = smallvec![];
334 for pat in pats {
335 patterns.push(pat.into());
336 }
337 for pat in &self.0[1..] {
338 patterns.push(*pat);
339 }
340 PatStack::from_vec(patterns)
341 }
342
343 /// Computes `D(self)`.
344 ///
345 /// See the module docs and the associated documentation in rustc for details.
346 fn specialize_wildcard(&self, cx: &MatchCheckCtx) -> Option<PatStack> {
347 if matches!(self.get_head()?.as_pat(cx), Pat::Wild) {
348 Some(self.to_tail())
349 } else {
350 None
351 }
352 }
353
354 /// Computes `S(constructor, self)`.
355 ///
356 /// See the module docs and the associated documentation in rustc for details.
357 fn specialize_constructor(
358 &self,
359 cx: &MatchCheckCtx,
360 constructor: &Constructor,
361 ) -> MatchCheckResult<Option<PatStack>> {
362 let head = match self.get_head() {
363 Some(head) => head,
364 None => return Ok(None),
365 };
366
367 let head_pat = head.as_pat(cx);
368 let result = match (head_pat, constructor) {
369 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
370 if ellipsis.is_some() {
371 // If there are ellipsis here, we should add the correct number of
372 // Pat::Wild patterns to `pat_ids`. We should be able to use the
373 // constructors arity for this, but at the time of writing we aren't
374 // correctly calculating this arity when ellipsis are present.
375 return Err(MatchCheckErr::NotImplemented);
376 }
377
378 Some(self.replace_head_with(pat_ids.iter()))
379 }
380 (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => {
381 match cx.body.exprs[lit_expr] {
382 Expr::Literal(Literal::Bool(pat_val)) if *constructor_val == pat_val => {
383 Some(self.to_tail())
384 }
385 // it was a bool but the value doesn't match
386 Expr::Literal(Literal::Bool(_)) => None,
387 // perhaps this is actually unreachable given we have
388 // already checked that these match arms have the appropriate type?
389 _ => return Err(MatchCheckErr::NotImplemented),
390 }
391 }
392 (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?),
393 (Pat::Path(_), Constructor::Enum(constructor)) => {
394 // unit enum variants become `Pat::Path`
395 let pat_id = head.as_id().expect("we know this isn't a wild");
396 if !enum_variant_matches(cx, pat_id, *constructor) {
397 None
398 } else {
399 Some(self.to_tail())
400 }
401 }
402 (
403 Pat::TupleStruct { args: ref pat_ids, ellipsis, .. },
404 Constructor::Enum(enum_constructor),
405 ) => {
406 let pat_id = head.as_id().expect("we know this isn't a wild");
407 if !enum_variant_matches(cx, pat_id, *enum_constructor) {
408 None
409 } else {
410 let constructor_arity = constructor.arity(cx)?;
411 if let Some(ellipsis_position) = ellipsis {
412 // If there are ellipsis in the pattern, the ellipsis must take the place
413 // of at least one sub-pattern, so `pat_ids` should be smaller than the
414 // constructor arity.
415 if pat_ids.len() < constructor_arity {
416 let mut new_patterns: Vec<PatIdOrWild> = vec![];
417
418 for pat_id in &pat_ids[0..ellipsis_position] {
419 new_patterns.push((*pat_id).into());
420 }
421
422 for _ in 0..(constructor_arity - pat_ids.len()) {
423 new_patterns.push(PatIdOrWild::Wild);
424 }
425
426 for pat_id in &pat_ids[ellipsis_position..pat_ids.len()] {
427 new_patterns.push((*pat_id).into());
428 }
429
430 Some(self.replace_head_with(new_patterns.into_iter()))
431 } else {
432 return Err(MatchCheckErr::MalformedMatchArm);
433 }
434 } else {
435 // If there is no ellipsis in the tuple pattern, the number
436 // of patterns must equal the constructor arity.
437 if pat_ids.len() == constructor_arity {
438 Some(self.replace_head_with(pat_ids.into_iter()))
439 } else {
440 return Err(MatchCheckErr::MalformedMatchArm);
441 }
442 }
443 }
444 }
445 (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => {
446 let pat_id = head.as_id().expect("we know this isn't a wild");
447 if !enum_variant_matches(cx, pat_id, *e) {
448 None
449 } else {
450 match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() {
451 VariantData::Record(struct_field_arena) => {
452 // Here we treat any missing fields in the record as the wild pattern, as
453 // if the record has ellipsis. We want to do this here even if the
454 // record does not contain ellipsis, because it allows us to continue
455 // enforcing exhaustiveness for the rest of the match statement.
456 //
457 // Creating the diagnostic for the missing field in the pattern
458 // should be done in a different diagnostic.
459 let patterns = struct_field_arena.iter().map(|(_, struct_field)| {
460 arg_patterns
461 .iter()
462 .find(|pat| pat.name == struct_field.name)
463 .map(|pat| PatIdOrWild::from(pat.pat))
464 .unwrap_or(PatIdOrWild::Wild)
465 });
466
467 Some(self.replace_head_with(patterns))
468 }
469 _ => return Err(MatchCheckErr::Unknown),
470 }
471 }
472 }
473 (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented),
474 (_, _) => return Err(MatchCheckErr::NotImplemented),
475 };
476
477 Ok(result)
478 }
479
480 /// A special case of `specialize_constructor` where the head of the pattern stack
481 /// is a Wild pattern.
482 ///
483 /// Replaces the Wild pattern at the head of the pattern stack with N Wild patterns
484 /// (N >= 0), where N is the arity of the given constructor.
485 fn expand_wildcard(
486 &self,
487 cx: &MatchCheckCtx,
488 constructor: &Constructor,
489 ) -> MatchCheckResult<PatStack> {
490 assert_eq!(
491 Pat::Wild,
492 self.get_head().expect("expand_wildcard called on empty PatStack").as_pat(cx),
493 "expand_wildcard must only be called on PatStack with wild at head",
494 );
495
496 let mut patterns: PatStackInner = smallvec![];
497
498 for _ in 0..constructor.arity(cx)? {
499 patterns.push(PatIdOrWild::Wild);
500 }
501
502 for pat in &self.0[1..] {
503 patterns.push(*pat);
504 }
505
506 Ok(PatStack::from_vec(patterns))
507 }
508}
509
510/// A collection of PatStack.
511///
512/// This type is modeled from the struct of the same name in `rustc`.
513pub(super) struct Matrix(Vec<PatStack>);
514
515impl Matrix {
516 pub(super) fn empty() -> Self {
517 Self(vec![])
518 }
519
520 pub(super) fn push(&mut self, cx: &MatchCheckCtx, row: PatStack) {
521 if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) {
522 // Or patterns are expanded here
523 for pat_id in pat_ids {
524 self.0.push(PatStack::from_pattern(pat_id));
525 }
526 } else {
527 self.0.push(row);
528 }
529 }
530
531 fn is_empty(&self) -> bool {
532 self.0.is_empty()
533 }
534
535 fn heads(&self) -> Vec<PatIdOrWild> {
536 self.0.iter().flat_map(|p| p.get_head()).collect()
537 }
538
539 /// Computes `D(self)` for each contained PatStack.
540 ///
541 /// See the module docs and the associated documentation in rustc for details.
542 fn specialize_wildcard(&self, cx: &MatchCheckCtx) -> Self {
543 Self::collect(cx, self.0.iter().filter_map(|r| r.specialize_wildcard(cx)))
544 }
545
546 /// Computes `S(constructor, self)` for each contained PatStack.
547 ///
548 /// See the module docs and the associated documentation in rustc for details.
549 fn specialize_constructor(
550 &self,
551 cx: &MatchCheckCtx,
552 constructor: &Constructor,
553 ) -> MatchCheckResult<Self> {
554 let mut new_matrix = Matrix::empty();
555 for pat in &self.0 {
556 if let Some(pat) = pat.specialize_constructor(cx, constructor)? {
557 new_matrix.push(cx, pat);
558 }
559 }
560
561 Ok(new_matrix)
562 }
563
564 fn collect<T: IntoIterator<Item = PatStack>>(cx: &MatchCheckCtx, iter: T) -> Self {
565 let mut matrix = Matrix::empty();
566
567 for pat in iter {
568 // using push ensures we expand or-patterns
569 matrix.push(cx, pat);
570 }
571
572 matrix
573 }
574}
575
576#[derive(Clone, Debug, PartialEq)]
577/// An indication of the usefulness of a given match arm, where
578/// usefulness is defined as matching some patterns which were
579/// not matched by an prior match arms.
580///
581/// We may eventually need an `Unknown` variant here.
582pub(super) enum Usefulness {
583 Useful,
584 NotUseful,
585}
586
587pub(super) struct MatchCheckCtx<'a> {
588 pub(super) match_expr: Idx<Expr>,
589 pub(super) body: Arc<Body>,
590 pub(super) infer: Arc<InferenceResult>,
591 pub(super) db: &'a dyn HirDatabase,
592}
593
594/// Given a set of patterns `matrix`, and pattern to consider `v`, determines
595/// whether `v` is useful. A pattern is useful if it covers cases which were
596/// not previously covered.
597///
598/// When calling this function externally (that is, not the recursive calls) it
599/// expected that you have already type checked the match arms. All patterns in
600/// matrix should be the same type as v, as well as they should all be the same
601/// type as the match expression.
602pub(super) fn is_useful(
603 cx: &MatchCheckCtx,
604 matrix: &Matrix,
605 v: &PatStack,
606) -> MatchCheckResult<Usefulness> {
607 // Handle two special cases:
608 // - enum with no variants
609 // - `!` type
610 // In those cases, no match arm is useful.
611 match cx.infer[cx.match_expr].strip_references() {
612 Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(AdtId::EnumId(enum_id)), .. }) => {
613 if cx.db.enum_data(*enum_id).variants.is_empty() {
614 return Ok(Usefulness::NotUseful);
615 }
616 }
617 Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) => {
618 return Ok(Usefulness::NotUseful);
619 }
620 _ => (),
621 }
622
623 let head = match v.get_head() {
624 Some(head) => head,
625 None => {
626 let result = if matrix.is_empty() { Usefulness::Useful } else { Usefulness::NotUseful };
627
628 return Ok(result);
629 }
630 };
631
632 if let Pat::Or(pat_ids) = head.as_pat(cx) {
633 let mut found_unimplemented = false;
634 let any_useful = pat_ids.iter().any(|&pat_id| {
635 let v = PatStack::from_pattern(pat_id);
636
637 match is_useful(cx, matrix, &v) {
638 Ok(Usefulness::Useful) => true,
639 Ok(Usefulness::NotUseful) => false,
640 _ => {
641 found_unimplemented = true;
642 false
643 }
644 }
645 });
646
647 return if any_useful {
648 Ok(Usefulness::Useful)
649 } else if found_unimplemented {
650 Err(MatchCheckErr::NotImplemented)
651 } else {
652 Ok(Usefulness::NotUseful)
653 };
654 }
655
656 if let Some(constructor) = pat_constructor(cx, head)? {
657 let matrix = matrix.specialize_constructor(&cx, &constructor)?;
658 let v = v
659 .specialize_constructor(&cx, &constructor)?
660 .expect("we know this can't fail because we get the constructor from `v.head()` above");
661
662 is_useful(&cx, &matrix, &v)
663 } else {
664 // expanding wildcard
665 let mut used_constructors: Vec<Constructor> = vec![];
666 for pat in matrix.heads() {
667 if let Some(constructor) = pat_constructor(cx, pat)? {
668 used_constructors.push(constructor);
669 }
670 }
671
672 // We assume here that the first constructor is the "correct" type. Since we
673 // only care about the "type" of the constructor (i.e. if it is a bool we
674 // don't care about the value), this assumption should be valid as long as
675 // the match statement is well formed. We currently uphold this invariant by
676 // filtering match arms before calling `is_useful`, only passing in match arms
677 // whose type matches the type of the match expression.
678 match &used_constructors.first() {
679 Some(constructor) if all_constructors_covered(&cx, constructor, &used_constructors) => {
680 // If all constructors are covered, then we need to consider whether
681 // any values are covered by this wildcard.
682 //
683 // For example, with matrix '[[Some(true)], [None]]', all
684 // constructors are covered (`Some`/`None`), so we need
685 // to perform specialization to see that our wildcard will cover
686 // the `Some(false)` case.
687 //
688 // Here we create a constructor for each variant and then check
689 // usefulness after specializing for that constructor.
690 let mut found_unimplemented = false;
691 for constructor in constructor.all_constructors(cx) {
692 let matrix = matrix.specialize_constructor(&cx, &constructor)?;
693 let v = v.expand_wildcard(&cx, &constructor)?;
694
695 match is_useful(&cx, &matrix, &v) {
696 Ok(Usefulness::Useful) => return Ok(Usefulness::Useful),
697 Ok(Usefulness::NotUseful) => continue,
698 _ => found_unimplemented = true,
699 };
700 }
701
702 if found_unimplemented {
703 Err(MatchCheckErr::NotImplemented)
704 } else {
705 Ok(Usefulness::NotUseful)
706 }
707 }
708 _ => {
709 // Either not all constructors are covered, or the only other arms
710 // are wildcards. Either way, this pattern is useful if it is useful
711 // when compared to those arms with wildcards.
712 let matrix = matrix.specialize_wildcard(&cx);
713 let v = v.to_tail();
714
715 is_useful(&cx, &matrix, &v)
716 }
717 }
718 }
719}
720
721#[derive(Debug, Clone, Copy)]
722/// Similar to TypeCtor, but includes additional information about the specific
723/// value being instantiated. For example, TypeCtor::Bool doesn't contain the
724/// boolean value.
725enum Constructor {
726 Bool(bool),
727 Tuple { arity: usize },
728 Enum(EnumVariantId),
729}
730
731impl Constructor {
732 fn arity(&self, cx: &MatchCheckCtx) -> MatchCheckResult<usize> {
733 let arity = match self {
734 Constructor::Bool(_) => 0,
735 Constructor::Tuple { arity } => *arity,
736 Constructor::Enum(e) => {
737 match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() {
738 VariantData::Tuple(struct_field_data) => struct_field_data.len(),
739 VariantData::Record(struct_field_data) => struct_field_data.len(),
740 VariantData::Unit => 0,
741 }
742 }
743 };
744
745 Ok(arity)
746 }
747
748 fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> {
749 match self {
750 Constructor::Bool(_) => vec![Constructor::Bool(true), Constructor::Bool(false)],
751 Constructor::Tuple { .. } => vec![*self],
752 Constructor::Enum(e) => cx
753 .db
754 .enum_data(e.parent)
755 .variants
756 .iter()
757 .map(|(local_id, _)| {
758 Constructor::Enum(EnumVariantId { parent: e.parent, local_id })
759 })
760 .collect(),
761 }
762 }
763}
764
765/// Returns the constructor for the given pattern. Should only return None
766/// in the case of a Wild pattern.
767fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> {
768 let res = match pat.as_pat(cx) {
769 Pat::Wild => None,
770 // FIXME somehow create the Tuple constructor with the proper arity. If there are
771 // ellipsis, the arity is not equal to the number of patterns.
772 Pat::Tuple { args: pats, ellipsis } if ellipsis.is_none() => {
773 Some(Constructor::Tuple { arity: pats.len() })
774 }
775 Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] {
776 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)),
777 _ => return Err(MatchCheckErr::NotImplemented),
778 },
779 Pat::TupleStruct { .. } | Pat::Path(_) | Pat::Record { .. } => {
780 let pat_id = pat.as_id().expect("we already know this pattern is not a wild");
781 let variant_id =
782 cx.infer.variant_resolution_for_pat(pat_id).ok_or(MatchCheckErr::Unknown)?;
783 match variant_id {
784 VariantId::EnumVariantId(enum_variant_id) => {
785 Some(Constructor::Enum(enum_variant_id))
786 }
787 _ => return Err(MatchCheckErr::NotImplemented),
788 }
789 }
790 _ => return Err(MatchCheckErr::NotImplemented),
791 };
792
793 Ok(res)
794}
795
796fn all_constructors_covered(
797 cx: &MatchCheckCtx,
798 constructor: &Constructor,
799 used_constructors: &[Constructor],
800) -> bool {
801 match constructor {
802 Constructor::Tuple { arity } => {
803 used_constructors.iter().any(|constructor| match constructor {
804 Constructor::Tuple { arity: used_arity } => arity == used_arity,
805 _ => false,
806 })
807 }
808 Constructor::Bool(_) => {
809 if used_constructors.is_empty() {
810 return false;
811 }
812
813 let covers_true =
814 used_constructors.iter().any(|c| matches!(c, Constructor::Bool(true)));
815 let covers_false =
816 used_constructors.iter().any(|c| matches!(c, Constructor::Bool(false)));
817
818 covers_true && covers_false
819 }
820 Constructor::Enum(e) => cx.db.enum_data(e.parent).variants.iter().all(|(id, _)| {
821 for constructor in used_constructors {
822 if let Constructor::Enum(e) = constructor {
823 if id == e.local_id {
824 return true;
825 }
826 }
827 }
828
829 false
830 }),
831 }
832}
833
834fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: EnumVariantId) -> bool {
835 Some(enum_variant_id.into()) == cx.infer.variant_resolution_for_pat(pat_id)
836}
837
838#[cfg(test)]
839mod tests {
840 use crate::diagnostics::tests::check_diagnostics;
841
842 #[test]
843 fn empty_tuple() {
844 check_diagnostics(
845 r#"
846fn main() {
847 match () { }
848 //^^ Missing match arm
849 match (()) { }
850 //^^^^ Missing match arm
851
852 match () { _ => (), }
853 match () { () => (), }
854 match (()) { (()) => (), }
855}
856"#,
857 );
858 }
859
860 #[test]
861 fn tuple_of_two_empty_tuple() {
862 check_diagnostics(
863 r#"
864fn main() {
865 match ((), ()) { }
866 //^^^^^^^^ Missing match arm
867
868 match ((), ()) { ((), ()) => (), }
869}
870"#,
871 );
872 }
873
874 #[test]
875 fn boolean() {
876 check_diagnostics(
877 r#"
878fn test_main() {
879 match false { }
880 //^^^^^ Missing match arm
881 match false { true => (), }
882 //^^^^^ Missing match arm
883 match (false, true) {}
884 //^^^^^^^^^^^^^ Missing match arm
885 match (false, true) { (true, true) => (), }
886 //^^^^^^^^^^^^^ Missing match arm
887 match (false, true) {
888 //^^^^^^^^^^^^^ Missing match arm
889 (false, true) => (),
890 (false, false) => (),
891 (true, false) => (),
892 }
893 match (false, true) { (true, _x) => (), }
894 //^^^^^^^^^^^^^ Missing match arm
895
896 match false { true => (), false => (), }
897 match (false, true) {
898 (false, _) => (),
899 (true, false) => (),
900 (_, true) => (),
901 }
902 match (false, true) {
903 (true, true) => (),
904 (true, false) => (),
905 (false, true) => (),
906 (false, false) => (),
907 }
908 match (false, true) {
909 (true, _x) => (),
910 (false, true) => (),
911 (false, false) => (),
912 }
913 match (false, true, false) {
914 (false, ..) => (),
915 (true, ..) => (),
916 }
917 match (false, true, false) {
918 (.., false) => (),
919 (.., true) => (),
920 }
921 match (false, true, false) { (..) => (), }
922}
923"#,
924 );
925 }
926
927 #[test]
928 fn tuple_of_tuple_and_bools() {
929 check_diagnostics(
930 r#"
931fn main() {
932 match (false, ((), false)) {}
933 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
934 match (false, ((), false)) { (true, ((), true)) => (), }
935 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
936 match (false, ((), false)) { (true, _) => (), }
937 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
938
939 match (false, ((), false)) {
940 (true, ((), true)) => (),
941 (true, ((), false)) => (),
942 (false, ((), true)) => (),
943 (false, ((), false)) => (),
944 }
945 match (false, ((), false)) {
946 (true, ((), true)) => (),
947 (true, ((), false)) => (),
948 (false, _) => (),
949 }
950}
951"#,
952 );
953 }
954
955 #[test]
956 fn enums() {
957 check_diagnostics(
958 r#"
959enum Either { A, B, }
960
961fn main() {
962 match Either::A { }
963 //^^^^^^^^^ Missing match arm
964 match Either::B { Either::A => (), }
965 //^^^^^^^^^ Missing match arm
966
967 match &Either::B {
968 //^^^^^^^^^^ Missing match arm
969 Either::A => (),
970 }
971
972 match Either::B {
973 Either::A => (), Either::B => (),
974 }
975 match &Either::B {
976 Either::A => (), Either::B => (),
977 }
978}
979"#,
980 );
981 }
982
983 #[test]
984 fn enum_containing_bool() {
985 check_diagnostics(
986 r#"
987enum Either { A(bool), B }
988
989fn main() {
990 match Either::B { }
991 //^^^^^^^^^ Missing match arm
992 match Either::B {
993 //^^^^^^^^^ Missing match arm
994 Either::A(true) => (), Either::B => ()
995 }
996
997 match Either::B {
998 Either::A(true) => (),
999 Either::A(false) => (),
1000 Either::B => (),
1001 }
1002 match Either::B {
1003 Either::B => (),
1004 _ => (),
1005 }
1006 match Either::B {
1007 Either::A(_) => (),
1008 Either::B => (),
1009 }
1010
1011}
1012 "#,
1013 );
1014 }
1015
1016 #[test]
1017 fn enum_different_sizes() {
1018 check_diagnostics(
1019 r#"
1020enum Either { A(bool), B(bool, bool) }
1021
1022fn main() {
1023 match Either::A(false) {
1024 //^^^^^^^^^^^^^^^^ Missing match arm
1025 Either::A(_) => (),
1026 Either::B(false, _) => (),
1027 }
1028
1029 match Either::A(false) {
1030 Either::A(_) => (),
1031 Either::B(true, _) => (),
1032 Either::B(false, _) => (),
1033 }
1034 match Either::A(false) {
1035 Either::A(true) | Either::A(false) => (),
1036 Either::B(true, _) => (),
1037 Either::B(false, _) => (),
1038 }
1039}
1040"#,
1041 );
1042 }
1043
1044 #[test]
1045 fn tuple_of_enum_no_diagnostic() {
1046 check_diagnostics(
1047 r#"
1048enum Either { A(bool), B(bool, bool) }
1049enum Either2 { C, D }
1050
1051fn main() {
1052 match (Either::A(false), Either2::C) {
1053 (Either::A(true), _) | (Either::A(false), _) => (),
1054 (Either::B(true, _), Either2::C) => (),
1055 (Either::B(false, _), Either2::C) => (),
1056 (Either::B(_, _), Either2::D) => (),
1057 }
1058}
1059"#,
1060 );
1061 }
1062
1063 #[test]
1064 fn mismatched_types() {
1065 // Match statements with arms that don't match the
1066 // expression pattern do not fire this diagnostic.
1067 check_diagnostics(
1068 r#"
1069enum Either { A, B }
1070enum Either2 { C, D }
1071
1072fn main() {
1073 match Either::A {
1074 Either2::C => (),
1075 Either2::D => (),
1076 }
1077 match (true, false) {
1078 (true, false, true) => (),
1079 (true) => (),
1080 }
1081 match (0) { () => () }
1082 match Unresolved::Bar { Unresolved::Baz => () }
1083}
1084 "#,
1085 );
1086 }
1087
1088 #[test]
1089 fn malformed_match_arm_tuple_enum_missing_pattern() {
1090 // We are testing to be sure we don't panic here when the match
1091 // arm `Either::B` is missing its pattern.
1092 check_diagnostics(
1093 r#"
1094enum Either { A, B(u32) }
1095
1096fn main() {
1097 match Either::A {
1098 Either::A => (),
1099 Either::B() => (),
1100 }
1101}
1102"#,
1103 );
1104 }
1105
1106 #[test]
1107 fn expr_diverges() {
1108 check_diagnostics(
1109 r#"
1110enum Either { A, B }
1111
1112fn main() {
1113 match loop {} {
1114 Either::A => (),
1115 Either::B => (),
1116 }
1117 match loop {} {
1118 Either::A => (),
1119 }
1120 match loop { break Foo::A } {
1121 //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
1122 Either::A => (),
1123 }
1124 match loop { break Foo::A } {
1125 Either::A => (),
1126 Either::B => (),
1127 }
1128}
1129"#,
1130 );
1131 }
1132
1133 #[test]
1134 fn expr_partially_diverges() {
1135 check_diagnostics(
1136 r#"
1137enum Either<T> { A(T), B }
1138
1139fn foo() -> Either<!> { Either::B }
1140fn main() -> u32 {
1141 match foo() {
1142 Either::A(val) => val,
1143 Either::B => 0,
1144 }
1145}
1146"#,
1147 );
1148 }
1149
1150 #[test]
1151 fn enum_record() {
1152 check_diagnostics(
1153 r#"
1154enum Either { A { foo: bool }, B }
1155
1156fn main() {
1157 let a = Either::A { foo: true };
1158 match a { }
1159 //^ Missing match arm
1160 match a { Either::A { foo: true } => () }
1161 //^ Missing match arm
1162 match a {
1163 Either::A { } => (),
1164 //^^^ Missing structure fields:
1165 // | - foo
1166 Either::B => (),
1167 }
1168 match a {
1169 //^ Missing match arm
1170 Either::A { } => (),
1171 } //^^^ Missing structure fields:
1172 // | - foo
1173
1174 match a {
1175 Either::A { foo: true } => (),
1176 Either::A { foo: false } => (),
1177 Either::B => (),
1178 }
1179 match a {
1180 Either::A { foo: _ } => (),
1181 Either::B => (),
1182 }
1183}
1184"#,
1185 );
1186 }
1187
1188 #[test]
1189 fn enum_record_fields_out_of_order() {
1190 check_diagnostics(
1191 r#"
1192enum Either {
1193 A { foo: bool, bar: () },
1194 B,
1195}
1196
1197fn main() {
1198 let a = Either::A { foo: true, bar: () };
1199 match a {
1200 //^ Missing match arm
1201 Either::A { bar: (), foo: false } => (),
1202 Either::A { foo: true, bar: () } => (),
1203 }
1204
1205 match a {
1206 Either::A { bar: (), foo: false } => (),
1207 Either::A { foo: true, bar: () } => (),
1208 Either::B => (),
1209 }
1210}
1211"#,
1212 );
1213 }
1214
1215 #[test]
1216 fn enum_record_ellipsis() {
1217 check_diagnostics(
1218 r#"
1219enum Either {
1220 A { foo: bool, bar: bool },
1221 B,
1222}
1223
1224fn main() {
1225 let a = Either::B;
1226 match a {
1227 //^ Missing match arm
1228 Either::A { foo: true, .. } => (),
1229 Either::B => (),
1230 }
1231 match a {
1232 //^ Missing match arm
1233 Either::A { .. } => (),
1234 }
1235
1236 match a {
1237 Either::A { foo: true, .. } => (),
1238 Either::A { foo: false, .. } => (),
1239 Either::B => (),
1240 }
1241
1242 match a {
1243 Either::A { .. } => (),
1244 Either::B => (),
1245 }
1246}
1247"#,
1248 );
1249 }
1250
1251 #[test]
1252 fn enum_tuple_partial_ellipsis() {
1253 check_diagnostics(
1254 r#"
1255enum Either {
1256 A(bool, bool, bool, bool),
1257 B,
1258}
1259
1260fn main() {
1261 match Either::B {
1262 //^^^^^^^^^ Missing match arm
1263 Either::A(true, .., true) => (),
1264 Either::A(true, .., false) => (),
1265 Either::A(false, .., false) => (),
1266 Either::B => (),
1267 }
1268 match Either::B {
1269 //^^^^^^^^^ Missing match arm
1270 Either::A(true, .., true) => (),
1271 Either::A(true, .., false) => (),
1272 Either::A(.., true) => (),
1273 Either::B => (),
1274 }
1275
1276 match Either::B {
1277 Either::A(true, .., true) => (),
1278 Either::A(true, .., false) => (),
1279 Either::A(false, .., true) => (),
1280 Either::A(false, .., false) => (),
1281 Either::B => (),
1282 }
1283 match Either::B {
1284 Either::A(true, .., true) => (),
1285 Either::A(true, .., false) => (),
1286 Either::A(.., true) => (),
1287 Either::A(.., false) => (),
1288 Either::B => (),
1289 }
1290}
1291"#,
1292 );
1293 }
1294
1295 #[test]
1296 fn never() {
1297 check_diagnostics(
1298 r#"
1299enum Never {}
1300
1301fn enum_(never: Never) {
1302 match never {}
1303}
1304fn enum_ref(never: &Never) {
1305 match never {}
1306}
1307fn bang(never: !) {
1308 match never {}
1309}
1310"#,
1311 );
1312 }
1313
1314 #[test]
1315 fn or_pattern_panic() {
1316 check_diagnostics(
1317 r#"
1318pub enum Category { Infinity, Zero }
1319
1320fn panic(a: Category, b: Category) {
1321 match (a, b) {
1322 (Category::Zero | Category::Infinity, _) => (),
1323 (_, Category::Zero | Category::Infinity) => (),
1324 }
1325
1326 // FIXME: This is a false positive, but the code used to cause a panic in the match checker,
1327 // so this acts as a regression test for that.
1328 match (a, b) {
1329 //^^^^^^ Missing match arm
1330 (Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => (),
1331 (Category::Infinity | Category::Zero, _) => (),
1332 }
1333}
1334"#,
1335 );
1336 }
1337
1338 mod false_negatives {
1339 //! The implementation of match checking here is a work in progress. As we roll this out, we
1340 //! prefer false negatives to false positives (ideally there would be no false positives). This
1341 //! test module should document known false negatives. Eventually we will have a complete
1342 //! implementation of match checking and this module will be empty.
1343 //!
1344 //! The reasons for documenting known false negatives:
1345 //!
1346 //! 1. It acts as a backlog of work that can be done to improve the behavior of the system.
1347 //! 2. It ensures the code doesn't panic when handling these cases.
1348 use super::*;
1349
1350 #[test]
1351 fn integers() {
1352 // We don't currently check integer exhaustiveness.
1353 check_diagnostics(
1354 r#"
1355fn main() {
1356 match 5 {
1357 10 => (),
1358 11..20 => (),
1359 }
1360}
1361"#,
1362 );
1363 }
1364
1365 #[test]
1366 fn internal_or() {
1367 // We do not currently handle patterns with internal `or`s.
1368 check_diagnostics(
1369 r#"
1370fn main() {
1371 enum Either { A(bool), B }
1372 match Either::B {
1373 Either::A(true | false) => (),
1374 }
1375}
1376"#,
1377 );
1378 }
1379
1380 #[test]
1381 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
1382 // We don't currently handle tuple patterns with ellipsis.
1383 check_diagnostics(
1384 r#"
1385fn main() {
1386 match (false, true, false) {
1387 (false, ..) => (),
1388 }
1389}
1390"#,
1391 );
1392 }
1393
1394 #[test]
1395 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
1396 // We don't currently handle tuple patterns with ellipsis.
1397 check_diagnostics(
1398 r#"
1399fn main() {
1400 match (false, true, false) {
1401 (.., false) => (),
1402 }
1403}
1404"#,
1405 );
1406 }
1407
1408 #[test]
1409 fn struct_missing_arm() {
1410 // We don't currently handle structs.
1411 check_diagnostics(
1412 r#"
1413struct Foo { a: bool }
1414fn main(f: Foo) {
1415 match f { Foo { a: true } => () }
1416}
1417"#,
1418 );
1419 }
1420 }
1421}
diff --git a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs
new file mode 100644
index 000000000..5cc76bdce
--- /dev/null
+++ b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs
@@ -0,0 +1,173 @@
1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
2//! unsafe blocks.
3
4use std::sync::Arc;
5
6use hir_def::{
7 body::Body,
8 expr::{Expr, ExprId, UnaryOp},
9 DefWithBodyId,
10};
11use hir_expand::diagnostics::DiagnosticSink;
12
13use crate::{
14 db::HirDatabase, diagnostics::MissingUnsafe, lower::CallableDefId, ApplicationTy,
15 InferenceResult, Ty, TypeCtor,
16};
17
18pub(super) struct UnsafeValidator<'a, 'b: 'a> {
19 owner: DefWithBodyId,
20 infer: Arc<InferenceResult>,
21 sink: &'a mut DiagnosticSink<'b>,
22}
23
24impl<'a, 'b> UnsafeValidator<'a, 'b> {
25 pub(super) fn new(
26 owner: DefWithBodyId,
27 infer: Arc<InferenceResult>,
28 sink: &'a mut DiagnosticSink<'b>,
29 ) -> UnsafeValidator<'a, 'b> {
30 UnsafeValidator { owner, infer, sink }
31 }
32
33 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) {
34 let def = self.owner.into();
35 let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def);
36 let is_unsafe = match self.owner {
37 DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe,
38 DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
39 };
40 if is_unsafe
41 || unsafe_expressions
42 .iter()
43 .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block)
44 .count()
45 == 0
46 {
47 return;
48 }
49
50 let (_, body_source) = db.body_with_source_map(def);
51 for unsafe_expr in unsafe_expressions {
52 if !unsafe_expr.inside_unsafe_block {
53 if let Ok(in_file) = body_source.as_ref().expr_syntax(unsafe_expr.expr) {
54 self.sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value })
55 }
56 }
57 }
58 }
59}
60
61pub struct UnsafeExpr {
62 pub expr: ExprId,
63 pub inside_unsafe_block: bool,
64}
65
66pub fn unsafe_expressions(
67 db: &dyn HirDatabase,
68 infer: &InferenceResult,
69 def: DefWithBodyId,
70) -> Vec<UnsafeExpr> {
71 let mut unsafe_exprs = vec![];
72 let body = db.body(def);
73 walk_unsafe(&mut unsafe_exprs, db, infer, &body, body.body_expr, false);
74
75 unsafe_exprs
76}
77
78fn walk_unsafe(
79 unsafe_exprs: &mut Vec<UnsafeExpr>,
80 db: &dyn HirDatabase,
81 infer: &InferenceResult,
82 body: &Body,
83 current: ExprId,
84 inside_unsafe_block: bool,
85) {
86 let expr = &body.exprs[current];
87 match expr {
88 Expr::Call { callee, .. } => {
89 let ty = &infer[*callee];
90 if let &Ty::Apply(ApplicationTy {
91 ctor: TypeCtor::FnDef(CallableDefId::FunctionId(func)),
92 ..
93 }) = ty
94 {
95 if db.function_data(func).is_unsafe {
96 unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
97 }
98 }
99 }
100 Expr::MethodCall { .. } => {
101 if infer
102 .method_resolution(current)
103 .map(|func| db.function_data(func).is_unsafe)
104 .unwrap_or(false)
105 {
106 unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
107 }
108 }
109 Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
110 if let Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }) = &infer[*expr] {
111 unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
112 }
113 }
114 Expr::Unsafe { body: child } => {
115 return walk_unsafe(unsafe_exprs, db, infer, body, *child, true);
116 }
117 _ => {}
118 }
119
120 expr.walk_child_exprs(|child| {
121 walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block);
122 });
123}
124
125#[cfg(test)]
126mod tests {
127 use crate::diagnostics::tests::check_diagnostics;
128
129 #[test]
130 fn missing_unsafe_diagnostic_with_raw_ptr() {
131 check_diagnostics(
132 r#"
133fn main() {
134 let x = &5 as *const usize;
135 unsafe { let y = *x; }
136 let z = *x;
137} //^^ This operation is unsafe and requires an unsafe function or block
138"#,
139 )
140 }
141
142 #[test]
143 fn missing_unsafe_diagnostic_with_unsafe_call() {
144 check_diagnostics(
145 r#"
146struct HasUnsafe;
147
148impl HasUnsafe {
149 unsafe fn unsafe_fn(&self) {
150 let x = &5 as *const usize;
151 let y = *x;
152 }
153}
154
155unsafe fn unsafe_fn() {
156 let x = &5 as *const usize;
157 let y = *x;
158}
159
160fn main() {
161 unsafe_fn();
162 //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
163 HasUnsafe.unsafe_fn();
164 //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
165 unsafe {
166 unsafe_fn();
167 HasUnsafe.unsafe_fn();
168 }
169}
170"#,
171 );
172 }
173}
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs
index b9c4d2e89..758d5f5ac 100644
--- a/crates/ra_hir_ty/src/display.rs
+++ b/crates/ra_hir_ty/src/display.rs
@@ -3,8 +3,8 @@
3use std::fmt; 3use std::fmt;
4 4
5use crate::{ 5use crate::{
6 db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, 6 db::HirDatabase, utils::generics, ApplicationTy, CallableDefId, FnSig, GenericPredicate,
7 Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, 7 Obligation, OpaqueTyId, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
8}; 8};
9use hir_def::{ 9use hir_def::{
10 find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, 10 find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId,
@@ -243,10 +243,17 @@ impl HirDisplay for ApplicationTy {
243 write!(f, ")")?; 243 write!(f, ")")?;
244 } 244 }
245 } 245 }
246 TypeCtor::FnPtr { .. } => { 246 TypeCtor::FnPtr { is_varargs, .. } => {
247 let sig = FnSig::from_fn_ptr_substs(&self.parameters); 247 let sig = FnSig::from_fn_ptr_substs(&self.parameters, is_varargs);
248 write!(f, "fn(")?; 248 write!(f, "fn(")?;
249 f.write_joined(sig.params(), ", ")?; 249 f.write_joined(sig.params(), ", ")?;
250 if is_varargs {
251 if sig.params().is_empty() {
252 write!(f, "...")?;
253 } else {
254 write!(f, ", ...")?;
255 }
256 }
250 write!(f, ")")?; 257 write!(f, ")")?;
251 let ret = sig.ret(); 258 let ret = sig.ret();
252 if *ret != Ty::unit() { 259 if *ret != Ty::unit() {
@@ -256,9 +263,11 @@ impl HirDisplay for ApplicationTy {
256 TypeCtor::FnDef(def) => { 263 TypeCtor::FnDef(def) => {
257 let sig = f.db.callable_item_signature(def).subst(&self.parameters); 264 let sig = f.db.callable_item_signature(def).subst(&self.parameters);
258 match def { 265 match def {
259 CallableDef::FunctionId(ff) => write!(f, "fn {}", f.db.function_data(ff).name)?, 266 CallableDefId::FunctionId(ff) => {
260 CallableDef::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?, 267 write!(f, "fn {}", f.db.function_data(ff).name)?
261 CallableDef::EnumVariantId(e) => { 268 }
269 CallableDefId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?,
270 CallableDefId::EnumVariantId(e) => {
262 write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)? 271 write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)?
263 } 272 }
264 }; 273 };
@@ -308,7 +317,6 @@ impl HirDisplay for ApplicationTy {
308 } 317 }
309 318
310 if self.parameters.len() > 0 { 319 if self.parameters.len() > 0 {
311 let mut non_default_parameters = Vec::with_capacity(self.parameters.len());
312 let parameters_to_write = 320 let parameters_to_write =
313 if f.display_target.is_source_code() || f.omit_verbose_types() { 321 if f.display_target.is_source_code() || f.omit_verbose_types() {
314 match self 322 match self
@@ -319,20 +327,23 @@ impl HirDisplay for ApplicationTy {
319 { 327 {
320 None => self.parameters.0.as_ref(), 328 None => self.parameters.0.as_ref(),
321 Some(default_parameters) => { 329 Some(default_parameters) => {
330 let mut default_from = 0;
322 for (i, parameter) in self.parameters.iter().enumerate() { 331 for (i, parameter) in self.parameters.iter().enumerate() {
323 match (parameter, default_parameters.get(i)) { 332 match (parameter, default_parameters.get(i)) {
324 (&Ty::Unknown, _) | (_, None) => { 333 (&Ty::Unknown, _) | (_, None) => {
325 non_default_parameters.push(parameter.clone()) 334 default_from = i + 1;
326 } 335 }
327 (_, Some(default_parameter)) 336 (_, Some(default_parameter)) => {
328 if parameter != default_parameter => 337 let actual_default = default_parameter
329 { 338 .clone()
330 non_default_parameters.push(parameter.clone()) 339 .subst(&self.parameters.prefix(i));
340 if parameter != &actual_default {
341 default_from = i + 1;
342 }
331 } 343 }
332 _ => (),
333 } 344 }
334 } 345 }
335 &non_default_parameters 346 &self.parameters.0[0..default_from]
336 } 347 }
337 } 348 }
338 } else { 349 } else {
@@ -359,6 +370,21 @@ impl HirDisplay for ApplicationTy {
359 write!(f, ">")?; 370 write!(f, ">")?;
360 } 371 }
361 } 372 }
373 TypeCtor::OpaqueType(opaque_ty_id) => {
374 let bounds = match opaque_ty_id {
375 OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
376 let datas =
377 f.db.return_type_impl_traits(func).expect("impl trait id without data");
378 let data = (*datas)
379 .as_ref()
380 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
381 data.subst(&self.parameters)
382 }
383 };
384 write!(f, "impl ")?;
385 write_bounds_like_dyn_trait(&bounds.value, f)?;
386 // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
387 }
362 TypeCtor::Closure { .. } => { 388 TypeCtor::Closure { .. } => {
363 let sig = self.parameters[0].callable_sig(f.db); 389 let sig = self.parameters[0].callable_sig(f.db);
364 if let Some(sig) = sig { 390 if let Some(sig) = sig {
@@ -427,14 +453,24 @@ impl HirDisplay for Ty {
427 } 453 }
428 } 454 }
429 Ty::Bound(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?, 455 Ty::Bound(idx) => write!(f, "?{}.{}", idx.debruijn.depth(), idx.index)?,
430 Ty::Dyn(predicates) | Ty::Opaque(predicates) => { 456 Ty::Dyn(predicates) => {
431 match self { 457 write!(f, "dyn ")?;
432 Ty::Dyn(_) => write!(f, "dyn ")?,
433 Ty::Opaque(_) => write!(f, "impl ")?,
434 _ => unreachable!(),
435 };
436 write_bounds_like_dyn_trait(predicates, f)?; 458 write_bounds_like_dyn_trait(predicates, f)?;
437 } 459 }
460 Ty::Opaque(opaque_ty) => {
461 let bounds = match opaque_ty.opaque_ty_id {
462 OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
463 let datas =
464 f.db.return_type_impl_traits(func).expect("impl trait id without data");
465 let data = (*datas)
466 .as_ref()
467 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
468 data.subst(&opaque_ty.parameters)
469 }
470 };
471 write!(f, "impl ")?;
472 write_bounds_like_dyn_trait(&bounds.value, f)?;
473 }
438 Ty::Unknown => write!(f, "{{unknown}}")?, 474 Ty::Unknown => write!(f, "{{unknown}}")?,
439 Ty::Infer(..) => write!(f, "_")?, 475 Ty::Infer(..) => write!(f, "_")?,
440 } 476 }
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index dc77e88e5..28f32a0a4 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -18,8 +18,6 @@ use std::mem;
18use std::ops::Index; 18use std::ops::Index;
19use std::sync::Arc; 19use std::sync::Arc;
20 20
21use rustc_hash::FxHashMap;
22
23use hir_def::{ 21use hir_def::{
24 body::Body, 22 body::Body,
25 data::{ConstData, FunctionData, StaticData}, 23 data::{ConstData, FunctionData, StaticData},
@@ -28,19 +26,20 @@ use hir_def::{
28 path::{path, Path}, 26 path::{path, Path},
29 resolver::{HasResolver, Resolver, TypeNs}, 27 resolver::{HasResolver, Resolver, TypeNs},
30 type_ref::{Mutability, TypeRef}, 28 type_ref::{Mutability, TypeRef},
31 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, TraitId, TypeAliasId, 29 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, Lookup, TraitId,
32 VariantId, 30 TypeAliasId, VariantId,
33}; 31};
34use hir_expand::{diagnostics::DiagnosticSink, name::name}; 32use hir_expand::{diagnostics::DiagnosticSink, name::name};
35use ra_arena::map::ArenaMap; 33use ra_arena::map::ArenaMap;
36use ra_prof::profile; 34use ra_prof::profile;
37use ra_syntax::SmolStr; 35use ra_syntax::SmolStr;
36use rustc_hash::FxHashMap;
37use stdx::impl_from;
38 38
39use super::{ 39use super::{
40 primitive::{FloatTy, IntTy}, 40 primitive::{FloatTy, IntTy},
41 traits::{Guidance, Obligation, ProjectionPredicate, Solution}, 41 traits::{Guidance, Obligation, ProjectionPredicate, Solution},
42 ApplicationTy, GenericPredicate, InEnvironment, ProjectionTy, Substs, TraitEnvironment, 42 InEnvironment, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
43 TraitRef, Ty, TypeCtor, TypeWalk, Uncertain,
44}; 43};
45use crate::{ 44use crate::{
46 db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode, 45 db::HirDatabase, infer::diagnostics::InferenceDiagnostic, lower::ImplTraitLoweringMode,
@@ -85,8 +84,7 @@ enum ExprOrPatId {
85 ExprId(ExprId), 84 ExprId(ExprId),
86 PatId(PatId), 85 PatId(PatId),
87} 86}
88 87impl_from!(ExprId, PatId for ExprOrPatId);
89impl_froms!(ExprOrPatId: ExprId, PatId);
90 88
91/// Binding modes inferred for patterns. 89/// Binding modes inferred for patterns.
92/// https://doc.rust-lang.org/reference/patterns.html#binding-modes 90/// https://doc.rust-lang.org/reference/patterns.html#binding-modes
@@ -170,7 +168,7 @@ impl InferenceResult {
170 pub fn add_diagnostics( 168 pub fn add_diagnostics(
171 &self, 169 &self,
172 db: &dyn HirDatabase, 170 db: &dyn HirDatabase,
173 owner: FunctionId, 171 owner: DefWithBodyId,
174 sink: &mut DiagnosticSink, 172 sink: &mut DiagnosticSink,
175 ) { 173 ) {
176 self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) 174 self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink))
@@ -312,12 +310,6 @@ impl<'a> InferenceContext<'a> {
312 fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { 310 fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
313 match ty { 311 match ty {
314 Ty::Unknown => self.table.new_type_var(), 312 Ty::Unknown => self.table.new_type_var(),
315 Ty::Apply(ApplicationTy { ctor: TypeCtor::Int(Uncertain::Unknown), .. }) => {
316 self.table.new_integer_var()
317 }
318 Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(Uncertain::Unknown), .. }) => {
319 self.table.new_float_var()
320 }
321 _ => ty, 313 _ => ty,
322 } 314 }
323 } 315 }
@@ -383,36 +375,21 @@ impl<'a> InferenceContext<'a> {
383 ) -> Ty { 375 ) -> Ty {
384 match assoc_ty { 376 match assoc_ty {
385 Some(res_assoc_ty) => { 377 Some(res_assoc_ty) => {
386 // FIXME: 378 let trait_ = match res_assoc_ty.lookup(self.db.upcast()).container {
387 // Check if inner_ty is is `impl Trait` and contained input TypeAlias id 379 hir_def::AssocContainerId::TraitId(trait_) => trait_,
388 // this is a workaround while Chalk assoc type projection doesn't always work yet, 380 _ => panic!("resolve_associated_type called with non-associated type"),
389 // but once that is fixed I don't think we should keep this 381 };
390 // (we'll probably change how associated types are resolved anyway)
391 if let Ty::Opaque(ref predicates) = inner_ty {
392 for p in predicates.iter() {
393 if let GenericPredicate::Projection(projection) = p {
394 if projection.projection_ty.associated_ty == res_assoc_ty {
395 if let ty_app!(_, params) = &projection.ty {
396 if params.len() == 0 {
397 return projection.ty.clone();
398 }
399 }
400 }
401 }
402 }
403 }
404
405 let ty = self.table.new_type_var(); 382 let ty = self.table.new_type_var();
406 let builder = Substs::build_for_def(self.db, res_assoc_ty) 383 let substs = Substs::build_for_def(self.db, res_assoc_ty)
407 .push(inner_ty) 384 .push(inner_ty)
408 .fill(params.iter().cloned()); 385 .fill(params.iter().cloned())
386 .build();
387 let trait_ref = TraitRef { trait_, substs: substs.clone() };
409 let projection = ProjectionPredicate { 388 let projection = ProjectionPredicate {
410 ty: ty.clone(), 389 ty: ty.clone(),
411 projection_ty: ProjectionTy { 390 projection_ty: ProjectionTy { associated_ty: res_assoc_ty, parameters: substs },
412 associated_ty: res_assoc_ty,
413 parameters: builder.build(),
414 },
415 }; 391 };
392 self.obligations.push(Obligation::Trait(trait_ref));
416 self.obligations.push(Obligation::Projection(projection)); 393 self.obligations.push(Obligation::Projection(projection));
417 self.resolve_ty_as_possible(ty) 394 self.resolve_ty_as_possible(ty)
418 } 395 }
@@ -458,13 +435,13 @@ impl<'a> InferenceContext<'a> {
458 }; 435 };
459 return match resolution { 436 return match resolution {
460 TypeNs::AdtId(AdtId::StructId(strukt)) => { 437 TypeNs::AdtId(AdtId::StructId(strukt)) => {
461 let substs = Ty::substs_from_path(&ctx, path, strukt.into()); 438 let substs = Ty::substs_from_path(&ctx, path, strukt.into(), true);
462 let ty = self.db.ty(strukt.into()); 439 let ty = self.db.ty(strukt.into());
463 let ty = self.insert_type_vars(ty.subst(&substs)); 440 let ty = self.insert_type_vars(ty.subst(&substs));
464 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) 441 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
465 } 442 }
466 TypeNs::EnumVariantId(var) => { 443 TypeNs::EnumVariantId(var) => {
467 let substs = Ty::substs_from_path(&ctx, path, var.into()); 444 let substs = Ty::substs_from_path(&ctx, path, var.into(), true);
468 let ty = self.db.ty(var.parent.into()); 445 let ty = self.db.ty(var.parent.into());
469 let ty = self.insert_type_vars(ty.subst(&substs)); 446 let ty = self.insert_type_vars(ty.subst(&substs));
470 forbid_unresolved_segments((ty, Some(var.into())), unresolved) 447 forbid_unresolved_segments((ty, Some(var.into())), unresolved)
@@ -581,13 +558,13 @@ impl<'a> InferenceContext<'a> {
581 } 558 }
582 559
583 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> { 560 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
584 let path = path![std::iter::IntoIterator]; 561 let path = path![core::iter::IntoIterator];
585 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; 562 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
586 self.db.trait_data(trait_).associated_type_by_name(&name![Item]) 563 self.db.trait_data(trait_).associated_type_by_name(&name![Item])
587 } 564 }
588 565
589 fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> { 566 fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> {
590 let path = path![std::ops::Try]; 567 let path = path![core::ops::Try];
591 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; 568 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
592 self.db.trait_data(trait_).associated_type_by_name(&name![Ok]) 569 self.db.trait_data(trait_).associated_type_by_name(&name![Ok])
593 } 570 }
@@ -613,37 +590,37 @@ impl<'a> InferenceContext<'a> {
613 } 590 }
614 591
615 fn resolve_range_full(&self) -> Option<AdtId> { 592 fn resolve_range_full(&self) -> Option<AdtId> {
616 let path = path![std::ops::RangeFull]; 593 let path = path![core::ops::RangeFull];
617 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 594 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
618 Some(struct_.into()) 595 Some(struct_.into())
619 } 596 }
620 597
621 fn resolve_range(&self) -> Option<AdtId> { 598 fn resolve_range(&self) -> Option<AdtId> {
622 let path = path![std::ops::Range]; 599 let path = path![core::ops::Range];
623 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 600 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
624 Some(struct_.into()) 601 Some(struct_.into())
625 } 602 }
626 603
627 fn resolve_range_inclusive(&self) -> Option<AdtId> { 604 fn resolve_range_inclusive(&self) -> Option<AdtId> {
628 let path = path![std::ops::RangeInclusive]; 605 let path = path![core::ops::RangeInclusive];
629 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 606 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
630 Some(struct_.into()) 607 Some(struct_.into())
631 } 608 }
632 609
633 fn resolve_range_from(&self) -> Option<AdtId> { 610 fn resolve_range_from(&self) -> Option<AdtId> {
634 let path = path![std::ops::RangeFrom]; 611 let path = path![core::ops::RangeFrom];
635 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 612 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
636 Some(struct_.into()) 613 Some(struct_.into())
637 } 614 }
638 615
639 fn resolve_range_to(&self) -> Option<AdtId> { 616 fn resolve_range_to(&self) -> Option<AdtId> {
640 let path = path![std::ops::RangeTo]; 617 let path = path![core::ops::RangeTo];
641 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 618 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
642 Some(struct_.into()) 619 Some(struct_.into())
643 } 620 }
644 621
645 fn resolve_range_to_inclusive(&self) -> Option<AdtId> { 622 fn resolve_range_to_inclusive(&self) -> Option<AdtId> {
646 let path = path![std::ops::RangeToInclusive]; 623 let path = path![core::ops::RangeToInclusive];
647 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 624 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
648 Some(struct_.into()) 625 Some(struct_.into())
649 } 626 }
@@ -683,8 +660,8 @@ impl InferTy {
683 fn fallback_value(self) -> Ty { 660 fn fallback_value(self) -> Ty {
684 match self { 661 match self {
685 InferTy::TypeVar(..) => Ty::Unknown, 662 InferTy::TypeVar(..) => Ty::Unknown,
686 InferTy::IntVar(..) => Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::i32()))), 663 InferTy::IntVar(..) => Ty::simple(TypeCtor::Int(IntTy::i32())),
687 InferTy::FloatVar(..) => Ty::simple(TypeCtor::Float(Uncertain::Known(FloatTy::f64()))), 664 InferTy::FloatVar(..) => Ty::simple(TypeCtor::Float(FloatTy::f64())),
688 InferTy::MaybeNeverTypeVar(..) => Ty::simple(TypeCtor::Never), 665 InferTy::MaybeNeverTypeVar(..) => Ty::simple(TypeCtor::Never),
689 } 666 }
690 } 667 }
@@ -783,7 +760,7 @@ impl std::ops::BitOrAssign for Diverges {
783} 760}
784 761
785mod diagnostics { 762mod diagnostics {
786 use hir_def::{expr::ExprId, FunctionId}; 763 use hir_def::{expr::ExprId, DefWithBodyId};
787 use hir_expand::diagnostics::DiagnosticSink; 764 use hir_expand::diagnostics::DiagnosticSink;
788 765
789 use crate::{ 766 use crate::{
@@ -801,17 +778,17 @@ mod diagnostics {
801 pub(super) fn add_to( 778 pub(super) fn add_to(
802 &self, 779 &self,
803 db: &dyn HirDatabase, 780 db: &dyn HirDatabase,
804 owner: FunctionId, 781 owner: DefWithBodyId,
805 sink: &mut DiagnosticSink, 782 sink: &mut DiagnosticSink,
806 ) { 783 ) {
807 match self { 784 match self {
808 InferenceDiagnostic::NoSuchField { expr, field } => { 785 InferenceDiagnostic::NoSuchField { expr, field } => {
809 let (_, source_map) = db.body_with_source_map(owner.into()); 786 let (_, source_map) = db.body_with_source_map(owner);
810 let field = source_map.field_syntax(*expr, *field); 787 let field = source_map.field_syntax(*expr, *field);
811 sink.push(NoSuchField { file: field.file_id, field: field.value }) 788 sink.push(NoSuchField { file: field.file_id, field: field.value })
812 } 789 }
813 InferenceDiagnostic::BreakOutsideOfLoop { expr } => { 790 InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
814 let (_, source_map) = db.body_with_source_map(owner.into()); 791 let (_, source_map) = db.body_with_source_map(owner);
815 let ptr = source_map 792 let ptr = source_map
816 .expr_syntax(*expr) 793 .expr_syntax(*expr)
817 .expect("break outside of loop in synthetic syntax"); 794 .expect("break outside of loop in synthetic syntax");
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 4a98e2deb..731b062c2 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -10,15 +10,15 @@ use hir_def::{
10 resolver::resolver_for_expr, 10 resolver::resolver_for_expr,
11 AdtId, AssocContainerId, FieldId, Lookup, 11 AdtId, AssocContainerId, FieldId, Lookup,
12}; 12};
13use hir_expand::name::Name; 13use hir_expand::name::{name, Name};
14use ra_syntax::ast::RangeOp; 14use ra_syntax::ast::RangeOp;
15 15
16use crate::{ 16use crate::{
17 autoderef, method_resolution, op, 17 autoderef, method_resolution, op,
18 traits::InEnvironment, 18 traits::{FnTrait, InEnvironment},
19 utils::{generics, variant_data, Generics}, 19 utils::{generics, variant_data, Generics},
20 ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, 20 ApplicationTy, Binders, CallableDefId, InferTy, IntTy, Mutability, Obligation, Rawness, Substs,
21 TraitRef, Ty, TypeCtor, Uncertain, 21 TraitRef, Ty, TypeCtor,
22}; 22};
23 23
24use super::{ 24use super::{
@@ -63,6 +63,56 @@ impl<'a> InferenceContext<'a> {
63 self.resolve_ty_as_possible(ty) 63 self.resolve_ty_as_possible(ty)
64 } 64 }
65 65
66 fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
67 let krate = self.resolver.krate()?;
68 let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
69 let output_assoc_type =
70 self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
71 let generic_params = generics(self.db.upcast(), fn_once_trait.into());
72 if generic_params.len() != 2 {
73 return None;
74 }
75
76 let mut param_builder = Substs::builder(num_args);
77 let mut arg_tys = vec![];
78 for _ in 0..num_args {
79 let arg = self.table.new_type_var();
80 param_builder = param_builder.push(arg.clone());
81 arg_tys.push(arg);
82 }
83 let parameters = param_builder.build();
84 let arg_ty = Ty::Apply(ApplicationTy {
85 ctor: TypeCtor::Tuple { cardinality: num_args as u16 },
86 parameters,
87 });
88 let substs =
89 Substs::build_for_generics(&generic_params).push(ty.clone()).push(arg_ty).build();
90
91 let trait_env = Arc::clone(&self.trait_env);
92 let implements_fn_trait =
93 Obligation::Trait(TraitRef { trait_: fn_once_trait, substs: substs.clone() });
94 let goal = self.canonicalizer().canonicalize_obligation(InEnvironment {
95 value: implements_fn_trait.clone(),
96 environment: trait_env,
97 });
98 if self.db.trait_solve(krate, goal.value).is_some() {
99 self.obligations.push(implements_fn_trait);
100 let output_proj_ty =
101 crate::ProjectionTy { associated_ty: output_assoc_type, parameters: substs };
102 let return_ty = self.normalize_projection_ty(output_proj_ty);
103 Some((arg_tys, return_ty))
104 } else {
105 None
106 }
107 }
108
109 pub fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec<Ty>, Ty)> {
110 match ty.callable_sig(self.db) {
111 Some(sig) => Some((sig.params().to_vec(), sig.ret().clone())),
112 None => self.callable_sig_from_fn_trait(ty, num_args),
113 }
114 }
115
66 fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { 116 fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty {
67 let body = Arc::clone(&self.body); // avoid borrow checker problem 117 let body = Arc::clone(&self.body); // avoid borrow checker problem
68 let ty = match &body[tgt_expr] { 118 let ty = match &body[tgt_expr] {
@@ -90,6 +140,7 @@ impl<'a> InferenceContext<'a> {
90 // FIXME: Breakable block inference 140 // FIXME: Breakable block inference
91 self.infer_block(statements, *tail, expected) 141 self.infer_block(statements, *tail, expected)
92 } 142 }
143 Expr::Unsafe { body } => self.infer_expr(*body, expected),
93 Expr::TryBlock { body } => { 144 Expr::TryBlock { body } => {
94 let _inner = self.infer_expr(*body, expected); 145 let _inner = self.infer_expr(*body, expected);
95 // FIXME should be std::result::Result<{inner}, _> 146 // FIXME should be std::result::Result<{inner}, _>
@@ -169,7 +220,7 @@ impl<'a> InferenceContext<'a> {
169 }; 220 };
170 sig_tys.push(ret_ty.clone()); 221 sig_tys.push(ret_ty.clone());
171 let sig_ty = Ty::apply( 222 let sig_ty = Ty::apply(
172 TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 }, 223 TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1, is_varargs: false },
173 Substs(sig_tys.clone().into()), 224 Substs(sig_tys.clone().into()),
174 ); 225 );
175 let closure_ty = 226 let closure_ty =
@@ -198,14 +249,23 @@ impl<'a> InferenceContext<'a> {
198 } 249 }
199 Expr::Call { callee, args } => { 250 Expr::Call { callee, args } => {
200 let callee_ty = self.infer_expr(*callee, &Expectation::none()); 251 let callee_ty = self.infer_expr(*callee, &Expectation::none());
201 let (param_tys, ret_ty) = match callee_ty.callable_sig(self.db) { 252 let canonicalized = self.canonicalizer().canonicalize_ty(callee_ty.clone());
202 Some(sig) => (sig.params().to_vec(), sig.ret().clone()), 253 let mut derefs = autoderef(
203 None => { 254 self.db,
204 // Not callable 255 self.resolver.krate(),
205 // FIXME: report an error 256 InEnvironment {
206 (Vec::new(), Ty::Unknown) 257 value: canonicalized.value.clone(),
207 } 258 environment: self.trait_env.clone(),
208 }; 259 },
260 );
261 let (param_tys, ret_ty): (Vec<Ty>, Ty) = derefs
262 .find_map(|callee_deref_ty| {
263 self.callable_sig(
264 &canonicalized.decanonicalize_ty(callee_deref_ty.value),
265 args.len(),
266 )
267 })
268 .unwrap_or((Vec::new(), Ty::Unknown));
209 self.register_obligations_for_call(&callee_ty); 269 self.register_obligations_for_call(&callee_ty);
210 self.check_call_arguments(args, &param_tys); 270 self.check_call_arguments(args, &param_tys);
211 self.normalize_associated_types_in(ret_ty) 271 self.normalize_associated_types_in(ret_ty)
@@ -345,8 +405,15 @@ impl<'a> InferenceContext<'a> {
345 .subst(&a_ty.parameters) 405 .subst(&a_ty.parameters)
346 }) 406 })
347 } 407 }
348 // FIXME: 408 TypeCtor::Adt(AdtId::UnionId(u)) => {
349 TypeCtor::Adt(AdtId::UnionId(_)) => None, 409 self.db.union_data(u).variant_data.field(name).map(|local_id| {
410 let field = FieldId { parent: u.into(), local_id };
411 self.write_field_resolution(tgt_expr, field);
412 self.db.field_types(u.into())[field.local_id]
413 .clone()
414 .subst(&a_ty.parameters)
415 })
416 }
350 _ => None, 417 _ => None,
351 }, 418 },
352 _ => None, 419 _ => None,
@@ -426,15 +493,7 @@ impl<'a> InferenceContext<'a> {
426 match &inner_ty { 493 match &inner_ty {
427 // Fast path for builtins 494 // Fast path for builtins
428 Ty::Apply(ApplicationTy { 495 Ty::Apply(ApplicationTy {
429 ctor: 496 ctor: TypeCtor::Int(IntTy { signedness: Signedness::Signed, .. }),
430 TypeCtor::Int(Uncertain::Known(IntTy {
431 signedness: Signedness::Signed,
432 ..
433 })),
434 ..
435 })
436 | Ty::Apply(ApplicationTy {
437 ctor: TypeCtor::Int(Uncertain::Unknown),
438 .. 497 ..
439 }) 498 })
440 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(_), .. }) 499 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Float(_), .. })
@@ -577,9 +636,7 @@ impl<'a> InferenceContext<'a> {
577 ); 636 );
578 self.infer_expr( 637 self.infer_expr(
579 *repeat, 638 *repeat,
580 &Expectation::has_type(Ty::simple(TypeCtor::Int(Uncertain::Known( 639 &Expectation::has_type(Ty::simple(TypeCtor::Int(IntTy::usize()))),
581 IntTy::usize(),
582 )))),
583 ); 640 );
584 } 641 }
585 } 642 }
@@ -592,13 +649,19 @@ impl<'a> InferenceContext<'a> {
592 Ty::apply_one(TypeCtor::Ref(Mutability::Shared), Ty::simple(TypeCtor::Str)) 649 Ty::apply_one(TypeCtor::Ref(Mutability::Shared), Ty::simple(TypeCtor::Str))
593 } 650 }
594 Literal::ByteString(..) => { 651 Literal::ByteString(..) => {
595 let byte_type = Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::u8()))); 652 let byte_type = Ty::simple(TypeCtor::Int(IntTy::u8()));
596 let array_type = Ty::apply_one(TypeCtor::Array, byte_type); 653 let array_type = Ty::apply_one(TypeCtor::Array, byte_type);
597 Ty::apply_one(TypeCtor::Ref(Mutability::Shared), array_type) 654 Ty::apply_one(TypeCtor::Ref(Mutability::Shared), array_type)
598 } 655 }
599 Literal::Char(..) => Ty::simple(TypeCtor::Char), 656 Literal::Char(..) => Ty::simple(TypeCtor::Char),
600 Literal::Int(_v, ty) => Ty::simple(TypeCtor::Int((*ty).into())), 657 Literal::Int(_v, ty) => match ty {
601 Literal::Float(_v, ty) => Ty::simple(TypeCtor::Float((*ty).into())), 658 Some(int_ty) => Ty::simple(TypeCtor::Int((*int_ty).into())),
659 None => self.table.new_integer_var(),
660 },
661 Literal::Float(_v, ty) => match ty {
662 Some(float_ty) => Ty::simple(TypeCtor::Float((*float_ty).into())),
663 None => self.table.new_float_var(),
664 },
602 }, 665 },
603 }; 666 };
604 // use a new type variable if we got Ty::Unknown here 667 // use a new type variable if we got Ty::Unknown here
@@ -727,11 +790,7 @@ impl<'a> InferenceContext<'a> {
727 for &check_closures in &[false, true] { 790 for &check_closures in &[false, true] {
728 let param_iter = param_tys.iter().cloned().chain(repeat(Ty::Unknown)); 791 let param_iter = param_tys.iter().cloned().chain(repeat(Ty::Unknown));
729 for (&arg, param_ty) in args.iter().zip(param_iter) { 792 for (&arg, param_ty) in args.iter().zip(param_iter) {
730 let is_closure = match &self.body[arg] { 793 let is_closure = matches!(&self.body[arg], Expr::Lambda { .. });
731 Expr::Lambda { .. } => true,
732 _ => false,
733 };
734
735 if is_closure != check_closures { 794 if is_closure != check_closures {
736 continue; 795 continue;
737 } 796 }
@@ -795,7 +854,7 @@ impl<'a> InferenceContext<'a> {
795 } 854 }
796 // add obligation for trait implementation, if this is a trait method 855 // add obligation for trait implementation, if this is a trait method
797 match def { 856 match def {
798 CallableDef::FunctionId(f) => { 857 CallableDefId::FunctionId(f) => {
799 if let AssocContainerId::TraitId(trait_) = 858 if let AssocContainerId::TraitId(trait_) =
800 f.lookup(self.db.upcast()).container 859 f.lookup(self.db.upcast()).container
801 { 860 {
@@ -806,7 +865,7 @@ impl<'a> InferenceContext<'a> {
806 self.obligations.push(Obligation::Trait(TraitRef { trait_, substs })); 865 self.obligations.push(Obligation::Trait(TraitRef { trait_, substs }));
807 } 866 }
808 } 867 }
809 CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => {} 868 CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {}
810 } 869 }
811 } 870 }
812 } 871 }
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs
index 4006f595d..4dd4f9802 100644
--- a/crates/ra_hir_ty/src/infer/pat.rs
+++ b/crates/ra_hir_ty/src/infer/pat.rs
@@ -4,7 +4,7 @@ use std::iter::repeat;
4use std::sync::Arc; 4use std::sync::Arc;
5 5
6use hir_def::{ 6use hir_def::{
7 expr::{BindingAnnotation, Pat, PatId, RecordFieldPat}, 7 expr::{BindingAnnotation, Expr, Literal, Pat, PatId, RecordFieldPat},
8 path::Path, 8 path::Path,
9 type_ref::Mutability, 9 type_ref::Mutability,
10 FieldId, 10 FieldId,
@@ -90,18 +90,7 @@ impl<'a> InferenceContext<'a> {
90 ) -> Ty { 90 ) -> Ty {
91 let body = Arc::clone(&self.body); // avoid borrow checker problem 91 let body = Arc::clone(&self.body); // avoid borrow checker problem
92 92
93 let is_non_ref_pat = match &body[pat] { 93 if is_non_ref_pat(&body, pat) {
94 Pat::Tuple { .. }
95 | Pat::Or(..)
96 | Pat::TupleStruct { .. }
97 | Pat::Record { .. }
98 | Pat::Range { .. }
99 | Pat::Slice { .. } => true,
100 // FIXME: Path/Lit might actually evaluate to ref, but inference is unimplemented.
101 Pat::Path(..) | Pat::Lit(..) => true,
102 Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Missing => false,
103 };
104 if is_non_ref_pat {
105 while let Some((inner, mutability)) = expected.as_reference() { 94 while let Some((inner, mutability)) = expected.as_reference() {
106 expected = inner; 95 expected = inner;
107 default_bm = match default_bm { 96 default_bm = match default_bm {
@@ -195,7 +184,7 @@ impl<'a> InferenceContext<'a> {
195 self.write_pat_ty(pat, bound_ty); 184 self.write_pat_ty(pat, bound_ty);
196 return inner_ty; 185 return inner_ty;
197 } 186 }
198 Pat::Slice { prefix, slice: _slice, suffix } => { 187 Pat::Slice { prefix, slice, suffix } => {
199 let (container_ty, elem_ty) = match &expected { 188 let (container_ty, elem_ty) = match &expected {
200 ty_app!(TypeCtor::Array, st) => (TypeCtor::Array, st.as_single().clone()), 189 ty_app!(TypeCtor::Array, st) => (TypeCtor::Array, st.as_single().clone()),
201 ty_app!(TypeCtor::Slice, st) => (TypeCtor::Slice, st.as_single().clone()), 190 ty_app!(TypeCtor::Slice, st) => (TypeCtor::Slice, st.as_single().clone()),
@@ -206,7 +195,12 @@ impl<'a> InferenceContext<'a> {
206 self.infer_pat(*pat_id, &elem_ty, default_bm); 195 self.infer_pat(*pat_id, &elem_ty, default_bm);
207 } 196 }
208 197
209 Ty::apply_one(container_ty, elem_ty) 198 let pat_ty = Ty::apply_one(container_ty, elem_ty);
199 if let Some(slice_pat_id) = slice {
200 self.infer_pat(*slice_pat_id, &pat_ty, default_bm);
201 }
202
203 pat_ty
210 } 204 }
211 Pat::Wild => expected.clone(), 205 Pat::Wild => expected.clone(),
212 Pat::Range { start, end } => { 206 Pat::Range { start, end } => {
@@ -227,3 +221,21 @@ impl<'a> InferenceContext<'a> {
227 ty 221 ty
228 } 222 }
229} 223}
224
225fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
226 match &body[pat] {
227 Pat::Tuple { .. }
228 | Pat::TupleStruct { .. }
229 | Pat::Record { .. }
230 | Pat::Range { .. }
231 | Pat::Slice { .. } => true,
232 Pat::Or(pats) => pats.iter().all(|p| is_non_ref_pat(body, *p)),
233 // FIXME: Path/Lit might actually evaluate to ref, but inference is unimplemented.
234 Pat::Path(..) => true,
235 Pat::Lit(expr) => match body[*expr] {
236 Expr::Literal(Literal::String(..)) => false,
237 _ => true,
238 },
239 Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Missing => false,
240 }
241}
diff --git a/crates/ra_hir_ty/src/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs
index 1c2e56fb0..80d7ed10e 100644
--- a/crates/ra_hir_ty/src/infer/path.rs
+++ b/crates/ra_hir_ty/src/infer/path.rs
@@ -81,7 +81,7 @@ impl<'a> InferenceContext<'a> {
81 let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); 81 let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
82 let substs = Substs::type_params_for_generics(&generics); 82 let substs = Substs::type_params_for_generics(&generics);
83 let ty = self.db.impl_self_ty(impl_id).subst(&substs); 83 let ty = self.db.impl_self_ty(impl_id).subst(&substs);
84 if let Some((AdtId::StructId(struct_id), _)) = ty.as_adt() { 84 if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() {
85 let ty = self.db.value_ty(struct_id.into()).subst(&substs); 85 let ty = self.db.value_ty(struct_id.into()).subst(&substs);
86 return Some(ty); 86 return Some(ty);
87 } else { 87 } else {
@@ -95,7 +95,7 @@ impl<'a> InferenceContext<'a> {
95 // self_subst is just for the parent 95 // self_subst is just for the parent
96 let parent_substs = self_subst.unwrap_or_else(Substs::empty); 96 let parent_substs = self_subst.unwrap_or_else(Substs::empty);
97 let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); 97 let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
98 let substs = Ty::substs_from_path(&ctx, path, typable); 98 let substs = Ty::substs_from_path(&ctx, path, typable, true);
99 let full_substs = Substs::builder(substs.len()) 99 let full_substs = Substs::builder(substs.len())
100 .use_parent_substs(&parent_substs) 100 .use_parent_substs(&parent_substs)
101 .fill(substs.0[parent_substs.len()..].iter().cloned()) 101 .fill(substs.0[parent_substs.len()..].iter().cloned())
@@ -141,6 +141,7 @@ impl<'a> InferenceContext<'a> {
141 def, 141 def,
142 resolved_segment, 142 resolved_segment,
143 remaining_segments_for_ty, 143 remaining_segments_for_ty,
144 true,
144 ); 145 );
145 if let Ty::Unknown = ty { 146 if let Ty::Unknown = ty {
146 return None; 147 return None;
diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs
index 269495ca0..2e895d911 100644
--- a/crates/ra_hir_ty/src/infer/unify.rs
+++ b/crates/ra_hir_ty/src/infer/unify.rs
@@ -9,7 +9,7 @@ use test_utils::mark;
9use super::{InferenceContext, Obligation}; 9use super::{InferenceContext, Obligation};
10use crate::{ 10use crate::{
11 BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Substs, Ty, 11 BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferTy, Substs, Ty,
12 TypeCtor, TypeWalk, 12 TyKind, TypeCtor, TypeWalk,
13}; 13};
14 14
15impl<'a> InferenceContext<'a> { 15impl<'a> InferenceContext<'a> {
@@ -86,10 +86,20 @@ where
86 } 86 }
87 87
88 fn into_canonicalized<T>(self, result: T) -> Canonicalized<T> { 88 fn into_canonicalized<T>(self, result: T) -> Canonicalized<T> {
89 Canonicalized { 89 let kinds = self
90 value: Canonical { value: result, num_vars: self.free_vars.len() }, 90 .free_vars
91 free_vars: self.free_vars, 91 .iter()
92 } 92 .map(|v| match v {
93 // mapping MaybeNeverTypeVar to the same kind as general ones
94 // should be fine, because as opposed to int or float type vars,
95 // they don't restrict what kind of type can go into them, they
96 // just affect fallback.
97 InferTy::TypeVar(_) | InferTy::MaybeNeverTypeVar(_) => TyKind::General,
98 InferTy::IntVar(_) => TyKind::Integer,
99 InferTy::FloatVar(_) => TyKind::Float,
100 })
101 .collect();
102 Canonicalized { value: Canonical { value: result, kinds }, free_vars: self.free_vars }
93 } 103 }
94 104
95 pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> { 105 pub(crate) fn canonicalize_ty(mut self, ty: Ty) -> Canonicalized<Ty> {
@@ -131,26 +141,41 @@ impl<T> Canonicalized<T> {
131 ty 141 ty
132 } 142 }
133 143
134 pub fn apply_solution(&self, ctx: &mut InferenceContext<'_>, solution: Canonical<Vec<Ty>>) { 144 pub fn apply_solution(&self, ctx: &mut InferenceContext<'_>, solution: Canonical<Substs>) {
135 // the solution may contain new variables, which we need to convert to new inference vars 145 // the solution may contain new variables, which we need to convert to new inference vars
136 let new_vars = Substs((0..solution.num_vars).map(|_| ctx.table.new_type_var()).collect()); 146 let new_vars = Substs(
147 solution
148 .kinds
149 .iter()
150 .map(|k| match k {
151 TyKind::General => ctx.table.new_type_var(),
152 TyKind::Integer => ctx.table.new_integer_var(),
153 TyKind::Float => ctx.table.new_float_var(),
154 })
155 .collect(),
156 );
137 for (i, ty) in solution.value.into_iter().enumerate() { 157 for (i, ty) in solution.value.into_iter().enumerate() {
138 let var = self.free_vars[i]; 158 let var = self.free_vars[i];
139 // eagerly replace projections in the type; we may be getting types 159 // eagerly replace projections in the type; we may be getting types
140 // e.g. from where clauses where this hasn't happened yet 160 // e.g. from where clauses where this hasn't happened yet
141 let ty = ctx.normalize_associated_types_in(ty.subst_bound_vars(&new_vars)); 161 let ty = ctx.normalize_associated_types_in(ty.clone().subst_bound_vars(&new_vars));
142 ctx.table.unify(&Ty::Infer(var), &ty); 162 ctx.table.unify(&Ty::Infer(var), &ty);
143 } 163 }
144 } 164 }
145} 165}
146 166
147pub fn unify(ty1: &Canonical<Ty>, ty2: &Canonical<Ty>) -> Option<Substs> { 167pub fn unify(tys: &Canonical<(Ty, Ty)>) -> Option<Substs> {
148 let mut table = InferenceTable::new(); 168 let mut table = InferenceTable::new();
149 let num_vars = ty1.num_vars.max(ty2.num_vars); 169 let vars = Substs(
150 let vars = 170 tys.kinds
151 Substs::builder(num_vars).fill(std::iter::repeat_with(|| table.new_type_var())).build(); 171 .iter()
152 let ty1_with_vars = ty1.value.clone().subst_bound_vars(&vars); 172 // we always use type vars here because we want everything to
153 let ty2_with_vars = ty2.value.clone().subst_bound_vars(&vars); 173 // fallback to Unknown in the end (kind of hacky, as below)
174 .map(|_| table.new_type_var())
175 .collect(),
176 );
177 let ty1_with_vars = tys.value.0.clone().subst_bound_vars(&vars);
178 let ty2_with_vars = tys.value.1.clone().subst_bound_vars(&vars);
154 if !table.unify(&ty1_with_vars, &ty2_with_vars) { 179 if !table.unify(&ty1_with_vars, &ty2_with_vars) {
155 return None; 180 return None;
156 } 181 }
@@ -162,7 +187,7 @@ pub fn unify(ty1: &Canonical<Ty>, ty2: &Canonical<Ty>) -> Option<Substs> {
162 } 187 }
163 } 188 }
164 Some( 189 Some(
165 Substs::builder(ty1.num_vars) 190 Substs::builder(tys.kinds.len())
166 .fill(vars.iter().map(|v| table.resolve_ty_completely(v.clone()))) 191 .fill(vars.iter().map(|v| table.resolve_ty_completely(v.clone())))
167 .build(), 192 .build(),
168 ) 193 )
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index 9fa8d3bdc..0ef5ca78f 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -6,25 +6,6 @@ macro_rules! eprintln {
6 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; 6 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
7} 7}
8 8
9macro_rules! impl_froms {
10 ($e:ident: $($v:ident $(($($sv:ident),*))?),*) => {
11 $(
12 impl From<$v> for $e {
13 fn from(it: $v) -> $e {
14 $e::$v(it)
15 }
16 }
17 $($(
18 impl From<$sv> for $e {
19 fn from(it: $sv) -> $e {
20 $e::$v($v::$sv(it))
21 }
22 }
23 )*)?
24 )*
25 }
26}
27
28mod autoderef; 9mod autoderef;
29pub mod primitive; 10pub mod primitive;
30pub mod traits; 11pub mod traits;
@@ -32,21 +13,18 @@ pub mod method_resolution;
32mod op; 13mod op;
33mod lower; 14mod lower;
34pub(crate) mod infer; 15pub(crate) mod infer;
35pub mod display;
36pub(crate) mod utils; 16pub(crate) mod utils;
17
18pub mod display;
37pub mod db; 19pub mod db;
38pub mod diagnostics; 20pub mod diagnostics;
39pub mod expr;
40 21
41#[cfg(test)] 22#[cfg(test)]
42mod tests; 23mod tests;
43#[cfg(test)] 24#[cfg(test)]
44mod test_db; 25mod test_db;
45mod _match;
46 26
47use std::ops::Deref; 27use std::{iter, mem, ops::Deref, sync::Arc};
48use std::sync::Arc;
49use std::{iter, mem};
50 28
51use hir_def::{ 29use hir_def::{
52 expr::ExprId, 30 expr::ExprId,
@@ -54,18 +32,19 @@ use hir_def::{
54 AdtId, AssocContainerId, DefWithBodyId, GenericDefId, HasModule, Lookup, TraitId, TypeAliasId, 32 AdtId, AssocContainerId, DefWithBodyId, GenericDefId, HasModule, Lookup, TraitId, TypeAliasId,
55 TypeParamId, 33 TypeParamId,
56}; 34};
57use ra_db::{impl_intern_key, salsa, CrateId}; 35use itertools::Itertools;
36use ra_db::{salsa, CrateId};
58 37
59use crate::{ 38use crate::{
60 db::HirDatabase, 39 db::HirDatabase,
61 primitive::{FloatTy, IntTy, Uncertain}, 40 display::HirDisplay,
41 primitive::{FloatTy, IntTy},
62 utils::{generics, make_mut_slice, Generics}, 42 utils::{generics, make_mut_slice, Generics},
63}; 43};
64use display::HirDisplay;
65 44
66pub use autoderef::autoderef; 45pub use autoderef::autoderef;
67pub use infer::{InferTy, InferenceResult}; 46pub use infer::{InferTy, InferenceResult};
68pub use lower::CallableDef; 47pub use lower::CallableDefId;
69pub use lower::{ 48pub use lower::{
70 associated_type_shorthand_candidates, callable_item_sig, ImplTraitLoweringMode, TyDefId, 49 associated_type_shorthand_candidates, callable_item_sig, ImplTraitLoweringMode, TyDefId,
71 TyLoweringContext, ValueTyDefId, 50 TyLoweringContext, ValueTyDefId,
@@ -87,10 +66,10 @@ pub enum TypeCtor {
87 Char, 66 Char,
88 67
89 /// A primitive integer type. For example, `i32`. 68 /// A primitive integer type. For example, `i32`.
90 Int(Uncertain<IntTy>), 69 Int(IntTy),
91 70
92 /// A primitive floating-point type. For example, `f64`. 71 /// A primitive floating-point type. For example, `f64`.
93 Float(Uncertain<FloatTy>), 72 Float(FloatTy),
94 73
95 /// Structures, enumerations and unions. 74 /// Structures, enumerations and unions.
96 Adt(AdtId), 75 Adt(AdtId),
@@ -123,7 +102,7 @@ pub enum TypeCtor {
123 /// fn foo() -> i32 { 1 } 102 /// fn foo() -> i32 { 1 }
124 /// let bar = foo; // bar: fn() -> i32 {foo} 103 /// let bar = foo; // bar: fn() -> i32 {foo}
125 /// ``` 104 /// ```
126 FnDef(CallableDef), 105 FnDef(CallableDefId),
127 106
128 /// A pointer to a function. Written as `fn() -> i32`. 107 /// A pointer to a function. Written as `fn() -> i32`.
129 /// 108 ///
@@ -133,7 +112,8 @@ pub enum TypeCtor {
133 /// fn foo() -> i32 { 1 } 112 /// fn foo() -> i32 { 1 }
134 /// let bar: fn() -> i32 = foo; 113 /// let bar: fn() -> i32 = foo;
135 /// ``` 114 /// ```
136 FnPtr { num_args: u16 }, 115 // FIXME make this a Ty variant like in Chalk
116 FnPtr { num_args: u16, is_varargs: bool },
137 117
138 /// The never type `!`. 118 /// The never type `!`.
139 Never, 119 Never,
@@ -147,6 +127,12 @@ pub enum TypeCtor {
147 /// an **application type** like `(Iterator::Item)<T>`. 127 /// an **application type** like `(Iterator::Item)<T>`.
148 AssociatedType(TypeAliasId), 128 AssociatedType(TypeAliasId),
149 129
130 /// This represents a placeholder for an opaque type in situations where we
131 /// don't know the hidden type (i.e. currently almost always). This is
132 /// analogous to the `AssociatedType` type constructor. As with that one,
133 /// these are only produced by Chalk.
134 OpaqueType(OpaqueTyId),
135
150 /// The type of a specific closure. 136 /// The type of a specific closure.
151 /// 137 ///
152 /// The closure signature is stored in a `FnPtr` type in the first type 138 /// The closure signature is stored in a `FnPtr` type in the first type
@@ -154,19 +140,6 @@ pub enum TypeCtor {
154 Closure { def: DefWithBodyId, expr: ExprId }, 140 Closure { def: DefWithBodyId, expr: ExprId },
155} 141}
156 142
157/// This exists just for Chalk, because Chalk just has a single `StructId` where
158/// we have different kinds of ADTs, primitive types and special type
159/// constructors like tuples and function pointers.
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
161pub struct TypeCtorId(salsa::InternId);
162impl_intern_key!(TypeCtorId);
163
164/// This exists just for Chalk, because Chalk just has a single `FnDefId` where
165/// we have different IDs for struct and enum variant constructors.
166#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
167pub struct CallableDefId(salsa::InternId);
168impl_intern_key!(CallableDefId);
169
170impl TypeCtor { 143impl TypeCtor {
171 pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize { 144 pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize {
172 match self { 145 match self {
@@ -194,7 +167,15 @@ impl TypeCtor {
194 let generic_params = generics(db.upcast(), type_alias.into()); 167 let generic_params = generics(db.upcast(), type_alias.into());
195 generic_params.len() 168 generic_params.len()
196 } 169 }
197 TypeCtor::FnPtr { num_args } => num_args as usize + 1, 170 TypeCtor::OpaqueType(opaque_ty_id) => {
171 match opaque_ty_id {
172 OpaqueTyId::ReturnTypeImplTrait(func, _) => {
173 let generic_params = generics(db.upcast(), func.into());
174 generic_params.len()
175 }
176 }
177 }
178 TypeCtor::FnPtr { num_args, is_varargs: _ } => num_args as usize + 1,
198 TypeCtor::Tuple { cardinality } => cardinality as usize, 179 TypeCtor::Tuple { cardinality } => cardinality as usize,
199 } 180 }
200 } 181 }
@@ -220,6 +201,11 @@ impl TypeCtor {
220 TypeCtor::AssociatedType(type_alias) => { 201 TypeCtor::AssociatedType(type_alias) => {
221 Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate) 202 Some(type_alias.lookup(db.upcast()).module(db.upcast()).krate)
222 } 203 }
204 TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id {
205 OpaqueTyId::ReturnTypeImplTrait(func, _) => {
206 Some(func.lookup(db.upcast()).module(db.upcast()).krate)
207 }
208 },
223 } 209 }
224 } 210 }
225 211
@@ -241,6 +227,7 @@ impl TypeCtor {
241 TypeCtor::Adt(adt) => Some(adt.into()), 227 TypeCtor::Adt(adt) => Some(adt.into()),
242 TypeCtor::FnDef(callable) => Some(callable.into()), 228 TypeCtor::FnDef(callable) => Some(callable.into()),
243 TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()), 229 TypeCtor::AssociatedType(type_alias) => Some(type_alias.into()),
230 TypeCtor::OpaqueType(_impl_trait_id) => None,
244 } 231 }
245 } 232 }
246} 233}
@@ -254,6 +241,12 @@ pub struct ApplicationTy {
254 pub parameters: Substs, 241 pub parameters: Substs,
255} 242}
256 243
244#[derive(Clone, PartialEq, Eq, Debug, Hash)]
245pub struct OpaqueTy {
246 pub opaque_ty_id: OpaqueTyId,
247 pub parameters: Substs,
248}
249
257/// A "projection" type corresponds to an (unnormalized) 250/// A "projection" type corresponds to an (unnormalized)
258/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the 251/// projection like `<P0 as Trait<P1..Pn>>::Foo`. Note that the
259/// trait and all its parameters are fully known. 252/// trait and all its parameters are fully known.
@@ -308,6 +301,12 @@ pub enum Ty {
308 /// trait and all its parameters are fully known. 301 /// trait and all its parameters are fully known.
309 Projection(ProjectionTy), 302 Projection(ProjectionTy),
310 303
304 /// An opaque type (`impl Trait`).
305 ///
306 /// This is currently only used for return type impl trait; each instance of
307 /// `impl Trait` in a return type gets its own ID.
308 Opaque(OpaqueTy),
309
311 /// A placeholder for a type parameter; for example, `T` in `fn f<T>(x: T) 310 /// A placeholder for a type parameter; for example, `T` in `fn f<T>(x: T)
312 /// {}` when we're type-checking the body of that function. In this 311 /// {}` when we're type-checking the body of that function. In this
313 /// situation, we know this stands for *some* type, but don't know the exact 312 /// situation, we know this stands for *some* type, but don't know the exact
@@ -332,12 +331,6 @@ pub enum Ty {
332 /// didn't seem worth the overhead yet. 331 /// didn't seem worth the overhead yet.
333 Dyn(Arc<[GenericPredicate]>), 332 Dyn(Arc<[GenericPredicate]>),
334 333
335 /// An opaque type (`impl Trait`).
336 ///
337 /// The predicates are quantified over the `Self` type; see `Ty::Dyn` for
338 /// more.
339 Opaque(Arc<[GenericPredicate]>),
340
341 /// A placeholder for a type which could not be computed; this is propagated 334 /// A placeholder for a type which could not be computed; this is propagated
342 /// to avoid useless error messages. Doubles as a placeholder where type 335 /// to avoid useless error messages. Doubles as a placeholder where type
343 /// variables are inserted before type checking, since we want to try to 336 /// variables are inserted before type checking, since we want to try to
@@ -490,7 +483,7 @@ impl Deref for Substs {
490 } 483 }
491} 484}
492 485
493#[derive(Copy, Clone, PartialEq, Eq, Debug)] 486#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
494pub struct Binders<T> { 487pub struct Binders<T> {
495 pub num_binders: usize, 488 pub num_binders: usize,
496 pub value: T, 489 pub value: T,
@@ -534,6 +527,20 @@ impl<T: TypeWalk> Binders<T> {
534 } 527 }
535} 528}
536 529
530impl<T: TypeWalk> TypeWalk for Binders<T> {
531 fn walk(&self, f: &mut impl FnMut(&Ty)) {
532 self.value.walk(f);
533 }
534
535 fn walk_mut_binders(
536 &mut self,
537 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
538 binders: DebruijnIndex,
539 ) {
540 self.value.walk_mut_binders(f, binders.shifted_in())
541 }
542}
543
537/// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait. 544/// A trait with type parameters. This includes the `Self`, so this represents a concrete type implementing the trait.
538/// Name to be bikeshedded: TraitBound? TraitImplements? 545/// Name to be bikeshedded: TraitBound? TraitImplements?
539#[derive(Clone, PartialEq, Eq, Debug, Hash)] 546#[derive(Clone, PartialEq, Eq, Debug, Hash)]
@@ -578,17 +585,11 @@ pub enum GenericPredicate {
578 585
579impl GenericPredicate { 586impl GenericPredicate {
580 pub fn is_error(&self) -> bool { 587 pub fn is_error(&self) -> bool {
581 match self { 588 matches!(self, GenericPredicate::Error)
582 GenericPredicate::Error => true,
583 _ => false,
584 }
585 } 589 }
586 590
587 pub fn is_implemented(&self) -> bool { 591 pub fn is_implemented(&self) -> bool {
588 match self { 592 matches!(self, GenericPredicate::Implemented(_))
589 GenericPredicate::Implemented(_) => true,
590 _ => false,
591 }
592 } 593 }
593 594
594 pub fn trait_ref(&self, db: &dyn HirDatabase) -> Option<TraitRef> { 595 pub fn trait_ref(&self, db: &dyn HirDatabase) -> Option<TraitRef> {
@@ -626,13 +627,27 @@ impl TypeWalk for GenericPredicate {
626 627
627/// Basically a claim (currently not validated / checked) that the contained 628/// Basically a claim (currently not validated / checked) that the contained
628/// type / trait ref contains no inference variables; any inference variables it 629/// type / trait ref contains no inference variables; any inference variables it
629/// contained have been replaced by bound variables, and `num_vars` tells us how 630/// contained have been replaced by bound variables, and `kinds` tells us how
630/// many there are. This is used to erase irrelevant differences between types 631/// many there are and whether they were normal or float/int variables. This is
631/// before using them in queries. 632/// used to erase irrelevant differences between types before using them in
633/// queries.
632#[derive(Debug, Clone, PartialEq, Eq, Hash)] 634#[derive(Debug, Clone, PartialEq, Eq, Hash)]
633pub struct Canonical<T> { 635pub struct Canonical<T> {
634 pub value: T, 636 pub value: T,
635 pub num_vars: usize, 637 pub kinds: Arc<[TyKind]>,
638}
639
640impl<T> Canonical<T> {
641 pub fn new(value: T, kinds: impl IntoIterator<Item = TyKind>) -> Self {
642 Self { value, kinds: kinds.into_iter().collect() }
643 }
644}
645
646#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
647pub enum TyKind {
648 General,
649 Integer,
650 Float,
636} 651}
637 652
638/// A function signature as seen by type inference: Several parameter types and 653/// A function signature as seen by type inference: Several parameter types and
@@ -640,19 +655,20 @@ pub struct Canonical<T> {
640#[derive(Clone, PartialEq, Eq, Debug)] 655#[derive(Clone, PartialEq, Eq, Debug)]
641pub struct FnSig { 656pub struct FnSig {
642 params_and_return: Arc<[Ty]>, 657 params_and_return: Arc<[Ty]>,
658 is_varargs: bool,
643} 659}
644 660
645/// A polymorphic function signature. 661/// A polymorphic function signature.
646pub type PolyFnSig = Binders<FnSig>; 662pub type PolyFnSig = Binders<FnSig>;
647 663
648impl FnSig { 664impl FnSig {
649 pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty) -> FnSig { 665 pub fn from_params_and_return(mut params: Vec<Ty>, ret: Ty, is_varargs: bool) -> FnSig {
650 params.push(ret); 666 params.push(ret);
651 FnSig { params_and_return: params.into() } 667 FnSig { params_and_return: params.into(), is_varargs }
652 } 668 }
653 669
654 pub fn from_fn_ptr_substs(substs: &Substs) -> FnSig { 670 pub fn from_fn_ptr_substs(substs: &Substs, is_varargs: bool) -> FnSig {
655 FnSig { params_and_return: Arc::clone(&substs.0) } 671 FnSig { params_and_return: Arc::clone(&substs.0), is_varargs }
656 } 672 }
657 673
658 pub fn params(&self) -> &[Ty] { 674 pub fn params(&self) -> &[Ty] {
@@ -697,7 +713,7 @@ impl Ty {
697 } 713 }
698 pub fn fn_ptr(sig: FnSig) -> Self { 714 pub fn fn_ptr(sig: FnSig) -> Self {
699 Ty::apply( 715 Ty::apply(
700 TypeCtor::FnPtr { num_args: sig.params().len() as u16 }, 716 TypeCtor::FnPtr { num_args: sig.params().len() as u16, is_varargs: sig.is_varargs },
701 Substs(sig.params_and_return), 717 Substs(sig.params_and_return),
702 ) 718 )
703 } 719 }
@@ -751,7 +767,7 @@ impl Ty {
751 } 767 }
752 } 768 }
753 769
754 pub fn as_callable(&self) -> Option<(CallableDef, &Substs)> { 770 pub fn as_callable(&self) -> Option<(CallableDefId, &Substs)> {
755 match self { 771 match self {
756 Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(callable_def), parameters }) => { 772 Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(callable_def), parameters }) => {
757 Some((*callable_def, parameters)) 773 Some((*callable_def, parameters))
@@ -775,6 +791,11 @@ impl Ty {
775 } 791 }
776 } 792 }
777 793
794 /// If this is a `dyn Trait`, returns that trait.
795 pub fn dyn_trait(&self) -> Option<TraitId> {
796 self.dyn_trait_ref().map(|it| it.trait_)
797 }
798
778 fn builtin_deref(&self) -> Option<Ty> { 799 fn builtin_deref(&self) -> Option<Ty> {
779 match self { 800 match self {
780 Ty::Apply(a_ty) => match a_ty.ctor { 801 Ty::Apply(a_ty) => match a_ty.ctor {
@@ -789,7 +810,9 @@ impl Ty {
789 fn callable_sig(&self, db: &dyn HirDatabase) -> Option<FnSig> { 810 fn callable_sig(&self, db: &dyn HirDatabase) -> Option<FnSig> {
790 match self { 811 match self {
791 Ty::Apply(a_ty) => match a_ty.ctor { 812 Ty::Apply(a_ty) => match a_ty.ctor {
792 TypeCtor::FnPtr { .. } => Some(FnSig::from_fn_ptr_substs(&a_ty.parameters)), 813 TypeCtor::FnPtr { is_varargs, .. } => {
814 Some(FnSig::from_fn_ptr_substs(&a_ty.parameters, is_varargs))
815 }
793 TypeCtor::FnDef(def) => { 816 TypeCtor::FnDef(def) => {
794 let sig = db.callable_item_signature(def); 817 let sig = db.callable_item_signature(def);
795 Some(sig.subst(&a_ty.parameters)) 818 Some(sig.subst(&a_ty.parameters))
@@ -827,13 +850,56 @@ impl Ty {
827 } 850 }
828 } 851 }
829 852
830 /// If this is a `dyn Trait`, returns that trait. 853 pub fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<GenericPredicate>> {
831 pub fn dyn_trait(&self) -> Option<TraitId> {
832 match self { 854 match self {
833 Ty::Dyn(predicates) => predicates.iter().find_map(|pred| match pred { 855 Ty::Opaque(opaque_ty) => {
834 GenericPredicate::Implemented(tr) => Some(tr.trait_), 856 let predicates = match opaque_ty.opaque_ty_id {
835 _ => None, 857 OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
836 }), 858 db.return_type_impl_traits(func).map(|it| {
859 let data = (*it)
860 .as_ref()
861 .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone());
862 data.subst(&opaque_ty.parameters)
863 })
864 }
865 };
866
867 predicates.map(|it| it.value)
868 }
869 Ty::Placeholder(id) => {
870 let generic_params = db.generic_params(id.parent);
871 let param_data = &generic_params.types[id.local_id];
872 match param_data.provenance {
873 hir_def::generics::TypeParamProvenance::ArgumentImplTrait => {
874 let predicates = db
875 .generic_predicates_for_param(*id)
876 .into_iter()
877 .map(|pred| pred.value.clone())
878 .collect_vec();
879
880 Some(predicates)
881 }
882 _ => None,
883 }
884 }
885 _ => None,
886 }
887 }
888
889 pub fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId> {
890 match self {
891 Ty::Apply(ApplicationTy { ctor: TypeCtor::AssociatedType(type_alias_id), .. }) => {
892 match type_alias_id.lookup(db.upcast()).container {
893 AssocContainerId::TraitId(trait_id) => Some(trait_id),
894 _ => None,
895 }
896 }
897 Ty::Projection(projection_ty) => {
898 match projection_ty.associated_ty.lookup(db.upcast()).container {
899 AssocContainerId::TraitId(trait_id) => Some(trait_id),
900 _ => None,
901 }
902 }
837 _ => None, 903 _ => None,
838 } 904 }
839 } 905 }
@@ -947,11 +1013,16 @@ impl TypeWalk for Ty {
947 t.walk(f); 1013 t.walk(f);
948 } 1014 }
949 } 1015 }
950 Ty::Dyn(predicates) | Ty::Opaque(predicates) => { 1016 Ty::Dyn(predicates) => {
951 for p in predicates.iter() { 1017 for p in predicates.iter() {
952 p.walk(f); 1018 p.walk(f);
953 } 1019 }
954 } 1020 }
1021 Ty::Opaque(o_ty) => {
1022 for t in o_ty.parameters.iter() {
1023 t.walk(f);
1024 }
1025 }
955 Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} 1026 Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
956 } 1027 }
957 f(self); 1028 f(self);
@@ -969,13 +1040,48 @@ impl TypeWalk for Ty {
969 Ty::Projection(p_ty) => { 1040 Ty::Projection(p_ty) => {
970 p_ty.parameters.walk_mut_binders(f, binders); 1041 p_ty.parameters.walk_mut_binders(f, binders);
971 } 1042 }
972 Ty::Dyn(predicates) | Ty::Opaque(predicates) => { 1043 Ty::Dyn(predicates) => {
973 for p in make_mut_slice(predicates) { 1044 for p in make_mut_slice(predicates) {
974 p.walk_mut_binders(f, binders.shifted_in()); 1045 p.walk_mut_binders(f, binders.shifted_in());
975 } 1046 }
976 } 1047 }
1048 Ty::Opaque(o_ty) => {
1049 o_ty.parameters.walk_mut_binders(f, binders);
1050 }
977 Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {} 1051 Ty::Placeholder { .. } | Ty::Bound(_) | Ty::Infer(_) | Ty::Unknown => {}
978 } 1052 }
979 f(self, binders); 1053 f(self, binders);
980 } 1054 }
981} 1055}
1056
1057impl<T: TypeWalk> TypeWalk for Vec<T> {
1058 fn walk(&self, f: &mut impl FnMut(&Ty)) {
1059 for t in self {
1060 t.walk(f);
1061 }
1062 }
1063 fn walk_mut_binders(
1064 &mut self,
1065 f: &mut impl FnMut(&mut Ty, DebruijnIndex),
1066 binders: DebruijnIndex,
1067 ) {
1068 for t in self {
1069 t.walk_mut_binders(f, binders);
1070 }
1071 }
1072}
1073
1074#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
1075pub enum OpaqueTyId {
1076 ReturnTypeImplTrait(hir_def::FunctionId, u16),
1077}
1078
1079#[derive(Clone, PartialEq, Eq, Debug, Hash)]
1080pub struct ReturnTypeImplTraits {
1081 pub(crate) impl_traits: Vec<ReturnTypeImplTrait>,
1082}
1083
1084#[derive(Clone, PartialEq, Eq, Debug, Hash)]
1085pub(crate) struct ReturnTypeImplTrait {
1086 pub bounds: Binders<Vec<GenericPredicate>>,
1087}
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index 35ac86a46..f274579ea 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -5,10 +5,7 @@
5//! - Building the type for an item: This happens through the `type_for_def` query. 5//! - Building the type for an item: This happens through the `type_for_def` query.
6//! 6//!
7//! This usually involves resolving names, collecting generic arguments etc. 7//! This usually involves resolving names, collecting generic arguments etc.
8use std::iter; 8use std::{iter, sync::Arc};
9use std::sync::Arc;
10
11use smallvec::SmallVec;
12 9
13use hir_def::{ 10use hir_def::{
14 adt::StructKind, 11 adt::StructKind,
@@ -21,8 +18,12 @@ use hir_def::{
21 HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, 18 HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
22 UnionId, VariantId, 19 UnionId, VariantId,
23}; 20};
21use hir_expand::name::Name;
24use ra_arena::map::ArenaMap; 22use ra_arena::map::ArenaMap;
25use ra_db::CrateId; 23use ra_db::CrateId;
24use smallvec::SmallVec;
25use stdx::impl_from;
26use test_utils::mark;
26 27
27use crate::{ 28use crate::{
28 db::HirDatabase, 29 db::HirDatabase,
@@ -31,10 +32,10 @@ use crate::{
31 all_super_trait_refs, associated_type_by_name_including_super_traits, generics, 32 all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
32 make_mut_slice, variant_data, 33 make_mut_slice, variant_data,
33 }, 34 },
34 Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, PolyFnSig, ProjectionPredicate, 35 Binders, BoundVar, DebruijnIndex, FnSig, GenericPredicate, OpaqueTy, OpaqueTyId, PolyFnSig,
35 ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, 36 ProjectionPredicate, ProjectionTy, ReturnTypeImplTrait, ReturnTypeImplTraits, Substs,
37 TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk,
36}; 38};
37use hir_expand::name::Name;
38 39
39#[derive(Debug)] 40#[derive(Debug)]
40pub struct TyLoweringContext<'a> { 41pub struct TyLoweringContext<'a> {
@@ -47,7 +48,16 @@ pub struct TyLoweringContext<'a> {
47 /// possible currently, so this should be fine for now. 48 /// possible currently, so this should be fine for now.
48 pub type_param_mode: TypeParamLoweringMode, 49 pub type_param_mode: TypeParamLoweringMode,
49 pub impl_trait_mode: ImplTraitLoweringMode, 50 pub impl_trait_mode: ImplTraitLoweringMode,
50 pub impl_trait_counter: std::cell::Cell<u16>, 51 impl_trait_counter: std::cell::Cell<u16>,
52 /// When turning `impl Trait` into opaque types, we have to collect the
53 /// bounds at the same time to get the IDs correct (without becoming too
54 /// complicated). I don't like using interior mutability (as for the
55 /// counter), but I've tried and failed to make the lifetimes work for
56 /// passing around a `&mut TyLoweringContext`. The core problem is that
57 /// we're grouping the mutable data (the counter and this field) together
58 /// with the immutable context (the references to the DB and resolver).
59 /// Splitting this up would be a possible fix.
60 opaque_type_data: std::cell::RefCell<Vec<ReturnTypeImplTrait>>,
51} 61}
52 62
53impl<'a> TyLoweringContext<'a> { 63impl<'a> TyLoweringContext<'a> {
@@ -56,26 +66,42 @@ impl<'a> TyLoweringContext<'a> {
56 let impl_trait_mode = ImplTraitLoweringMode::Disallowed; 66 let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
57 let type_param_mode = TypeParamLoweringMode::Placeholder; 67 let type_param_mode = TypeParamLoweringMode::Placeholder;
58 let in_binders = DebruijnIndex::INNERMOST; 68 let in_binders = DebruijnIndex::INNERMOST;
59 Self { db, resolver, in_binders, impl_trait_mode, impl_trait_counter, type_param_mode } 69 let opaque_type_data = std::cell::RefCell::new(Vec::new());
70 Self {
71 db,
72 resolver,
73 in_binders,
74 impl_trait_mode,
75 impl_trait_counter,
76 type_param_mode,
77 opaque_type_data,
78 }
60 } 79 }
61 80
62 pub fn with_shifted_in<T>( 81 pub fn with_debruijn<T>(
63 &self, 82 &self,
64 debruijn: DebruijnIndex, 83 debruijn: DebruijnIndex,
65 f: impl FnOnce(&TyLoweringContext) -> T, 84 f: impl FnOnce(&TyLoweringContext) -> T,
66 ) -> T { 85 ) -> T {
86 let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new());
67 let new_ctx = Self { 87 let new_ctx = Self {
68 in_binders: self.in_binders.shifted_in_from(debruijn), 88 in_binders: debruijn,
69 impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()), 89 impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()),
90 opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec),
70 ..*self 91 ..*self
71 }; 92 };
72 let result = f(&new_ctx); 93 let result = f(&new_ctx);
73 self.impl_trait_counter.set(new_ctx.impl_trait_counter.get()); 94 self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
95 self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner());
74 result 96 result
75 } 97 }
76 98
77 pub fn shifted_in(self, debruijn: DebruijnIndex) -> Self { 99 pub fn with_shifted_in<T>(
78 Self { in_binders: self.in_binders.shifted_in_from(debruijn), ..self } 100 &self,
101 debruijn: DebruijnIndex,
102 f: impl FnOnce(&TyLoweringContext) -> T,
103 ) -> T {
104 self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f)
79 } 105 }
80 106
81 pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { 107 pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self {
@@ -150,9 +176,12 @@ impl Ty {
150 Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty) 176 Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty)
151 } 177 }
152 TypeRef::Placeholder => Ty::Unknown, 178 TypeRef::Placeholder => Ty::Unknown,
153 TypeRef::Fn(params) => { 179 TypeRef::Fn(params, is_varargs) => {
154 let sig = Substs(params.iter().map(|tr| Ty::from_hir(ctx, tr)).collect()); 180 let sig = Substs(params.iter().map(|tr| Ty::from_hir(ctx, tr)).collect());
155 Ty::apply(TypeCtor::FnPtr { num_args: sig.len() as u16 - 1 }, sig) 181 Ty::apply(
182 TypeCtor::FnPtr { num_args: sig.len() as u16 - 1, is_varargs: *is_varargs },
183 sig,
184 )
156 } 185 }
157 TypeRef::DynTrait(bounds) => { 186 TypeRef::DynTrait(bounds) => {
158 let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); 187 let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
@@ -167,20 +196,44 @@ impl Ty {
167 TypeRef::ImplTrait(bounds) => { 196 TypeRef::ImplTrait(bounds) => {
168 match ctx.impl_trait_mode { 197 match ctx.impl_trait_mode {
169 ImplTraitLoweringMode::Opaque => { 198 ImplTraitLoweringMode::Opaque => {
170 let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)); 199 let idx = ctx.impl_trait_counter.get();
171 let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { 200 ctx.impl_trait_counter.set(idx + 1);
172 bounds 201
173 .iter() 202 assert!(idx as usize == ctx.opaque_type_data.borrow().len());
174 .flat_map(|b| { 203 // this dance is to make sure the data is in the right
175 GenericPredicate::from_type_bound(ctx, b, self_ty.clone()) 204 // place even if we encounter more opaque types while
176 }) 205 // lowering the bounds
177 .collect() 206 ctx.opaque_type_data
178 }); 207 .borrow_mut()
179 Ty::Opaque(predicates) 208 .push(ReturnTypeImplTrait { bounds: Binders::new(1, Vec::new()) });
209 // We don't want to lower the bounds inside the binders
210 // we're currently in, because they don't end up inside
211 // those binders. E.g. when we have `impl Trait<impl
212 // OtherTrait<T>>`, the `impl OtherTrait<T>` can't refer
213 // to the self parameter from `impl Trait`, and the
214 // bounds aren't actually stored nested within each
215 // other, but separately. So if the `T` refers to a type
216 // parameter of the outer function, it's just one binder
217 // away instead of two.
218 let actual_opaque_type_data = ctx
219 .with_debruijn(DebruijnIndex::INNERMOST, |ctx| {
220 ReturnTypeImplTrait::from_hir(ctx, &bounds)
221 });
222 ctx.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data;
223
224 let func = match ctx.resolver.generic_def() {
225 Some(GenericDefId::FunctionId(f)) => f,
226 _ => panic!("opaque impl trait lowering in non-function"),
227 };
228 let impl_trait_id = OpaqueTyId::ReturnTypeImplTrait(func, idx);
229 let generics = generics(ctx.db.upcast(), func.into());
230 let parameters = Substs::bound_vars(&generics, ctx.in_binders);
231 Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters })
180 } 232 }
181 ImplTraitLoweringMode::Param => { 233 ImplTraitLoweringMode::Param => {
182 let idx = ctx.impl_trait_counter.get(); 234 let idx = ctx.impl_trait_counter.get();
183 ctx.impl_trait_counter.set(idx + 1); 235 // FIXME we're probably doing something wrong here
236 ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
184 if let Some(def) = ctx.resolver.generic_def() { 237 if let Some(def) = ctx.resolver.generic_def() {
185 let generics = generics(ctx.db.upcast(), def); 238 let generics = generics(ctx.db.upcast(), def);
186 let param = generics 239 let param = generics
@@ -197,7 +250,8 @@ impl Ty {
197 } 250 }
198 ImplTraitLoweringMode::Variable => { 251 ImplTraitLoweringMode::Variable => {
199 let idx = ctx.impl_trait_counter.get(); 252 let idx = ctx.impl_trait_counter.get();
200 ctx.impl_trait_counter.set(idx + 1); 253 // FIXME we're probably doing something wrong here
254 ctx.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
201 let (parent_params, self_params, list_params, _impl_trait_params) = 255 let (parent_params, self_params, list_params, _impl_trait_params) =
202 if let Some(def) = ctx.resolver.generic_def() { 256 if let Some(def) = ctx.resolver.generic_def() {
203 let generics = generics(ctx.db.upcast(), def); 257 let generics = generics(ctx.db.upcast(), def);
@@ -271,6 +325,7 @@ impl Ty {
271 resolution: TypeNs, 325 resolution: TypeNs,
272 resolved_segment: PathSegment<'_>, 326 resolved_segment: PathSegment<'_>,
273 remaining_segments: PathSegments<'_>, 327 remaining_segments: PathSegments<'_>,
328 infer_args: bool,
274 ) -> (Ty, Option<TypeNs>) { 329 ) -> (Ty, Option<TypeNs>) {
275 let ty = match resolution { 330 let ty = match resolution {
276 TypeNs::TraitId(trait_) => { 331 TypeNs::TraitId(trait_) => {
@@ -284,17 +339,17 @@ impl Ty {
284 TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty); 339 TraitRef::from_resolved_path(ctx, trait_, resolved_segment, self_ty);
285 let ty = if remaining_segments.len() == 1 { 340 let ty = if remaining_segments.len() == 1 {
286 let segment = remaining_segments.first().unwrap(); 341 let segment = remaining_segments.first().unwrap();
287 let associated_ty = associated_type_by_name_including_super_traits( 342 let found = associated_type_by_name_including_super_traits(
288 ctx.db.upcast(), 343 ctx.db,
289 trait_ref.trait_, 344 trait_ref.clone(),
290 &segment.name, 345 &segment.name,
291 ); 346 );
292 match associated_ty { 347 match found {
293 Some(associated_ty) => { 348 Some((super_trait_ref, associated_ty)) => {
294 // FIXME handle type parameters on the segment 349 // FIXME handle type parameters on the segment
295 Ty::Projection(ProjectionTy { 350 Ty::Projection(ProjectionTy {
296 associated_ty, 351 associated_ty,
297 parameters: trait_ref.substs, 352 parameters: super_trait_ref.substs,
298 }) 353 })
299 } 354 }
300 None => { 355 None => {
@@ -348,9 +403,15 @@ impl Ty {
348 ctx.db.ty(adt.into()).subst(&substs) 403 ctx.db.ty(adt.into()).subst(&substs)
349 } 404 }
350 405
351 TypeNs::AdtId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), 406 TypeNs::AdtId(it) => {
352 TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), 407 Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
353 TypeNs::TypeAliasId(it) => Ty::from_hir_path_inner(ctx, resolved_segment, it.into()), 408 }
409 TypeNs::BuiltinType(it) => {
410 Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
411 }
412 TypeNs::TypeAliasId(it) => {
413 Ty::from_hir_path_inner(ctx, resolved_segment, it.into(), infer_args)
414 }
354 // FIXME: report error 415 // FIXME: report error
355 TypeNs::EnumVariantId(_) => return (Ty::Unknown, None), 416 TypeNs::EnumVariantId(_) => return (Ty::Unknown, None),
356 }; 417 };
@@ -376,7 +437,13 @@ impl Ty {
376 ), 437 ),
377 Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)), 438 Some(i) => (path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
378 }; 439 };
379 Ty::from_partly_resolved_hir_path(ctx, resolution, resolved_segment, remaining_segments) 440 Ty::from_partly_resolved_hir_path(
441 ctx,
442 resolution,
443 resolved_segment,
444 remaining_segments,
445 false,
446 )
380 } 447 }
381 448
382 fn select_associated_type( 449 fn select_associated_type(
@@ -402,6 +469,9 @@ impl Ty {
402 } 469 }
403 TypeParamLoweringMode::Variable => t.substs.clone(), 470 TypeParamLoweringMode::Variable => t.substs.clone(),
404 }; 471 };
472 // We need to shift in the bound vars, since
473 // associated_type_shorthand_candidates does not do that
474 let substs = substs.shift_bound_vars(ctx.in_binders);
405 // FIXME handle type parameters on the segment 475 // FIXME handle type parameters on the segment
406 return Some(Ty::Projection(ProjectionTy { 476 return Some(Ty::Projection(ProjectionTy {
407 associated_ty, 477 associated_ty,
@@ -422,13 +492,14 @@ impl Ty {
422 ctx: &TyLoweringContext<'_>, 492 ctx: &TyLoweringContext<'_>,
423 segment: PathSegment<'_>, 493 segment: PathSegment<'_>,
424 typable: TyDefId, 494 typable: TyDefId,
495 infer_args: bool,
425 ) -> Ty { 496 ) -> Ty {
426 let generic_def = match typable { 497 let generic_def = match typable {
427 TyDefId::BuiltinType(_) => None, 498 TyDefId::BuiltinType(_) => None,
428 TyDefId::AdtId(it) => Some(it.into()), 499 TyDefId::AdtId(it) => Some(it.into()),
429 TyDefId::TypeAliasId(it) => Some(it.into()), 500 TyDefId::TypeAliasId(it) => Some(it.into()),
430 }; 501 };
431 let substs = substs_from_path_segment(ctx, segment, generic_def, false); 502 let substs = substs_from_path_segment(ctx, segment, generic_def, infer_args);
432 ctx.db.ty(typable).subst(&substs) 503 ctx.db.ty(typable).subst(&substs)
433 } 504 }
434 505
@@ -441,6 +512,7 @@ impl Ty {
441 // `ValueTyDefId` is just a convenient way to pass generics and 512 // `ValueTyDefId` is just a convenient way to pass generics and
442 // special-case enum variants 513 // special-case enum variants
443 resolved: ValueTyDefId, 514 resolved: ValueTyDefId,
515 infer_args: bool,
444 ) -> Substs { 516 ) -> Substs {
445 let last = path.segments().last().expect("path should have at least one segment"); 517 let last = path.segments().last().expect("path should have at least one segment");
446 let (segment, generic_def) = match resolved { 518 let (segment, generic_def) = match resolved {
@@ -463,22 +535,27 @@ impl Ty {
463 (segment, Some(var.parent.into())) 535 (segment, Some(var.parent.into()))
464 } 536 }
465 }; 537 };
466 substs_from_path_segment(ctx, segment, generic_def, false) 538 substs_from_path_segment(ctx, segment, generic_def, infer_args)
467 } 539 }
468} 540}
469 541
470pub(super) fn substs_from_path_segment( 542fn substs_from_path_segment(
471 ctx: &TyLoweringContext<'_>, 543 ctx: &TyLoweringContext<'_>,
472 segment: PathSegment<'_>, 544 segment: PathSegment<'_>,
473 def_generic: Option<GenericDefId>, 545 def_generic: Option<GenericDefId>,
474 _add_self_param: bool, 546 infer_args: bool,
475) -> Substs { 547) -> Substs {
476 let mut substs = Vec::new(); 548 let mut substs = Vec::new();
477 let def_generics = def_generic.map(|def| generics(ctx.db.upcast(), def)); 549 let def_generics = def_generic.map(|def| generics(ctx.db.upcast(), def));
478 550
479 let (parent_params, self_params, type_params, impl_trait_params) = 551 let (parent_params, self_params, type_params, impl_trait_params) =
480 def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split()); 552 def_generics.map_or((0, 0, 0, 0), |g| g.provenance_split());
553 let total_len = parent_params + self_params + type_params + impl_trait_params;
554
481 substs.extend(iter::repeat(Ty::Unknown).take(parent_params)); 555 substs.extend(iter::repeat(Ty::Unknown).take(parent_params));
556
557 let mut had_explicit_args = false;
558
482 if let Some(generic_args) = &segment.args_and_bindings { 559 if let Some(generic_args) = &segment.args_and_bindings {
483 if !generic_args.has_self_type { 560 if !generic_args.has_self_type {
484 substs.extend(iter::repeat(Ty::Unknown).take(self_params)); 561 substs.extend(iter::repeat(Ty::Unknown).take(self_params));
@@ -490,31 +567,37 @@ pub(super) fn substs_from_path_segment(
490 for arg in generic_args.args.iter().skip(skip).take(expected_num) { 567 for arg in generic_args.args.iter().skip(skip).take(expected_num) {
491 match arg { 568 match arg {
492 GenericArg::Type(type_ref) => { 569 GenericArg::Type(type_ref) => {
570 had_explicit_args = true;
493 let ty = Ty::from_hir(ctx, type_ref); 571 let ty = Ty::from_hir(ctx, type_ref);
494 substs.push(ty); 572 substs.push(ty);
495 } 573 }
496 } 574 }
497 } 575 }
498 } 576 }
499 let total_len = parent_params + self_params + type_params + impl_trait_params; 577
578 // handle defaults. In expression or pattern path segments without
579 // explicitly specified type arguments, missing type arguments are inferred
580 // (i.e. defaults aren't used).
581 if !infer_args || had_explicit_args {
582 if let Some(def_generic) = def_generic {
583 let defaults = ctx.db.generic_defaults(def_generic);
584 assert_eq!(total_len, defaults.len());
585
586 for default_ty in defaults.iter().skip(substs.len()) {
587 // each default can depend on the previous parameters
588 let substs_so_far = Substs(substs.clone().into());
589 substs.push(default_ty.clone().subst(&substs_so_far));
590 }
591 }
592 }
593
500 // add placeholders for args that were not provided 594 // add placeholders for args that were not provided
595 // FIXME: emit diagnostics in contexts where this is not allowed
501 for _ in substs.len()..total_len { 596 for _ in substs.len()..total_len {
502 substs.push(Ty::Unknown); 597 substs.push(Ty::Unknown);
503 } 598 }
504 assert_eq!(substs.len(), total_len); 599 assert_eq!(substs.len(), total_len);
505 600
506 // handle defaults
507 if let Some(def_generic) = def_generic {
508 let default_substs = ctx.db.generic_defaults(def_generic);
509 assert_eq!(substs.len(), default_substs.len());
510
511 for (i, default_ty) in default_substs.iter().enumerate() {
512 if substs[i] == Ty::Unknown {
513 substs[i] = default_ty.clone();
514 }
515 }
516 }
517
518 Substs(substs.into()) 601 Substs(substs.into())
519} 602}
520 603
@@ -563,9 +646,7 @@ impl TraitRef {
563 segment: PathSegment<'_>, 646 segment: PathSegment<'_>,
564 resolved: TraitId, 647 resolved: TraitId,
565 ) -> Substs { 648 ) -> Substs {
566 let has_self_param = 649 substs_from_path_segment(ctx, segment, Some(resolved.into()), false)
567 segment.args_and_bindings.as_ref().map(|a| a.has_self_type).unwrap_or(false);
568 substs_from_path_segment(ctx, segment, Some(resolved.into()), !has_self_param)
569 } 650 }
570 651
571 pub(crate) fn from_type_bound( 652 pub(crate) fn from_type_bound(
@@ -632,17 +713,16 @@ fn assoc_type_bindings_from_type_bound<'a>(
632 .flat_map(|segment| segment.args_and_bindings.into_iter()) 713 .flat_map(|segment| segment.args_and_bindings.into_iter())
633 .flat_map(|args_and_bindings| args_and_bindings.bindings.iter()) 714 .flat_map(|args_and_bindings| args_and_bindings.bindings.iter())
634 .flat_map(move |binding| { 715 .flat_map(move |binding| {
635 let associated_ty = associated_type_by_name_including_super_traits( 716 let found = associated_type_by_name_including_super_traits(
636 ctx.db.upcast(), 717 ctx.db,
637 trait_ref.trait_, 718 trait_ref.clone(),
638 &binding.name, 719 &binding.name,
639 ); 720 );
640 let associated_ty = match associated_ty { 721 let (super_trait_ref, associated_ty) = match found {
641 None => return SmallVec::<[GenericPredicate; 1]>::new(), 722 None => return SmallVec::<[GenericPredicate; 1]>::new(),
642 Some(t) => t, 723 Some(t) => t,
643 }; 724 };
644 let projection_ty = 725 let projection_ty = ProjectionTy { associated_ty, parameters: super_trait_ref.substs };
645 ProjectionTy { associated_ty, parameters: trait_ref.substs.clone() };
646 let mut preds = SmallVec::with_capacity( 726 let mut preds = SmallVec::with_capacity(
647 binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), 727 binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
648 ); 728 );
@@ -663,12 +743,36 @@ fn assoc_type_bindings_from_type_bound<'a>(
663 }) 743 })
664} 744}
665 745
746impl ReturnTypeImplTrait {
747 fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self {
748 mark::hit!(lower_rpit);
749 let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
750 let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| {
751 bounds
752 .iter()
753 .flat_map(|b| GenericPredicate::from_type_bound(ctx, b, self_ty.clone()))
754 .collect()
755 });
756 ReturnTypeImplTrait { bounds: Binders::new(1, predicates) }
757 }
758}
759
760fn count_impl_traits(type_ref: &TypeRef) -> usize {
761 let mut count = 0;
762 type_ref.walk(&mut |type_ref| {
763 if matches!(type_ref, TypeRef::ImplTrait(_)) {
764 count += 1;
765 }
766 });
767 count
768}
769
666/// Build the signature of a callable item (function, struct or enum variant). 770/// Build the signature of a callable item (function, struct or enum variant).
667pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDef) -> PolyFnSig { 771pub fn callable_item_sig(db: &dyn HirDatabase, def: CallableDefId) -> PolyFnSig {
668 match def { 772 match def {
669 CallableDef::FunctionId(f) => fn_sig_for_fn(db, f), 773 CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f),
670 CallableDef::StructId(s) => fn_sig_for_struct_constructor(db, s), 774 CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s),
671 CallableDef::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e), 775 CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e),
672 } 776 }
673} 777}
674 778
@@ -844,17 +948,42 @@ pub(crate) fn generic_predicates_query(
844} 948}
845 949
846/// Resolve the default type params from generics 950/// Resolve the default type params from generics
847pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) -> Substs { 951pub(crate) fn generic_defaults_query(
952 db: &dyn HirDatabase,
953 def: GenericDefId,
954) -> Arc<[Binders<Ty>]> {
848 let resolver = def.resolver(db.upcast()); 955 let resolver = def.resolver(db.upcast());
849 let ctx = TyLoweringContext::new(db, &resolver); 956 let ctx =
957 TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable);
850 let generic_params = generics(db.upcast(), def); 958 let generic_params = generics(db.upcast(), def);
851 959
852 let defaults = generic_params 960 let defaults = generic_params
853 .iter() 961 .iter()
854 .map(|(_idx, p)| p.default.as_ref().map_or(Ty::Unknown, |t| Ty::from_hir(&ctx, t))) 962 .enumerate()
963 .map(|(idx, (_, p))| {
964 let mut ty = p.default.as_ref().map_or(Ty::Unknown, |t| Ty::from_hir(&ctx, t));
965
966 // Each default can only refer to previous parameters.
967 ty.walk_mut_binders(
968 &mut |ty, binders| match ty {
969 Ty::Bound(BoundVar { debruijn, index }) if *debruijn == binders => {
970 if *index >= idx {
971 // type variable default referring to parameter coming
972 // after it. This is forbidden (FIXME: report
973 // diagnostic)
974 *ty = Ty::Unknown;
975 }
976 }
977 _ => {}
978 },
979 DebruijnIndex::INNERMOST,
980 );
981
982 Binders::new(idx, ty)
983 })
855 .collect(); 984 .collect();
856 985
857 Substs(defaults) 986 defaults
858} 987}
859 988
860fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { 989fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
@@ -864,11 +993,13 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
864 .with_impl_trait_mode(ImplTraitLoweringMode::Variable) 993 .with_impl_trait_mode(ImplTraitLoweringMode::Variable)
865 .with_type_param_mode(TypeParamLoweringMode::Variable); 994 .with_type_param_mode(TypeParamLoweringMode::Variable);
866 let params = data.params.iter().map(|tr| Ty::from_hir(&ctx_params, tr)).collect::<Vec<_>>(); 995 let params = data.params.iter().map(|tr| Ty::from_hir(&ctx_params, tr)).collect::<Vec<_>>();
867 let ctx_ret = ctx_params.with_impl_trait_mode(ImplTraitLoweringMode::Opaque); 996 let ctx_ret = TyLoweringContext::new(db, &resolver)
997 .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
998 .with_type_param_mode(TypeParamLoweringMode::Variable);
868 let ret = Ty::from_hir(&ctx_ret, &data.ret_type); 999 let ret = Ty::from_hir(&ctx_ret, &data.ret_type);
869 let generics = generics(db.upcast(), def.into()); 1000 let generics = generics(db.upcast(), def.into());
870 let num_binders = generics.len(); 1001 let num_binders = generics.len();
871 Binders::new(num_binders, FnSig::from_params_and_return(params, ret)) 1002 Binders::new(num_binders, FnSig::from_params_and_return(params, ret, data.is_varargs))
872} 1003}
873 1004
874/// Build the declared type of a function. This should not need to look at the 1005/// Build the declared type of a function. This should not need to look at the
@@ -919,7 +1050,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS
919 let params = 1050 let params =
920 fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>(); 1051 fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>();
921 let ret = type_for_adt(db, def.into()); 1052 let ret = type_for_adt(db, def.into());
922 Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value)) 1053 Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value, false))
923} 1054}
924 1055
925/// Build the type of a tuple struct constructor. 1056/// Build the type of a tuple struct constructor.
@@ -943,7 +1074,7 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
943 let params = 1074 let params =
944 fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>(); 1075 fields.iter().map(|(_, field)| Ty::from_hir(&ctx, &field.type_ref)).collect::<Vec<_>>();
945 let ret = type_for_adt(db, def.parent.into()); 1076 let ret = type_for_adt(db, def.parent.into());
946 Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value)) 1077 Binders::new(ret.num_binders, FnSig::from_params_and_return(params, ret.value, false))
947} 1078}
948 1079
949/// Build the type of a tuple enum variant constructor. 1080/// Build the type of a tuple enum variant constructor.
@@ -976,31 +1107,31 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
976} 1107}
977 1108
978#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 1109#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
979pub enum CallableDef { 1110pub enum CallableDefId {
980 FunctionId(FunctionId), 1111 FunctionId(FunctionId),
981 StructId(StructId), 1112 StructId(StructId),
982 EnumVariantId(EnumVariantId), 1113 EnumVariantId(EnumVariantId),
983} 1114}
984impl_froms!(CallableDef: FunctionId, StructId, EnumVariantId); 1115impl_from!(FunctionId, StructId, EnumVariantId for CallableDefId);
985 1116
986impl CallableDef { 1117impl CallableDefId {
987 pub fn krate(self, db: &dyn HirDatabase) -> CrateId { 1118 pub fn krate(self, db: &dyn HirDatabase) -> CrateId {
988 let db = db.upcast(); 1119 let db = db.upcast();
989 match self { 1120 match self {
990 CallableDef::FunctionId(f) => f.lookup(db).module(db), 1121 CallableDefId::FunctionId(f) => f.lookup(db).module(db),
991 CallableDef::StructId(s) => s.lookup(db).container.module(db), 1122 CallableDefId::StructId(s) => s.lookup(db).container.module(db),
992 CallableDef::EnumVariantId(e) => e.parent.lookup(db).container.module(db), 1123 CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container.module(db),
993 } 1124 }
994 .krate 1125 .krate
995 } 1126 }
996} 1127}
997 1128
998impl From<CallableDef> for GenericDefId { 1129impl From<CallableDefId> for GenericDefId {
999 fn from(def: CallableDef) -> GenericDefId { 1130 fn from(def: CallableDefId) -> GenericDefId {
1000 match def { 1131 match def {
1001 CallableDef::FunctionId(f) => f.into(), 1132 CallableDefId::FunctionId(f) => f.into(),
1002 CallableDef::StructId(s) => s.into(), 1133 CallableDefId::StructId(s) => s.into(),
1003 CallableDef::EnumVariantId(e) => e.into(), 1134 CallableDefId::EnumVariantId(e) => e.into(),
1004 } 1135 }
1005 } 1136 }
1006} 1137}
@@ -1011,7 +1142,7 @@ pub enum TyDefId {
1011 AdtId(AdtId), 1142 AdtId(AdtId),
1012 TypeAliasId(TypeAliasId), 1143 TypeAliasId(TypeAliasId),
1013} 1144}
1014impl_froms!(TyDefId: BuiltinType, AdtId(StructId, EnumId, UnionId), TypeAliasId); 1145impl_from!(BuiltinType, AdtId(StructId, EnumId, UnionId), TypeAliasId for TyDefId);
1015 1146
1016#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 1147#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1017pub enum ValueTyDefId { 1148pub enum ValueTyDefId {
@@ -1021,7 +1152,7 @@ pub enum ValueTyDefId {
1021 ConstId(ConstId), 1152 ConstId(ConstId),
1022 StaticId(StaticId), 1153 StaticId(StaticId),
1023} 1154}
1024impl_froms!(ValueTyDefId: FunctionId, StructId, EnumVariantId, ConstId, StaticId); 1155impl_from!(FunctionId, StructId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
1025 1156
1026/// Build the declared type of an item. This depends on the namespace; e.g. for 1157/// Build the declared type of an item. This depends on the namespace; e.g. for
1027/// `struct Foo(usize)`, we have two types: The type of the struct itself, and 1158/// `struct Foo(usize)`, we have two types: The type of the struct itself, and
@@ -1084,3 +1215,25 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<
1084 TraitRef::from_hir(&ctx, target_trait, Some(self_ty.value))?, 1215 TraitRef::from_hir(&ctx, target_trait, Some(self_ty.value))?,
1085 )) 1216 ))
1086} 1217}
1218
1219pub(crate) fn return_type_impl_traits(
1220 db: &dyn HirDatabase,
1221 def: hir_def::FunctionId,
1222) -> Option<Arc<Binders<ReturnTypeImplTraits>>> {
1223 // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
1224 let data = db.function_data(def);
1225 let resolver = def.resolver(db.upcast());
1226 let ctx_ret = TyLoweringContext::new(db, &resolver)
1227 .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
1228 .with_type_param_mode(TypeParamLoweringMode::Variable);
1229 let _ret = Ty::from_hir(&ctx_ret, &data.ret_type);
1230 let generics = generics(db.upcast(), def.into());
1231 let num_binders = generics.len();
1232 let return_type_impl_traits =
1233 ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() };
1234 if return_type_impl_traits.impl_traits.is_empty() {
1235 None
1236 } else {
1237 Some(Arc::new(Binders::new(num_binders, return_type_impl_traits)))
1238 }
1239}
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index e19628fdf..fb4b30a13 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -2,12 +2,14 @@
2//! For details about how this works in rustc, see the method lookup page in the 2//! For details about how this works in rustc, see the method lookup page in the
3//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html) 3//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
4//! and the corresponding code mostly in librustc_typeck/check/method/probe.rs. 4//! and the corresponding code mostly in librustc_typeck/check/method/probe.rs.
5use std::sync::Arc; 5use std::{iter, sync::Arc};
6 6
7use arrayvec::ArrayVec; 7use arrayvec::ArrayVec;
8use hir_def::{ 8use hir_def::{
9 lang_item::LangItemTarget, type_ref::Mutability, AssocContainerId, AssocItemId, FunctionId, 9 builtin_type::{IntBitness, Signedness},
10 HasModule, ImplId, Lookup, TraitId, 10 lang_item::LangItemTarget,
11 type_ref::Mutability,
12 AssocContainerId, AssocItemId, FunctionId, HasModule, ImplId, Lookup, TraitId,
11}; 13};
12use hir_expand::name::Name; 14use hir_expand::name::Name;
13use ra_db::CrateId; 15use ra_db::CrateId;
@@ -18,9 +20,9 @@ use super::Substs;
18use crate::{ 20use crate::{
19 autoderef, 21 autoderef,
20 db::HirDatabase, 22 db::HirDatabase,
21 primitive::{FloatBitness, Uncertain}, 23 primitive::{FloatBitness, FloatTy, IntTy},
22 utils::all_super_traits, 24 utils::all_super_traits,
23 ApplicationTy, Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, 25 ApplicationTy, Canonical, DebruijnIndex, InEnvironment, TraitEnvironment, TraitRef, Ty, TyKind,
24 TypeCtor, TypeWalk, 26 TypeCtor, TypeWalk,
25}; 27};
26 28
@@ -42,83 +44,187 @@ impl TyFingerprint {
42 } 44 }
43} 45}
44 46
45#[derive(Debug, PartialEq, Eq)] 47pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [
46pub struct CrateImplDefs { 48 TyFingerprint::Apply(TypeCtor::Int(IntTy {
47 impls: FxHashMap<TyFingerprint, Vec<ImplId>>, 49 signedness: Signedness::Unsigned,
48 impls_by_trait: FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>, 50 bitness: IntBitness::X8,
51 })),
52 TyFingerprint::Apply(TypeCtor::Int(IntTy {
53 signedness: Signedness::Unsigned,
54 bitness: IntBitness::X16,
55 })),
56 TyFingerprint::Apply(TypeCtor::Int(IntTy {
57 signedness: Signedness::Unsigned,
58 bitness: IntBitness::X32,
59 })),
60 TyFingerprint::Apply(TypeCtor::Int(IntTy {
61 signedness: Signedness::Unsigned,
62 bitness: IntBitness::X64,
63 })),
64 TyFingerprint::Apply(TypeCtor::Int(IntTy {
65 signedness: Signedness::Unsigned,
66 bitness: IntBitness::X128,
67 })),
68 TyFingerprint::Apply(TypeCtor::Int(IntTy {
69 signedness: Signedness::Unsigned,
70 bitness: IntBitness::Xsize,
71 })),
72 TyFingerprint::Apply(TypeCtor::Int(IntTy {
73 signedness: Signedness::Signed,
74 bitness: IntBitness::X8,
75 })),
76 TyFingerprint::Apply(TypeCtor::Int(IntTy {
77 signedness: Signedness::Signed,
78 bitness: IntBitness::X16,
79 })),
80 TyFingerprint::Apply(TypeCtor::Int(IntTy {
81 signedness: Signedness::Signed,
82 bitness: IntBitness::X32,
83 })),
84 TyFingerprint::Apply(TypeCtor::Int(IntTy {
85 signedness: Signedness::Signed,
86 bitness: IntBitness::X64,
87 })),
88 TyFingerprint::Apply(TypeCtor::Int(IntTy {
89 signedness: Signedness::Signed,
90 bitness: IntBitness::X128,
91 })),
92 TyFingerprint::Apply(TypeCtor::Int(IntTy {
93 signedness: Signedness::Signed,
94 bitness: IntBitness::Xsize,
95 })),
96];
97
98pub(crate) const ALL_FLOAT_FPS: [TyFingerprint; 2] = [
99 TyFingerprint::Apply(TypeCtor::Float(FloatTy { bitness: FloatBitness::X32 })),
100 TyFingerprint::Apply(TypeCtor::Float(FloatTy { bitness: FloatBitness::X64 })),
101];
102
103/// Trait impls defined or available in some crate.
104#[derive(Debug, Eq, PartialEq)]
105pub struct TraitImpls {
106 // If the `Option<TyFingerprint>` is `None`, the impl may apply to any self type.
107 map: FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>,
49} 108}
50 109
51impl CrateImplDefs { 110impl TraitImpls {
52 pub(crate) fn impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<CrateImplDefs> { 111 pub(crate) fn trait_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
53 let _p = profile("impls_in_crate_query"); 112 let _p = profile("trait_impls_in_crate_query");
54 let mut res = 113 let mut impls = Self { map: FxHashMap::default() };
55 CrateImplDefs { impls: FxHashMap::default(), impls_by_trait: FxHashMap::default() };
56 114
57 let crate_def_map = db.crate_def_map(krate); 115 let crate_def_map = db.crate_def_map(krate);
58 for (_module_id, module_data) in crate_def_map.modules.iter() { 116 for (_module_id, module_data) in crate_def_map.modules.iter() {
59 for impl_id in module_data.scope.impls() { 117 for impl_id in module_data.scope.impls() {
60 match db.impl_trait(impl_id) { 118 let target_trait = match db.impl_trait(impl_id) {
61 Some(tr) => { 119 Some(tr) => tr.value.trait_,
62 let self_ty = db.impl_self_ty(impl_id); 120 None => continue,
63 let self_ty_fp = TyFingerprint::for_impl(&self_ty.value); 121 };
64 res.impls_by_trait 122 let self_ty = db.impl_self_ty(impl_id);
65 .entry(tr.value.trait_) 123 let self_ty_fp = TyFingerprint::for_impl(&self_ty.value);
66 .or_default() 124 impls
67 .entry(self_ty_fp) 125 .map
68 .or_default() 126 .entry(target_trait)
69 .push(impl_id); 127 .or_default()
70 } 128 .entry(self_ty_fp)
71 None => { 129 .or_default()
72 let self_ty = db.impl_self_ty(impl_id); 130 .push(impl_id);
73 if let Some(self_ty_fp) = TyFingerprint::for_impl(&self_ty.value) {
74 res.impls.entry(self_ty_fp).or_default().push(impl_id);
75 }
76 }
77 }
78 } 131 }
79 } 132 }
80 133
134 Arc::new(impls)
135 }
136
137 pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
138 let _p = profile("trait_impls_in_deps_query");
139 let crate_graph = db.crate_graph();
140 let mut res = Self { map: FxHashMap::default() };
141
142 for krate in crate_graph.transitive_deps(krate) {
143 res.merge(&db.trait_impls_in_crate(krate));
144 }
145
81 Arc::new(res) 146 Arc::new(res)
82 } 147 }
83 pub fn lookup_impl_defs(&self, ty: &Ty) -> impl Iterator<Item = ImplId> + '_ { 148
84 let fingerprint = TyFingerprint::for_impl(ty); 149 fn merge(&mut self, other: &Self) {
85 fingerprint.and_then(|f| self.impls.get(&f)).into_iter().flatten().copied() 150 for (trait_, other_map) in &other.map {
151 let map = self.map.entry(*trait_).or_default();
152 for (fp, impls) in other_map {
153 let vec = map.entry(*fp).or_default();
154 vec.extend(impls);
155 }
156 }
86 } 157 }
87 158
88 pub fn lookup_impl_defs_for_trait(&self, tr: TraitId) -> impl Iterator<Item = ImplId> + '_ { 159 /// Queries all impls of the given trait.
89 self.impls_by_trait 160 pub fn for_trait(&self, trait_: TraitId) -> impl Iterator<Item = ImplId> + '_ {
90 .get(&tr) 161 self.map
162 .get(&trait_)
91 .into_iter() 163 .into_iter()
92 .flat_map(|m| m.values().flat_map(|v| v.iter().copied())) 164 .flat_map(|map| map.values().flat_map(|v| v.iter().copied()))
93 } 165 }
94 166
95 pub fn lookup_impl_defs_for_trait_and_ty( 167 /// Queries all impls of `trait_` that may apply to `self_ty`.
168 pub fn for_trait_and_self_ty(
96 &self, 169 &self,
97 tr: TraitId, 170 trait_: TraitId,
98 fp: TyFingerprint, 171 self_ty: TyFingerprint,
99 ) -> impl Iterator<Item = ImplId> + '_ { 172 ) -> impl Iterator<Item = ImplId> + '_ {
100 self.impls_by_trait 173 self.map
101 .get(&tr) 174 .get(&trait_)
102 .and_then(|m| m.get(&Some(fp)))
103 .into_iter() 175 .into_iter()
104 .flatten() 176 .flat_map(move |map| map.get(&None).into_iter().chain(map.get(&Some(self_ty))))
105 .copied() 177 .flat_map(|v| v.iter().copied())
106 .chain(
107 self.impls_by_trait
108 .get(&tr)
109 .and_then(|m| m.get(&None))
110 .into_iter()
111 .flatten()
112 .copied(),
113 )
114 } 178 }
115 179
116 pub fn all_impls<'a>(&'a self) -> impl Iterator<Item = ImplId> + 'a { 180 pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
117 self.impls 181 self.map.values().flat_map(|map| map.values().flat_map(|v| v.iter().copied()))
118 .values() 182 }
119 .chain(self.impls_by_trait.values().flat_map(|m| m.values())) 183}
120 .flatten() 184
121 .copied() 185/// Inherent impls defined in some crate.
186///
187/// Inherent impls can only be defined in the crate that also defines the self type of the impl
188/// (note that some primitives are considered to be defined by both libcore and liballoc).
189///
190/// This makes inherent impl lookup easier than trait impl lookup since we only have to consider a
191/// single crate.
192#[derive(Debug, Eq, PartialEq)]
193pub struct InherentImpls {
194 map: FxHashMap<TyFingerprint, Vec<ImplId>>,
195}
196
197impl InherentImpls {
198 pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
199 let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default();
200
201 let crate_def_map = db.crate_def_map(krate);
202 for (_module_id, module_data) in crate_def_map.modules.iter() {
203 for impl_id in module_data.scope.impls() {
204 let data = db.impl_data(impl_id);
205 if data.target_trait.is_some() {
206 continue;
207 }
208
209 let self_ty = db.impl_self_ty(impl_id);
210 if let Some(fp) = TyFingerprint::for_impl(&self_ty.value) {
211 map.entry(fp).or_default().push(impl_id);
212 }
213 }
214 }
215
216 Arc::new(Self { map })
217 }
218
219 pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] {
220 match TyFingerprint::for_impl(self_ty) {
221 Some(fp) => self.map.get(&fp).map(|vec| vec.as_ref()).unwrap_or(&[]),
222 None => &[],
223 }
224 }
225
226 pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
227 self.map.values().flat_map(|v| v.iter().copied())
122 } 228 }
123} 229}
124 230
@@ -147,12 +253,12 @@ impl Ty {
147 } 253 }
148 TypeCtor::Bool => lang_item_crate!("bool"), 254 TypeCtor::Bool => lang_item_crate!("bool"),
149 TypeCtor::Char => lang_item_crate!("char"), 255 TypeCtor::Char => lang_item_crate!("char"),
150 TypeCtor::Float(Uncertain::Known(f)) => match f.bitness { 256 TypeCtor::Float(f) => match f.bitness {
151 // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime) 257 // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime)
152 FloatBitness::X32 => lang_item_crate!("f32", "f32_runtime"), 258 FloatBitness::X32 => lang_item_crate!("f32", "f32_runtime"),
153 FloatBitness::X64 => lang_item_crate!("f64", "f64_runtime"), 259 FloatBitness::X64 => lang_item_crate!("f64", "f64_runtime"),
154 }, 260 },
155 TypeCtor::Int(Uncertain::Known(i)) => lang_item_crate!(i.ty_to_string()), 261 TypeCtor::Int(i) => lang_item_crate!(i.ty_to_string()),
156 TypeCtor::Str => lang_item_crate!("str_alloc", "str"), 262 TypeCtor::Str => lang_item_crate!("str_alloc", "str"),
157 TypeCtor::Slice => lang_item_crate!("slice_alloc", "slice"), 263 TypeCtor::Slice => lang_item_crate!("slice_alloc", "slice"),
158 TypeCtor::RawPtr(Mutability::Shared) => lang_item_crate!("const_ptr"), 264 TypeCtor::RawPtr(Mutability::Shared) => lang_item_crate!("const_ptr"),
@@ -222,6 +328,34 @@ pub fn iterate_method_candidates<T>(
222 mode: LookupMode, 328 mode: LookupMode,
223 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 329 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
224) -> Option<T> { 330) -> Option<T> {
331 let mut slot = None;
332 iterate_method_candidates_impl(
333 ty,
334 db,
335 env,
336 krate,
337 traits_in_scope,
338 name,
339 mode,
340 &mut |ty, item| {
341 assert!(slot.is_none());
342 slot = callback(ty, item);
343 slot.is_some()
344 },
345 );
346 slot
347}
348
349fn iterate_method_candidates_impl(
350 ty: &Canonical<Ty>,
351 db: &dyn HirDatabase,
352 env: Arc<TraitEnvironment>,
353 krate: CrateId,
354 traits_in_scope: &FxHashSet<TraitId>,
355 name: Option<&Name>,
356 mode: LookupMode,
357 callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
358) -> bool {
225 match mode { 359 match mode {
226 LookupMode::MethodCall => { 360 LookupMode::MethodCall => {
227 // For method calls, rust first does any number of autoderef, and then one 361 // For method calls, rust first does any number of autoderef, and then one
@@ -249,19 +383,19 @@ pub fn iterate_method_candidates<T>(
249 383
250 let deref_chain = autoderef_method_receiver(db, krate, ty); 384 let deref_chain = autoderef_method_receiver(db, krate, ty);
251 for i in 0..deref_chain.len() { 385 for i in 0..deref_chain.len() {
252 if let Some(result) = iterate_method_candidates_with_autoref( 386 if iterate_method_candidates_with_autoref(
253 &deref_chain[i..], 387 &deref_chain[i..],
254 db, 388 db,
255 env.clone(), 389 env.clone(),
256 krate, 390 krate,
257 traits_in_scope, 391 traits_in_scope,
258 name, 392 name,
259 &mut callback, 393 callback,
260 ) { 394 ) {
261 return Some(result); 395 return true;
262 } 396 }
263 } 397 }
264 None 398 false
265 } 399 }
266 LookupMode::Path => { 400 LookupMode::Path => {
267 // No autoderef for path lookups 401 // No autoderef for path lookups
@@ -272,22 +406,22 @@ pub fn iterate_method_candidates<T>(
272 krate, 406 krate,
273 traits_in_scope, 407 traits_in_scope,
274 name, 408 name,
275 &mut callback, 409 callback,
276 ) 410 )
277 } 411 }
278 } 412 }
279} 413}
280 414
281fn iterate_method_candidates_with_autoref<T>( 415fn iterate_method_candidates_with_autoref(
282 deref_chain: &[Canonical<Ty>], 416 deref_chain: &[Canonical<Ty>],
283 db: &dyn HirDatabase, 417 db: &dyn HirDatabase,
284 env: Arc<TraitEnvironment>, 418 env: Arc<TraitEnvironment>,
285 krate: CrateId, 419 krate: CrateId,
286 traits_in_scope: &FxHashSet<TraitId>, 420 traits_in_scope: &FxHashSet<TraitId>,
287 name: Option<&Name>, 421 name: Option<&Name>,
288 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 422 mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
289) -> Option<T> { 423) -> bool {
290 if let Some(result) = iterate_method_candidates_by_receiver( 424 if iterate_method_candidates_by_receiver(
291 &deref_chain[0], 425 &deref_chain[0],
292 &deref_chain[1..], 426 &deref_chain[1..],
293 db, 427 db,
@@ -297,13 +431,13 @@ fn iterate_method_candidates_with_autoref<T>(
297 name, 431 name,
298 &mut callback, 432 &mut callback,
299 ) { 433 ) {
300 return Some(result); 434 return true;
301 } 435 }
302 let refed = Canonical { 436 let refed = Canonical {
303 num_vars: deref_chain[0].num_vars, 437 kinds: deref_chain[0].kinds.clone(),
304 value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()), 438 value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()),
305 }; 439 };
306 if let Some(result) = iterate_method_candidates_by_receiver( 440 if iterate_method_candidates_by_receiver(
307 &refed, 441 &refed,
308 deref_chain, 442 deref_chain,
309 db, 443 db,
@@ -313,13 +447,13 @@ fn iterate_method_candidates_with_autoref<T>(
313 name, 447 name,
314 &mut callback, 448 &mut callback,
315 ) { 449 ) {
316 return Some(result); 450 return true;
317 } 451 }
318 let ref_muted = Canonical { 452 let ref_muted = Canonical {
319 num_vars: deref_chain[0].num_vars, 453 kinds: deref_chain[0].kinds.clone(),
320 value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()), 454 value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()),
321 }; 455 };
322 if let Some(result) = iterate_method_candidates_by_receiver( 456 if iterate_method_candidates_by_receiver(
323 &ref_muted, 457 &ref_muted,
324 deref_chain, 458 deref_chain,
325 db, 459 db,
@@ -329,12 +463,12 @@ fn iterate_method_candidates_with_autoref<T>(
329 name, 463 name,
330 &mut callback, 464 &mut callback,
331 ) { 465 ) {
332 return Some(result); 466 return true;
333 } 467 }
334 None 468 false
335} 469}
336 470
337fn iterate_method_candidates_by_receiver<T>( 471fn iterate_method_candidates_by_receiver(
338 receiver_ty: &Canonical<Ty>, 472 receiver_ty: &Canonical<Ty>,
339 rest_of_deref_chain: &[Canonical<Ty>], 473 rest_of_deref_chain: &[Canonical<Ty>],
340 db: &dyn HirDatabase, 474 db: &dyn HirDatabase,
@@ -342,20 +476,18 @@ fn iterate_method_candidates_by_receiver<T>(
342 krate: CrateId, 476 krate: CrateId,
343 traits_in_scope: &FxHashSet<TraitId>, 477 traits_in_scope: &FxHashSet<TraitId>,
344 name: Option<&Name>, 478 name: Option<&Name>,
345 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 479 mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
346) -> Option<T> { 480) -> bool {
347 // We're looking for methods with *receiver* type receiver_ty. These could 481 // We're looking for methods with *receiver* type receiver_ty. These could
348 // be found in any of the derefs of receiver_ty, so we have to go through 482 // be found in any of the derefs of receiver_ty, so we have to go through
349 // that. 483 // that.
350 for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) { 484 for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) {
351 if let Some(result) = 485 if iterate_inherent_methods(self_ty, db, name, Some(receiver_ty), krate, &mut callback) {
352 iterate_inherent_methods(self_ty, db, name, Some(receiver_ty), krate, &mut callback) 486 return true;
353 {
354 return Some(result);
355 } 487 }
356 } 488 }
357 for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) { 489 for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) {
358 if let Some(result) = iterate_trait_method_candidates( 490 if iterate_trait_method_candidates(
359 self_ty, 491 self_ty,
360 db, 492 db,
361 env.clone(), 493 env.clone(),
@@ -365,40 +497,28 @@ fn iterate_method_candidates_by_receiver<T>(
365 Some(receiver_ty), 497 Some(receiver_ty),
366 &mut callback, 498 &mut callback,
367 ) { 499 ) {
368 return Some(result); 500 return true;
369 } 501 }
370 } 502 }
371 None 503 false
372} 504}
373 505
374fn iterate_method_candidates_for_self_ty<T>( 506fn iterate_method_candidates_for_self_ty(
375 self_ty: &Canonical<Ty>, 507 self_ty: &Canonical<Ty>,
376 db: &dyn HirDatabase, 508 db: &dyn HirDatabase,
377 env: Arc<TraitEnvironment>, 509 env: Arc<TraitEnvironment>,
378 krate: CrateId, 510 krate: CrateId,
379 traits_in_scope: &FxHashSet<TraitId>, 511 traits_in_scope: &FxHashSet<TraitId>,
380 name: Option<&Name>, 512 name: Option<&Name>,
381 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 513 mut callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
382) -> Option<T> { 514) -> bool {
383 if let Some(result) = iterate_inherent_methods(self_ty, db, name, None, krate, &mut callback) { 515 if iterate_inherent_methods(self_ty, db, name, None, krate, &mut callback) {
384 return Some(result); 516 return true;
385 }
386 if let Some(result) = iterate_trait_method_candidates(
387 self_ty,
388 db,
389 env,
390 krate,
391 traits_in_scope,
392 name,
393 None,
394 &mut callback,
395 ) {
396 return Some(result);
397 } 517 }
398 None 518 iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback)
399} 519}
400 520
401fn iterate_trait_method_candidates<T>( 521fn iterate_trait_method_candidates(
402 self_ty: &Canonical<Ty>, 522 self_ty: &Canonical<Ty>,
403 db: &dyn HirDatabase, 523 db: &dyn HirDatabase,
404 env: Arc<TraitEnvironment>, 524 env: Arc<TraitEnvironment>,
@@ -406,8 +526,8 @@ fn iterate_trait_method_candidates<T>(
406 traits_in_scope: &FxHashSet<TraitId>, 526 traits_in_scope: &FxHashSet<TraitId>,
407 name: Option<&Name>, 527 name: Option<&Name>,
408 receiver_ty: Option<&Canonical<Ty>>, 528 receiver_ty: Option<&Canonical<Ty>>,
409 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 529 callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
410) -> Option<T> { 530) -> bool {
411 // if ty is `dyn Trait`, the trait doesn't need to be in scope 531 // if ty is `dyn Trait`, the trait doesn't need to be in scope
412 let inherent_trait = 532 let inherent_trait =
413 self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); 533 self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
@@ -440,26 +560,30 @@ fn iterate_trait_method_candidates<T>(
440 } 560 }
441 } 561 }
442 known_implemented = true; 562 known_implemented = true;
443 if let Some(result) = callback(&self_ty.value, *item) { 563 if callback(&self_ty.value, *item) {
444 return Some(result); 564 return true;
445 } 565 }
446 } 566 }
447 } 567 }
448 None 568 false
449} 569}
450 570
451fn iterate_inherent_methods<T>( 571fn iterate_inherent_methods(
452 self_ty: &Canonical<Ty>, 572 self_ty: &Canonical<Ty>,
453 db: &dyn HirDatabase, 573 db: &dyn HirDatabase,
454 name: Option<&Name>, 574 name: Option<&Name>,
455 receiver_ty: Option<&Canonical<Ty>>, 575 receiver_ty: Option<&Canonical<Ty>>,
456 krate: CrateId, 576 krate: CrateId,
457 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, 577 callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
458) -> Option<T> { 578) -> bool {
459 for krate in self_ty.value.def_crates(db, krate)? { 579 let def_crates = match self_ty.value.def_crates(db, krate) {
460 let impls = db.impls_in_crate(krate); 580 Some(k) => k,
581 None => return false,
582 };
583 for krate in def_crates {
584 let impls = db.inherent_impls_in_crate(krate);
461 585
462 for impl_def in impls.lookup_impl_defs(&self_ty.value) { 586 for &impl_def in impls.for_self_ty(&self_ty.value) {
463 for &item in db.impl_data(impl_def).items.iter() { 587 for &item in db.impl_data(impl_def).items.iter() {
464 if !is_valid_candidate(db, name, receiver_ty, item, self_ty) { 588 if !is_valid_candidate(db, name, receiver_ty, item, self_ty) {
465 continue; 589 continue;
@@ -472,13 +596,13 @@ fn iterate_inherent_methods<T>(
472 test_utils::mark::hit!(impl_self_type_match_without_receiver); 596 test_utils::mark::hit!(impl_self_type_match_without_receiver);
473 continue; 597 continue;
474 } 598 }
475 if let Some(result) = callback(&self_ty.value, item) { 599 if callback(&self_ty.value, item) {
476 return Some(result); 600 return true;
477 } 601 }
478 } 602 }
479 } 603 }
480 } 604 }
481 None 605 false
482} 606}
483 607
484/// Returns the self type for the index trait call. 608/// Returns the self type for the index trait call.
@@ -545,18 +669,19 @@ pub(crate) fn inherent_impl_substs(
545 // we create a var for each type parameter of the impl; we need to keep in 669 // we create a var for each type parameter of the impl; we need to keep in
546 // mind here that `self_ty` might have vars of its own 670 // mind here that `self_ty` might have vars of its own
547 let vars = Substs::build_for_def(db, impl_id) 671 let vars = Substs::build_for_def(db, impl_id)
548 .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.num_vars) 672 .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty.kinds.len())
549 .build(); 673 .build();
550 let self_ty_with_vars = db.impl_self_ty(impl_id).subst(&vars); 674 let self_ty_with_vars = db.impl_self_ty(impl_id).subst(&vars);
551 let self_ty_with_vars = 675 let mut kinds = self_ty.kinds.to_vec();
552 Canonical { num_vars: vars.len() + self_ty.num_vars, value: self_ty_with_vars }; 676 kinds.extend(iter::repeat(TyKind::General).take(vars.len()));
553 let substs = super::infer::unify(&self_ty_with_vars, self_ty); 677 let tys = Canonical { kinds: kinds.into(), value: (self_ty_with_vars, self_ty.value.clone()) };
678 let substs = super::infer::unify(&tys);
554 // We only want the substs for the vars we added, not the ones from self_ty. 679 // We only want the substs for the vars we added, not the ones from self_ty.
555 // Also, if any of the vars we added are still in there, we replace them by 680 // Also, if any of the vars we added are still in there, we replace them by
556 // Unknown. I think this can only really happen if self_ty contained 681 // Unknown. I think this can only really happen if self_ty contained
557 // Unknown, and in that case we want the result to contain Unknown in those 682 // Unknown, and in that case we want the result to contain Unknown in those
558 // places again. 683 // places again.
559 substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.num_vars)) 684 substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.kinds.len()))
560} 685}
561 686
562/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past 687/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
@@ -616,15 +741,15 @@ fn generic_implements_goal(
616 trait_: TraitId, 741 trait_: TraitId,
617 self_ty: Canonical<Ty>, 742 self_ty: Canonical<Ty>,
618) -> Canonical<InEnvironment<super::Obligation>> { 743) -> Canonical<InEnvironment<super::Obligation>> {
619 let num_vars = self_ty.num_vars; 744 let mut kinds = self_ty.kinds.to_vec();
620 let substs = super::Substs::build_for_def(db, trait_) 745 let substs = super::Substs::build_for_def(db, trait_)
621 .push(self_ty.value) 746 .push(self_ty.value)
622 .fill_with_bound_vars(DebruijnIndex::INNERMOST, num_vars) 747 .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len())
623 .build(); 748 .build();
624 let num_vars = substs.len() - 1 + self_ty.num_vars; 749 kinds.extend(iter::repeat(TyKind::General).take(substs.len() - 1));
625 let trait_ref = TraitRef { trait_, substs }; 750 let trait_ref = TraitRef { trait_, substs };
626 let obligation = super::Obligation::Trait(trait_ref); 751 let obligation = super::Obligation::Trait(trait_ref);
627 Canonical { num_vars, value: InEnvironment::new(env, obligation) } 752 Canonical { kinds: kinds.into(), value: InEnvironment::new(env, obligation) }
628} 753}
629 754
630fn autoderef_method_receiver( 755fn autoderef_method_receiver(
@@ -637,9 +762,9 @@ fn autoderef_method_receiver(
637 if let Some(Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, parameters })) = 762 if let Some(Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, parameters })) =
638 deref_chain.last().map(|ty| &ty.value) 763 deref_chain.last().map(|ty| &ty.value)
639 { 764 {
640 let num_vars = deref_chain.last().unwrap().num_vars; 765 let kinds = deref_chain.last().unwrap().kinds.clone();
641 let unsized_ty = Ty::apply(TypeCtor::Slice, parameters.clone()); 766 let unsized_ty = Ty::apply(TypeCtor::Slice, parameters.clone());
642 deref_chain.push(Canonical { value: unsized_ty, num_vars }) 767 deref_chain.push(Canonical { value: unsized_ty, kinds })
643 } 768 }
644 deref_chain 769 deref_chain
645} 770}
diff --git a/crates/ra_hir_ty/src/primitive.rs b/crates/ra_hir_ty/src/primitive.rs
index 02a8179d9..37966b709 100644
--- a/crates/ra_hir_ty/src/primitive.rs
+++ b/crates/ra_hir_ty/src/primitive.rs
@@ -7,42 +7,6 @@ use std::fmt;
7 7
8pub use hir_def::builtin_type::{BuiltinFloat, BuiltinInt, FloatBitness, IntBitness, Signedness}; 8pub use hir_def::builtin_type::{BuiltinFloat, BuiltinInt, FloatBitness, IntBitness, Signedness};
9 9
10#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
11pub enum Uncertain<T> {
12 Unknown,
13 Known(T),
14}
15
16impl From<IntTy> for Uncertain<IntTy> {
17 fn from(ty: IntTy) -> Self {
18 Uncertain::Known(ty)
19 }
20}
21
22impl fmt::Display for Uncertain<IntTy> {
23 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24 match *self {
25 Uncertain::Unknown => write!(f, "{{integer}}"),
26 Uncertain::Known(ty) => write!(f, "{}", ty),
27 }
28 }
29}
30
31impl From<FloatTy> for Uncertain<FloatTy> {
32 fn from(ty: FloatTy) -> Self {
33 Uncertain::Known(ty)
34 }
35}
36
37impl fmt::Display for Uncertain<FloatTy> {
38 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39 match *self {
40 Uncertain::Unknown => write!(f, "{{float}}"),
41 Uncertain::Known(ty) => write!(f, "{}", ty),
42 }
43 }
44}
45
46#[derive(Copy, Clone, Eq, PartialEq, Hash)] 10#[derive(Copy, Clone, Eq, PartialEq, Hash)]
47pub struct IntTy { 11pub struct IntTy {
48 pub signedness: Signedness, 12 pub signedness: Signedness,
@@ -173,21 +137,3 @@ impl From<BuiltinFloat> for FloatTy {
173 FloatTy { bitness: t.bitness } 137 FloatTy { bitness: t.bitness }
174 } 138 }
175} 139}
176
177impl From<Option<BuiltinInt>> for Uncertain<IntTy> {
178 fn from(t: Option<BuiltinInt>) -> Self {
179 match t {
180 None => Uncertain::Unknown,
181 Some(t) => Uncertain::Known(t.into()),
182 }
183 }
184}
185
186impl From<Option<BuiltinFloat>> for Uncertain<FloatTy> {
187 fn from(t: Option<BuiltinFloat>) -> Self {
188 match t {
189 None => Uncertain::Unknown,
190 Some(t) => Uncertain::Known(t.into()),
191 }
192 }
193}
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs
index 8498d3d96..a1714ff0f 100644
--- a/crates/ra_hir_ty/src/test_db.rs
+++ b/crates/ra_hir_ty/src/test_db.rs
@@ -1,18 +1,16 @@
1//! Database used for testing `hir`. 1//! Database used for testing `hir`.
2 2
3use std::{ 3use std::{
4 panic, 4 fmt, panic,
5 sync::{Arc, Mutex}, 5 sync::{Arc, Mutex},
6}; 6};
7 7
8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; 8use hir_def::{db::DefDatabase, ModuleId};
9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; 9use hir_expand::db::AstDatabase;
10use ra_db::{ 10use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast};
11 salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, SourceDatabase, Upcast, 11use ra_syntax::TextRange;
12}; 12use rustc_hash::{FxHashMap, FxHashSet};
13use stdx::format_to; 13use test_utils::extract_annotations;
14
15use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator};
16 14
17#[salsa::database( 15#[salsa::database(
18 ra_db::SourceDatabaseExtStorage, 16 ra_db::SourceDatabaseExtStorage,
@@ -22,10 +20,15 @@ use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator};
22 hir_def::db::DefDatabaseStorage, 20 hir_def::db::DefDatabaseStorage,
23 crate::db::HirDatabaseStorage 21 crate::db::HirDatabaseStorage
24)] 22)]
25#[derive(Debug, Default)] 23#[derive(Default)]
26pub struct TestDB { 24pub struct TestDB {
27 events: Mutex<Option<Vec<salsa::Event<TestDB>>>>, 25 storage: salsa::Storage<TestDB>,
28 runtime: salsa::Runtime<TestDB>, 26 events: Mutex<Option<Vec<salsa::Event>>>,
27}
28impl fmt::Debug for TestDB {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 f.debug_struct("TestDB").finish()
31 }
29} 32}
30 33
31impl Upcast<dyn AstDatabase> for TestDB { 34impl Upcast<dyn AstDatabase> for TestDB {
@@ -41,18 +44,10 @@ impl Upcast<dyn DefDatabase> for TestDB {
41} 44}
42 45
43impl salsa::Database for TestDB { 46impl salsa::Database for TestDB {
44 fn salsa_runtime(&self) -> &salsa::Runtime<TestDB> { 47 fn salsa_event(&self, event: salsa::Event) {
45 &self.runtime
46 }
47
48 fn salsa_runtime_mut(&mut self) -> &mut salsa::Runtime<Self> {
49 &mut self.runtime
50 }
51
52 fn salsa_event(&self, event: impl Fn() -> salsa::Event<TestDB>) {
53 let mut events = self.events.lock().unwrap(); 48 let mut events = self.events.lock().unwrap();
54 if let Some(events) = &mut *events { 49 if let Some(events) = &mut *events {
55 events.push(event()); 50 events.push(event);
56 } 51 }
57 } 52 }
58} 53}
@@ -60,8 +55,8 @@ impl salsa::Database for TestDB {
60impl salsa::ParallelDatabase for TestDB { 55impl salsa::ParallelDatabase for TestDB {
61 fn snapshot(&self) -> salsa::Snapshot<TestDB> { 56 fn snapshot(&self) -> salsa::Snapshot<TestDB> {
62 salsa::Snapshot::new(TestDB { 57 salsa::Snapshot::new(TestDB {
58 storage: self.storage.snapshot(),
63 events: Default::default(), 59 events: Default::default(),
64 runtime: self.runtime.snapshot(self),
65 }) 60 })
66 } 61 }
67} 62}
@@ -72,27 +67,16 @@ impl FileLoader for TestDB {
72 fn file_text(&self, file_id: FileId) -> Arc<String> { 67 fn file_text(&self, file_id: FileId) -> Arc<String> {
73 FileLoaderDelegate(self).file_text(file_id) 68 FileLoaderDelegate(self).file_text(file_id)
74 } 69 }
75 fn resolve_relative_path( 70 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
76 &self, 71 FileLoaderDelegate(self).resolve_path(anchor, path)
77 anchor: FileId,
78 relative_path: &RelativePath,
79 ) -> Option<FileId> {
80 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
81 } 72 }
82 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 73 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
83 FileLoaderDelegate(self).relevant_crates(file_id) 74 FileLoaderDelegate(self).relevant_crates(file_id)
84 } 75 }
85 fn resolve_extern_path(
86 &self,
87 extern_id: ra_db::ExternSourceId,
88 relative_path: &RelativePath,
89 ) -> Option<FileId> {
90 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
91 }
92} 76}
93 77
94impl TestDB { 78impl TestDB {
95 pub fn module_for_file(&self, file_id: FileId) -> ModuleId { 79 pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId {
96 for &krate in self.relevant_crates(file_id).iter() { 80 for &krate in self.relevant_crates(file_id).iter() {
97 let crate_def_map = self.crate_def_map(krate); 81 let crate_def_map = self.crate_def_map(krate);
98 for (local_id, data) in crate_def_map.modules.iter() { 82 for (local_id, data) in crate_def_map.modules.iter() {
@@ -104,67 +88,32 @@ impl TestDB {
104 panic!("Can't find module for file") 88 panic!("Can't find module for file")
105 } 89 }
106 90
107 fn diag<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { 91 pub(crate) fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
92 let mut files = Vec::new();
108 let crate_graph = self.crate_graph(); 93 let crate_graph = self.crate_graph();
109 for krate in crate_graph.iter() { 94 for krate in crate_graph.iter() {
110 let crate_def_map = self.crate_def_map(krate); 95 let crate_def_map = self.crate_def_map(krate);
111
112 let mut fns = Vec::new();
113 for (module_id, _) in crate_def_map.modules.iter() { 96 for (module_id, _) in crate_def_map.modules.iter() {
114 for decl in crate_def_map[module_id].scope.declarations() { 97 let file_id = crate_def_map[module_id].origin.file_id();
115 if let ModuleDefId::FunctionId(f) = decl { 98 files.extend(file_id)
116 fns.push(f)
117 }
118 }
119
120 for impl_id in crate_def_map[module_id].scope.impls() {
121 let impl_data = self.impl_data(impl_id);
122 for item in impl_data.items.iter() {
123 if let AssocItemId::FunctionId(f) = item {
124 fns.push(*f)
125 }
126 }
127 }
128 }
129
130 for f in fns {
131 let infer = self.infer(f.into());
132 let mut sink = DiagnosticSink::new(&mut cb);
133 infer.add_diagnostics(self, f, &mut sink);
134 let mut validator = ExprValidator::new(f, infer, &mut sink);
135 validator.validate_body(self);
136 } 99 }
137 } 100 }
138 } 101 files
139 102 .into_iter()
140 pub fn diagnostics(&self) -> (String, u32) { 103 .filter_map(|file_id| {
141 let mut buf = String::new(); 104 let text = self.file_text(file_id);
142 let mut count = 0; 105 let annotations = extract_annotations(&text);
143 self.diag(|d| { 106 if annotations.is_empty() {
144 format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); 107 return None;
145 count += 1; 108 }
146 }); 109 Some((file_id, annotations))
147 (buf, count) 110 })
148 } 111 .collect()
149
150 /// Like `diagnostics`, but filtered for a single diagnostic.
151 pub fn diagnostic<D: Diagnostic>(&self) -> (String, u32) {
152 let mut buf = String::new();
153 let mut count = 0;
154 self.diag(|d| {
155 // We want to filter diagnostics by the particular one we are testing for, to
156 // avoid surprising results in tests.
157 if d.downcast_ref::<D>().is_some() {
158 format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message());
159 count += 1;
160 };
161 });
162 (buf, count)
163 } 112 }
164} 113}
165 114
166impl TestDB { 115impl TestDB {
167 pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<TestDB>> { 116 pub fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event> {
168 *self.events.lock().unwrap() = Some(Vec::new()); 117 *self.events.lock().unwrap() = Some(Vec::new());
169 f(); 118 f();
170 self.events.lock().unwrap().take().unwrap() 119 self.events.lock().unwrap().take().unwrap()
@@ -178,7 +127,7 @@ impl TestDB {
178 // This pretty horrible, but `Debug` is the only way to inspect 127 // This pretty horrible, but `Debug` is the only way to inspect
179 // QueryDescriptor at the moment. 128 // QueryDescriptor at the moment.
180 salsa::EventKind::WillExecute { database_key } => { 129 salsa::EventKind::WillExecute { database_key } => {
181 Some(format!("{:?}", database_key)) 130 Some(format!("{:?}", database_key.debug(self)))
182 } 131 }
183 _ => None, 132 _ => None,
184 }) 133 })
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 1fe05c70c..c972bf845 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -17,11 +17,10 @@ use hir_def::{
17 item_scope::ItemScope, 17 item_scope::ItemScope,
18 keys, 18 keys,
19 nameres::CrateDefMap, 19 nameres::CrateDefMap,
20 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, ModuleId, 20 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
21}; 21};
22use hir_expand::{db::AstDatabase, InFile}; 22use hir_expand::{db::AstDatabase, InFile};
23use insta::assert_snapshot; 23use ra_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt};
24use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase};
25use ra_syntax::{ 24use ra_syntax::{
26 algo, 25 algo,
27 ast::{self, AstNode}, 26 ast::{self, AstNode},
@@ -37,21 +36,50 @@ use crate::{
37// against snapshots of the expected results using insta. Use cargo-insta to 36// against snapshots of the expected results using insta. Use cargo-insta to
38// update the snapshots. 37// update the snapshots.
39 38
40fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { 39fn setup_tracing() -> tracing::subscriber::DefaultGuard {
41 type_at_pos_displayed(db, pos, |ty, _| ty.display(db).to_string()) 40 use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
41 use tracing_tree::HierarchicalLayer;
42 let filter = EnvFilter::from_env("CHALK_DEBUG");
43 let layer = HierarchicalLayer::default()
44 .with_indent_lines(true)
45 .with_ansi(false)
46 .with_indent_amount(2)
47 .with_writer(std::io::stderr);
48 let subscriber = Registry::default().with(filter).with(layer);
49 tracing::subscriber::set_default(subscriber)
42} 50}
43 51
44fn displayed_source_at_pos(db: &TestDB, pos: FilePosition) -> String { 52fn check_types(ra_fixture: &str) {
45 type_at_pos_displayed(db, pos, |ty, module_id| ty.display_source_code(db, module_id).unwrap()) 53 check_types_impl(ra_fixture, false)
46} 54}
47 55
48fn type_at_pos_displayed( 56fn check_types_source_code(ra_fixture: &str) {
49 db: &TestDB, 57 check_types_impl(ra_fixture, true)
50 pos: FilePosition, 58}
51 display_fn: impl FnOnce(&Ty, ModuleId) -> String, 59
52) -> String { 60fn check_types_impl(ra_fixture: &str, display_source: bool) {
61 let _tracing = setup_tracing();
62 let db = TestDB::with_files(ra_fixture);
63 let mut checked_one = false;
64 for (file_id, annotations) in db.extract_annotations() {
65 for (range, expected) in annotations {
66 let ty = type_at_range(&db, FileRange { file_id, range });
67 let actual = if display_source {
68 let module = db.module_for_file(file_id);
69 ty.display_source_code(&db, module).unwrap()
70 } else {
71 ty.display(&db).to_string()
72 };
73 assert_eq!(expected, actual);
74 checked_one = true;
75 }
76 }
77 assert!(checked_one, "no `//^` annotations found");
78}
79
80fn type_at_range(db: &TestDB, pos: FileRange) -> Ty {
53 let file = db.parse(pos.file_id).ok().unwrap(); 81 let file = db.parse(pos.file_id).ok().unwrap();
54 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); 82 let expr = algo::find_node_at_range::<ast::Expr>(file.syntax(), pos.range).unwrap();
55 let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); 83 let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
56 let module = db.module_for_file(pos.file_id); 84 let module = db.module_for_file(pos.file_id);
57 let func = *module.child_by_source(db)[keys::FUNCTION] 85 let func = *module.child_by_source(db)[keys::FUNCTION]
@@ -61,22 +89,17 @@ fn type_at_pos_displayed(
61 let (_body, source_map) = db.body_with_source_map(func.into()); 89 let (_body, source_map) = db.body_with_source_map(func.into());
62 if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { 90 if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) {
63 let infer = db.infer(func.into()); 91 let infer = db.infer(func.into());
64 let ty = &infer[expr_id]; 92 return infer[expr_id].clone();
65 return display_fn(ty, module);
66 } 93 }
67 panic!("Can't find expression") 94 panic!("Can't find expression")
68} 95}
69 96
70fn type_at(content: &str) -> String {
71 let (db, file_pos) = TestDB::with_position(content);
72 type_at_pos(&db, file_pos)
73}
74
75fn infer(ra_fixture: &str) -> String { 97fn infer(ra_fixture: &str) -> String {
76 infer_with_mismatches(ra_fixture, false) 98 infer_with_mismatches(ra_fixture, false)
77} 99}
78 100
79fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { 101fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
102 let _tracing = setup_tracing();
80 let (db, file_id) = TestDB::with_single_file(content); 103 let (db, file_id) = TestDB::with_single_file(content);
81 104
82 let mut buf = String::new(); 105 let mut buf = String::new();
@@ -164,13 +187,19 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
164 visit_module(&db, &crate_def_map, module.local_id, &mut |it| defs.push(it)); 187 visit_module(&db, &crate_def_map, module.local_id, &mut |it| defs.push(it));
165 defs.sort_by_key(|def| match def { 188 defs.sort_by_key(|def| match def {
166 DefWithBodyId::FunctionId(it) => { 189 DefWithBodyId::FunctionId(it) => {
167 it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start() 190 let loc = it.lookup(&db);
191 let tree = db.item_tree(loc.id.file_id);
192 tree.source(&db, loc.id).syntax().text_range().start()
168 } 193 }
169 DefWithBodyId::ConstId(it) => { 194 DefWithBodyId::ConstId(it) => {
170 it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start() 195 let loc = it.lookup(&db);
196 let tree = db.item_tree(loc.id.file_id);
197 tree.source(&db, loc.id).syntax().text_range().start()
171 } 198 }
172 DefWithBodyId::StaticId(it) => { 199 DefWithBodyId::StaticId(it) => {
173 it.lookup(&db).ast_id.to_node(&db).syntax().text_range().start() 200 let loc = it.lookup(&db);
201 let tree = db.item_tree(loc.id.file_id);
202 tree.source(&db, loc.id).syntax().text_range().start()
174 } 203 }
175 }); 204 });
176 for def in defs { 205 for def in defs {
@@ -302,7 +331,7 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
302 " 331 "
303 .to_string(); 332 .to_string();
304 333
305 db.query_mut(ra_db::FileTextQuery).set(pos.file_id, Arc::new(new_text)); 334 db.set_file_text(pos.file_id, Arc::new(new_text));
306 335
307 { 336 {
308 let events = db.log_executed(|| { 337 let events = db.log_executed(|| {
@@ -315,237 +344,3 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
315 assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) 344 assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
316 } 345 }
317} 346}
318
319#[test]
320fn no_such_field_diagnostics() {
321 let diagnostics = TestDB::with_files(
322 r"
323 //- /lib.rs
324 struct S { foo: i32, bar: () }
325 impl S {
326 fn new() -> S {
327 S {
328 foo: 92,
329 baz: 62,
330 }
331 }
332 }
333 ",
334 )
335 .diagnostics()
336 .0;
337
338 assert_snapshot!(diagnostics, @r###"
339 "baz: 62": no such field
340 "{\n foo: 92,\n baz: 62,\n }": Missing structure fields:
341 - bar
342 "###
343 );
344}
345
346#[test]
347fn no_such_field_with_feature_flag_diagnostics() {
348 let diagnostics = TestDB::with_files(
349 r#"
350 //- /lib.rs crate:foo cfg:feature=foo
351 struct MyStruct {
352 my_val: usize,
353 #[cfg(feature = "foo")]
354 bar: bool,
355 }
356
357 impl MyStruct {
358 #[cfg(feature = "foo")]
359 pub(crate) fn new(my_val: usize, bar: bool) -> Self {
360 Self { my_val, bar }
361 }
362
363 #[cfg(not(feature = "foo"))]
364 pub(crate) fn new(my_val: usize, _bar: bool) -> Self {
365 Self { my_val }
366 }
367 }
368 "#,
369 )
370 .diagnostics()
371 .0;
372
373 assert_snapshot!(diagnostics, @r###""###);
374}
375
376#[test]
377fn no_such_field_enum_with_feature_flag_diagnostics() {
378 let diagnostics = TestDB::with_files(
379 r#"
380 //- /lib.rs crate:foo cfg:feature=foo
381 enum Foo {
382 #[cfg(not(feature = "foo"))]
383 Buz,
384 #[cfg(feature = "foo")]
385 Bar,
386 Baz
387 }
388
389 fn test_fn(f: Foo) {
390 match f {
391 Foo::Bar => {},
392 Foo::Baz => {},
393 }
394 }
395 "#,
396 )
397 .diagnostics()
398 .0;
399
400 assert_snapshot!(diagnostics, @r###""###);
401}
402
403#[test]
404fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() {
405 let diagnostics = TestDB::with_files(
406 r#"
407 //- /lib.rs crate:foo cfg:feature=foo
408 struct S {
409 #[cfg(feature = "foo")]
410 foo: u32,
411 #[cfg(not(feature = "foo"))]
412 bar: u32,
413 }
414
415 impl S {
416 #[cfg(feature = "foo")]
417 fn new(foo: u32) -> Self {
418 Self { foo }
419 }
420 #[cfg(not(feature = "foo"))]
421 fn new(bar: u32) -> Self {
422 Self { bar }
423 }
424 }
425 "#,
426 )
427 .diagnostics()
428 .0;
429
430 assert_snapshot!(diagnostics, @r###""###);
431}
432
433#[test]
434fn no_such_field_with_feature_flag_diagnostics_on_block_expr() {
435 let diagnostics = TestDB::with_files(
436 r#"
437 //- /lib.rs crate:foo cfg:feature=foo
438 struct S {
439 #[cfg(feature = "foo")]
440 foo: u32,
441 #[cfg(not(feature = "foo"))]
442 bar: u32,
443 }
444
445 impl S {
446 fn new(bar: u32) -> Self {
447 #[cfg(feature = "foo")]
448 {
449 Self { foo: bar }
450 }
451 #[cfg(not(feature = "foo"))]
452 {
453 Self { bar }
454 }
455 }
456 }
457 "#,
458 )
459 .diagnostics()
460 .0;
461
462 assert_snapshot!(diagnostics, @r###""###);
463}
464
465#[test]
466fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() {
467 let diagnostics = TestDB::with_files(
468 r#"
469 //- /lib.rs crate:foo cfg:feature=foo
470 struct S {
471 #[cfg(feature = "foo")]
472 foo: u32,
473 #[cfg(not(feature = "foo"))]
474 bar: u32,
475 }
476
477 impl S {
478 fn new(val: u32) -> Self {
479 Self {
480 #[cfg(feature = "foo")]
481 foo: val,
482 #[cfg(not(feature = "foo"))]
483 bar: val,
484 }
485 }
486 }
487 "#,
488 )
489 .diagnostics()
490 .0;
491
492 assert_snapshot!(diagnostics, @r###""###);
493}
494
495#[test]
496fn missing_record_pat_field_diagnostic() {
497 let diagnostics = TestDB::with_files(
498 r"
499 //- /lib.rs
500 struct S { foo: i32, bar: () }
501 fn baz(s: S) {
502 let S { foo: _ } = s;
503 }
504 ",
505 )
506 .diagnostics()
507 .0;
508
509 assert_snapshot!(diagnostics, @r###"
510 "{ foo: _ }": Missing structure fields:
511 - bar
512 "###
513 );
514}
515
516#[test]
517fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
518 let diagnostics = TestDB::with_files(
519 r"
520 //- /lib.rs
521 struct S { foo: i32, bar: () }
522 fn baz(s: S) -> i32 {
523 match s {
524 S { foo, .. } => foo,
525 }
526 }
527 ",
528 )
529 .diagnostics()
530 .0;
531
532 assert_snapshot!(diagnostics, @"");
533}
534
535#[test]
536fn break_outside_of_loop() {
537 let diagnostics = TestDB::with_files(
538 r"
539 //- /lib.rs
540 fn foo() {
541 break;
542 }
543 ",
544 )
545 .diagnostics()
546 .0;
547
548 assert_snapshot!(diagnostics, @r###""break": break outside of loop
549 "###
550 );
551}
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs
index 6f777ed8c..d7fb6a962 100644
--- a/crates/ra_hir_ty/src/tests/coercion.rs
+++ b/crates/ra_hir_ty/src/tests/coercion.rs
@@ -1,7 +1,8 @@
1use super::infer_with_mismatches;
2use insta::assert_snapshot; 1use insta::assert_snapshot;
3use test_utils::mark; 2use test_utils::mark;
4 3
4use super::infer_with_mismatches;
5
5// Infer with some common definitions and impls. 6// Infer with some common definitions and impls.
6fn infer(source: &str) -> String { 7fn infer(source: &str) -> String {
7 let defs = r#" 8 let defs = r#"
@@ -29,10 +30,10 @@ fn test() {
29} 30}
30"#), 31"#),
31 @r###" 32 @r###"
32 11..41 '{ ...4 }; }': () 33 10..40 '{ ...4 }; }': ()
33 21..22 'a': i32 34 20..21 'a': i32
34 30..38 '{ 1i64 }': i64 35 29..37 '{ 1i64 }': i64
35 32..36 '1i64': i64 36 31..35 '1i64': i64
36 "###); 37 "###);
37} 38}
38 39
@@ -63,50 +64,50 @@ fn test2() {
63} 64}
64"#), 65"#),
65 @r###" 66 @r###"
66 31..32 '_': &[T] 67 30..31 '_': &[T]
67 45..56 '{ loop {} }': T 68 44..55 '{ loop {} }': T
68 47..54 'loop {}': ! 69 46..53 'loop {}': !
69 52..54 '{}': () 70 51..53 '{}': ()
70 65..66 '_': S<&[T]> 71 64..65 '_': S<&[T]>
71 82..93 '{ loop {} }': T 72 81..92 '{ loop {} }': T
72 84..91 'loop {}': ! 73 83..90 'loop {}': !
73 89..91 '{}': () 74 88..90 '{}': ()
74 122..133 '{ loop {} }': *mut [T; _] 75 121..132 '{ loop {} }': *mut [T; _]
75 124..131 'loop {}': ! 76 123..130 'loop {}': !
76 129..131 '{}': () 77 128..130 '{}': ()
77 160..173 '{ gen() }': *mut [U] 78 159..172 '{ gen() }': *mut [U]
78 166..169 'gen': fn gen<U>() -> *mut [U; _] 79 165..168 'gen': fn gen<U>() -> *mut [U; _]
79 166..171 'gen()': *mut [U; _] 80 165..170 'gen()': *mut [U; _]
80 186..420 '{ ...rr); }': () 81 185..419 '{ ...rr); }': ()
81 196..199 'arr': &[u8; _] 82 195..198 'arr': &[u8; _]
82 212..216 '&[1]': &[u8; _] 83 211..215 '&[1]': &[u8; _]
83 213..216 '[1]': [u8; _] 84 212..215 '[1]': [u8; _]
84 214..215 '1': u8 85 213..214 '1': u8
85 227..228 'a': &[u8] 86 226..227 'a': &[u8]
86 237..240 'arr': &[u8; _] 87 236..239 'arr': &[u8; _]
87 250..251 'b': u8 88 249..250 'b': u8
88 254..255 'f': fn f<u8>(&[u8]) -> u8 89 253..254 'f': fn f<u8>(&[u8]) -> u8
89 254..260 'f(arr)': u8 90 253..259 'f(arr)': u8
90 256..259 'arr': &[u8; _] 91 255..258 'arr': &[u8; _]
91 270..271 'c': &[u8] 92 269..270 'c': &[u8]
92 280..287 '{ arr }': &[u8] 93 279..286 '{ arr }': &[u8]
93 282..285 'arr': &[u8; _] 94 281..284 'arr': &[u8; _]
94 297..298 'd': u8 95 296..297 'd': u8
95 301..302 'g': fn g<u8>(S<&[u8]>) -> u8 96 300..301 'g': fn g<u8>(S<&[u8]>) -> u8
96 301..316 'g(S { a: arr })': u8 97 300..315 'g(S { a: arr })': u8
97 303..315 'S { a: arr }': S<&[u8]> 98 302..314 'S { a: arr }': S<&[u8]>
98 310..313 'arr': &[u8; _] 99 309..312 'arr': &[u8; _]
99 326..327 'e': [&[u8]; _] 100 325..326 'e': [&[u8]; _]
100 341..346 '[arr]': [&[u8]; _] 101 340..345 '[arr]': [&[u8]; _]
101 342..345 'arr': &[u8; _] 102 341..344 'arr': &[u8; _]
102 356..357 'f': [&[u8]; _] 103 355..356 'f': [&[u8]; _]
103 371..379 '[arr; 2]': [&[u8]; _] 104 370..378 '[arr; 2]': [&[u8]; _]
104 372..375 'arr': &[u8; _] 105 371..374 'arr': &[u8; _]
105 377..378 '2': usize 106 376..377 '2': usize
106 389..390 'g': (&[u8], &[u8]) 107 388..389 'g': (&[u8], &[u8])
107 407..417 '(arr, arr)': (&[u8], &[u8]) 108 406..416 '(arr, arr)': (&[u8], &[u8])
108 408..411 'arr': &[u8; _] 109 407..410 'arr': &[u8; _]
109 413..416 'arr': &[u8; _] 110 412..415 'arr': &[u8; _]
110 "### 111 "###
111 ); 112 );
112} 113}
@@ -121,15 +122,15 @@ fn test() {
121} 122}
122"#), 123"#),
123 @r###" 124 @r###"
124 11..76 '{ ...[1]; }': () 125 10..75 '{ ...[1]; }': ()
125 21..22 'x': &[isize] 126 20..21 'x': &[isize]
126 35..39 '&[1]': &[isize; _] 127 34..38 '&[1]': &[isize; _]
127 36..39 '[1]': [isize; _] 128 35..38 '[1]': [isize; _]
128 37..38 '1': isize 129 36..37 '1': isize
129 49..50 'x': *const [isize] 130 48..49 'x': *const [isize]
130 69..73 '&[1]': &[isize; _] 131 68..72 '&[1]': &[isize; _]
131 70..73 '[1]': [isize; _] 132 69..72 '[1]': [isize; _]
132 71..72 '1': isize 133 70..71 '1': isize
133 "###); 134 "###);
134} 135}
135 136
@@ -155,31 +156,31 @@ fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) {
155} 156}
156"#), 157"#),
157 @r###" 158 @r###"
158 258..259 'x': A<[T]> 159 257..258 'x': A<[T]>
159 279..284 '{ x }': A<[T]> 160 278..283 '{ x }': A<[T]>
160 281..282 'x': A<[T]> 161 280..281 'x': A<[T]>
161 296..297 'x': B<[T]> 162 295..296 'x': B<[T]>
162 317..322 '{ x }': B<[T]> 163 316..321 '{ x }': B<[T]>
163 319..320 'x': B<[T]> 164 318..319 'x': B<[T]>
164 334..335 'x': C<[T]> 165 333..334 'x': C<[T]>
165 355..360 '{ x }': C<[T]> 166 354..359 '{ x }': C<[T]>
166 357..358 'x': C<[T]> 167 356..357 'x': C<[T]>
167 370..371 'a': A<[u8; _]> 168 369..370 'a': A<[u8; _]>
168 385..386 'b': B<[u8; _]> 169 384..385 'b': B<[u8; _]>
169 400..401 'c': C<[u8; _]> 170 399..400 'c': C<[u8; _]>
170 415..481 '{ ...(c); }': () 171 414..480 '{ ...(c); }': ()
171 425..426 'd': A<[{unknown}]> 172 424..425 'd': A<[{unknown}]>
172 429..433 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]> 173 428..432 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]>
173 429..436 'foo1(a)': A<[{unknown}]> 174 428..435 'foo1(a)': A<[{unknown}]>
174 434..435 'a': A<[u8; _]> 175 433..434 'a': A<[u8; _]>
175 446..447 'e': B<[u8]> 176 445..446 'e': B<[u8]>
176 450..454 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]> 177 449..453 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]>
177 450..457 'foo2(b)': B<[u8]> 178 449..456 'foo2(b)': B<[u8]>
178 455..456 'b': B<[u8; _]> 179 454..455 'b': B<[u8; _]>
179 467..468 'f': C<[u8]> 180 466..467 'f': C<[u8]>
180 471..475 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]> 181 470..474 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]>
181 471..478 'foo3(c)': C<[u8]> 182 470..477 'foo3(c)': C<[u8]>
182 476..477 'c': C<[u8; _]> 183 475..476 'c': C<[u8; _]>
183 "### 184 "###
184 ); 185 );
185} 186}
@@ -198,24 +199,24 @@ fn test() {
198} 199}
199"#), 200"#),
200 @r###" 201 @r###"
201 11..12 'x': &[T] 202 10..11 'x': &[T]
202 28..39 '{ loop {} }': &[T] 203 27..38 '{ loop {} }': &[T]
203 30..37 'loop {}': ! 204 29..36 'loop {}': !
204 35..37 '{}': () 205 34..36 '{}': ()
205 50..126 '{ ... }; }': () 206 49..125 '{ ... }; }': ()
206 60..61 'x': &[i32] 207 59..60 'x': &[i32]
207 64..123 'if tru... }': &[i32] 208 63..122 'if tru... }': &[i32]
208 67..71 'true': bool 209 66..70 'true': bool
209 72..97 '{ ... }': &[i32] 210 71..96 '{ ... }': &[i32]
210 82..85 'foo': fn foo<i32>(&[i32]) -> &[i32] 211 81..84 'foo': fn foo<i32>(&[i32]) -> &[i32]
211 82..91 'foo(&[1])': &[i32] 212 81..90 'foo(&[1])': &[i32]
212 86..90 '&[1]': &[i32; _] 213 85..89 '&[1]': &[i32; _]
213 87..90 '[1]': [i32; _] 214 86..89 '[1]': [i32; _]
214 88..89 '1': i32 215 87..88 '1': i32
215 103..123 '{ ... }': &[i32; _] 216 102..122 '{ ... }': &[i32; _]
216 113..117 '&[1]': &[i32; _] 217 112..116 '&[1]': &[i32; _]
217 114..117 '[1]': [i32; _] 218 113..116 '[1]': [i32; _]
218 115..116 '1': i32 219 114..115 '1': i32
219 "### 220 "###
220 ); 221 );
221} 222}
@@ -234,24 +235,24 @@ fn test() {
234} 235}
235"#), 236"#),
236 @r###" 237 @r###"
237 11..12 'x': &[T] 238 10..11 'x': &[T]
238 28..39 '{ loop {} }': &[T] 239 27..38 '{ loop {} }': &[T]
239 30..37 'loop {}': ! 240 29..36 'loop {}': !
240 35..37 '{}': () 241 34..36 '{}': ()
241 50..126 '{ ... }; }': () 242 49..125 '{ ... }; }': ()
242 60..61 'x': &[i32] 243 59..60 'x': &[i32]
243 64..123 'if tru... }': &[i32] 244 63..122 'if tru... }': &[i32]
244 67..71 'true': bool 245 66..70 'true': bool
245 72..92 '{ ... }': &[i32; _] 246 71..91 '{ ... }': &[i32; _]
246 82..86 '&[1]': &[i32; _] 247 81..85 '&[1]': &[i32; _]
247 83..86 '[1]': [i32; _] 248 82..85 '[1]': [i32; _]
248 84..85 '1': i32 249 83..84 '1': i32
249 98..123 '{ ... }': &[i32] 250 97..122 '{ ... }': &[i32]
250 108..111 'foo': fn foo<i32>(&[i32]) -> &[i32] 251 107..110 'foo': fn foo<i32>(&[i32]) -> &[i32]
251 108..117 'foo(&[1])': &[i32] 252 107..116 'foo(&[1])': &[i32]
252 112..116 '&[1]': &[i32; _] 253 111..115 '&[1]': &[i32; _]
253 113..116 '[1]': [i32; _] 254 112..115 '[1]': [i32; _]
254 114..115 '1': i32 255 113..114 '1': i32
255 "### 256 "###
256 ); 257 );
257} 258}
@@ -270,31 +271,31 @@ fn test(i: i32) {
270} 271}
271"#), 272"#),
272 @r###" 273 @r###"
273 11..12 'x': &[T] 274 10..11 'x': &[T]
274 28..39 '{ loop {} }': &[T] 275 27..38 '{ loop {} }': &[T]
275 30..37 'loop {}': ! 276 29..36 'loop {}': !
276 35..37 '{}': () 277 34..36 '{}': ()
277 48..49 'i': i32 278 47..48 'i': i32
278 56..150 '{ ... }; }': () 279 55..149 '{ ... }; }': ()
279 66..67 'x': &[i32] 280 65..66 'x': &[i32]
280 70..147 'match ... }': &[i32] 281 69..146 'match ... }': &[i32]
281 76..77 'i': i32 282 75..76 'i': i32
282 88..89 '2': i32 283 87..88 '2': i32
283 88..89 '2': i32 284 87..88 '2': i32
284 93..96 'foo': fn foo<i32>(&[i32]) -> &[i32] 285 92..95 'foo': fn foo<i32>(&[i32]) -> &[i32]
285 93..102 'foo(&[2])': &[i32] 286 92..101 'foo(&[2])': &[i32]
286 97..101 '&[2]': &[i32; _] 287 96..100 '&[2]': &[i32; _]
287 98..101 '[2]': [i32; _] 288 97..100 '[2]': [i32; _]
288 99..100 '2': i32 289 98..99 '2': i32
289 112..113 '1': i32 290 111..112 '1': i32
290 112..113 '1': i32 291 111..112 '1': i32
291 117..121 '&[1]': &[i32; _] 292 116..120 '&[1]': &[i32; _]
292 118..121 '[1]': [i32; _] 293 117..120 '[1]': [i32; _]
293 119..120 '1': i32 294 118..119 '1': i32
294 131..132 '_': i32 295 130..131 '_': i32
295 136..140 '&[3]': &[i32; _] 296 135..139 '&[3]': &[i32; _]
296 137..140 '[3]': [i32; _] 297 136..139 '[3]': [i32; _]
297 138..139 '3': i32 298 137..138 '3': i32
298 "### 299 "###
299 ); 300 );
300} 301}
@@ -313,31 +314,31 @@ fn test(i: i32) {
313} 314}
314"#), 315"#),
315 @r###" 316 @r###"
316 11..12 'x': &[T] 317 10..11 'x': &[T]
317 28..39 '{ loop {} }': &[T] 318 27..38 '{ loop {} }': &[T]
318 30..37 'loop {}': ! 319 29..36 'loop {}': !
319 35..37 '{}': () 320 34..36 '{}': ()
320 48..49 'i': i32 321 47..48 'i': i32
321 56..150 '{ ... }; }': () 322 55..149 '{ ... }; }': ()
322 66..67 'x': &[i32] 323 65..66 'x': &[i32]
323 70..147 'match ... }': &[i32] 324 69..146 'match ... }': &[i32]
324 76..77 'i': i32 325 75..76 'i': i32
325 88..89 '1': i32 326 87..88 '1': i32
326 88..89 '1': i32 327 87..88 '1': i32
327 93..97 '&[1]': &[i32; _] 328 92..96 '&[1]': &[i32; _]
328 94..97 '[1]': [i32; _] 329 93..96 '[1]': [i32; _]
329 95..96 '1': i32 330 94..95 '1': i32
330 107..108 '2': i32 331 106..107 '2': i32
331 107..108 '2': i32 332 106..107 '2': i32
332 112..115 'foo': fn foo<i32>(&[i32]) -> &[i32] 333 111..114 'foo': fn foo<i32>(&[i32]) -> &[i32]
333 112..121 'foo(&[2])': &[i32] 334 111..120 'foo(&[2])': &[i32]
334 116..120 '&[2]': &[i32; _] 335 115..119 '&[2]': &[i32; _]
335 117..120 '[2]': [i32; _] 336 116..119 '[2]': [i32; _]
336 118..119 '2': i32 337 117..118 '2': i32
337 131..132 '_': i32 338 130..131 '_': i32
338 136..140 '&[3]': &[i32; _] 339 135..139 '&[3]': &[i32; _]
339 137..140 '[3]': [i32; _] 340 136..139 '[3]': [i32; _]
340 138..139 '3': i32 341 137..138 '3': i32
341 "### 342 "###
342 ); 343 );
343} 344}
@@ -358,24 +359,24 @@ fn test() {
358} 359}
359"#), 360"#),
360 @r###" 361 @r###"
361 11..145 '{ ... }; }': () 362 10..144 '{ ... }; }': ()
362 21..22 't': &mut i32 363 20..21 't': &mut i32
363 25..31 '&mut 1': &mut i32 364 24..30 '&mut 1': &mut i32
364 30..31 '1': i32 365 29..30 '1': i32
365 41..42 'x': *const i32 366 40..41 'x': *const i32
366 45..142 'match ... }': *const i32 367 44..141 'match ... }': *const i32
367 51..52 '1': i32 368 50..51 '1': i32
368 63..64 '1': i32 369 62..63 '1': i32
369 63..64 '1': i32 370 62..63 '1': i32
370 68..69 't': &mut i32 371 67..68 't': &mut i32
371 68..81 't as *mut i32': *mut i32 372 67..80 't as *mut i32': *mut i32
372 91..92 '2': i32 373 90..91 '2': i32
373 91..92 '2': i32 374 90..91 '2': i32
374 96..97 't': &mut i32 375 95..96 't': &mut i32
375 96..105 't as &i32': &i32 376 95..104 't as &i32': &i32
376 115..116 '_': i32 377 114..115 '_': i32
377 120..121 't': &mut i32 378 119..120 't': &mut i32
378 120..135 't as *const i32': *const i32 379 119..134 't as *const i32': *const i32
379 "### 380 "###
380 ); 381 );
381} 382}
@@ -389,9 +390,9 @@ fn foo() -> u32 {
389} 390}
390"#, true), 391"#, true),
391 @r###" 392 @r###"
392 17..40 '{ ...own; }': u32 393 16..39 '{ ...own; }': u32
393 23..37 'return unknown': ! 394 22..36 'return unknown': !
394 30..37 'unknown': u32 395 29..36 'unknown': u32
395 "### 396 "###
396 ); 397 );
397} 398}
@@ -409,24 +410,24 @@ fn test() {
409} 410}
410"#, true), 411"#, true),
411 @r###" 412 @r###"
412 30..31 'x': &Foo 413 29..30 'x': &Foo
413 39..41 '{}': () 414 38..40 '{}': ()
414 52..133 '{ ...oo); }': () 415 51..132 '{ ...oo); }': ()
415 58..71 'takes_ref_foo': fn takes_ref_foo(&Foo) 416 57..70 'takes_ref_foo': fn takes_ref_foo(&Foo)
416 58..77 'takes_...(&Foo)': () 417 57..76 'takes_...(&Foo)': ()
417 72..76 '&Foo': &Foo 418 71..75 '&Foo': &Foo
418 73..76 'Foo': Foo 419 72..75 'Foo': Foo
419 83..96 'takes_ref_foo': fn takes_ref_foo(&Foo) 420 82..95 'takes_ref_foo': fn takes_ref_foo(&Foo)
420 83..103 'takes_...&&Foo)': () 421 82..102 'takes_...&&Foo)': ()
421 97..102 '&&Foo': &&Foo 422 96..101 '&&Foo': &&Foo
422 98..102 '&Foo': &Foo 423 97..101 '&Foo': &Foo
423 99..102 'Foo': Foo 424 98..101 'Foo': Foo
424 109..122 'takes_ref_foo': fn takes_ref_foo(&Foo) 425 108..121 'takes_ref_foo': fn takes_ref_foo(&Foo)
425 109..130 'takes_...&&Foo)': () 426 108..129 'takes_...&&Foo)': ()
426 123..129 '&&&Foo': &&&Foo 427 122..128 '&&&Foo': &&&Foo
427 124..129 '&&Foo': &&Foo 428 123..128 '&&Foo': &&Foo
428 125..129 '&Foo': &Foo 429 124..128 '&Foo': &Foo
429 126..129 'Foo': Foo 430 125..128 'Foo': Foo
430 "### 431 "###
431 ); 432 );
432} 433}
@@ -444,26 +445,26 @@ fn test() {
444} 445}
445"#, true), 446"#, true),
446 @r###" 447 @r###"
447 29..30 'x': &T 448 28..29 'x': &T
448 41..47 '{ *x }': T 449 40..46 '{ *x }': T
449 43..45 '*x': T 450 42..44 '*x': T
450 44..45 'x': &T 451 43..44 'x': &T
451 58..127 '{ ...oo); }': () 452 57..126 '{ ...oo); }': ()
452 64..73 'takes_ref': fn takes_ref<Foo>(&Foo) -> Foo 453 63..72 'takes_ref': fn takes_ref<Foo>(&Foo) -> Foo
453 64..79 'takes_ref(&Foo)': Foo 454 63..78 'takes_ref(&Foo)': Foo
454 74..78 '&Foo': &Foo 455 73..77 '&Foo': &Foo
455 75..78 'Foo': Foo 456 74..77 'Foo': Foo
456 85..94 'takes_ref': fn takes_ref<&Foo>(&&Foo) -> &Foo 457 84..93 'takes_ref': fn takes_ref<&Foo>(&&Foo) -> &Foo
457 85..101 'takes_...&&Foo)': &Foo 458 84..100 'takes_...&&Foo)': &Foo
458 95..100 '&&Foo': &&Foo 459 94..99 '&&Foo': &&Foo
459 96..100 '&Foo': &Foo 460 95..99 '&Foo': &Foo
460 97..100 'Foo': Foo 461 96..99 'Foo': Foo
461 107..116 'takes_ref': fn takes_ref<&&Foo>(&&&Foo) -> &&Foo 462 106..115 'takes_ref': fn takes_ref<&&Foo>(&&&Foo) -> &&Foo
462 107..124 'takes_...&&Foo)': &&Foo 463 106..123 'takes_...&&Foo)': &&Foo
463 117..123 '&&&Foo': &&&Foo 464 116..122 '&&&Foo': &&&Foo
464 118..123 '&&Foo': &&Foo 465 117..122 '&&Foo': &&Foo
465 119..123 '&Foo': &Foo 466 118..122 '&Foo': &Foo
466 120..123 'Foo': Foo 467 119..122 'Foo': Foo
467 "### 468 "###
468 ); 469 );
469} 470}
@@ -483,18 +484,18 @@ fn test() {
483} 484}
484"#, true), 485"#, true),
485 @r###" 486 @r###"
486 127..128 'x': &str 487 126..127 'x': &str
487 136..138 '{}': () 488 135..137 '{}': ()
488 169..180 '{ loop {} }': String 489 168..179 '{ loop {} }': String
489 171..178 'loop {}': ! 490 170..177 'loop {}': !
490 176..178 '{}': () 491 175..177 '{}': ()
491 191..236 '{ ... }); }': () 492 190..235 '{ ... }); }': ()
492 197..210 'takes_ref_str': fn takes_ref_str(&str) 493 196..209 'takes_ref_str': fn takes_ref_str(&str)
493 197..233 'takes_...g() })': () 494 196..232 'takes_...g() })': ()
494 211..232 '&{ ret...ng() }': &String 495 210..231 '&{ ret...ng() }': &String
495 212..232 '{ retu...ng() }': String 496 211..231 '{ retu...ng() }': String
496 214..228 'returns_string': fn returns_string() -> String 497 213..227 'returns_string': fn returns_string() -> String
497 214..230 'return...ring()': String 498 213..229 'return...ring()': String
498 "### 499 "###
499 ); 500 );
500} 501}
@@ -513,19 +514,19 @@ fn foo() {
513} 514}
514"#, true), 515"#, true),
515 @r###" 516 @r###"
516 10..106 '{ ... }; }': () 517 9..105 '{ ... }; }': ()
517 20..21 'x': || -> &u32 518 19..20 'x': || -> &u32
518 24..103 '|| { ... }': || -> &u32 519 23..102 '|| { ... }': || -> &u32
519 27..103 '{ ... }': &u32 520 26..102 '{ ... }': &u32
520 37..82 'if tru... }': () 521 36..81 'if tru... }': ()
521 40..44 'true': bool 522 39..43 'true': bool
522 45..82 '{ ... }': () 523 44..81 '{ ... }': ()
523 59..71 'return &1u32': ! 524 58..70 'return &1u32': !
524 66..71 '&1u32': &u32 525 65..70 '&1u32': &u32
525 67..71 '1u32': u32 526 66..70 '1u32': u32
526 91..97 '&&1u32': &&u32 527 90..96 '&&1u32': &&u32
527 92..97 '&1u32': &u32 528 91..96 '&1u32': &u32
528 93..97 '1u32': u32 529 92..96 '1u32': u32
529 "### 530 "###
530 ); 531 );
531} 532}
@@ -540,12 +541,12 @@ fn test() {
540} 541}
541"#, true), 542"#, true),
542 @r###" 543 @r###"
543 8..9 'x': u32 544 7..8 'x': u32
544 25..30 '{ 1 }': isize 545 24..29 '{ 1 }': isize
545 27..28 '1': isize 546 26..27 '1': isize
546 41..79 '{ ...foo; }': () 547 40..78 '{ ...foo; }': ()
547 51..52 'f': fn(u32) -> isize 548 50..51 'f': fn(u32) -> isize
548 73..76 'foo': fn foo(u32) -> isize 549 72..75 'foo': fn foo(u32) -> isize
549 "### 550 "###
550 ); 551 );
551} 552}
@@ -567,27 +568,27 @@ fn test() {
567} 568}
568"#, true), 569"#, true),
569 @r###" 570 @r###"
570 9..10 'x': u32 571 8..9 'x': u32
571 26..31 '{ 1 }': isize 572 25..30 '{ 1 }': isize
572 28..29 '1': isize 573 27..28 '1': isize
573 40..41 'x': u32 574 39..40 'x': u32
574 57..62 '{ 2 }': isize 575 56..61 '{ 2 }': isize
575 59..60 '2': isize 576 58..59 '2': isize
576 71..72 'x': u32 577 70..71 'x': u32
577 88..93 '{ 3 }': isize 578 87..92 '{ 3 }': isize
578 90..91 '3': isize 579 89..90 '3': isize
579 104..193 '{ ... }; }': () 580 103..192 '{ ... }; }': ()
580 114..115 'x': fn(u32) -> isize 581 113..114 'x': fn(u32) -> isize
581 118..190 'match ... }': fn(u32) -> isize 582 117..189 'match ... }': fn(u32) -> isize
582 124..125 '1': i32 583 123..124 '1': i32
583 136..137 '1': i32 584 135..136 '1': i32
584 136..137 '1': i32 585 135..136 '1': i32
585 141..145 'foo1': fn foo1(u32) -> isize 586 140..144 'foo1': fn foo1(u32) -> isize
586 155..156 '2': i32 587 154..155 '2': i32
587 155..156 '2': i32 588 154..155 '2': i32
588 160..164 'foo2': fn foo2(u32) -> isize 589 159..163 'foo2': fn foo2(u32) -> isize
589 174..175 '_': i32 590 173..174 '_': i32
590 179..183 'foo3': fn foo3(u32) -> isize 591 178..182 'foo3': fn foo3(u32) -> isize
591 "### 592 "###
592 ); 593 );
593} 594}
@@ -601,12 +602,12 @@ fn test() {
601} 602}
602"#, true), 603"#, true),
603 @r###" 604 @r###"
604 11..55 '{ ...1 }; }': () 605 10..54 '{ ...1 }; }': ()
605 21..22 'f': fn(u32) -> isize 606 20..21 'f': fn(u32) -> isize
606 43..52 '|x| { 1 }': |u32| -> isize 607 42..51 '|x| { 1 }': |u32| -> isize
607 44..45 'x': u32 608 43..44 'x': u32
608 47..52 '{ 1 }': isize 609 46..51 '{ 1 }': isize
609 49..50 '1': isize 610 48..49 '1': isize
610 "### 611 "###
611 ); 612 );
612} 613}
@@ -624,11 +625,11 @@ impl<TT> S<TT> {
624} 625}
625"#, true), 626"#, true),
626 @r###" 627 @r###"
627 51..55 'self': &S<TT> 628 50..54 'self': &S<TT>
628 64..87 '{ ... }': &TT 629 63..86 '{ ... }': &TT
629 74..81 '&self.t': &TT 630 73..80 '&self.t': &TT
630 75..79 'self': &S<TT> 631 74..78 'self': &S<TT>
631 75..81 'self.t': TT 632 74..80 'self.t': TT
632 "### 633 "###
633 ); 634 );
634} 635}
@@ -649,21 +650,69 @@ fn test() {
649} 650}
650"#, true), 651"#, true),
651 @r###" 652 @r###"
652 162..199 '{ ... 3]; }': () 653 161..198 '{ ... 3]; }': ()
653 172..173 'f': &[usize] 654 171..172 'f': &[usize]
654 186..196 '&[1, 2, 3]': &[usize; _] 655 185..195 '&[1, 2, 3]': &[usize; _]
655 187..196 '[1, 2, 3]': [usize; _] 656 186..195 '[1, 2, 3]': [usize; _]
656 188..189 '1': usize 657 187..188 '1': usize
657 191..192 '2': usize 658 190..191 '2': usize
658 194..195 '3': usize 659 193..194 '3': usize
660 "###
661 );
662}
663
664#[test]
665fn coerce_unsize_trait_object_simple() {
666 assert_snapshot!(
667 infer_with_mismatches(r#"
668#[lang = "sized"]
669pub trait Sized {}
670#[lang = "unsize"]
671pub trait Unsize<T> {}
672#[lang = "coerce_unsized"]
673pub trait CoerceUnsized<T> {}
674
675impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {}
676
677trait Foo<T, U> {}
678trait Bar<U, T, X>: Foo<T, U> {}
679trait Baz<T, X>: Bar<usize, T, X> {}
680
681struct S<T, X>;
682impl<T, X> Foo<T, usize> for S<T, X> {}
683impl<T, X> Bar<usize, T, X> for S<T, X> {}
684impl<T, X> Baz<T, X> for S<T, X> {}
685
686fn test() {
687 let obj: &dyn Baz<i8, i16> = &S;
688 let obj: &dyn Bar<_, i8, i16> = &S;
689 let obj: &dyn Foo<i8, _> = &S;
690}
691"#, true),
692 @r###"
693 424..539 '{ ... &S; }': ()
694 434..437 'obj': &dyn Baz<i8, i16>
695 459..461 '&S': &S<i8, i16>
696 460..461 'S': S<i8, i16>
697 471..474 'obj': &dyn Bar<usize, i8, i16>
698 499..501 '&S': &S<i8, i16>
699 500..501 'S': S<i8, i16>
700 511..514 'obj': &dyn Foo<i8, usize>
701 534..536 '&S': &S<i8, {unknown}>
702 535..536 'S': S<i8, {unknown}>
659 "### 703 "###
660 ); 704 );
661} 705}
662 706
663#[test] 707#[test]
664fn coerce_unsize_trait_object() { 708// The rust reference says this should be possible, but rustc doesn't implement
709// it. We used to support it, but Chalk doesn't.
710#[ignore]
711fn coerce_unsize_trait_object_to_trait_object() {
665 assert_snapshot!( 712 assert_snapshot!(
666 infer_with_mismatches(r#" 713 infer_with_mismatches(r#"
714#[lang = "sized"]
715pub trait Sized {}
667#[lang = "unsize"] 716#[lang = "unsize"]
668pub trait Unsize<T> {} 717pub trait Unsize<T> {}
669#[lang = "coerce_unsized"] 718#[lang = "coerce_unsized"]
@@ -689,19 +738,19 @@ fn test() {
689} 738}
690"#, true), 739"#, true),
691 @r###" 740 @r###"
692 388..573 '{ ...bj2; }': () 741 424..609 '{ ...bj2; }': ()
693 398..401 'obj': &dyn Baz<i8, i16> 742 434..437 'obj': &dyn Baz<i8, i16>
694 423..425 '&S': &S<i8, i16> 743 459..461 '&S': &S<i8, i16>
695 424..425 'S': S<i8, i16> 744 460..461 'S': S<i8, i16>
696 435..438 'obj': &dyn Bar<usize, i8, i16> 745 471..474 'obj': &dyn Bar<usize, i8, i16>
697 460..463 'obj': &dyn Baz<i8, i16> 746 496..499 'obj': &dyn Baz<i8, i16>
698 473..476 'obj': &dyn Foo<i8, usize> 747 509..512 'obj': &dyn Foo<i8, usize>
699 495..498 'obj': &dyn Bar<usize, i8, i16> 748 531..534 'obj': &dyn Bar<usize, i8, i16>
700 508..512 'obj2': &dyn Baz<i8, i16> 749 544..548 'obj2': &dyn Baz<i8, i16>
701 534..536 '&S': &S<i8, i16> 750 570..572 '&S': &S<i8, i16>
702 535..536 'S': S<i8, i16> 751 571..572 'S': S<i8, i16>
703 546..547 '_': &dyn Foo<i8, usize> 752 582..583 '_': &dyn Foo<i8, usize>
704 566..570 'obj2': &dyn Baz<i8, i16> 753 602..606 'obj2': &dyn Baz<i8, i16>
705 "### 754 "###
706 ); 755 );
707} 756}
@@ -710,6 +759,8 @@ fn test() {
710fn coerce_unsize_super_trait_cycle() { 759fn coerce_unsize_super_trait_cycle() {
711 assert_snapshot!( 760 assert_snapshot!(
712 infer_with_mismatches(r#" 761 infer_with_mismatches(r#"
762#[lang = "sized"]
763pub trait Sized {}
713#[lang = "unsize"] 764#[lang = "unsize"]
714pub trait Unsize<T> {} 765pub trait Unsize<T> {}
715#[lang = "coerce_unsized"] 766#[lang = "coerce_unsized"]
@@ -730,16 +781,17 @@ impl D for S {}
730 781
731fn test() { 782fn test() {
732 let obj: &dyn D = &S; 783 let obj: &dyn D = &S;
733 let obj: &dyn A = obj; 784 let obj: &dyn A = &S;
734} 785}
735"#, true), 786"#, true),
736 @r###" 787 @r###"
737 292..348 '{ ...obj; }': () 788 328..383 '{ ... &S; }': ()
738 302..305 'obj': &dyn D 789 338..341 'obj': &dyn D
739 316..318 '&S': &S 790 352..354 '&S': &S
740 317..318 'S': S 791 353..354 'S': S
741 328..331 'obj': &dyn A 792 364..367 'obj': &dyn A
742 342..345 'obj': &dyn D 793 378..380 '&S': &S
794 379..380 'S': S
743 "### 795 "###
744 ); 796 );
745} 797}
diff --git a/crates/ra_hir_ty/src/tests/display_source_code.rs b/crates/ra_hir_ty/src/tests/display_source_code.rs
index 4088b1d22..b502135d8 100644
--- a/crates/ra_hir_ty/src/tests/display_source_code.rs
+++ b/crates/ra_hir_ty/src/tests/display_source_code.rs
@@ -1,50 +1,41 @@
1use super::displayed_source_at_pos; 1use super::check_types_source_code;
2use crate::test_db::TestDB;
3use ra_db::fixture::WithFixture;
4 2
5#[test] 3#[test]
6fn qualify_path_to_submodule() { 4fn qualify_path_to_submodule() {
7 let (db, pos) = TestDB::with_position( 5 check_types_source_code(
8 r#" 6 r#"
9//- /main.rs
10
11mod foo { 7mod foo {
12 pub struct Foo; 8 pub struct Foo;
13} 9}
14 10
15fn bar() { 11fn bar() {
16 let foo: foo::Foo = foo::Foo; 12 let foo: foo::Foo = foo::Foo;
17 foo<|> 13 foo
18} 14} //^ foo::Foo
19 15
20"#, 16"#,
21 ); 17 );
22 assert_eq!("foo::Foo", displayed_source_at_pos(&db, pos));
23} 18}
24 19
25#[test] 20#[test]
26fn omit_default_type_parameters() { 21fn omit_default_type_parameters() {
27 let (db, pos) = TestDB::with_position( 22 check_types_source_code(
28 r" 23 r#"
29 //- /main.rs 24struct Foo<T = u8> { t: T }
30 struct Foo<T = u8> { t: T } 25fn main() {
31 fn main() { 26 let foo = Foo { t: 5u8 };
32 let foo = Foo { t: 5 }; 27 foo;
33 foo<|>; 28} //^ Foo
34 } 29"#,
35 ",
36 ); 30 );
37 assert_eq!("Foo", displayed_source_at_pos(&db, pos));
38 31
39 let (db, pos) = TestDB::with_position( 32 check_types_source_code(
40 r" 33 r#"
41 //- /main.rs 34struct Foo<K, T = u8> { k: K, t: T }
42 struct Foo<K, T = u8> { k: K, t: T } 35fn main() {
43 fn main() { 36 let foo = Foo { k: 400, t: 5u8 };
44 let foo = Foo { k: 400, t: 5 }; 37 foo;
45 foo<|>; 38} //^ Foo<i32>
46 } 39"#,
47 ",
48 ); 40 );
49 assert_eq!("Foo<i32>", displayed_source_at_pos(&db, pos));
50} 41}
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index 4c6099aa2..45c4e309e 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -1,16 +1,13 @@
1use std::fs; 1use std::fs;
2 2
3use insta::assert_snapshot; 3use insta::assert_snapshot;
4use ra_db::fixture::WithFixture;
5use test_utils::project_dir; 4use test_utils::project_dir;
6 5
7use crate::test_db::TestDB; 6use super::{check_types, infer};
8
9use super::{infer, type_at, type_at_pos};
10 7
11#[test] 8#[test]
12fn cfg_impl_def() { 9fn cfg_impl_def() {
13 let (db, pos) = TestDB::with_position( 10 check_types(
14 r#" 11 r#"
15//- /main.rs crate:main deps:foo cfg:test 12//- /main.rs crate:main deps:foo cfg:test
16use foo::S as T; 13use foo::S as T;
@@ -28,8 +25,8 @@ impl S {
28 25
29fn test() { 26fn test() {
30 let t = (S.foo1(), S.foo2(), T.foo3(), T.foo4()); 27 let t = (S.foo1(), S.foo2(), T.foo3(), T.foo4());
31 t<|>; 28 t;
32} 29} //^ (i32, {unknown}, i32, {unknown})
33 30
34//- /foo.rs crate:foo 31//- /foo.rs crate:foo
35struct S; 32struct S;
@@ -45,7 +42,6 @@ impl S {
45} 42}
46"#, 43"#,
47 ); 44 );
48 assert_eq!("(i32, {unknown}, i32, {unknown})", type_at_pos(&db, pos));
49} 45}
50 46
51#[test] 47#[test]
@@ -71,8 +67,8 @@ fn main() {
71 !1..4 'Foo': Foo({unknown}) -> Foo 67 !1..4 'Foo': Foo({unknown}) -> Foo
72 !1..16 'Foo(vec![1,2,])': Foo 68 !1..16 'Foo(vec![1,2,])': Foo
73 !5..15 'vec![1,2,]': {unknown} 69 !5..15 'vec![1,2,]': {unknown}
74 156..182 '{ ...,2); }': () 70 155..181 '{ ...,2); }': ()
75 166..167 'x': Foo 71 165..166 'x': Foo
76 "### 72 "###
77 ); 73 );
78} 74}
@@ -104,10 +100,10 @@ fn main() {
104 !1..4 'Foo': Foo({unknown}) -> Foo 100 !1..4 'Foo': Foo({unknown}) -> Foo
105 !1..16 'Foo(vec![1,2,])': Foo 101 !1..16 'Foo(vec![1,2,])': Foo
106 !5..15 'vec![1,2,]': {unknown} 102 !5..15 'vec![1,2,]': {unknown}
107 195..251 '{ ...,2); }': () 103 194..250 '{ ...,2); }': ()
108 205..206 'x': Foo 104 204..205 'x': Foo
109 228..229 'y': {unknown} 105 227..228 'y': {unknown}
110 232..248 'crate:...!(1,2)': {unknown} 106 231..247 'crate:...!(1,2)': {unknown}
111 "### 107 "###
112 ); 108 );
113} 109}
@@ -133,9 +129,9 @@ fn main() {
133 @r###" 129 @r###"
134 !0..5 '42i32': i32 130 !0..5 '42i32': i32
135 !0..5 '42i32': i32 131 !0..5 '42i32': i32
136 111..164 '{ ...!(); }': () 132 110..163 '{ ...!(); }': ()
137 121..122 'x': i32 133 120..121 'x': i32
138 148..149 'y': i32 134 147..148 'y': i32
139 "### 135 "###
140 ); 136 );
141} 137}
@@ -197,26 +193,26 @@ fn spam() {
197 !0..6 '1isize': isize 193 !0..6 '1isize': isize
198 !0..6 '1isize': isize 194 !0..6 '1isize': isize
199 !0..6 '1isize': isize 195 !0..6 '1isize': isize
200 54..457 '{ ...!(); }': () 196 53..456 '{ ...!(); }': ()
201 88..109 'spam!(...am!())': {unknown} 197 87..108 'spam!(...am!())': {unknown}
202 115..134 'for _ ...!() {}': () 198 114..133 'for _ ...!() {}': ()
203 119..120 '_': {unknown} 199 118..119 '_': {unknown}
204 132..134 '{}': () 200 131..133 '{}': ()
205 139..149 '|| spam!()': || -> isize 201 138..148 '|| spam!()': || -> isize
206 155..171 'while ...!() {}': () 202 154..170 'while ...!() {}': ()
207 169..171 '{}': () 203 168..170 '{}': ()
208 176..189 'break spam!()': ! 204 175..188 'break spam!()': !
209 195..209 'return spam!()': ! 205 194..208 'return spam!()': !
210 215..269 'match ... }': isize 206 214..268 'match ... }': isize
211 239..240 '_': isize 207 238..239 '_': isize
212 274..290 'spam!(...am!())': {unknown} 208 273..289 'spam!(...am!())': {unknown}
213 296..318 'Spam {...m!() }': {unknown} 209 295..317 'Spam {...m!() }': {unknown}
214 324..340 'spam!(...am!()]': {unknown} 210 323..339 'spam!(...am!()]': {unknown}
215 365..381 'spam!(... usize': usize 211 364..380 'spam!(... usize': usize
216 387..395 '&spam!()': &isize 212 386..394 '&spam!()': &isize
217 401..409 '-spam!()': isize 213 400..408 '-spam!()': isize
218 415..431 'spam!(...pam!()': {unknown} 214 414..430 'spam!(...pam!()': {unknown}
219 437..454 'spam!(...pam!()': isize 215 436..453 'spam!(...pam!()': isize
220 "### 216 "###
221 ); 217 );
222} 218}
@@ -245,34 +241,32 @@ fn foo() {
245"#), 241"#),
246 @r###" 242 @r###"
247 !0..5 '42i32': i32 243 !0..5 '42i32': i32
248 171..206 '{ ...32); }': () 244 170..205 '{ ...32); }': ()
249 181..184 'foo': i32 245 180..183 'foo': i32
250 "### 246 "###
251 ); 247 );
252} 248}
253 249
254#[test] 250#[test]
255fn processes_impls_generated_by_macros() { 251fn processes_impls_generated_by_macros() {
256 let t = type_at( 252 check_types(
257 r#" 253 r#"
258//- /main.rs
259macro_rules! m { 254macro_rules! m {
260 ($ident:ident) => (impl Trait for $ident {}) 255 ($ident:ident) => (impl Trait for $ident {})
261} 256}
262trait Trait { fn foo(self) -> u128 {} } 257trait Trait { fn foo(self) -> u128 {} }
263struct S; 258struct S;
264m!(S); 259m!(S);
265fn test() { S.foo()<|>; } 260fn test() { S.foo(); }
261 //^ u128
266"#, 262"#,
267 ); 263 );
268 assert_eq!(t, "u128");
269} 264}
270 265
271#[test] 266#[test]
272fn infer_assoc_items_generated_by_macros() { 267fn infer_assoc_items_generated_by_macros() {
273 let t = type_at( 268 check_types(
274 r#" 269 r#"
275//- /main.rs
276macro_rules! m { 270macro_rules! m {
277 () => (fn foo(&self) -> u128 {0}) 271 () => (fn foo(&self) -> u128 {0})
278} 272}
@@ -281,17 +275,16 @@ impl S {
281 m!(); 275 m!();
282} 276}
283 277
284fn test() { S.foo()<|>; } 278fn test() { S.foo(); }
279 //^ u128
285"#, 280"#,
286 ); 281 );
287 assert_eq!(t, "u128");
288} 282}
289 283
290#[test] 284#[test]
291fn infer_assoc_items_generated_by_macros_chain() { 285fn infer_assoc_items_generated_by_macros_chain() {
292 let t = type_at( 286 check_types(
293 r#" 287 r#"
294//- /main.rs
295macro_rules! m_inner { 288macro_rules! m_inner {
296 () => {fn foo(&self) -> u128 {0}} 289 () => {fn foo(&self) -> u128 {0}}
297} 290}
@@ -304,21 +297,21 @@ impl S {
304 m!(); 297 m!();
305} 298}
306 299
307fn test() { S.foo()<|>; } 300fn test() { S.foo(); }
301 //^ u128
308"#, 302"#,
309 ); 303 );
310 assert_eq!(t, "u128");
311} 304}
312 305
313#[test] 306#[test]
314fn infer_macro_with_dollar_crate_is_correct_in_expr() { 307fn infer_macro_with_dollar_crate_is_correct_in_expr() {
315 let (db, pos) = TestDB::with_position( 308 check_types(
316 r#" 309 r#"
317//- /main.rs crate:main deps:foo 310//- /main.rs crate:main deps:foo
318fn test() { 311fn test() {
319 let x = (foo::foo!(1), foo::foo!(2)); 312 let x = (foo::foo!(1), foo::foo!(2));
320 x<|>; 313 x;
321} 314} //^ (i32, usize)
322 315
323//- /lib.rs crate:foo 316//- /lib.rs crate:foo
324#[macro_export] 317#[macro_export]
@@ -335,12 +328,11 @@ macro_rules! bar {
335pub fn baz() -> usize { 31usize } 328pub fn baz() -> usize { 31usize }
336"#, 329"#,
337 ); 330 );
338 assert_eq!("(i32, usize)", type_at_pos(&db, pos));
339} 331}
340 332
341#[test] 333#[test]
342fn infer_macro_with_dollar_crate_is_correct_in_trait_associate_type() { 334fn infer_macro_with_dollar_crate_is_correct_in_trait_associate_type() {
343 let (db, pos) = TestDB::with_position( 335 check_types(
344 r#" 336 r#"
345//- /main.rs crate:main deps:foo 337//- /main.rs crate:main deps:foo
346use foo::Trait; 338use foo::Trait;
@@ -348,7 +340,8 @@ use foo::Trait;
348fn test() { 340fn test() {
349 let msg = foo::Message(foo::MessageRef); 341 let msg = foo::Message(foo::MessageRef);
350 let r = msg.deref(); 342 let r = msg.deref();
351 r<|>; 343 r;
344 //^ &MessageRef
352} 345}
353 346
354//- /lib.rs crate:foo 347//- /lib.rs crate:foo
@@ -375,7 +368,6 @@ macro_rules! expand {
375expand!(); 368expand!();
376"#, 369"#,
377 ); 370 );
378 assert_eq!("&MessageRef", type_at_pos(&db, pos));
379} 371}
380 372
381#[test] 373#[test]
@@ -397,12 +389,12 @@ fn main() {
397} 389}
398"#), 390"#),
399 @r###" 391 @r###"
400 159..164 '{ 0 }': u64 392 158..163 '{ 0 }': u64
401 161..162 '0': u64 393 160..161 '0': u64
402 175..197 '{ ...f(); }': () 394 174..196 '{ ...f(); }': ()
403 185..187 '_a': u64 395 184..186 '_a': u64
404 191..192 'f': fn f() -> u64 396 190..191 'f': fn f() -> u64
405 191..194 'f()': u64 397 190..193 'f()': u64
406 "### 398 "###
407 ); 399 );
408} 400}
@@ -419,23 +411,23 @@ fn main() {
419} 411}
420"#), 412"#),
421 @r###" 413 @r###"
422 !0..6 '1usize': usize 414 !0..6 '1usize': usize
423 11..90 '{ ...!(); }': () 415 10..89 '{ ...!(); }': ()
424 17..66 'macro_... }': {unknown} 416 16..65 'macro_... }': {unknown}
425 75..77 '_a': usize 417 74..76 '_a': usize
426 "### 418 "###
427 ); 419 );
428} 420}
429 421
430#[test] 422#[test]
431fn infer_local_inner_macros() { 423fn infer_local_inner_macros() {
432 let (db, pos) = TestDB::with_position( 424 check_types(
433 r#" 425 r#"
434//- /main.rs crate:main deps:foo 426//- /main.rs crate:main deps:foo
435fn test() { 427fn test() {
436 let x = foo::foo!(1); 428 let x = foo::foo!(1);
437 x<|>; 429 x;
438} 430} //^ i32
439 431
440//- /lib.rs crate:foo 432//- /lib.rs crate:foo
441#[macro_export(local_inner_macros)] 433#[macro_export(local_inner_macros)]
@@ -450,7 +442,6 @@ macro_rules! bar {
450 442
451"#, 443"#,
452 ); 444 );
453 assert_eq!("i32", type_at_pos(&db, pos));
454} 445}
455 446
456#[test] 447#[test]
@@ -466,8 +457,8 @@ fn main() {
466"#), 457"#),
467 @r###" 458 @r###"
468 !0..1 '0': i32 459 !0..1 '0': i32
469 64..88 '{ ...!(); }': () 460 63..87 '{ ...!(); }': ()
470 74..75 'x': i32 461 73..74 'x': i32
471 "### 462 "###
472 ); 463 );
473} 464}
@@ -485,8 +476,8 @@ fn main() {
485"#), 476"#),
486 @r###" 477 @r###"
487 !0..2 '""': &str 478 !0..2 '""': &str
488 64..88 '{ ...!(); }': () 479 63..87 '{ ...!(); }': ()
489 74..75 'x': &str 480 73..74 'x': &str
490 "### 481 "###
491 ); 482 );
492} 483}
@@ -504,8 +495,8 @@ fn main() {
504"#), 495"#),
505 @r###" 496 @r###"
506 !0..1 '0': i32 497 !0..1 '0': i32
507 66..92 '{ ...!(); }': () 498 65..91 '{ ...!(); }': ()
508 76..77 'x': i32 499 75..76 'x': i32
509 "### 500 "###
510 ); 501 );
511} 502}
@@ -523,15 +514,15 @@ fn main() {
523"#), 514"#),
524 @r###" 515 @r###"
525 !0..13 '"helloworld!"': &str 516 !0..13 '"helloworld!"': &str
526 66..122 '{ ...")); }': () 517 65..121 '{ ...")); }': ()
527 76..77 'x': &str 518 75..76 'x': &str
528 "### 519 "###
529 ); 520 );
530} 521}
531 522
532#[test] 523#[test]
533fn infer_builtin_macros_include() { 524fn infer_builtin_macros_include() {
534 let (db, pos) = TestDB::with_position( 525 check_types(
535 r#" 526 r#"
536//- /main.rs 527//- /main.rs
537#[rustc_builtin_macro] 528#[rustc_builtin_macro]
@@ -540,14 +531,13 @@ macro_rules! include {() => {}}
540include!("foo.rs"); 531include!("foo.rs");
541 532
542fn main() { 533fn main() {
543 bar()<|>; 534 bar();
544} 535} //^ u32
545 536
546//- /foo.rs 537//- /foo.rs
547fn bar() -> u32 {0} 538fn bar() -> u32 {0}
548"#, 539"#,
549 ); 540 );
550 assert_eq!("u32", type_at_pos(&db, pos));
551} 541}
552 542
553#[test] 543#[test]
@@ -565,18 +555,17 @@ macro_rules! include {() => {}}
565include!("foo.rs"); 555include!("foo.rs");
566 556
567fn main() { 557fn main() {
568 RegisterBlock { }<|>; 558 RegisterBlock { };
559 //^ RegisterBlock
569} 560}
570 "#; 561 "#;
571 let fixture = format!("{}\n//- /foo.rs\n{}", fixture, big_file); 562 let fixture = format!("{}\n//- /foo.rs\n{}", fixture, big_file);
572 563 check_types(&fixture);
573 let (db, pos) = TestDB::with_position(&fixture);
574 assert_eq!("RegisterBlock", type_at_pos(&db, pos));
575} 564}
576 565
577#[test] 566#[test]
578fn infer_builtin_macros_include_concat() { 567fn infer_builtin_macros_include_concat() {
579 let (db, pos) = TestDB::with_position( 568 check_types(
580 r#" 569 r#"
581//- /main.rs 570//- /main.rs
582#[rustc_builtin_macro] 571#[rustc_builtin_macro]
@@ -588,19 +577,18 @@ macro_rules! concat {() => {}}
588include!(concat!("f", "oo.rs")); 577include!(concat!("f", "oo.rs"));
589 578
590fn main() { 579fn main() {
591 bar()<|>; 580 bar();
592} 581} //^ u32
593 582
594//- /foo.rs 583//- /foo.rs
595fn bar() -> u32 {0} 584fn bar() -> u32 {0}
596"#, 585"#,
597 ); 586 );
598 assert_eq!("u32", type_at_pos(&db, pos));
599} 587}
600 588
601#[test] 589#[test]
602fn infer_builtin_macros_include_concat_with_bad_env_should_failed() { 590fn infer_builtin_macros_include_concat_with_bad_env_should_failed() {
603 let (db, pos) = TestDB::with_position( 591 check_types(
604 r#" 592 r#"
605//- /main.rs 593//- /main.rs
606#[rustc_builtin_macro] 594#[rustc_builtin_macro]
@@ -615,32 +603,29 @@ macro_rules! env {() => {}}
615include!(concat!(env!("OUT_DIR"), "/foo.rs")); 603include!(concat!(env!("OUT_DIR"), "/foo.rs"));
616 604
617fn main() { 605fn main() {
618 bar()<|>; 606 bar();
619} 607} //^ {unknown}
620 608
621//- /foo.rs 609//- /foo.rs
622fn bar() -> u32 {0} 610fn bar() -> u32 {0}
623"#, 611"#,
624 ); 612 );
625 assert_eq!("{unknown}", type_at_pos(&db, pos));
626} 613}
627 614
628#[test] 615#[test]
629fn infer_builtin_macros_include_itself_should_failed() { 616fn infer_builtin_macros_include_itself_should_failed() {
630 let (db, pos) = TestDB::with_position( 617 check_types(
631 r#" 618 r#"
632//- /main.rs
633#[rustc_builtin_macro] 619#[rustc_builtin_macro]
634macro_rules! include {() => {}} 620macro_rules! include {() => {}}
635 621
636include!("main.rs"); 622include!("main.rs");
637 623
638fn main() { 624fn main() {
639 0<|> 625 0
640} 626} //^ i32
641"#, 627"#,
642 ); 628 );
643 assert_eq!("i32", type_at_pos(&db, pos));
644} 629}
645 630
646#[test] 631#[test]
@@ -658,8 +643,8 @@ fn main() {
658"#), 643"#),
659 @r###" 644 @r###"
660 !0..13 '"helloworld!"': &str 645 !0..13 '"helloworld!"': &str
661 104..161 '{ ...")); }': () 646 103..160 '{ ...")); }': ()
662 114..115 'x': &str 647 113..114 'x': &str
663 "### 648 "###
664 ); 649 );
665} 650}
@@ -677,23 +662,23 @@ fn main() {
677} 662}
678"#), 663"#),
679 @r###" 664 @r###"
680 !0..5 '"bar"': &str 665 !0..22 '"__RA_...TED__"': &str
681 88..116 '{ ...o"); }': () 666 62..90 '{ ...o"); }': ()
682 98..99 'x': &str 667 72..73 'x': &str
683 "### 668 "###
684 ); 669 );
685} 670}
686 671
687#[test] 672#[test]
688fn infer_derive_clone_simple() { 673fn infer_derive_clone_simple() {
689 let (db, pos) = TestDB::with_position( 674 check_types(
690 r#" 675 r#"
691//- /main.rs crate:main deps:core 676//- /main.rs crate:main deps:core
692#[derive(Clone)] 677#[derive(Clone)]
693struct S; 678struct S;
694fn test() { 679fn test() {
695 S.clone()<|>; 680 S.clone();
696} 681} //^ S
697 682
698//- /lib.rs crate:core 683//- /lib.rs crate:core
699#[prelude_import] 684#[prelude_import]
@@ -705,12 +690,11 @@ mod clone {
705} 690}
706"#, 691"#,
707 ); 692 );
708 assert_eq!("S", type_at_pos(&db, pos));
709} 693}
710 694
711#[test] 695#[test]
712fn infer_derive_clone_in_core() { 696fn infer_derive_clone_in_core() {
713 let (db, pos) = TestDB::with_position( 697 check_types(
714 r#" 698 r#"
715//- /lib.rs crate:core 699//- /lib.rs crate:core
716#[prelude_import] 700#[prelude_import]
@@ -726,16 +710,15 @@ pub struct S;
726//- /main.rs crate:main deps:core 710//- /main.rs crate:main deps:core
727use core::S; 711use core::S;
728fn test() { 712fn test() {
729 S.clone()<|>; 713 S.clone();
730} 714} //^ S
731"#, 715"#,
732 ); 716 );
733 assert_eq!("S", type_at_pos(&db, pos));
734} 717}
735 718
736#[test] 719#[test]
737fn infer_derive_clone_with_params() { 720fn infer_derive_clone_with_params() {
738 let (db, pos) = TestDB::with_position( 721 check_types(
739 r#" 722 r#"
740//- /main.rs crate:main deps:core 723//- /main.rs crate:main deps:core
741#[derive(Clone)] 724#[derive(Clone)]
@@ -744,7 +727,8 @@ struct S;
744struct Wrapper<T>(T); 727struct Wrapper<T>(T);
745struct NonClone; 728struct NonClone;
746fn test() { 729fn test() {
747 (Wrapper(S).clone(), Wrapper(NonClone).clone())<|>; 730 (Wrapper(S).clone(), Wrapper(NonClone).clone());
731 //^ (Wrapper<S>, {unknown})
748} 732}
749 733
750//- /lib.rs crate:core 734//- /lib.rs crate:core
@@ -757,13 +741,12 @@ mod clone {
757} 741}
758"#, 742"#,
759 ); 743 );
760 assert_eq!("(Wrapper<S>, {unknown})", type_at_pos(&db, pos));
761} 744}
762 745
763#[test] 746#[test]
764fn infer_custom_derive_simple() { 747fn infer_custom_derive_simple() {
765 // FIXME: this test current now do nothing 748 // FIXME: this test current now do nothing
766 let (db, pos) = TestDB::with_position( 749 check_types(
767 r#" 750 r#"
768//- /main.rs crate:main 751//- /main.rs crate:main
769use foo::Foo; 752use foo::Foo;
@@ -772,11 +755,10 @@ use foo::Foo;
772struct S{} 755struct S{}
773 756
774fn test() { 757fn test() {
775 S{}<|>; 758 S{};
776} 759} //^ S
777"#, 760"#,
778 ); 761 );
779 assert_eq!("S", type_at_pos(&db, pos));
780} 762}
781 763
782#[test] 764#[test]
@@ -794,12 +776,12 @@ fn main() {
794} 776}
795"#), 777"#),
796 @r###" 778 @r###"
797 52..111 '{ ... }; }': () 779 51..110 '{ ... }; }': ()
798 62..63 'x': u32 780 61..62 'x': u32
799 66..108 'match ... }': u32 781 65..107 'match ... }': u32
800 72..74 '()': () 782 71..73 '()': ()
801 85..92 'unit!()': () 783 84..91 'unit!()': ()
802 96..101 '92u32': u32 784 95..100 '92u32': u32
803 "### 785 "###
804 ); 786 );
805} 787}
diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs
index 558a70022..9c8f22314 100644
--- a/crates/ra_hir_ty/src/tests/method_resolution.rs
+++ b/crates/ra_hir_ty/src/tests/method_resolution.rs
@@ -1,7 +1,6 @@
1use super::{infer, type_at, type_at_pos};
2use crate::test_db::TestDB;
3use insta::assert_snapshot; 1use insta::assert_snapshot;
4use ra_db::fixture::WithFixture; 2
3use super::{check_types, infer};
5 4
6#[test] 5#[test]
7fn infer_slice_method() { 6fn infer_slice_method() {
@@ -22,15 +21,15 @@ fn test(x: &[u8]) {
22} 21}
23"#), 22"#),
24 @r###" 23 @r###"
25 45..49 'self': &[T] 24 44..48 'self': &[T]
26 56..79 '{ ... }': T 25 55..78 '{ ... }': T
27 66..73 'loop {}': ! 26 65..72 'loop {}': !
28 71..73 '{}': () 27 70..72 '{}': ()
29 131..132 'x': &[u8] 28 130..131 'x': &[u8]
30 141..163 '{ ...(x); }': () 29 140..162 '{ ...(x); }': ()
31 147..157 '<[_]>::foo': fn foo<u8>(&[u8]) -> u8 30 146..156 '<[_]>::foo': fn foo<u8>(&[u8]) -> u8
32 147..160 '<[_]>::foo(x)': u8 31 146..159 '<[_]>::foo(x)': u8
33 158..159 'x': &[u8] 32 157..158 'x': &[u8]
34 "### 33 "###
35 ); 34 );
36} 35}
@@ -52,15 +51,15 @@ fn test() {
52} 51}
53"#), 52"#),
54 @r###" 53 @r###"
55 49..75 '{ ... }': A 54 48..74 '{ ... }': A
56 59..69 'A { x: 0 }': A 55 58..68 'A { x: 0 }': A
57 66..67 '0': u32 56 65..66 '0': u32
58 88..122 '{ ...a.x; }': () 57 87..121 '{ ...a.x; }': ()
59 98..99 'a': A 58 97..98 'a': A
60 102..108 'A::new': fn new() -> A 59 101..107 'A::new': fn new() -> A
61 102..110 'A::new()': A 60 101..109 'A::new()': A
62 116..117 'a': A 61 115..116 'a': A
63 116..119 'a.x': u32 62 115..118 'a.x': u32
64 "### 63 "###
65 ); 64 );
66} 65}
@@ -87,19 +86,19 @@ fn test() {
87} 86}
88"#), 87"#),
89 @r###" 88 @r###"
90 47..67 '{ ... }': A 89 46..66 '{ ... }': A
91 57..61 'A::B': A 90 56..60 'A::B': A
92 88..108 '{ ... }': A 91 87..107 '{ ... }': A
93 98..102 'A::C': A 92 97..101 'A::C': A
94 121..178 '{ ... c; }': () 93 120..177 '{ ... c; }': ()
95 131..132 'a': A 94 130..131 'a': A
96 135..139 'A::b': fn b() -> A 95 134..138 'A::b': fn b() -> A
97 135..141 'A::b()': A 96 134..140 'A::b()': A
98 147..148 'a': A 97 146..147 'a': A
99 158..159 'c': A 98 157..158 'c': A
100 162..166 'A::c': fn c() -> A 99 161..165 'A::c': fn c() -> A
101 162..168 'A::c()': A 100 161..167 'A::c()': A
102 174..175 'c': A 101 173..174 'c': A
103 "### 102 "###
104 ); 103 );
105} 104}
@@ -131,22 +130,22 @@ fn test() {
131} 130}
132"#), 131"#),
133 @r###" 132 @r###"
134 56..64 '{ A {} }': A 133 55..63 '{ A {} }': A
135 58..62 'A {}': A 134 57..61 'A {}': A
136 126..132 '{ 99 }': u32 135 125..131 '{ 99 }': u32
137 128..130 '99': u32 136 127..129 '99': u32
138 202..210 '{ C {} }': C 137 201..209 '{ C {} }': C
139 204..208 'C {}': C 138 203..207 'C {}': C
140 241..325 '{ ...g(); }': () 139 240..324 '{ ...g(); }': ()
141 251..252 'x': A 140 250..251 'x': A
142 255..266 'a::A::thing': fn thing() -> A 141 254..265 'a::A::thing': fn thing() -> A
143 255..268 'a::A::thing()': A 142 254..267 'a::A::thing()': A
144 278..279 'y': u32 143 277..278 'y': u32
145 282..293 'b::B::thing': fn thing() -> u32 144 281..292 'b::B::thing': fn thing() -> u32
146 282..295 'b::B::thing()': u32 145 281..294 'b::B::thing()': u32
147 305..306 'z': C 146 304..305 'z': C
148 309..320 'c::C::thing': fn thing() -> C 147 308..319 'c::C::thing': fn thing() -> C
149 309..322 'c::C::thing()': C 148 308..321 'c::C::thing()': C
150 "### 149 "###
151 ); 150 );
152} 151}
@@ -170,74 +169,20 @@ fn test() {
170} 169}
171"#), 170"#),
172 @r###" 171 @r###"
173 64..67 'val': T 172 63..66 'val': T
174 82..109 '{ ... }': Gen<T> 173 81..108 '{ ... }': Gen<T>
175 92..103 'Gen { val }': Gen<T> 174 91..102 'Gen { val }': Gen<T>
176 98..101 'val': T 175 97..100 'val': T
177 123..155 '{ ...32); }': () 176 122..154 '{ ...32); }': ()
178 133..134 'a': Gen<u32> 177 132..133 'a': Gen<u32>
179 137..146 'Gen::make': fn make<u32>(u32) -> Gen<u32> 178 136..145 'Gen::make': fn make<u32>(u32) -> Gen<u32>
180 137..152 'Gen::make(0u32)': Gen<u32> 179 136..151 'Gen::make(0u32)': Gen<u32>
181 147..151 '0u32': u32 180 146..150 '0u32': u32
182 "### 181 "###
183 ); 182 );
184} 183}
185 184
186#[test] 185#[test]
187fn infer_associated_method_generics_with_default_param() {
188 assert_snapshot!(
189 infer(r#"
190struct Gen<T=u32> {
191 val: T
192}
193
194impl<T> Gen<T> {
195 pub fn make() -> Gen<T> {
196 loop { }
197 }
198}
199
200fn test() {
201 let a = Gen::make();
202}
203"#),
204 @r###"
205 80..104 '{ ... }': Gen<T>
206 90..98 'loop { }': !
207 95..98 '{ }': ()
208 118..146 '{ ...e(); }': ()
209 128..129 'a': Gen<u32>
210 132..141 'Gen::make': fn make<u32>() -> Gen<u32>
211 132..143 'Gen::make()': Gen<u32>
212 "###
213 );
214}
215
216#[test]
217fn infer_associated_method_generics_with_default_tuple_param() {
218 let t = type_at(
219 r#"
220//- /main.rs
221struct Gen<T=()> {
222 val: T
223}
224
225impl<T> Gen<T> {
226 pub fn make() -> Gen<T> {
227 loop { }
228 }
229}
230
231fn test() {
232 let a = Gen::make();
233 a.val<|>;
234}
235"#,
236 );
237 assert_eq!(t, "()");
238}
239
240#[test]
241fn infer_associated_method_generics_without_args() { 186fn infer_associated_method_generics_without_args() {
242 assert_snapshot!( 187 assert_snapshot!(
243 infer(r#" 188 infer(r#"
@@ -256,13 +201,13 @@ fn test() {
256} 201}
257"#), 202"#),
258 @r###" 203 @r###"
259 76..100 '{ ... }': Gen<T> 204 75..99 '{ ... }': Gen<T>
260 86..94 'loop { }': ! 205 85..93 'loop { }': !
261 91..94 '{ }': () 206 90..93 '{ }': ()
262 114..149 '{ ...e(); }': () 207 113..148 '{ ...e(); }': ()
263 124..125 'a': Gen<u32> 208 123..124 'a': Gen<u32>
264 128..144 'Gen::<...::make': fn make<u32>() -> Gen<u32> 209 127..143 'Gen::<...::make': fn make<u32>() -> Gen<u32>
265 128..146 'Gen::<...make()': Gen<u32> 210 127..145 'Gen::<...make()': Gen<u32>
266 "### 211 "###
267 ); 212 );
268} 213}
@@ -287,26 +232,26 @@ fn test() {
287} 232}
288"#), 233"#),
289 @r###" 234 @r###"
290 102..126 '{ ... }': Gen<u32, T> 235 101..125 '{ ... }': Gen<u32, T>
291 112..120 'loop { }': ! 236 111..119 'loop { }': !
292 117..120 '{ }': () 237 116..119 '{ }': ()
293 140..180 '{ ...e(); }': () 238 139..179 '{ ...e(); }': ()
294 150..151 'a': Gen<u32, u64> 239 149..150 'a': Gen<u32, u64>
295 154..175 'Gen::<...::make': fn make<u64>() -> Gen<u32, u64> 240 153..174 'Gen::<...::make': fn make<u64>() -> Gen<u32, u64>
296 154..177 'Gen::<...make()': Gen<u32, u64> 241 153..176 'Gen::<...make()': Gen<u32, u64>
297 "### 242 "###
298 ); 243 );
299} 244}
300 245
301#[test] 246#[test]
302fn cross_crate_associated_method_call() { 247fn cross_crate_associated_method_call() {
303 let (db, pos) = TestDB::with_position( 248 check_types(
304 r#" 249 r#"
305//- /main.rs crate:main deps:other_crate 250//- /main.rs crate:main deps:other_crate
306fn test() { 251fn test() {
307 let x = other_crate::foo::S::thing(); 252 let x = other_crate::foo::S::thing();
308 x<|>; 253 x;
309} 254} //^ i128
310 255
311//- /lib.rs crate:other_crate 256//- /lib.rs crate:other_crate
312mod foo { 257mod foo {
@@ -317,7 +262,6 @@ mod foo {
317} 262}
318"#, 263"#,
319 ); 264 );
320 assert_eq!("i128", type_at_pos(&db, pos));
321} 265}
322 266
323#[test] 267#[test]
@@ -341,13 +285,13 @@ fn test() {
341} 285}
342"#), 286"#),
343 @r###" 287 @r###"
344 31..35 'self': &Self 288 30..34 'self': &Self
345 110..114 'self': &Self 289 109..113 'self': &Self
346 170..228 '{ ...i128 }': () 290 169..227 '{ ...i128 }': ()
347 176..178 'S1': S1 291 175..177 'S1': S1
348 176..187 'S1.method()': u32 292 175..186 'S1.method()': u32
349 203..205 'S2': S2 293 202..204 'S2': S2
350 203..214 'S2.method()': i128 294 202..213 'S2.method()': i128
351 "### 295 "###
352 ); 296 );
353} 297}
@@ -388,14 +332,14 @@ mod bar_test {
388} 332}
389"#), 333"#),
390 @r###" 334 @r###"
391 63..67 'self': &Self 335 62..66 'self': &Self
392 169..173 'self': &Self 336 168..172 'self': &Self
393 300..337 '{ ... }': () 337 299..336 '{ ... }': ()
394 310..311 'S': S 338 309..310 'S': S
395 310..320 'S.method()': u32 339 309..319 'S.method()': u32
396 416..454 '{ ... }': () 340 415..453 '{ ... }': ()
397 426..427 'S': S 341 425..426 'S': S
398 426..436 'S.method()': i128 342 425..435 'S.method()': i128
399 "### 343 "###
400 ); 344 );
401} 345}
@@ -415,10 +359,10 @@ fn test() {
415} 359}
416"#), 360"#),
417 @r###" 361 @r###"
418 33..37 'self': &Self 362 32..36 'self': &Self
419 92..111 '{ ...d(); }': () 363 91..110 '{ ...d(); }': ()
420 98..99 'S': S 364 97..98 'S': S
421 98..108 'S.method()': u32 365 97..107 'S.method()': u32
422 "### 366 "###
423 ); 367 );
424} 368}
@@ -444,17 +388,17 @@ fn test() {
444} 388}
445"#), 389"#),
446 @r###" 390 @r###"
447 43..47 'self': &Self 391 42..46 'self': &Self
448 82..86 'self': &Self 392 81..85 'self': &Self
449 210..361 '{ ..., i8 }': () 393 209..360 '{ ..., i8 }': ()
450 216..218 'S1': S1 394 215..217 'S1': S1
451 216..228 'S1.method1()': (u8, u16, u32) 395 215..227 'S1.method1()': (u8, u16, u32)
452 250..252 'S1': S1 396 249..251 'S1': S1
453 250..262 'S1.method2()': (u32, u16, u8) 397 249..261 'S1.method2()': (u32, u16, u8)
454 284..286 'S2': S2 398 283..285 'S2': S2
455 284..296 'S2.method1()': (i8, i16, {unknown}) 399 283..295 'S2.method1()': (i8, i16, {unknown})
456 324..326 'S2': S2 400 323..325 'S2': S2
457 324..336 'S2.method2()': ({unknown}, i16, i8) 401 323..335 'S2.method2()': ({unknown}, i16, i8)
458 "### 402 "###
459 ); 403 );
460} 404}
@@ -474,12 +418,12 @@ fn test() {
474} 418}
475"#), 419"#),
476 @r###" 420 @r###"
477 33..37 'self': &Self 421 32..36 'self': &Self
478 102..127 '{ ...d(); }': () 422 101..126 '{ ...d(); }': ()
479 108..109 'S': S<u32>(u32) -> S<u32> 423 107..108 'S': S<u32>(u32) -> S<u32>
480 108..115 'S(1u32)': S<u32> 424 107..114 'S(1u32)': S<u32>
481 108..124 'S(1u32...thod()': u32 425 107..123 'S(1u32...thod()': u32
482 110..114 '1u32': u32 426 109..113 '1u32': u32
483 "### 427 "###
484 ); 428 );
485} 429}
@@ -500,16 +444,16 @@ fn test() {
500} 444}
501"#), 445"#),
502 @r###" 446 @r###"
503 87..193 '{ ...t(); }': () 447 86..192 '{ ...t(); }': ()
504 97..99 's1': S 448 96..98 's1': S
505 105..121 'Defaul...efault': fn default<S>() -> S 449 104..120 'Defaul...efault': fn default<S>() -> S
506 105..123 'Defaul...ault()': S 450 104..122 'Defaul...ault()': S
507 133..135 's2': S 451 132..134 's2': S
508 138..148 'S::default': fn default<S>() -> S 452 137..147 'S::default': fn default<S>() -> S
509 138..150 'S::default()': S 453 137..149 'S::default()': S
510 160..162 's3': S 454 159..161 's3': S
511 165..188 '<S as ...efault': fn default<S>() -> S 455 164..187 '<S as ...efault': fn default<S>() -> S
512 165..190 '<S as ...ault()': S 456 164..189 '<S as ...ault()': S
513 "### 457 "###
514 ); 458 );
515} 459}
@@ -532,16 +476,16 @@ fn test() {
532} 476}
533"#), 477"#),
534 @r###" 478 @r###"
535 127..211 '{ ...e(); }': () 479 126..210 '{ ...e(); }': ()
536 137..138 'a': u32 480 136..137 'a': u32
537 141..148 'S::make': fn make<S, u32>() -> u32 481 140..147 'S::make': fn make<S, u32>() -> u32
538 141..150 'S::make()': u32 482 140..149 'S::make()': u32
539 160..161 'b': u64 483 159..160 'b': u64
540 164..178 'G::<u64>::make': fn make<G<u64>, u64>() -> u64 484 163..177 'G::<u64>::make': fn make<G<u64>, u64>() -> u64
541 164..180 'G::<u6...make()': u64 485 163..179 'G::<u6...make()': u64
542 190..191 'c': f64 486 189..190 'c': f64
543 199..206 'G::make': fn make<G<f64>, f64>() -> f64 487 198..205 'G::make': fn make<G<f64>, f64>() -> f64
544 199..208 'G::make()': f64 488 198..207 'G::make()': f64
545 "### 489 "###
546 ); 490 );
547} 491}
@@ -566,22 +510,22 @@ fn test() {
566} 510}
567"#), 511"#),
568 @r###" 512 @r###"
569 135..313 '{ ...e(); }': () 513 134..312 '{ ...e(); }': ()
570 145..146 'a': (u32, i64) 514 144..145 'a': (u32, i64)
571 149..163 'S::make::<i64>': fn make<S, u32, i64>() -> (u32, i64) 515 148..162 'S::make::<i64>': fn make<S, u32, i64>() -> (u32, i64)
572 149..165 'S::mak...i64>()': (u32, i64) 516 148..164 'S::mak...i64>()': (u32, i64)
573 175..176 'b': (u32, i64) 517 174..175 'b': (u32, i64)
574 189..196 'S::make': fn make<S, u32, i64>() -> (u32, i64) 518 188..195 'S::make': fn make<S, u32, i64>() -> (u32, i64)
575 189..198 'S::make()': (u32, i64) 519 188..197 'S::make()': (u32, i64)
576 208..209 'c': (u32, i64) 520 207..208 'c': (u32, i64)
577 212..233 'G::<u3...:<i64>': fn make<G<u32>, u32, i64>() -> (u32, i64) 521 211..232 'G::<u3...:<i64>': fn make<G<u32>, u32, i64>() -> (u32, i64)
578 212..235 'G::<u3...i64>()': (u32, i64) 522 211..234 'G::<u3...i64>()': (u32, i64)
579 245..246 'd': (u32, i64) 523 244..245 'd': (u32, i64)
580 259..273 'G::make::<i64>': fn make<G<u32>, u32, i64>() -> (u32, i64) 524 258..272 'G::make::<i64>': fn make<G<u32>, u32, i64>() -> (u32, i64)
581 259..275 'G::mak...i64>()': (u32, i64) 525 258..274 'G::mak...i64>()': (u32, i64)
582 285..286 'e': (u32, i64) 526 284..285 'e': (u32, i64)
583 301..308 'G::make': fn make<G<u32>, u32, i64>() -> (u32, i64) 527 300..307 'G::make': fn make<G<u32>, u32, i64>() -> (u32, i64)
584 301..310 'G::make()': (u32, i64) 528 300..309 'G::make()': (u32, i64)
585 "### 529 "###
586 ); 530 );
587} 531}
@@ -600,10 +544,10 @@ fn test() {
600} 544}
601"#), 545"#),
602 @r###" 546 @r###"
603 101..127 '{ ...e(); }': () 547 100..126 '{ ...e(); }': ()
604 111..112 'a': (S<i32>, i64) 548 110..111 'a': (S<i32>, i64)
605 115..122 'S::make': fn make<S<i32>, i64>() -> (S<i32>, i64) 549 114..121 'S::make': fn make<S<i32>, i64>() -> (S<i32>, i64)
606 115..124 'S::make()': (S<i32>, i64) 550 114..123 'S::make()': (S<i32>, i64)
607 "### 551 "###
608 ); 552 );
609} 553}
@@ -624,13 +568,13 @@ fn test() {
624} 568}
625"#), 569"#),
626 @r###" 570 @r###"
627 131..203 '{ ...e(); }': () 571 130..202 '{ ...e(); }': ()
628 141..142 'a': (S<u64>, i64) 572 140..141 'a': (S<u64>, i64)
629 158..165 'S::make': fn make<S<u64>, i64>() -> (S<u64>, i64) 573 157..164 'S::make': fn make<S<u64>, i64>() -> (S<u64>, i64)
630 158..167 'S::make()': (S<u64>, i64) 574 157..166 'S::make()': (S<u64>, i64)
631 177..178 'b': (S<u32>, i32) 575 176..177 'b': (S<u32>, i32)
632 191..198 'S::make': fn make<S<u32>, i32>() -> (S<u32>, i32) 576 190..197 'S::make': fn make<S<u32>, i32>() -> (S<u32>, i32)
633 191..200 'S::make()': (S<u32>, i32) 577 190..199 'S::make()': (S<u32>, i32)
634 "### 578 "###
635 ); 579 );
636} 580}
@@ -650,13 +594,13 @@ fn test() {
650} 594}
651"#), 595"#),
652 @r###" 596 @r###"
653 107..211 '{ ...>(); }': () 597 106..210 '{ ...>(); }': ()
654 117..118 'a': (S<u64>, i64, u8) 598 116..117 'a': (S<u64>, i64, u8)
655 121..150 '<S as ...::<u8>': fn make<S<u64>, i64, u8>() -> (S<u64>, i64, u8) 599 120..149 '<S as ...::<u8>': fn make<S<u64>, i64, u8>() -> (S<u64>, i64, u8)
656 121..152 '<S as ...<u8>()': (S<u64>, i64, u8) 600 120..151 '<S as ...<u8>()': (S<u64>, i64, u8)
657 162..163 'b': (S<u64>, i64, u8) 601 161..162 'b': (S<u64>, i64, u8)
658 182..206 'Trait:...::<u8>': fn make<S<u64>, i64, u8>() -> (S<u64>, i64, u8) 602 181..205 'Trait:...::<u8>': fn make<S<u64>, i64, u8>() -> (S<u64>, i64, u8)
659 182..208 'Trait:...<u8>()': (S<u64>, i64, u8) 603 181..207 'Trait:...<u8>()': (S<u64>, i64, u8)
660 "### 604 "###
661 ); 605 );
662} 606}
@@ -673,11 +617,11 @@ fn test<T: Trait>(t: T) {
673} 617}
674"#), 618"#),
675 @r###" 619 @r###"
676 30..34 'self': &Self 620 29..33 'self': &Self
677 64..65 't': T 621 63..64 't': T
678 70..89 '{ ...d(); }': () 622 69..88 '{ ...d(); }': ()
679 76..77 't': T 623 75..76 't': T
680 76..86 't.method()': u32 624 75..85 't.method()': u32
681 "### 625 "###
682 ); 626 );
683} 627}
@@ -694,11 +638,11 @@ fn test<U, T: Trait<U>>(t: T) {
694} 638}
695"#), 639"#),
696 @r###" 640 @r###"
697 33..37 'self': &Self 641 32..36 'self': &Self
698 71..72 't': T 642 70..71 't': T
699 77..96 '{ ...d(); }': () 643 76..95 '{ ...d(); }': ()
700 83..84 't': T 644 82..83 't': T
701 83..93 't.method()': U 645 82..92 't.method()': U
702 "### 646 "###
703 ); 647 );
704} 648}
@@ -720,153 +664,145 @@ fn test() {
720} 664}
721"#), 665"#),
722 @r###" 666 @r###"
723 29..33 'self': Self 667 28..32 'self': Self
724 111..202 '{ ...(S); }': () 668 110..201 '{ ...(S); }': ()
725 121..122 'x': u32 669 120..121 'x': u32
726 130..131 'S': S 670 129..130 'S': S
727 130..138 'S.into()': u32 671 129..137 'S.into()': u32
728 148..149 'y': u64 672 147..148 'y': u64
729 157..158 'S': S 673 156..157 'S': S
730 157..165 'S.into()': u64 674 156..164 'S.into()': u64
731 175..176 'z': u64 675 174..175 'z': u64
732 179..196 'Into::...::into': fn into<S, u64>(S) -> u64 676 178..195 'Into::...::into': fn into<S, u64>(S) -> u64
733 179..199 'Into::...nto(S)': u64 677 178..198 'Into::...nto(S)': u64
734 197..198 'S': S 678 196..197 'S': S
735 "### 679 "###
736 ); 680 );
737} 681}
738 682
739#[test] 683#[test]
740fn method_resolution_unify_impl_self_type() { 684fn method_resolution_unify_impl_self_type() {
741 let t = type_at( 685 check_types(
742 r#" 686 r#"
743//- /main.rs
744struct S<T>; 687struct S<T>;
745impl S<u32> { fn foo(&self) -> u8 {} } 688impl S<u32> { fn foo(&self) -> u8 {} }
746impl S<i32> { fn foo(&self) -> i8 {} } 689impl S<i32> { fn foo(&self) -> i8 {} }
747fn test() { (S::<u32>.foo(), S::<i32>.foo())<|>; } 690fn test() { (S::<u32>.foo(), S::<i32>.foo()); }
691 //^ (u8, i8)
748"#, 692"#,
749 ); 693 );
750 assert_eq!(t, "(u8, i8)");
751} 694}
752 695
753#[test] 696#[test]
754fn method_resolution_trait_before_autoref() { 697fn method_resolution_trait_before_autoref() {
755 let t = type_at( 698 check_types(
756 r#" 699 r#"
757//- /main.rs
758trait Trait { fn foo(self) -> u128; } 700trait Trait { fn foo(self) -> u128; }
759struct S; 701struct S;
760impl S { fn foo(&self) -> i8 { 0 } } 702impl S { fn foo(&self) -> i8 { 0 } }
761impl Trait for S { fn foo(self) -> u128 { 0 } } 703impl Trait for S { fn foo(self) -> u128 { 0 } }
762fn test() { S.foo()<|>; } 704fn test() { S.foo(); }
705 //^ u128
763"#, 706"#,
764 ); 707 );
765 assert_eq!(t, "u128");
766} 708}
767 709
768#[test] 710#[test]
769fn method_resolution_by_value_before_autoref() { 711fn method_resolution_by_value_before_autoref() {
770 let t = type_at( 712 check_types(
771 r#" 713 r#"
772//- /main.rs
773trait Clone { fn clone(&self) -> Self; } 714trait Clone { fn clone(&self) -> Self; }
774struct S; 715struct S;
775impl Clone for S {} 716impl Clone for S {}
776impl Clone for &S {} 717impl Clone for &S {}
777fn test() { (S.clone(), (&S).clone(), (&&S).clone())<|>; } 718fn test() { (S.clone(), (&S).clone(), (&&S).clone()); }
719 //^ (S, S, &S)
778"#, 720"#,
779 ); 721 );
780 assert_eq!(t, "(S, S, &S)");
781} 722}
782 723
783#[test] 724#[test]
784fn method_resolution_trait_before_autoderef() { 725fn method_resolution_trait_before_autoderef() {
785 let t = type_at( 726 check_types(
786 r#" 727 r#"
787//- /main.rs
788trait Trait { fn foo(self) -> u128; } 728trait Trait { fn foo(self) -> u128; }
789struct S; 729struct S;
790impl S { fn foo(self) -> i8 { 0 } } 730impl S { fn foo(self) -> i8 { 0 } }
791impl Trait for &S { fn foo(self) -> u128 { 0 } } 731impl Trait for &S { fn foo(self) -> u128 { 0 } }
792fn test() { (&S).foo()<|>; } 732fn test() { (&S).foo(); }
733 //^ u128
793"#, 734"#,
794 ); 735 );
795 assert_eq!(t, "u128");
796} 736}
797 737
798#[test] 738#[test]
799fn method_resolution_impl_before_trait() { 739fn method_resolution_impl_before_trait() {
800 let t = type_at( 740 check_types(
801 r#" 741 r#"
802//- /main.rs
803trait Trait { fn foo(self) -> u128; } 742trait Trait { fn foo(self) -> u128; }
804struct S; 743struct S;
805impl S { fn foo(self) -> i8 { 0 } } 744impl S { fn foo(self) -> i8 { 0 } }
806impl Trait for S { fn foo(self) -> u128 { 0 } } 745impl Trait for S { fn foo(self) -> u128 { 0 } }
807fn test() { S.foo()<|>; } 746fn test() { S.foo(); }
747 //^ i8
808"#, 748"#,
809 ); 749 );
810 assert_eq!(t, "i8");
811} 750}
812 751
813#[test] 752#[test]
814fn method_resolution_impl_ref_before_trait() { 753fn method_resolution_impl_ref_before_trait() {
815 let t = type_at( 754 check_types(
816 r#" 755 r#"
817//- /main.rs
818trait Trait { fn foo(self) -> u128; } 756trait Trait { fn foo(self) -> u128; }
819struct S; 757struct S;
820impl S { fn foo(&self) -> i8 { 0 } } 758impl S { fn foo(&self) -> i8 { 0 } }
821impl Trait for &S { fn foo(self) -> u128 { 0 } } 759impl Trait for &S { fn foo(self) -> u128 { 0 } }
822fn test() { S.foo()<|>; } 760fn test() { S.foo(); }
761 //^ i8
823"#, 762"#,
824 ); 763 );
825 assert_eq!(t, "i8");
826} 764}
827 765
828#[test] 766#[test]
829fn method_resolution_trait_autoderef() { 767fn method_resolution_trait_autoderef() {
830 let t = type_at( 768 check_types(
831 r#" 769 r#"
832//- /main.rs
833trait Trait { fn foo(self) -> u128; } 770trait Trait { fn foo(self) -> u128; }
834struct S; 771struct S;
835impl Trait for S { fn foo(self) -> u128 { 0 } } 772impl Trait for S { fn foo(self) -> u128 { 0 } }
836fn test() { (&S).foo()<|>; } 773fn test() { (&S).foo(); }
774 //^ u128
837"#, 775"#,
838 ); 776 );
839 assert_eq!(t, "u128");
840} 777}
841 778
842#[test] 779#[test]
843fn method_resolution_unsize_array() { 780fn method_resolution_unsize_array() {
844 let t = type_at( 781 check_types(
845 r#" 782 r#"
846//- /main.rs
847#[lang = "slice"] 783#[lang = "slice"]
848impl<T> [T] { 784impl<T> [T] {
849 fn len(&self) -> usize { loop {} } 785 fn len(&self) -> usize { loop {} }
850} 786}
851fn test() { 787fn test() {
852 let a = [1, 2, 3]; 788 let a = [1, 2, 3];
853 a.len()<|>; 789 a.len();
854} 790} //^ usize
855"#, 791"#,
856 ); 792 );
857 assert_eq!(t, "usize");
858} 793}
859 794
860#[test] 795#[test]
861fn method_resolution_trait_from_prelude() { 796fn method_resolution_trait_from_prelude() {
862 let (db, pos) = TestDB::with_position( 797 check_types(
863 r#" 798 r#"
864//- /main.rs crate:main deps:other_crate 799//- /main.rs crate:main deps:other_crate
865struct S; 800struct S;
866impl Clone for S {} 801impl Clone for S {}
867 802
868fn test() { 803fn test() {
869 S.clone()<|>; 804 S.clone();
805 //^ S
870} 806}
871 807
872//- /lib.rs crate:other_crate 808//- /lib.rs crate:other_crate
@@ -879,115 +815,107 @@ mod foo {
879} 815}
880"#, 816"#,
881 ); 817 );
882 assert_eq!("S", type_at_pos(&db, pos));
883} 818}
884 819
885#[test] 820#[test]
886fn method_resolution_where_clause_for_unknown_trait() { 821fn method_resolution_where_clause_for_unknown_trait() {
887 // The blanket impl currently applies because we ignore the unresolved where clause 822 // The blanket impl currently applies because we ignore the unresolved where clause
888 let t = type_at( 823 check_types(
889 r#" 824 r#"
890//- /main.rs
891trait Trait { fn foo(self) -> u128; } 825trait Trait { fn foo(self) -> u128; }
892struct S; 826struct S;
893impl<T> Trait for T where T: UnknownTrait {} 827impl<T> Trait for T where T: UnknownTrait {}
894fn test() { (&S).foo()<|>; } 828fn test() { (&S).foo(); }
829 //^ u128
895"#, 830"#,
896 ); 831 );
897 assert_eq!(t, "u128");
898} 832}
899 833
900#[test] 834#[test]
901fn method_resolution_where_clause_not_met() { 835fn method_resolution_where_clause_not_met() {
902 // The blanket impl shouldn't apply because we can't prove S: Clone 836 // The blanket impl shouldn't apply because we can't prove S: Clone
903 let t = type_at( 837 // This is also to make sure that we don't resolve to the foo method just
838 // because that's the only method named foo we can find, which would make
839 // the below tests not work
840 check_types(
904 r#" 841 r#"
905//- /main.rs
906trait Clone {} 842trait Clone {}
907trait Trait { fn foo(self) -> u128; } 843trait Trait { fn foo(self) -> u128; }
908struct S; 844struct S;
909impl<T> Trait for T where T: Clone {} 845impl<T> Trait for T where T: Clone {}
910fn test() { (&S).foo()<|>; } 846fn test() { (&S).foo(); }
847 //^ {unknown}
911"#, 848"#,
912 ); 849 );
913 // This is also to make sure that we don't resolve to the foo method just
914 // because that's the only method named foo we can find, which would make
915 // the below tests not work
916 assert_eq!(t, "{unknown}");
917} 850}
918 851
919#[test] 852#[test]
920fn method_resolution_where_clause_inline_not_met() { 853fn method_resolution_where_clause_inline_not_met() {
921 // The blanket impl shouldn't apply because we can't prove S: Clone 854 // The blanket impl shouldn't apply because we can't prove S: Clone
922 let t = type_at( 855 check_types(
923 r#" 856 r#"
924//- /main.rs
925trait Clone {} 857trait Clone {}
926trait Trait { fn foo(self) -> u128; } 858trait Trait { fn foo(self) -> u128; }
927struct S; 859struct S;
928impl<T: Clone> Trait for T {} 860impl<T: Clone> Trait for T {}
929fn test() { (&S).foo()<|>; } 861fn test() { (&S).foo(); }
862 //^ {unknown}
930"#, 863"#,
931 ); 864 );
932 assert_eq!(t, "{unknown}");
933} 865}
934 866
935#[test] 867#[test]
936fn method_resolution_where_clause_1() { 868fn method_resolution_where_clause_1() {
937 let t = type_at( 869 check_types(
938 r#" 870 r#"
939//- /main.rs
940trait Clone {} 871trait Clone {}
941trait Trait { fn foo(self) -> u128; } 872trait Trait { fn foo(self) -> u128; }
942struct S; 873struct S;
943impl Clone for S {} 874impl Clone for S {}
944impl<T> Trait for T where T: Clone {} 875impl<T> Trait for T where T: Clone {}
945fn test() { S.foo()<|>; } 876fn test() { S.foo(); }
877 //^ u128
946"#, 878"#,
947 ); 879 );
948 assert_eq!(t, "u128");
949} 880}
950 881
951#[test] 882#[test]
952fn method_resolution_where_clause_2() { 883fn method_resolution_where_clause_2() {
953 let t = type_at( 884 check_types(
954 r#" 885 r#"
955//- /main.rs
956trait Into<T> { fn into(self) -> T; } 886trait Into<T> { fn into(self) -> T; }
957trait From<T> { fn from(other: T) -> Self; } 887trait From<T> { fn from(other: T) -> Self; }
958struct S1; 888struct S1;
959struct S2; 889struct S2;
960impl From<S2> for S1 {} 890impl From<S2> for S1 {}
961impl<T, U> Into<U> for T where U: From<T> {} 891impl<T, U> Into<U> for T where U: From<T> {}
962fn test() { S2.into()<|>; } 892fn test() { S2.into(); }
893 //^ {unknown}
963"#, 894"#,
964 ); 895 );
965 assert_eq!(t, "{unknown}");
966} 896}
967 897
968#[test] 898#[test]
969fn method_resolution_where_clause_inline() { 899fn method_resolution_where_clause_inline() {
970 let t = type_at( 900 check_types(
971 r#" 901 r#"
972//- /main.rs
973trait Into<T> { fn into(self) -> T; } 902trait Into<T> { fn into(self) -> T; }
974trait From<T> { fn from(other: T) -> Self; } 903trait From<T> { fn from(other: T) -> Self; }
975struct S1; 904struct S1;
976struct S2; 905struct S2;
977impl From<S2> for S1 {} 906impl From<S2> for S1 {}
978impl<T, U: From<T>> Into<U> for T {} 907impl<T, U: From<T>> Into<U> for T {}
979fn test() { S2.into()<|>; } 908fn test() { S2.into(); }
909 //^ {unknown}
980"#, 910"#,
981 ); 911 );
982 assert_eq!(t, "{unknown}");
983} 912}
984 913
985#[test] 914#[test]
986fn method_resolution_overloaded_method() { 915fn method_resolution_overloaded_method() {
987 test_utils::mark::check!(impl_self_type_match_without_receiver); 916 test_utils::mark::check!(impl_self_type_match_without_receiver);
988 let t = type_at( 917 check_types(
989 r#" 918 r#"
990//- main.rs
991struct Wrapper<T>(T); 919struct Wrapper<T>(T);
992struct Foo<T>(T); 920struct Foo<T>(T);
993struct Bar<T>(T); 921struct Bar<T>(T);
@@ -1007,30 +935,30 @@ impl<T> Wrapper<Bar<T>> {
1007fn main() { 935fn main() {
1008 let a = Wrapper::<Foo<f32>>::new(1.0); 936 let a = Wrapper::<Foo<f32>>::new(1.0);
1009 let b = Wrapper::<Bar<f32>>::new(1.0); 937 let b = Wrapper::<Bar<f32>>::new(1.0);
1010 (a, b)<|>; 938 (a, b);
939 //^ (Wrapper<Foo<f32>>, Wrapper<Bar<f32>>)
1011} 940}
1012"#, 941"#,
1013 ); 942 );
1014 assert_eq!(t, "(Wrapper<Foo<f32>>, Wrapper<Bar<f32>>)")
1015} 943}
1016 944
1017#[test] 945#[test]
1018fn method_resolution_encountering_fn_type() { 946fn method_resolution_encountering_fn_type() {
1019 type_at( 947 check_types(
1020 r#" 948 r#"
1021//- /main.rs 949//- /main.rs
1022fn foo() {} 950fn foo() {}
1023trait FnOnce { fn call(self); } 951trait FnOnce { fn call(self); }
1024fn test() { foo.call()<|>; } 952fn test() { foo.call(); }
953 //^ {unknown}
1025"#, 954"#,
1026 ); 955 );
1027} 956}
1028 957
1029#[test] 958#[test]
1030fn method_resolution_non_parameter_type() { 959fn method_resolution_non_parameter_type() {
1031 let t = type_at( 960 check_types(
1032 r#" 961 r#"
1033//- /main.rs
1034mod a { 962mod a {
1035 pub trait Foo { 963 pub trait Foo {
1036 fn foo(&self); 964 fn foo(&self);
@@ -1042,18 +970,16 @@ fn foo<T>(t: Wrapper<T>)
1042where 970where
1043 Wrapper<T>: a::Foo, 971 Wrapper<T>: a::Foo,
1044{ 972{
1045 t.foo()<|>; 973 t.foo();
1046} 974} //^ {unknown}
1047"#, 975"#,
1048 ); 976 );
1049 assert_eq!(t, "{unknown}");
1050} 977}
1051 978
1052#[test] 979#[test]
1053fn method_resolution_3373() { 980fn method_resolution_3373() {
1054 let t = type_at( 981 check_types(
1055 r#" 982 r#"
1056//- /main.rs
1057struct A<T>(T); 983struct A<T>(T);
1058 984
1059impl A<i32> { 985impl A<i32> {
@@ -1061,19 +987,17 @@ impl A<i32> {
1061} 987}
1062 988
1063fn main() { 989fn main() {
1064 A::from(3)<|>; 990 A::from(3);
1065} 991} //^ A<i32>
1066"#, 992"#,
1067 ); 993 );
1068 assert_eq!(t, "A<i32>");
1069} 994}
1070 995
1071#[test] 996#[test]
1072fn method_resolution_slow() { 997fn method_resolution_slow() {
1073 // this can get quite slow if we set the solver size limit too high 998 // this can get quite slow if we set the solver size limit too high
1074 let t = type_at( 999 check_types(
1075 r#" 1000 r#"
1076//- /main.rs
1077trait SendX {} 1001trait SendX {}
1078 1002
1079struct S1; impl SendX for S1 {} 1003struct S1; impl SendX for S1 {}
@@ -1091,10 +1015,10 @@ trait FnX {}
1091 1015
1092impl<B, C> Trait for S<B, C> where C: FnX, B: SendX {} 1016impl<B, C> Trait for S<B, C> where C: FnX, B: SendX {}
1093 1017
1094fn test() { (S {}).method()<|>; } 1018fn test() { (S {}).method(); }
1019 //^ ()
1095"#, 1020"#,
1096 ); 1021 );
1097 assert_eq!(t, "()");
1098} 1022}
1099 1023
1100#[test] 1024#[test]
@@ -1117,13 +1041,13 @@ fn test(d: &dyn Trait) {
1117} 1041}
1118"#), 1042"#),
1119 @r###" 1043 @r###"
1120 52..56 'self': &Self 1044 51..55 'self': &Self
1121 65..70 '{ 0 }': u32 1045 64..69 '{ 0 }': u32
1122 67..68 '0': u32 1046 66..67 '0': u32
1123 177..178 'd': &dyn Trait 1047 176..177 'd': &dyn Trait
1124 192..208 '{ ...o(); }': () 1048 191..207 '{ ...o(); }': ()
1125 198..199 'd': &dyn Trait 1049 197..198 'd': &dyn Trait
1126 198..205 'd.foo()': u32 1050 197..204 'd.foo()': u32
1127 "### 1051 "###
1128 ); 1052 );
1129} 1053}
diff --git a/crates/ra_hir_ty/src/tests/never_type.rs b/crates/ra_hir_ty/src/tests/never_type.rs
index 082c47208..64d421d40 100644
--- a/crates/ra_hir_ty/src/tests/never_type.rs
+++ b/crates/ra_hir_ty/src/tests/never_type.rs
@@ -1,99 +1,91 @@
1use insta::assert_snapshot; 1use insta::assert_snapshot;
2 2
3use super::{infer_with_mismatches, type_at}; 3use super::{check_types, infer_with_mismatches};
4 4
5#[test] 5#[test]
6fn infer_never1() { 6fn infer_never1() {
7 let t = type_at( 7 check_types(
8 r#" 8 r#"
9//- /main.rs
10fn test() { 9fn test() {
11 let t = return; 10 let t = return;
12 t<|>; 11 t;
13} 12} //^ !
14"#, 13"#,
15 ); 14 );
16 assert_eq!(t, "!");
17} 15}
18 16
19#[test] 17#[test]
20fn infer_never2() { 18fn infer_never2() {
21 let t = type_at( 19 check_types(
22 r#" 20 r#"
23//- /main.rs
24fn gen<T>() -> T { loop {} } 21fn gen<T>() -> T { loop {} }
25 22
26fn test() { 23fn test() {
27 let a = gen(); 24 let a = gen();
28 if false { a } else { loop {} }; 25 if false { a } else { loop {} };
29 a<|>; 26 a;
30} 27} //^ !
31"#, 28"#,
32 ); 29 );
33 assert_eq!(t, "!");
34} 30}
35 31
36#[test] 32#[test]
37fn infer_never3() { 33fn infer_never3() {
38 let t = type_at( 34 check_types(
39 r#" 35 r#"
40//- /main.rs
41fn gen<T>() -> T { loop {} } 36fn gen<T>() -> T { loop {} }
42 37
43fn test() { 38fn test() {
44 let a = gen(); 39 let a = gen();
45 if false { loop {} } else { a }; 40 if false { loop {} } else { a };
46 a<|>; 41 a;
42 //^ !
47} 43}
48"#, 44"#,
49 ); 45 );
50 assert_eq!(t, "!");
51} 46}
52 47
53#[test] 48#[test]
54fn never_type_in_generic_args() { 49fn never_type_in_generic_args() {
55 let t = type_at( 50 check_types(
56 r#" 51 r#"
57//- /main.rs
58enum Option<T> { None, Some(T) } 52enum Option<T> { None, Some(T) }
59 53
60fn test() { 54fn test() {
61 let a = if true { Option::None } else { Option::Some(return) }; 55 let a = if true { Option::None } else { Option::Some(return) };
62 a<|>; 56 a;
63} 57} //^ Option<!>
64"#, 58"#,
65 ); 59 );
66 assert_eq!(t, "Option<!>");
67} 60}
68 61
69#[test] 62#[test]
70fn never_type_can_be_reinferred1() { 63fn never_type_can_be_reinferred1() {
71 let t = type_at( 64 check_types(
72 r#" 65 r#"
73//- /main.rs
74fn gen<T>() -> T { loop {} } 66fn gen<T>() -> T { loop {} }
75 67
76fn test() { 68fn test() {
77 let a = gen(); 69 let a = gen();
78 if false { loop {} } else { a }; 70 if false { loop {} } else { a };
79 a<|>; 71 a;
72 //^ ()
80 if false { a }; 73 if false { a };
81} 74}
82"#, 75"#,
83 ); 76 );
84 assert_eq!(t, "()");
85} 77}
86 78
87#[test] 79#[test]
88fn never_type_can_be_reinferred2() { 80fn never_type_can_be_reinferred2() {
89 let t = type_at( 81 check_types(
90 r#" 82 r#"
91//- /main.rs
92enum Option<T> { None, Some(T) } 83enum Option<T> { None, Some(T) }
93 84
94fn test() { 85fn test() {
95 let a = if true { Option::None } else { Option::Some(return) }; 86 let a = if true { Option::None } else { Option::Some(return) };
96 a<|>; 87 a;
88 //^ Option<i32>
97 match 42 { 89 match 42 {
98 42 => a, 90 42 => a,
99 _ => Option::Some(42), 91 _ => Option::Some(42),
@@ -101,19 +93,18 @@ fn test() {
101} 93}
102"#, 94"#,
103 ); 95 );
104 assert_eq!(t, "Option<i32>");
105} 96}
106 97
107#[test] 98#[test]
108fn never_type_can_be_reinferred3() { 99fn never_type_can_be_reinferred3() {
109 let t = type_at( 100 check_types(
110 r#" 101 r#"
111//- /main.rs
112enum Option<T> { None, Some(T) } 102enum Option<T> { None, Some(T) }
113 103
114fn test() { 104fn test() {
115 let a = if true { Option::None } else { Option::Some(return) }; 105 let a = if true { Option::None } else { Option::Some(return) };
116 a<|>; 106 a;
107 //^ Option<&str>
117 match 42 { 108 match 42 {
118 42 => a, 109 42 => a,
119 _ => Option::Some("str"), 110 _ => Option::Some("str"),
@@ -121,82 +112,72 @@ fn test() {
121} 112}
122"#, 113"#,
123 ); 114 );
124 assert_eq!(t, "Option<&str>");
125} 115}
126 116
127#[test] 117#[test]
128fn match_no_arm() { 118fn match_no_arm() {
129 let t = type_at( 119 check_types(
130 r#" 120 r#"
131//- /main.rs
132enum Void {} 121enum Void {}
133 122
134fn test(a: Void) { 123fn test(a: Void) {
135 let t = match a {}; 124 let t = match a {};
136 t<|>; 125 t;
137} 126} //^ !
138"#, 127"#,
139 ); 128 );
140 assert_eq!(t, "!");
141} 129}
142 130
143#[test] 131#[test]
144fn match_unknown_arm() { 132fn match_unknown_arm() {
145 let t = type_at( 133 check_types(
146 r#" 134 r#"
147//- /main.rs
148fn test(a: Option) { 135fn test(a: Option) {
149 let t = match 0 { 136 let t = match 0 {
150 _ => unknown, 137 _ => unknown,
151 }; 138 };
152 t<|>; 139 t;
153} 140} //^ {unknown}
154"#, 141"#,
155 ); 142 );
156 assert_eq!(t, "{unknown}");
157} 143}
158 144
159#[test] 145#[test]
160fn if_never() { 146fn if_never() {
161 let t = type_at( 147 check_types(
162 r#" 148 r#"
163//- /main.rs
164fn test() { 149fn test() {
165 let i = if true { 150 let i = if true {
166 loop {} 151 loop {}
167 } else { 152 } else {
168 3.0 153 3.0
169 }; 154 };
170 i<|>; 155 i;
171} 156} //^ f64
172"#, 157"#,
173 ); 158 );
174 assert_eq!(t, "f64");
175} 159}
176 160
177#[test] 161#[test]
178fn if_else_never() { 162fn if_else_never() {
179 let t = type_at( 163 check_types(
180 r#" 164 r#"
181//- /main.rs
182fn test(input: bool) { 165fn test(input: bool) {
183 let i = if input { 166 let i = if input {
184 2.0 167 2.0
185 } else { 168 } else {
186 return 169 return
187 }; 170 };
188 i<|>; 171 i;
189} 172} //^ f64
190"#, 173"#,
191 ); 174 );
192 assert_eq!(t, "f64");
193} 175}
194 176
195#[test] 177#[test]
196fn match_first_arm_never() { 178fn match_first_arm_never() {
197 let t = type_at( 179 check_types(
198 r#" 180 r#"
199//- /main.rs
200fn test(a: i32) { 181fn test(a: i32) {
201 let i = match a { 182 let i = match a {
202 1 => return, 183 1 => return,
@@ -204,18 +185,16 @@ fn test(a: i32) {
204 3 => loop {}, 185 3 => loop {},
205 _ => 3.0, 186 _ => 3.0,
206 }; 187 };
207 i<|>; 188 i;
208} 189} //^ f64
209"#, 190"#,
210 ); 191 );
211 assert_eq!(t, "f64");
212} 192}
213 193
214#[test] 194#[test]
215fn match_second_arm_never() { 195fn match_second_arm_never() {
216 let t = type_at( 196 check_types(
217 r#" 197 r#"
218//- /main.rs
219fn test(a: i32) { 198fn test(a: i32) {
220 let i = match a { 199 let i = match a {
221 1 => 3.0, 200 1 => 3.0,
@@ -223,45 +202,40 @@ fn test(a: i32) {
223 3 => 3.0, 202 3 => 3.0,
224 _ => return, 203 _ => return,
225 }; 204 };
226 i<|>; 205 i;
227} 206} //^ f64
228"#, 207"#,
229 ); 208 );
230 assert_eq!(t, "f64");
231} 209}
232 210
233#[test] 211#[test]
234fn match_all_arms_never() { 212fn match_all_arms_never() {
235 let t = type_at( 213 check_types(
236 r#" 214 r#"
237//- /main.rs
238fn test(a: i32) { 215fn test(a: i32) {
239 let i = match a { 216 let i = match a {
240 2 => return, 217 2 => return,
241 _ => loop {}, 218 _ => loop {},
242 }; 219 };
243 i<|>; 220 i;
244} 221} //^ !
245"#, 222"#,
246 ); 223 );
247 assert_eq!(t, "!");
248} 224}
249 225
250#[test] 226#[test]
251fn match_no_never_arms() { 227fn match_no_never_arms() {
252 let t = type_at( 228 check_types(
253 r#" 229 r#"
254//- /main.rs
255fn test(a: i32) { 230fn test(a: i32) {
256 let i = match a { 231 let i = match a {
257 2 => 2.0, 232 2 => 2.0,
258 _ => 3.0, 233 _ => 3.0,
259 }; 234 };
260 i<|>; 235 i;
261} 236} //^ f64
262"#, 237"#,
263 ); 238 );
264 assert_eq!(t, "f64");
265} 239}
266 240
267#[test] 241#[test]
@@ -291,40 +265,40 @@ fn test6() {
291 true, 265 true,
292 ); 266 );
293 assert_snapshot!(t, @r###" 267 assert_snapshot!(t, @r###"
294 25..53 '{ ...urn; }': () 268 11..39 '{ ...urn; }': ()
295 35..36 'x': u32 269 21..22 'x': u32
296 44..50 'return': ! 270 30..36 'return': !
297 65..98 '{ ...; }; }': () 271 51..84 '{ ...; }; }': ()
298 75..76 'x': u32 272 61..62 'x': u32
299 84..95 '{ return; }': u32 273 70..81 '{ return; }': u32
300 86..92 'return': ! 274 72..78 'return': !
301 110..139 '{ ... {}; }': () 275 96..125 '{ ... {}; }': ()
302 120..121 'x': u32 276 106..107 'x': u32
303 129..136 'loop {}': ! 277 115..122 'loop {}': !
304 134..136 '{}': () 278 120..122 '{}': ()
305 151..184 '{ ...} }; }': () 279 137..170 '{ ...} }; }': ()
306 161..162 'x': u32 280 147..148 'x': u32
307 170..181 '{ loop {} }': u32 281 156..167 '{ loop {} }': u32
308 172..179 'loop {}': ! 282 158..165 'loop {}': !
309 177..179 '{}': () 283 163..165 '{}': ()
310 196..260 '{ ...} }; }': () 284 182..246 '{ ...} }; }': ()
311 206..207 'x': u32 285 192..193 'x': u32
312 215..257 '{ if t...}; } }': u32 286 201..243 '{ if t...}; } }': u32
313 217..255 'if tru... {}; }': u32 287 203..241 'if tru... {}; }': u32
314 220..224 'true': bool 288 206..210 'true': bool
315 225..237 '{ loop {}; }': u32 289 211..223 '{ loop {}; }': u32
316 227..234 'loop {}': ! 290 213..220 'loop {}': !
317 232..234 '{}': () 291 218..220 '{}': ()
318 243..255 '{ loop {}; }': u32 292 229..241 '{ loop {}; }': u32
319 245..252 'loop {}': ! 293 231..238 'loop {}': !
320 250..252 '{}': () 294 236..238 '{}': ()
321 272..324 '{ ...; }; }': () 295 258..310 '{ ...; }; }': ()
322 282..283 'x': u32 296 268..269 'x': u32
323 291..321 '{ let ...; }; }': u32 297 277..307 '{ let ...; }; }': u32
324 297..298 'y': u32 298 283..284 'y': u32
325 306..318 '{ loop {}; }': u32 299 292..304 '{ loop {}; }': u32
326 308..315 'loop {}': ! 300 294..301 'loop {}': !
327 313..315 '{}': () 301 299..301 '{}': ()
328 "###); 302 "###);
329} 303}
330 304
@@ -341,14 +315,14 @@ fn test1() {
341 true, 315 true,
342 ); 316 );
343 assert_snapshot!(t, @r###" 317 assert_snapshot!(t, @r###"
344 25..98 '{ ..." }; }': () 318 11..84 '{ ..." }; }': ()
345 68..69 'x': u32 319 54..55 'x': u32
346 77..95 '{ loop...foo" }': &str 320 63..81 '{ loop...foo" }': &str
347 79..86 'loop {}': ! 321 65..72 'loop {}': !
348 84..86 '{}': () 322 70..72 '{}': ()
349 88..93 '"foo"': &str 323 74..79 '"foo"': &str
350 77..95: expected u32, got &str 324 63..81: expected u32, got &str
351 88..93: expected u32, got &str 325 74..79: expected u32, got &str
352 "###); 326 "###);
353} 327}
354 328
@@ -381,58 +355,58 @@ fn test3() {
381 true, 355 true,
382 ); 356 );
383 assert_snapshot!(t, @r###" 357 assert_snapshot!(t, @r###"
384 25..99 '{ ...} }; }': () 358 11..85 '{ ...} }; }': ()
385 68..69 'x': u32 359 54..55 'x': u32
386 77..96 '{ loop...k; } }': () 360 63..82 '{ loop...k; } }': ()
387 79..94 'loop { break; }': () 361 65..80 'loop { break; }': ()
388 84..94 '{ break; }': () 362 70..80 '{ break; }': ()
389 86..91 'break': ! 363 72..77 'break': !
390 77..96: expected u32, got () 364 63..82: expected u32, got ()
391 79..94: expected u32, got () 365 65..80: expected u32, got ()
392 111..357 '{ ...; }; }': () 366 97..343 '{ ...; }; }': ()
393 154..155 'x': u32 367 140..141 'x': u32
394 163..189 '{ for ...; }; }': () 368 149..175 '{ for ...; }; }': ()
395 165..186 'for a ...eak; }': () 369 151..172 'for a ...eak; }': ()
396 169..170 'a': {unknown} 370 155..156 'a': {unknown}
397 174..175 'b': {unknown} 371 160..161 'b': {unknown}
398 176..186 '{ break; }': () 372 162..172 '{ break; }': ()
399 178..183 'break': ! 373 164..169 'break': !
400 240..241 'x': u32 374 226..227 'x': u32
401 249..267 '{ for ... {}; }': () 375 235..253 '{ for ... {}; }': ()
402 251..264 'for a in b {}': () 376 237..250 'for a in b {}': ()
403 255..256 'a': {unknown} 377 241..242 'a': {unknown}
404 260..261 'b': {unknown} 378 246..247 'b': {unknown}
405 262..264 '{}': () 379 248..250 '{}': ()
406 318..319 'x': u32 380 304..305 'x': u32
407 327..354 '{ for ...; }; }': () 381 313..340 '{ for ...; }; }': ()
408 329..351 'for a ...urn; }': () 382 315..337 'for a ...urn; }': ()
409 333..334 'a': {unknown} 383 319..320 'a': {unknown}
410 338..339 'b': {unknown} 384 324..325 'b': {unknown}
411 340..351 '{ return; }': () 385 326..337 '{ return; }': ()
412 342..348 'return': ! 386 328..334 'return': !
413 163..189: expected u32, got () 387 149..175: expected u32, got ()
414 249..267: expected u32, got () 388 235..253: expected u32, got ()
415 327..354: expected u32, got () 389 313..340: expected u32, got ()
416 369..668 '{ ...; }; }': () 390 355..654 '{ ...; }; }': ()
417 412..413 'x': u32 391 398..399 'x': u32
418 421..447 '{ whil...; }; }': () 392 407..433 '{ whil...; }; }': ()
419 423..444 'while ...eak; }': () 393 409..430 'while ...eak; }': ()
420 429..433 'true': bool 394 415..419 'true': bool
421 434..444 '{ break; }': () 395 420..430 '{ break; }': ()
422 436..441 'break': ! 396 422..427 'break': !
423 551..552 'x': u32 397 537..538 'x': u32
424 560..578 '{ whil... {}; }': () 398 546..564 '{ whil... {}; }': ()
425 562..575 'while true {}': () 399 548..561 'while true {}': ()
426 568..572 'true': bool 400 554..558 'true': bool
427 573..575 '{}': () 401 559..561 '{}': ()
428 629..630 'x': u32 402 615..616 'x': u32
429 638..665 '{ whil...; }; }': () 403 624..651 '{ whil...; }; }': ()
430 640..662 'while ...urn; }': () 404 626..648 'while ...urn; }': ()
431 646..650 'true': bool 405 632..636 'true': bool
432 651..662 '{ return; }': () 406 637..648 '{ return; }': ()
433 653..659 'return': ! 407 639..645 'return': !
434 421..447: expected u32, got () 408 407..433: expected u32, got ()
435 560..578: expected u32, got () 409 546..564: expected u32, got ()
436 638..665: expected u32, got () 410 624..651: expected u32, got ()
437 "###); 411 "###);
438} 412}
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs
index fe62587c0..f937426bd 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -30,54 +30,54 @@ fn test(x: &i32) {
30} 30}
31"#), 31"#),
32 @r###" 32 @r###"
33 9..10 'x': &i32 33 8..9 'x': &i32
34 18..369 '{ ...o_x; }': () 34 17..368 '{ ...o_x; }': ()
35 28..29 'y': &i32 35 27..28 'y': &i32
36 32..33 'x': &i32 36 31..32 'x': &i32
37 43..45 '&z': &i32 37 42..44 '&z': &i32
38 44..45 'z': i32 38 43..44 'z': i32
39 48..49 'x': &i32 39 47..48 'x': &i32
40 59..60 'a': i32 40 58..59 'a': i32
41 63..64 'z': i32 41 62..63 'z': i32
42 74..80 '(c, d)': (i32, &str) 42 73..79 '(c, d)': (i32, &str)
43 75..76 'c': i32 43 74..75 'c': i32
44 78..79 'd': &str 44 77..78 'd': &str
45 83..95 '(1, "hello")': (i32, &str) 45 82..94 '(1, "hello")': (i32, &str)
46 84..85 '1': i32 46 83..84 '1': i32
47 87..94 '"hello"': &str 47 86..93 '"hello"': &str
48 102..152 'for (e... }': () 48 101..151 'for (e... }': ()
49 106..112 '(e, f)': ({unknown}, {unknown}) 49 105..111 '(e, f)': ({unknown}, {unknown})
50 107..108 'e': {unknown} 50 106..107 'e': {unknown}
51 110..111 'f': {unknown} 51 109..110 'f': {unknown}
52 116..125 'some_iter': {unknown} 52 115..124 'some_iter': {unknown}
53 126..152 '{ ... }': () 53 125..151 '{ ... }': ()
54 140..141 'g': {unknown} 54 139..140 'g': {unknown}
55 144..145 'e': {unknown} 55 143..144 'e': {unknown}
56 158..205 'if let... }': () 56 157..204 'if let... }': ()
57 165..170 '[val]': [{unknown}] 57 164..169 '[val]': [{unknown}]
58 166..169 'val': {unknown} 58 165..168 'val': {unknown}
59 173..176 'opt': [{unknown}] 59 172..175 'opt': [{unknown}]
60 177..205 '{ ... }': () 60 176..204 '{ ... }': ()
61 191..192 'h': {unknown} 61 190..191 'h': {unknown}
62 195..198 'val': {unknown} 62 194..197 'val': {unknown}
63 215..221 'lambda': |u64, u64, i32| -> i32 63 214..220 'lambda': |u64, u64, i32| -> i32
64 224..256 '|a: u6...b; c }': |u64, u64, i32| -> i32 64 223..255 '|a: u6...b; c }': |u64, u64, i32| -> i32
65 225..226 'a': u64 65 224..225 'a': u64
66 233..234 'b': u64 66 232..233 'b': u64
67 236..237 'c': i32 67 235..236 'c': i32
68 244..256 '{ a + b; c }': i32 68 243..255 '{ a + b; c }': i32
69 246..247 'a': u64 69 245..246 'a': u64
70 246..251 'a + b': u64 70 245..250 'a + b': u64
71 250..251 'b': u64 71 249..250 'b': u64
72 253..254 'c': i32 72 252..253 'c': i32
73 267..279 'ref ref_to_x': &&i32 73 266..278 'ref ref_to_x': &&i32
74 282..283 'x': &i32 74 281..282 'x': &i32
75 293..302 'mut mut_x': &i32 75 292..301 'mut mut_x': &i32
76 305..306 'x': &i32 76 304..305 'x': &i32
77 316..336 'ref mu...f_to_x': &mut &i32 77 315..335 'ref mu...f_to_x': &mut &i32
78 339..340 'x': &i32 78 338..339 'x': &i32
79 350..351 'k': &mut &i32 79 349..350 'k': &mut &i32
80 354..366 'mut_ref_to_x': &mut &i32 80 353..365 'mut_ref_to_x': &mut &i32
81 "### 81 "###
82 ); 82 );
83} 83}
@@ -97,47 +97,47 @@ fn test(x: &i32) {
97} 97}
98"#, true), 98"#, true),
99 @r###" 99 @r###"
100 18..29 '{ loop {} }': T 100 17..28 '{ loop {} }': T
101 20..27 'loop {}': ! 101 19..26 'loop {}': !
102 25..27 '{}': () 102 24..26 '{}': ()
103 38..39 'x': &i32 103 37..38 'x': &i32
104 47..209 '{ ...) {} }': () 104 46..208 '{ ...) {} }': ()
105 53..76 'if let...y() {}': () 105 52..75 'if let...y() {}': ()
106 60..65 '"foo"': &str 106 59..64 '"foo"': &str
107 60..65 '"foo"': &str 107 59..64 '"foo"': &str
108 68..71 'any': fn any<&str>() -> &str 108 67..70 'any': fn any<&str>() -> &str
109 68..73 'any()': &str 109 67..72 'any()': &str
110 74..76 '{}': () 110 73..75 '{}': ()
111 81..100 'if let...y() {}': () 111 80..99 'if let...y() {}': ()
112 88..89 '1': i32 112 87..88 '1': i32
113 88..89 '1': i32 113 87..88 '1': i32
114 92..95 'any': fn any<i32>() -> i32 114 91..94 'any': fn any<i32>() -> i32
115 92..97 'any()': i32 115 91..96 'any()': i32
116 98..100 '{}': () 116 97..99 '{}': ()
117 105..127 'if let...y() {}': () 117 104..126 'if let...y() {}': ()
118 112..116 '1u32': u32 118 111..115 '1u32': u32
119 112..116 '1u32': u32 119 111..115 '1u32': u32
120 119..122 'any': fn any<u32>() -> u32 120 118..121 'any': fn any<u32>() -> u32
121 119..124 'any()': u32 121 118..123 'any()': u32
122 125..127 '{}': () 122 124..126 '{}': ()
123 132..154 'if let...y() {}': () 123 131..153 'if let...y() {}': ()
124 139..143 '1f32': f32 124 138..142 '1f32': f32
125 139..143 '1f32': f32 125 138..142 '1f32': f32
126 146..149 'any': fn any<f32>() -> f32 126 145..148 'any': fn any<f32>() -> f32
127 146..151 'any()': f32 127 145..150 'any()': f32
128 152..154 '{}': () 128 151..153 '{}': ()
129 159..180 'if let...y() {}': () 129 158..179 'if let...y() {}': ()
130 166..169 '1.0': f64 130 165..168 '1.0': f64
131 166..169 '1.0': f64 131 165..168 '1.0': f64
132 172..175 'any': fn any<f64>() -> f64 132 171..174 'any': fn any<f64>() -> f64
133 172..177 'any()': f64 133 171..176 'any()': f64
134 178..180 '{}': () 134 177..179 '{}': ()
135 185..207 'if let...y() {}': () 135 184..206 'if let...y() {}': ()
136 192..196 'true': bool 136 191..195 'true': bool
137 192..196 'true': bool 137 191..195 'true': bool
138 199..202 'any': fn any<bool>() -> bool 138 198..201 'any': fn any<bool>() -> bool
139 199..204 'any()': bool 139 198..203 'any()': bool
140 205..207 '{}': () 140 204..206 '{}': ()
141 "### 141 "###
142 ); 142 );
143} 143}
@@ -152,16 +152,16 @@ fn test(x: &i32) {
152} 152}
153"#, true), 153"#, true),
154 @r###" 154 @r###"
155 9..10 'x': &i32 155 8..9 'x': &i32
156 18..76 '{ ...2 {} }': () 156 17..75 '{ ...2 {} }': ()
157 24..46 'if let...u32 {}': () 157 23..45 'if let...u32 {}': ()
158 31..36 '1..76': u32 158 30..35 '1..76': u32
159 39..43 '2u32': u32 159 38..42 '2u32': u32
160 44..46 '{}': () 160 43..45 '{}': ()
161 51..74 'if let...u32 {}': () 161 50..73 'if let...u32 {}': ()
162 58..64 '1..=76': u32 162 57..63 '1..=76': u32
163 67..71 '2u32': u32 163 66..70 '2u32': u32
164 72..74 '{}': () 164 71..73 '{}': ()
165 "### 165 "###
166 ); 166 );
167} 167}
@@ -178,19 +178,19 @@ fn test() {
178} 178}
179"#), 179"#),
180 @r###" 180 @r###"
181 28..79 '{ ...(1); }': () 181 27..78 '{ ...(1); }': ()
182 38..42 'A(n)': A<i32> 182 37..41 'A(n)': A<i32>
183 40..41 'n': &i32 183 39..40 'n': &i32
184 45..50 '&A(1)': &A<i32> 184 44..49 '&A(1)': &A<i32>
185 46..47 'A': A<i32>(i32) -> A<i32> 185 45..46 'A': A<i32>(i32) -> A<i32>
186 46..50 'A(1)': A<i32> 186 45..49 'A(1)': A<i32>
187 48..49 '1': i32 187 47..48 '1': i32
188 60..64 'A(n)': A<i32> 188 59..63 'A(n)': A<i32>
189 62..63 'n': &mut i32 189 61..62 'n': &mut i32
190 67..76 '&mut A(1)': &mut A<i32> 190 66..75 '&mut A(1)': &mut A<i32>
191 72..73 'A': A<i32>(i32) -> A<i32> 191 71..72 'A': A<i32>(i32) -> A<i32>
192 72..76 'A(1)': A<i32> 192 71..75 'A(1)': A<i32>
193 74..75 '1': i32 193 73..74 '1': i32
194 "### 194 "###
195 ); 195 );
196} 196}
@@ -206,18 +206,18 @@ fn test() {
206} 206}
207"#), 207"#),
208 @r###" 208 @r###"
209 11..57 '{ ...= v; }': () 209 10..56 '{ ...= v; }': ()
210 21..22 'v': &(i32, &i32) 210 20..21 'v': &(i32, &i32)
211 25..33 '&(1, &2)': &(i32, &i32) 211 24..32 '&(1, &2)': &(i32, &i32)
212 26..33 '(1, &2)': (i32, &i32) 212 25..32 '(1, &2)': (i32, &i32)
213 27..28 '1': i32 213 26..27 '1': i32
214 30..32 '&2': &i32 214 29..31 '&2': &i32
215 31..32 '2': i32 215 30..31 '2': i32
216 43..50 '(_, &w)': (i32, &i32) 216 42..49 '(_, &w)': (i32, &i32)
217 44..45 '_': i32 217 43..44 '_': i32
218 47..49 '&w': &i32 218 46..48 '&w': &i32
219 48..49 'w': i32 219 47..48 'w': i32
220 53..54 'v': &(i32, &i32) 220 52..53 'v': &(i32, &i32)
221 "### 221 "###
222 ); 222 );
223} 223}
@@ -242,30 +242,87 @@ fn test() {
242} 242}
243"#), 243"#),
244 @r###" 244 @r###"
245 11..210 '{ ... } }': () 245 10..209 '{ ... } }': ()
246 21..26 'slice': &[f64] 246 20..25 'slice': &[f64]
247 37..43 '&[0.0]': &[f64; _] 247 36..42 '&[0.0]': &[f64; _]
248 38..43 '[0.0]': [f64; _] 248 37..42 '[0.0]': [f64; _]
249 39..42 '0.0': f64 249 38..41 '0.0': f64
250 49..208 'match ... }': () 250 48..207 'match ... }': ()
251 55..60 'slice': &[f64] 251 54..59 'slice': &[f64]
252 71..74 '&[]': &[f64] 252 70..73 '&[]': &[f64]
253 72..74 '[]': [f64] 253 71..73 '[]': [f64]
254 78..80 '{}': () 254 77..79 '{}': ()
255 90..94 '&[a]': &[f64] 255 89..93 '&[a]': &[f64]
256 91..94 '[a]': [f64] 256 90..93 '[a]': [f64]
257 92..93 'a': f64 257 91..92 'a': f64
258 98..124 '{ ... }': () 258 97..123 '{ ... }': ()
259 112..113 'a': f64 259 111..112 'a': f64
260 134..141 '&[b, c]': &[f64] 260 133..140 '&[b, c]': &[f64]
261 135..141 '[b, c]': [f64] 261 134..140 '[b, c]': [f64]
262 136..137 'b': f64 262 135..136 'b': f64
263 139..140 'c': f64 263 138..139 'c': f64
264 145..186 '{ ... }': () 264 144..185 '{ ... }': ()
265 159..160 'b': f64 265 158..159 'b': f64
266 174..175 'c': f64 266 173..174 'c': f64
267 195..196 '_': &[f64] 267 194..195 '_': &[f64]
268 200..202 '{}': () 268 199..201 '{}': ()
269 "###
270 );
271}
272
273#[test]
274fn infer_pattern_match_string_literal() {
275 assert_snapshot!(
276 infer_with_mismatches(r#"
277fn test() {
278 let s: &str = "hello";
279 match s {
280 "hello" => {}
281 _ => {}
282 }
283}
284"#, true),
285 @r###"
286 10..98 '{ ... } }': ()
287 20..21 's': &str
288 30..37 '"hello"': &str
289 43..96 'match ... }': ()
290 49..50 's': &str
291 61..68 '"hello"': &str
292 61..68 '"hello"': &str
293 72..74 '{}': ()
294 83..84 '_': &str
295 88..90 '{}': ()
296 "###
297 );
298}
299
300#[test]
301fn infer_pattern_match_or() {
302 assert_snapshot!(
303 infer_with_mismatches(r#"
304fn test() {
305 let s: &str = "hello";
306 match s {
307 "hello" | "world" => {}
308 _ => {}
309 }
310}
311"#, true),
312 @r###"
313 10..108 '{ ... } }': ()
314 20..21 's': &str
315 30..37 '"hello"': &str
316 43..106 'match ... }': ()
317 49..50 's': &str
318 61..68 '"hello"': &str
319 61..68 '"hello"': &str
320 61..78 '"hello...world"': &str
321 71..78 '"world"': &str
322 71..78 '"world"': &str
323 82..84 '{}': ()
324 93..94 '_': &str
325 98..100 '{}': ()
269 "### 326 "###
270 ); 327 );
271} 328}
@@ -288,25 +345,25 @@ fn test() {
288} 345}
289"#), 346"#),
290 @r###" 347 @r###"
291 11..180 '{ ... } }': () 348 10..179 '{ ... } }': ()
292 21..24 'arr': [f64; _] 349 20..23 'arr': [f64; _]
293 37..47 '[0.0, 1.0]': [f64; _] 350 36..46 '[0.0, 1.0]': [f64; _]
294 38..41 '0.0': f64 351 37..40 '0.0': f64
295 43..46 '1.0': f64 352 42..45 '1.0': f64
296 53..178 'match ... }': () 353 52..177 'match ... }': ()
297 59..62 'arr': [f64; _] 354 58..61 'arr': [f64; _]
298 73..81 '[1.0, a]': [f64; _] 355 72..80 '[1.0, a]': [f64; _]
299 74..77 '1.0': f64 356 73..76 '1.0': f64
300 74..77 '1.0': f64 357 73..76 '1.0': f64
301 79..80 'a': f64 358 78..79 'a': f64
302 85..111 '{ ... }': () 359 84..110 '{ ... }': ()
303 99..100 'a': f64 360 98..99 'a': f64
304 121..127 '[b, c]': [f64; _] 361 120..126 '[b, c]': [f64; _]
305 122..123 'b': f64 362 121..122 'b': f64
306 125..126 'c': f64 363 124..125 'c': f64
307 131..172 '{ ... }': () 364 130..171 '{ ... }': ()
308 145..146 'b': f64 365 144..145 'b': f64
309 160..161 'c': f64 366 159..160 'c': f64
310 "### 367 "###
311 ); 368 );
312} 369}
@@ -339,31 +396,31 @@ fn test() {
339} 396}
340"#), 397"#),
341 @r###" 398 @r###"
342 68..289 '{ ... d; }': () 399 67..288 '{ ... d; }': ()
343 78..79 'e': E 400 77..78 'e': E
344 82..95 'E::A { x: 3 }': E 401 81..94 'E::A { x: 3 }': E
345 92..93 '3': usize 402 91..92 '3': usize
346 106..113 'S(y, z)': S 403 105..112 'S(y, z)': S
347 108..109 'y': u32 404 107..108 'y': u32
348 111..112 'z': E 405 110..111 'z': E
349 116..119 'foo': S 406 115..118 'foo': S
350 129..148 'E::A {..._var }': E 407 128..147 'E::A {..._var }': E
351 139..146 'new_var': usize 408 138..145 'new_var': usize
352 151..152 'e': E 409 150..151 'e': E
353 159..245 'match ... }': usize 410 158..244 'match ... }': usize
354 165..166 'e': E 411 164..165 'e': E
355 177..187 'E::A { x }': E 412 176..186 'E::A { x }': E
356 184..185 'x': usize 413 183..184 'x': usize
357 191..192 'x': usize 414 190..191 'x': usize
358 202..206 'E::B': E 415 201..205 'E::B': E
359 210..213 'foo': bool 416 209..212 'foo': bool
360 217..218 '1': usize 417 216..217 '1': usize
361 228..232 'E::B': E 418 227..231 'E::B': E
362 236..238 '10': usize 419 235..237 '10': usize
363 256..275 'ref d ...{ .. }': &E 420 255..274 'ref d ...{ .. }': &E
364 264..275 'E::A { .. }': E 421 263..274 'E::A { .. }': E
365 278..279 'e': E 422 277..278 'e': E
366 285..286 'd': &E 423 284..285 'd': &E
367 "### 424 "###
368 ); 425 );
369} 426}
@@ -389,20 +446,20 @@ impl E {
389} 446}
390"#), 447"#),
391 @r###" 448 @r###"
392 76..218 '{ ... }': () 449 75..217 '{ ... }': ()
393 86..211 'match ... }': () 450 85..210 'match ... }': ()
394 93..100 'loop {}': ! 451 92..99 'loop {}': !
395 98..100 '{}': () 452 97..99 '{}': ()
396 116..129 'Self::A { x }': E 453 115..128 'Self::A { x }': E
397 126..127 'x': usize 454 125..126 'x': usize
398 133..139 '{ x; }': () 455 132..138 '{ x; }': ()
399 135..136 'x': usize 456 134..135 'x': usize
400 153..163 'Self::B(x)': E 457 152..162 'Self::B(x)': E
401 161..162 'x': usize 458 160..161 'x': usize
402 167..173 '{ x; }': () 459 166..172 '{ x; }': ()
403 169..170 'x': usize 460 168..169 'x': usize
404 187..194 'Self::C': E 461 186..193 'Self::C': E
405 198..200 '{}': () 462 197..199 '{}': ()
406 "### 463 "###
407 ); 464 );
408} 465}
@@ -430,23 +487,23 @@ fn test(a1: A<u32>, o: Option<u64>) {
430} 487}
431"#), 488"#),
432 @r###" 489 @r###"
433 79..81 'a1': A<u32> 490 78..80 'a1': A<u32>
434 91..92 'o': Option<u64> 491 90..91 'o': Option<u64>
435 107..244 '{ ... }; }': () 492 106..243 '{ ... }; }': ()
436 117..128 'A { x: x2 }': A<u32> 493 116..127 'A { x: x2 }': A<u32>
437 124..126 'x2': u32 494 123..125 'x2': u32
438 131..133 'a1': A<u32> 495 130..132 'a1': A<u32>
439 143..161 'A::<i6...: x3 }': A<i64> 496 142..160 'A::<i6...: x3 }': A<i64>
440 157..159 'x3': i64 497 156..158 'x3': i64
441 164..174 'A { x: 1 }': A<i64> 498 163..173 'A { x: 1 }': A<i64>
442 171..172 '1': i64 499 170..171 '1': i64
443 180..241 'match ... }': u64 500 179..240 'match ... }': u64
444 186..187 'o': Option<u64> 501 185..186 'o': Option<u64>
445 198..213 'Option::Some(t)': Option<u64> 502 197..212 'Option::Some(t)': Option<u64>
446 211..212 't': u64 503 210..211 't': u64
447 217..218 't': u64 504 216..217 't': u64
448 228..229 '_': Option<u64> 505 227..228 '_': Option<u64>
449 233..234 '1': u64 506 232..233 '1': u64
450 "### 507 "###
451 ); 508 );
452} 509}
@@ -470,27 +527,27 @@ fn test() {
470} 527}
471"#, true), 528"#, true),
472 @r###" 529 @r###"
473 74..75 '1': usize 530 73..74 '1': usize
474 88..310 '{ ...atch }': () 531 87..309 '{ ...atch }': ()
475 98..99 'a': Option<u32> 532 97..98 'a': Option<u32>
476 115..119 'None': Option<u32> 533 114..118 'None': Option<u32>
477 129..130 'b': Option<i64> 534 128..129 'b': Option<i64>
478 146..183 'match ... }': Option<i64> 535 145..182 'match ... }': Option<i64>
479 152..153 'a': Option<u32> 536 151..152 'a': Option<u32>
480 164..168 'None': Option<u32> 537 163..167 'None': Option<u32>
481 172..176 'None': Option<i64> 538 171..175 'None': Option<i64>
482 193..194 '_': () 539 192..193 '_': ()
483 201..224 'match ... Foo }': Foo 540 200..223 'match ... Foo }': Foo
484 207..209 '()': () 541 206..208 '()': ()
485 212..215 'Foo': Foo 542 211..214 'Foo': Foo
486 219..222 'Foo': Foo 543 218..221 'Foo': Foo
487 255..256 '_': () 544 254..255 '_': ()
488 263..286 'match ... Bar }': usize 545 262..285 'match ... Bar }': usize
489 269..271 '()': () 546 268..270 '()': ()
490 274..277 'Bar': usize 547 273..276 'Bar': usize
491 281..284 'Bar': usize 548 280..283 'Bar': usize
492 201..224: expected (), got Foo 549 200..223: expected (), got Foo
493 263..286: expected (), got usize 550 262..285: expected (), got usize
494 "### 551 "###
495 ); 552 );
496} 553}
@@ -507,18 +564,18 @@ fn main() {
507 s if s.foo() => (), 564 s if s.foo() => (),
508 } 565 }
509} 566}
510 "#), @" 567 "#), @r###"
511 28..32 'self': &S 568 27..31 'self': &S
512 42..51 '{ false }': bool 569 41..50 '{ false }': bool
513 44..49 'false': bool 570 43..48 'false': bool
514 65..116 '{ ... } }': () 571 64..115 '{ ... } }': ()
515 71..114 'match ... }': () 572 70..113 'match ... }': ()
516 77..78 'S': S 573 76..77 'S': S
517 89..90 's': S 574 88..89 's': S
518 94..95 's': S 575 93..94 's': S
519 94..101 's.foo()': bool 576 93..100 's.foo()': bool
520 105..107 '()': () 577 104..106 '()': ()
521 ") 578 "###)
522} 579}
523 580
524#[test] 581#[test]
@@ -538,35 +595,60 @@ fn test() {
538} 595}
539"#), 596"#),
540 @r###" 597 @r###"
541 94..95 't': T 598 93..94 't': T
542 100..101 'f': F 599 99..100 'f': F
543 111..122 '{ loop {} }': U 600 110..121 '{ loop {} }': U
544 113..120 'loop {}': ! 601 112..119 'loop {}': !
545 118..120 '{}': () 602 117..119 '{}': ()
546 134..233 '{ ... x); }': () 603 133..232 '{ ... x); }': ()
547 140..143 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32 604 139..142 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32
548 140..167 'foo(&(...y)| x)': i32 605 139..166 'foo(&(...y)| x)': i32
549 144..153 '&(1, "a")': &(i32, &str) 606 143..152 '&(1, "a")': &(i32, &str)
550 145..153 '(1, "a")': (i32, &str) 607 144..152 '(1, "a")': (i32, &str)
551 146..147 '1': i32 608 145..146 '1': i32
552 149..152 '"a"': &str 609 148..151 '"a"': &str
553 155..166 '|&(x, y)| x': |&(i32, &str)| -> i32 610 154..165 '|&(x, y)| x': |&(i32, &str)| -> i32
554 156..163 '&(x, y)': &(i32, &str) 611 155..162 '&(x, y)': &(i32, &str)
555 157..163 '(x, y)': (i32, &str) 612 156..162 '(x, y)': (i32, &str)
556 158..159 'x': i32 613 157..158 'x': i32
557 161..162 'y': &str 614 160..161 'y': &str
558 165..166 'x': i32 615 164..165 'x': i32
559 204..207 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32 616 203..206 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32
560 204..230 'foo(&(...y)| x)': &i32 617 203..229 'foo(&(...y)| x)': &i32
561 208..217 '&(1, "a")': &(i32, &str) 618 207..216 '&(1, "a")': &(i32, &str)
562 209..217 '(1, "a")': (i32, &str) 619 208..216 '(1, "a")': (i32, &str)
563 210..211 '1': i32 620 209..210 '1': i32
564 213..216 '"a"': &str 621 212..215 '"a"': &str
565 219..229 '|(x, y)| x': |&(i32, &str)| -> &i32 622 218..228 '|(x, y)| x': |&(i32, &str)| -> &i32
566 220..226 '(x, y)': (i32, &str) 623 219..225 '(x, y)': (i32, &str)
567 221..222 'x': &i32 624 220..221 'x': &i32
568 224..225 'y': &&str 625 223..224 'y': &&str
569 228..229 'x': &i32 626 227..228 'x': &i32
627 "###
628 );
629}
630
631#[test]
632fn slice_tail_pattern() {
633 assert_snapshot!(
634 infer(r#"
635fn foo(params: &[i32]) {
636 match params {
637 [head, tail @ ..] => {
638 }
639 }
640}
641"#),
642 @r###"
643 7..13 'params': &[i32]
644 23..92 '{ ... } }': ()
645 29..90 'match ... }': ()
646 35..41 'params': &[i32]
647 52..69 '[head,... @ ..]': [i32]
648 53..57 'head': &i32
649 59..68 'tail @ ..': &[i32]
650 66..68 '..': [i32]
651 73..84 '{ }': ()
570 "### 652 "###
571 ); 653 );
572} 654}
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index 1f004bd63..4367621fc 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -1,10 +1,7 @@
1use insta::assert_snapshot; 1use insta::assert_snapshot;
2use ra_db::fixture::WithFixture;
3use test_utils::mark; 2use test_utils::mark;
4 3
5use crate::test_db::TestDB; 4use super::{check_types, infer};
6
7use super::infer;
8 5
9#[test] 6#[test]
10fn bug_484() { 7fn bug_484() {
@@ -15,11 +12,11 @@ fn test() {
15} 12}
16"#), 13"#),
17 @r###" 14 @r###"
18 11..37 '{ l... {}; }': () 15 10..36 '{ l... {}; }': ()
19 20..21 'x': () 16 19..20 'x': ()
20 24..34 'if true {}': () 17 23..33 'if true {}': ()
21 27..31 'true': bool 18 26..30 'true': bool
22 32..34 '{}': () 19 31..33 '{}': ()
23 "### 20 "###
24 ); 21 );
25} 22}
@@ -35,10 +32,10 @@ fn test(x: X) {
35} 32}
36"#), 33"#),
37 @r###" 34 @r###"
38 20..21 'x': X 35 19..20 'x': X
39 26..47 '{ ...eld; }': () 36 25..46 '{ ...eld; }': ()
40 32..33 'x': X 37 31..32 'x': X
41 32..44 'x.some_field': {unknown} 38 31..43 'x.some_field': {unknown}
42 "### 39 "###
43 ); 40 );
44} 41}
@@ -56,14 +53,14 @@ fn test() {
56} 53}
57"#), 54"#),
58 @r###" 55 @r###"
59 11..89 '{ ... } }': () 56 10..88 '{ ... } }': ()
60 17..21 'X {}': {unknown} 57 16..20 'X {}': {unknown}
61 27..87 'match ... }': () 58 26..86 'match ... }': ()
62 33..34 'x': {unknown} 59 32..33 'x': {unknown}
63 45..52 'A::B {}': {unknown} 60 44..51 'A::B {}': {unknown}
64 56..58 '()': () 61 55..57 '()': ()
65 68..74 'A::Y()': {unknown} 62 67..73 'A::Y()': {unknown}
66 78..80 '()': () 63 77..79 '()': ()
67 "### 64 "###
68 ); 65 );
69} 66}
@@ -78,12 +75,12 @@ fn quux() {
78} 75}
79"#), 76"#),
80 @r###" 77 @r###"
81 11..41 '{ ...+ y; }': () 78 10..40 '{ ...+ y; }': ()
82 21..22 'y': i32 79 20..21 'y': i32
83 25..27 '92': i32 80 24..26 '92': i32
84 33..34 '1': i32 81 32..33 '1': i32
85 33..38 '1 + y': i32 82 32..37 '1 + y': i32
86 37..38 'y': i32 83 36..37 'y': i32
87 "### 84 "###
88 ); 85 );
89} 86}
@@ -100,13 +97,13 @@ fn test() {
100} 97}
101"#), 98"#),
102 @r###" 99 @r###"
103 11..48 '{ ...&y]; }': () 100 10..47 '{ ...&y]; }': ()
104 21..22 'y': &{unknown} 101 20..21 'y': &{unknown}
105 25..32 'unknown': &{unknown} 102 24..31 'unknown': &{unknown}
106 38..45 '[y, &y]': [&&{unknown}; _] 103 37..44 '[y, &y]': [&&{unknown}; _]
107 39..40 'y': &{unknown} 104 38..39 'y': &{unknown}
108 42..44 '&y': &&{unknown} 105 41..43 '&y': &&{unknown}
109 43..44 'y': &{unknown} 106 42..43 'y': &{unknown}
110 "### 107 "###
111 ); 108 );
112} 109}
@@ -122,20 +119,20 @@ fn test() {
122} 119}
123"#), 120"#),
124 @r###" 121 @r###"
125 11..80 '{ ...x)]; }': () 122 10..79 '{ ...x)]; }': ()
126 21..22 'x': &&{unknown} 123 20..21 'x': &&{unknown}
127 25..32 'unknown': &&{unknown} 124 24..31 'unknown': &&{unknown}
128 42..43 'y': &&{unknown} 125 41..42 'y': &&{unknown}
129 46..53 'unknown': &&{unknown} 126 45..52 'unknown': &&{unknown}
130 59..77 '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown}); _] 127 58..76 '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown}); _]
131 60..66 '(x, y)': (&&&{unknown}, &&&{unknown}) 128 59..65 '(x, y)': (&&&{unknown}, &&&{unknown})
132 61..62 'x': &&{unknown} 129 60..61 'x': &&{unknown}
133 64..65 'y': &&{unknown} 130 63..64 'y': &&{unknown}
134 68..76 '(&y, &x)': (&&&{unknown}, &&&{unknown}) 131 67..75 '(&y, &x)': (&&&{unknown}, &&&{unknown})
135 69..71 '&y': &&&{unknown} 132 68..70 '&y': &&&{unknown}
136 70..71 'y': &&{unknown} 133 69..70 'y': &&{unknown}
137 73..75 '&x': &&&{unknown} 134 72..74 '&x': &&&{unknown}
138 74..75 'x': &&{unknown} 135 73..74 'x': &&{unknown}
139 "### 136 "###
140 ); 137 );
141} 138}
@@ -157,12 +154,12 @@ fn write() {
157} 154}
158"#), 155"#),
159 @r###" 156 @r###"
160 54..139 '{ ... } }': () 157 53..138 '{ ... } }': ()
161 60..137 'match ... }': () 158 59..136 'match ... }': ()
162 66..83 'someth...nknown': Maybe<{unknown}> 159 65..82 'someth...nknown': Maybe<{unknown}>
163 94..124 'Maybe:...thing)': Maybe<{unknown}> 160 93..123 'Maybe:...thing)': Maybe<{unknown}>
164 106..123 'ref mu...ething': &mut {unknown} 161 105..122 'ref mu...ething': &mut {unknown}
165 128..130 '()': () 162 127..129 '()': ()
166 "### 163 "###
167 ); 164 );
168} 165}
@@ -178,13 +175,13 @@ fn test_line_buffer() {
178} 175}
179"#), 176"#),
180 @r###" 177 @r###"
181 23..53 '{ ...n']; }': () 178 22..52 '{ ...n']; }': ()
182 29..50 '&[0, b...b'\n']': &[u8; _] 179 28..49 '&[0, b...b'\n']': &[u8; _]
183 30..50 '[0, b'...b'\n']': [u8; _] 180 29..49 '[0, b'...b'\n']': [u8; _]
184 31..32 '0': u8 181 30..31 '0': u8
185 34..39 'b'\n'': u8 182 33..38 'b'\n'': u8
186 41..42 '1': u8 183 40..41 '1': u8
187 44..49 'b'\n'': u8 184 43..48 'b'\n'': u8
188 "### 185 "###
189 ); 186 );
190} 187}
@@ -201,14 +198,14 @@ pub fn compute() {
201} 198}
202"#), 199"#),
203 @r###" 200 @r###"
204 18..108 '{ ... } }': () 201 17..107 '{ ... } }': ()
205 24..106 'match ... }': () 202 23..105 'match ... }': ()
206 30..37 'nope!()': {unknown} 203 29..36 'nope!()': {unknown}
207 48..94 'SizeSk...tail }': {unknown} 204 47..93 'SizeSk...tail }': {unknown}
208 82..86 'true': bool 205 81..85 'true': bool
209 82..86 'true': bool 206 81..85 'true': bool
210 88..92 'tail': {unknown} 207 87..91 'tail': {unknown}
211 98..100 '{}': () 208 97..99 '{}': ()
212 "### 209 "###
213 ); 210 );
214} 211}
@@ -225,14 +222,14 @@ pub fn primitive_type() {
225} 222}
226"#), 223"#),
227 @r###" 224 @r###"
228 25..106 '{ ... } }': () 225 24..105 '{ ... } }': ()
229 31..104 'match ... }': () 226 30..103 'match ... }': ()
230 37..42 '*self': {unknown} 227 36..41 '*self': {unknown}
231 38..42 'self': {unknown} 228 37..41 'self': {unknown}
232 53..91 'Borrow...), ..}': {unknown} 229 52..90 'Borrow...), ..}': {unknown}
233 74..86 'Primitive(p)': {unknown} 230 73..85 'Primitive(p)': {unknown}
234 84..85 'p': {unknown} 231 83..84 'p': {unknown}
235 95..97 '{}': () 232 94..96 '{}': ()
236 "### 233 "###
237 ); 234 );
238} 235}
@@ -259,29 +256,29 @@ fn extra_compiler_flags() {
259} 256}
260"#), 257"#),
261 @r###" 258 @r###"
262 27..323 '{ ... } }': () 259 26..322 '{ ... } }': ()
263 33..321 'for co... }': () 260 32..320 'for co... }': ()
264 37..44 'content': &{unknown} 261 36..43 'content': &{unknown}
265 48..61 'doesnt_matter': {unknown} 262 47..60 'doesnt_matter': {unknown}
266 62..321 '{ ... }': () 263 61..320 '{ ... }': ()
267 76..80 'name': &&{unknown} 264 75..79 'name': &&{unknown}
268 83..167 'if doe... }': &&{unknown} 265 82..166 'if doe... }': &&{unknown}
269 86..99 'doesnt_matter': bool 266 85..98 'doesnt_matter': bool
270 100..129 '{ ... }': &&{unknown} 267 99..128 '{ ... }': &&{unknown}
271 114..119 'first': &&{unknown} 268 113..118 'first': &&{unknown}
272 135..167 '{ ... }': &&{unknown} 269 134..166 '{ ... }': &&{unknown}
273 149..157 '&content': &&{unknown} 270 148..156 '&content': &&{unknown}
274 150..157 'content': &{unknown} 271 149..156 'content': &{unknown}
275 182..189 'content': &{unknown} 272 181..188 'content': &{unknown}
276 192..314 'if ICE... }': &{unknown} 273 191..313 'if ICE... }': &{unknown}
277 195..232 'ICE_RE..._VALUE': {unknown} 274 194..231 'ICE_RE..._VALUE': {unknown}
278 195..248 'ICE_RE...&name)': bool 275 194..247 'ICE_RE...&name)': bool
279 242..247 '&name': &&&{unknown} 276 241..246 '&name': &&&{unknown}
280 243..247 'name': &&{unknown} 277 242..246 'name': &&{unknown}
281 249..277 '{ ... }': &&{unknown} 278 248..276 '{ ... }': &&{unknown}
282 263..267 'name': &&{unknown} 279 262..266 'name': &&{unknown}
283 283..314 '{ ... }': &{unknown} 280 282..313 '{ ... }': &{unknown}
284 297..304 'content': &{unknown} 281 296..303 'content': &{unknown}
285 "### 282 "###
286 ); 283 );
287} 284}
@@ -302,11 +299,11 @@ fn test<R>(query_response: Canonical<QueryResponse<R>>) {
302} 299}
303"#), 300"#),
304 @r###" 301 @r###"
305 92..106 'query_response': Canonical<QueryResponse<R>> 302 91..105 'query_response': Canonical<QueryResponse<R>>
306 137..167 '{ ...lue; }': () 303 136..166 '{ ...lue; }': ()
307 143..164 '&query....value': &QueryResponse<R> 304 142..163 '&query....value': &QueryResponse<R>
308 144..158 'query_response': Canonical<QueryResponse<R>> 305 143..157 'query_response': Canonical<QueryResponse<R>>
309 144..164 'query_....value': QueryResponse<R> 306 143..163 'query_....value': QueryResponse<R>
310 "### 307 "###
311 ); 308 );
312} 309}
@@ -322,9 +319,9 @@ fn test() {
322"#), 319"#),
323 @r###" 320 @r###"
324 !0..4 '0u32': u32 321 !0..4 '0u32': u32
325 45..70 '{ ...()); }': () 322 44..69 '{ ...()); }': ()
326 55..56 'a': u32 323 54..55 'a': u32
327 "### 324 "###
328 ); 325 );
329} 326}
330 327
@@ -344,10 +341,10 @@ pub fn main_loop() {
344} 341}
345"#), 342"#),
346 @r###" 343 @r###"
347 144..146 '{}': () 344 143..145 '{}': ()
348 169..198 '{ ...t(); }': () 345 168..197 '{ ...t(); }': ()
349 175..193 'FxHash...efault': fn default<{unknown}, FxHasher>() -> HashSet<{unknown}, FxHasher> 346 174..192 'FxHash...efault': fn default<{unknown}, FxHasher>() -> HashSet<{unknown}, FxHasher>
350 175..195 'FxHash...ault()': HashSet<{unknown}, FxHasher> 347 174..194 'FxHash...ault()': HashSet<{unknown}, FxHasher>
351 "### 348 "###
352 ); 349 );
353} 350}
@@ -395,22 +392,22 @@ fn test() {
395} 392}
396"#), 393"#),
397 @r###" 394 @r###"
398 26..53 '{ ...oo() }': () 395 25..52 '{ ...oo() }': ()
399 32..49 '<Trait...>::foo': {unknown} 396 31..48 '<Trait...>::foo': {unknown}
400 32..51 '<Trait...:foo()': () 397 31..50 '<Trait...:foo()': ()
401 "### 398 "###
402 ); 399 );
403} 400}
404 401
405#[test] 402#[test]
406fn issue_2683_chars_impl() { 403fn issue_2683_chars_impl() {
407 let (db, pos) = TestDB::with_position( 404 check_types(
408 r#" 405 r#"
409//- /main.rs crate:main deps:std 406//- /main.rs crate:main deps:std
410fn test() { 407fn test() {
411 let chars: std::str::Chars<'_>; 408 let chars: std::str::Chars<'_>;
412 (chars.next(), chars.nth(1))<|>; 409 (chars.next(), chars.nth(1));
413} 410} //^ (Option<char>, Option<char>)
414 411
415//- /std.rs crate:std 412//- /std.rs crate:std
416#[prelude_import] 413#[prelude_import]
@@ -449,15 +446,12 @@ pub mod str {
449} 446}
450"#, 447"#,
451 ); 448 );
452
453 assert_eq!("(Option<char>, Option<char>)", super::type_at_pos(&db, pos));
454} 449}
455 450
456#[test] 451#[test]
457fn issue_3642_bad_macro_stackover() { 452fn issue_3642_bad_macro_stackover() {
458 let (db, pos) = TestDB::with_position( 453 check_types(
459 r#" 454 r#"
460//- /main.rs
461#[macro_export] 455#[macro_export]
462macro_rules! match_ast { 456macro_rules! match_ast {
463 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; 457 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
@@ -472,7 +466,8 @@ macro_rules! match_ast {
472} 466}
473 467
474fn main() { 468fn main() {
475 let anchor<|> = match_ast! { 469 let anchor = match_ast! {
470 //^ ()
476 match parent { 471 match parent {
477 as => {}, 472 as => {},
478 _ => return None 473 _ => return None
@@ -480,8 +475,6 @@ fn main() {
480 }; 475 };
481}"#, 476}"#,
482 ); 477 );
483
484 assert_eq!("()", super::type_at_pos(&db, pos));
485} 478}
486 479
487#[test] 480#[test]
@@ -495,13 +488,15 @@ fn foo(params: &[usize]) {
495} 488}
496"#), 489"#),
497 @r###" 490 @r###"
498 8..14 'params': &[usize] 491 7..13 'params': &[usize]
499 26..81 '{ ... } }': () 492 25..80 '{ ... } }': ()
500 32..79 'match ... }': () 493 31..78 'match ... }': ()
501 38..44 'params': &[usize] 494 37..43 'params': &[usize]
502 55..67 '[ps @ .., _]': [usize] 495 54..66 '[ps @ .., _]': [usize]
503 65..66 '_': usize 496 55..62 'ps @ ..': &[usize]
504 71..73 '{}': () 497 60..62 '..': [usize]
498 64..65 '_': usize
499 70..72 '{}': ()
505 "### 500 "###
506 ); 501 );
507} 502}
@@ -522,13 +517,13 @@ fn foo(b: Bar) {
522} 517}
523"#), 518"#),
524 @r###" 519 @r###"
525 36..37 'b': Bar 520 35..36 'b': Bar
526 44..96 '{ ... } }': () 521 43..95 '{ ... } }': ()
527 50..94 'match ... }': () 522 49..93 'match ... }': ()
528 56..57 'b': Bar 523 55..56 'b': Bar
529 68..81 'Bar { a: .. }': Bar 524 67..80 'Bar { a: .. }': Bar
530 77..79 '..': bool 525 76..78 '..': bool
531 85..87 '{}': () 526 84..86 '{}': ()
532 "### 527 "###
533 ); 528 );
534} 529}
@@ -549,16 +544,16 @@ fn main() {
549 a.foo(); 544 a.foo();
550} 545}
551"#), @r###" 546"#), @r###"
552 32..38 'FOO {}': FOO 547 31..37 'FOO {}': FOO
553 64..68 'self': &FOO 548 63..67 'self': &FOO
554 70..72 '{}': () 549 69..71 '{}': ()
555 86..120 '{ ...o(); }': () 550 85..119 '{ ...o(); }': ()
556 96..97 'a': &FOO 551 95..96 'a': &FOO
557 100..104 '&FOO': &FOO 552 99..103 '&FOO': &FOO
558 101..104 'FOO': FOO 553 100..103 'FOO': FOO
559 110..111 'a': &FOO 554 109..110 'a': &FOO
560 110..117 'a.foo()': () 555 109..116 'a.foo()': ()
561"### 556 "###
562 ); 557 );
563} 558}
564 559
@@ -580,17 +575,17 @@ fn main() {
580 let _a = foo!(); 575 let _a = foo!();
581} 576}
582"#), @r###" 577"#), @r###"
583 45..60 '{ loop {} }': T 578 44..59 '{ loop {} }': T
584 51..58 'loop {}': ! 579 50..57 'loop {}': !
585 56..58 '{}': () 580 55..57 '{}': ()
586 !0..31 '{letr:...g();r}': Foo 581 !0..31 '{letr:...g();r}': Foo
587 !4..5 'r': Foo 582 !4..5 'r': Foo
588 !18..26 'anything': fn anything<Foo>() -> Foo 583 !18..26 'anything': fn anything<Foo>() -> Foo
589 !18..28 'anything()': Foo 584 !18..28 'anything()': Foo
590 !29..30 'r': Foo 585 !29..30 'r': Foo
591 164..188 '{ ...!(); }': () 586 163..187 '{ ...!(); }': ()
592 174..176 '_a': Foo 587 173..175 '_a': Foo
593"###); 588 "###);
594} 589}
595 590
596#[test] 591#[test]
@@ -623,13 +618,221 @@ where
623} 618}
624"#), 619"#),
625 @r###" 620 @r###"
626 66..70 'self': Self 621 65..69 'self': Self
627 268..272 'self': Self 622 267..271 'self': Self
628 467..471 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}> 623 466..470 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}>
629 489..523 '{ ... }': () 624 488..522 '{ ... }': ()
630 499..503 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}> 625 498..502 'self': SelectStatement<F, S, D, W, O, LOf, {unknown}, {unknown}>
631 499..509 'self.order': O 626 498..508 'self.order': O
632 499..516 'self.o...into()': dyn QueryFragment<DB> 627 498..515 'self.o...into()': dyn QueryFragment<DB>
628 "###
629 );
630}
631
632#[test]
633fn issue_4953() {
634 assert_snapshot!(
635 infer(r#"
636pub struct Foo(pub i64);
637impl Foo {
638 fn test() -> Self { Self(0i64) }
639}
640"#),
641 @r###"
642 58..72 '{ Self(0i64) }': Foo
643 60..64 'Self': Foo(i64) -> Foo
644 60..70 'Self(0i64)': Foo
645 65..69 '0i64': i64
646 "###
647 );
648 assert_snapshot!(
649 infer(r#"
650pub struct Foo<T>(pub T);
651impl Foo<i64> {
652 fn test() -> Self { Self(0i64) }
653}
654"#),
655 @r###"
656 64..78 '{ Self(0i64) }': Foo<i64>
657 66..70 'Self': Foo<i64>(i64) -> Foo<i64>
658 66..76 'Self(0i64)': Foo<i64>
659 71..75 '0i64': i64
660 "###
661 );
662}
663
664#[test]
665fn issue_4931() {
666 assert_snapshot!(
667 infer(r#"
668trait Div<T> {
669 type Output;
670}
671
672trait CheckedDiv: Div<()> {}
673
674trait PrimInt: CheckedDiv<Output = ()> {
675 fn pow(self);
676}
677
678fn check<T: PrimInt>(i: T) {
679 i.pow();
680}
681"#),
682 @r###"
683 117..121 'self': Self
684 148..149 'i': T
685 154..170 '{ ...w(); }': ()
686 160..161 'i': T
687 160..167 'i.pow()': ()
688 "###
689 );
690}
691
692#[test]
693fn issue_4885() {
694 assert_snapshot!(
695 infer(r#"
696#[lang = "coerce_unsized"]
697pub trait CoerceUnsized<T> {}
698
699trait Future {
700 type Output;
701}
702trait Foo<R> {
703 type Bar;
704}
705fn foo<R, K>(key: &K) -> impl Future<Output = K::Bar>
706where
707 K: Foo<R>,
708{
709 bar(key)
710}
711fn bar<R, K>(key: &K) -> impl Future<Output = K::Bar>
712where
713 K: Foo<R>,
714{
715}
716"#),
717 @r###"
718 136..139 'key': &K
719 198..214 '{ ...key) }': impl Future<Output = <K as Foo<R>>::Bar>
720 204..207 'bar': fn bar<R, K>(&K) -> impl Future<Output = <K as Foo<R>>::Bar>
721 204..212 'bar(key)': impl Future<Output = <K as Foo<R>>::Bar>
722 208..211 'key': &K
723 228..231 'key': &K
724 290..293 '{ }': ()
725 "###
726 );
727}
728
729#[test]
730fn issue_4800() {
731 assert_snapshot!(
732 infer(r#"
733trait Debug {}
734
735struct Foo<T>;
736
737type E1<T> = (T, T, T);
738type E2<T> = E1<E1<E1<(T, T, T)>>>;
739
740impl Debug for Foo<E2<()>> {}
741
742struct Request;
743
744pub trait Future {
745 type Output;
746}
747
748pub struct PeerSet<D>;
749
750impl<D> Service<Request> for PeerSet<D>
751where
752 D: Discover,
753 D::Key: Debug,
754{
755 type Error = ();
756 type Future = dyn Future<Output = Self::Error>;
757
758 fn call(&mut self) -> Self::Future {
759 loop {}
760 }
761}
762
763pub trait Discover {
764 type Key;
765}
766
767pub trait Service<Request> {
768 type Error;
769 type Future: Future<Output = Self::Error>;
770 fn call(&mut self) -> Self::Future;
771}
772"#),
773 @r###"
774 379..383 'self': &mut PeerSet<D>
775 401..424 '{ ... }': dyn Future<Output = ()>
776 411..418 'loop {}': !
777 416..418 '{}': ()
778 575..579 'self': &mut Self
779 "###
780 );
781}
782
783#[test]
784fn issue_4966() {
785 assert_snapshot!(
786 infer(r#"
787pub trait IntoIterator {
788 type Item;
789}
790
791struct Repeat<A> { element: A }
792
793struct Map<F> { f: F }
794
795struct Vec<T> {}
796
797#[lang = "deref"]
798pub trait Deref {
799 type Target;
800}
801
802impl<T> Deref for Vec<T> {
803 type Target = [T];
804}
805
806fn from_iter<A, T: IntoIterator<Item = A>>(iter: T) -> Vec<A> {}
807
808fn main() {
809 let inner = Map { f: |_: &f64| 0.0 };
810
811 let repeat = Repeat { element: inner };
812
813 let vec = from_iter(repeat);
814
815 vec.foo_bar();
816}
817"#),
818 @r###"
819 270..274 'iter': T
820 289..291 '{}': ()
821 303..447 '{ ...r(); }': ()
822 313..318 'inner': Map<|&f64| -> f64>
823 321..345 'Map { ... 0.0 }': Map<|&f64| -> f64>
824 330..343 '|_: &f64| 0.0': |&f64| -> f64
825 331..332 '_': &f64
826 340..343 '0.0': f64
827 356..362 'repeat': Repeat<Map<|&f64| -> f64>>
828 365..390 'Repeat...nner }': Repeat<Map<|&f64| -> f64>>
829 383..388 'inner': Map<|&f64| -> f64>
830 401..404 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
831 407..416 'from_iter': fn from_iter<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>, Repeat<Map<|&f64| -> f64>>>(Repeat<Map<|&f64| -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
832 407..424 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
833 417..423 'repeat': Repeat<Map<|&f64| -> f64>>
834 431..434 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
835 431..444 'vec.foo_bar()': {unknown}
633 "### 836 "###
634 ); 837 );
635} 838}
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 88309157b..6d3e264af 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -1,19 +1,17 @@
1use super::{infer, type_at, type_at_pos};
2use crate::test_db::TestDB;
3use insta::assert_snapshot; 1use insta::assert_snapshot;
4use ra_db::fixture::WithFixture; 2
3use super::{check_types, infer};
5 4
6#[test] 5#[test]
7fn infer_box() { 6fn infer_box() {
8 let (db, pos) = TestDB::with_position( 7 check_types(
9 r#" 8 r#"
10//- /main.rs crate:main deps:std 9//- /main.rs crate:main deps:std
11
12fn test() { 10fn test() {
13 let x = box 1; 11 let x = box 1;
14 let t = (x, box x, box &1, box [1]); 12 let t = (x, box x, box &1, box [1]);
15 t<|>; 13 t;
16} 14} //^ (Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; _]>)
17 15
18//- /std.rs crate:std 16//- /std.rs crate:std
19#[prelude_import] use prelude::*; 17#[prelude_import] use prelude::*;
@@ -25,29 +23,24 @@ mod boxed {
25 inner: *mut T, 23 inner: *mut T,
26 } 24 }
27} 25}
28
29"#, 26"#,
30 ); 27 );
31 assert_eq!("(Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; _]>)", type_at_pos(&db, pos));
32} 28}
33 29
34#[test] 30#[test]
35fn infer_adt_self() { 31fn infer_adt_self() {
36 let (db, pos) = TestDB::with_position( 32 check_types(
37 r#" 33 r#"
38//- /main.rs
39enum Nat { Succ(Self), Demo(Nat), Zero } 34enum Nat { Succ(Self), Demo(Nat), Zero }
40 35
41fn test() { 36fn test() {
42 let foo: Nat = Nat::Zero; 37 let foo: Nat = Nat::Zero;
43 if let Nat::Succ(x) = foo { 38 if let Nat::Succ(x) = foo {
44 x<|> 39 x
45 } 40 } //^ Nat
46} 41}
47
48"#, 42"#,
49 ); 43 );
50 assert_eq!("Nat", type_at_pos(&db, pos));
51} 44}
52 45
53#[test] 46#[test]
@@ -64,9 +57,9 @@ impl S<u32> {
64} 57}
65"#, 58"#,
66 ), @r###" 59 ), @r###"
67 63..93 '{ ... }': () 60 49..79 '{ ... }': ()
68 73..86 'Self { x: 1 }': S<u32> 61 59..72 'Self { x: 1 }': S<u32>
69 83..84 '1': u32 62 69..70 '1': u32
70 "###); 63 "###);
71} 64}
72 65
@@ -85,17 +78,17 @@ fn foo() {
85 78
86"#, 79"#,
87 ), @r###" 80 ), @r###"
88 64..84 '{ ...1 }; }': () 81 50..70 '{ ...1 }; }': ()
89 70..81 'SS { x: 1 }': S<u32> 82 56..67 'SS { x: 1 }': S<u32>
90 78..79 '1': u32 83 64..65 '1': u32
91 "###); 84 "###);
92} 85}
93 86
94#[test] 87#[test]
95fn infer_ranges() { 88fn infer_ranges() {
96 let (db, pos) = TestDB::with_position( 89 check_types(
97 r#" 90 r#"
98//- /main.rs crate:main deps:std 91//- /main.rs crate:main deps:core
99fn test() { 92fn test() {
100 let a = ..; 93 let a = ..;
101 let b = 1..; 94 let b = 1..;
@@ -105,10 +98,10 @@ fn test() {
105 let f = 'a'..='z'; 98 let f = 'a'..='z';
106 99
107 let t = (a, b, c, d, e, f); 100 let t = (a, b, c, d, e, f);
108 t<|>; 101 t;
109} 102} //^ (RangeFull, RangeFrom<i32>, RangeTo<u32>, Range<usize>, RangeToInclusive<i32>, RangeInclusive<char>)
110 103
111//- /std.rs crate:std 104//- /core.rs crate:core
112#[prelude_import] use prelude::*; 105#[prelude_import] use prelude::*;
113mod prelude {} 106mod prelude {}
114 107
@@ -135,29 +128,22 @@ pub mod ops {
135} 128}
136"#, 129"#,
137 ); 130 );
138 assert_eq!(
139 "(RangeFull, RangeFrom<i32>, RangeTo<u32>, Range<usize>, RangeToInclusive<i32>, RangeInclusive<char>)",
140 type_at_pos(&db, pos),
141 );
142} 131}
143 132
144#[test] 133#[test]
145fn infer_while_let() { 134fn infer_while_let() {
146 let (db, pos) = TestDB::with_position( 135 check_types(
147 r#" 136 r#"
148//- /main.rs
149enum Option<T> { Some(T), None } 137enum Option<T> { Some(T), None }
150 138
151fn test() { 139fn test() {
152 let foo: Option<f32> = None; 140 let foo: Option<f32> = None;
153 while let Option::Some(x) = foo { 141 while let Option::Some(x) = foo {
154 <|>x 142 x
155 } 143 } //^ f32
156} 144}
157
158"#, 145"#,
159 ); 146 );
160 assert_eq!("f32", type_at_pos(&db, pos));
161} 147}
162 148
163#[test] 149#[test]
@@ -175,19 +161,19 @@ fn test(a: u32, b: isize, c: !, d: &str) {
175 1.0f32; 161 1.0f32;
176}"#), 162}"#),
177 @r###" 163 @r###"
178 9..10 'a': u32 164 8..9 'a': u32
179 17..18 'b': isize 165 16..17 'b': isize
180 27..28 'c': ! 166 26..27 'c': !
181 33..34 'd': &str 167 32..33 'd': &str
182 42..121 '{ ...f32; }': () 168 41..120 '{ ...f32; }': ()
183 48..49 'a': u32 169 47..48 'a': u32
184 55..56 'b': isize 170 54..55 'b': isize
185 62..63 'c': ! 171 61..62 'c': !
186 69..70 'd': &str 172 68..69 'd': &str
187 76..82 '1usize': usize 173 75..81 '1usize': usize
188 88..94 '1isize': isize 174 87..93 '1isize': isize
189 100..106 '"test"': &str 175 99..105 '"test"': &str
190 112..118 '1.0f32': f32 176 111..117 '1.0f32': f32
191 "### 177 "###
192 ); 178 );
193} 179}
@@ -206,17 +192,17 @@ fn test() {
206} 192}
207"#), 193"#),
208 @r###" 194 @r###"
209 11..118 '{ ...= e; }': () 195 10..117 '{ ...= e; }': ()
210 21..22 'a': isize 196 20..21 'a': isize
211 25..31 '1isize': isize 197 24..30 '1isize': isize
212 41..42 'b': usize 198 40..41 'b': usize
213 52..53 '1': usize 199 51..52 '1': usize
214 63..64 'c': usize 200 62..63 'c': usize
215 67..68 'b': usize 201 66..67 'b': usize
216 78..79 'd': u32 202 77..78 'd': u32
217 94..95 'e': i32 203 93..94 'e': i32
218 105..106 'f': i32 204 104..105 'f': i32
219 114..115 'e': i32 205 113..114 'e': i32
220 "### 206 "###
221 ); 207 );
222} 208}
@@ -237,15 +223,15 @@ fn test() {
237} 223}
238"#), 224"#),
239 @r###" 225 @r###"
240 15..20 '{ 1 }': u32 226 14..19 '{ 1 }': u32
241 17..18 '1': u32 227 16..17 '1': u32
242 48..53 '{ 1 }': u32 228 47..52 '{ 1 }': u32
243 50..51 '1': u32 229 49..50 '1': u32
244 67..91 '{ ...c(); }': () 230 66..90 '{ ...c(); }': ()
245 73..74 'a': fn a() -> u32 231 72..73 'a': fn a() -> u32
246 73..76 'a()': u32 232 72..75 'a()': u32
247 82..86 'b::c': fn c() -> u32 233 81..85 'b::c': fn c() -> u32
248 82..88 'b::c()': u32 234 81..87 'b::c()': u32
249 "### 235 "###
250 ); 236 );
251} 237}
@@ -266,13 +252,13 @@ fn test() {
266} 252}
267"#), 253"#),
268 @r###" 254 @r###"
269 41..46 '{ 1 }': i32 255 40..45 '{ 1 }': i32
270 43..44 '1': i32 256 42..43 '1': i32
271 60..93 '{ ...o(); }': () 257 59..92 '{ ...o(); }': ()
272 66..72 'S::foo': fn foo() -> i32 258 65..71 'S::foo': fn foo() -> i32
273 66..74 'S::foo()': i32 259 65..73 'S::foo()': i32
274 80..88 '<S>::foo': fn foo() -> i32 260 79..87 '<S>::foo': fn foo() -> i32
275 80..90 '<S>::foo()': i32 261 79..89 '<S>::foo()': i32
276 "### 262 "###
277 ); 263 );
278} 264}
@@ -297,22 +283,22 @@ fn test() {
297} 283}
298"#), 284"#),
299 @r###" 285 @r###"
300 72..154 '{ ...a.c; }': () 286 71..153 '{ ...a.c; }': ()
301 82..83 'c': C 287 81..82 'c': C
302 86..87 'C': C(usize) -> C 288 85..86 'C': C(usize) -> C
303 86..90 'C(1)': C 289 85..89 'C(1)': C
304 88..89 '1': usize 290 87..88 '1': usize
305 96..97 'B': B 291 95..96 'B': B
306 107..108 'a': A 292 106..107 'a': A
307 114..133 'A { b:...C(1) }': A 293 113..132 'A { b:...C(1) }': A
308 121..122 'B': B 294 120..121 'B': B
309 127..128 'C': C(usize) -> C 295 126..127 'C': C(usize) -> C
310 127..131 'C(1)': C 296 126..130 'C(1)': C
311 129..130 '1': usize 297 128..129 '1': usize
312 139..140 'a': A 298 138..139 'a': A
313 139..142 'a.b': B 299 138..141 'a.b': B
314 148..149 'a': A 300 147..148 'a': A
315 148..151 'a.c': C 301 147..150 'a.c': C
316 "### 302 "###
317 ); 303 );
318} 304}
@@ -330,10 +316,33 @@ fn test() {
330 E::V2; 316 E::V2;
331}"#), 317}"#),
332 @r###" 318 @r###"
333 48..82 '{ E:...:V2; }': () 319 47..81 '{ E:...:V2; }': ()
334 52..70 'E::V1 ...d: 1 }': E 320 51..69 'E::V1 ...d: 1 }': E
335 67..68 '1': u32 321 66..67 '1': u32
336 74..79 'E::V2': E 322 73..78 'E::V2': E
323 "###
324 );
325}
326
327#[test]
328fn infer_union() {
329 assert_snapshot!(
330 infer(r#"
331union MyUnion {
332 foo: u32,
333 bar: f32,
334}
335
336unsafe fn baz(u: MyUnion) {
337 let inner = u.foo;
338}
339"#),
340 @r###"
341 61..62 'u': MyUnion
342 73..99 '{ ...foo; }': ()
343 83..88 'inner': u32
344 91..92 'u': MyUnion
345 91..96 'u.foo': u32
337 "### 346 "###
338 ); 347 );
339} 348}
@@ -357,29 +366,29 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) {
357} 366}
358"#), 367"#),
359 @r###" 368 @r###"
360 9..10 'a': &u32 369 8..9 'a': &u32
361 18..19 'b': &mut u32 370 17..18 'b': &mut u32
362 31..32 'c': *const u32 371 30..31 'c': *const u32
363 46..47 'd': *mut u32 372 45..46 'd': *mut u32
364 59..150 '{ ... *d; }': () 373 58..149 '{ ... *d; }': ()
365 65..66 'a': &u32 374 64..65 'a': &u32
366 72..74 '*a': u32 375 71..73 '*a': u32
367 73..74 'a': &u32 376 72..73 'a': &u32
368 80..82 '&a': &&u32 377 79..81 '&a': &&u32
369 81..82 'a': &u32 378 80..81 'a': &u32
370 88..94 '&mut a': &mut &u32 379 87..93 '&mut a': &mut &u32
371 93..94 'a': &u32 380 92..93 'a': &u32
372 100..101 'b': &mut u32 381 99..100 'b': &mut u32
373 107..109 '*b': u32 382 106..108 '*b': u32
374 108..109 'b': &mut u32 383 107..108 'b': &mut u32
375 115..117 '&b': &&mut u32 384 114..116 '&b': &&mut u32
376 116..117 'b': &mut u32 385 115..116 'b': &mut u32
377 123..124 'c': *const u32 386 122..123 'c': *const u32
378 130..132 '*c': u32 387 129..131 '*c': u32
379 131..132 'c': *const u32 388 130..131 'c': *const u32
380 138..139 'd': *mut u32 389 137..138 'd': *mut u32
381 145..147 '*d': u32 390 144..146 '*d': u32
382 146..147 'd': *mut u32 391 145..146 'd': *mut u32
383 "### 392 "###
384 ); 393 );
385} 394}
@@ -394,12 +403,12 @@ fn test(a: i32) {
394} 403}
395"#), 404"#),
396 @r###" 405 @r###"
397 9..10 'a': i32 406 8..9 'a': i32
398 17..54 '{ ...t a; }': () 407 16..53 '{ ...t a; }': ()
399 23..33 '&raw mut a': *mut i32 408 22..32 '&raw mut a': *mut i32
400 32..33 'a': i32 409 31..32 'a': i32
401 39..51 '&raw const a': *const i32 410 38..50 '&raw const a': *const i32
402 50..51 'a': i32 411 49..50 'a': i32
403 "### 412 "###
404 ); 413 );
405} 414}
@@ -429,20 +438,20 @@ fn test() {
429} 438}
430"##), 439"##),
431 @r###" 440 @r###"
432 11..221 '{ ...o"#; }': () 441 10..220 '{ ...o"#; }': ()
433 17..21 '5i32': i32 442 16..20 '5i32': i32
434 27..31 '5f32': f32 443 26..30 '5f32': f32
435 37..41 '5f64': f64 444 36..40 '5f64': f64
436 47..54 '"hello"': &str 445 46..53 '"hello"': &str
437 60..68 'b"bytes"': &[u8; _] 446 59..67 'b"bytes"': &[u8; _]
438 74..77 ''c'': char 447 73..76 ''c'': char
439 83..87 'b'b'': u8 448 82..86 'b'b'': u8
440 93..97 '3.14': f64 449 92..96 '3.14': f64
441 103..107 '5000': i32 450 102..106 '5000': i32
442 113..118 'false': bool 451 112..117 'false': bool
443 124..128 'true': bool 452 123..127 'true': bool
444 134..202 'r#" ... "#': &str 453 133..201 'r#" ... "#': &str
445 208..218 'br#"yolo"#': &[u8; _] 454 207..217 'br#"yolo"#': &[u8; _]
446 "### 455 "###
447 ); 456 );
448} 457}
@@ -472,47 +481,47 @@ fn test(x: SomeType) {
472} 481}
473"#), 482"#),
474 @r###" 483 @r###"
475 27..28 'x': SomeType 484 26..27 'x': SomeType
476 40..272 '{ ...lo"; }': () 485 39..271 '{ ...lo"; }': ()
477 50..51 'b': bool 486 49..50 'b': bool
478 54..59 'false': bool 487 53..58 'false': bool
479 69..70 'c': bool 488 68..69 'c': bool
480 73..75 '!b': bool 489 72..74 '!b': bool
481 74..75 'b': bool 490 73..74 'b': bool
482 85..86 'a': i128 491 84..85 'a': i128
483 89..92 '100': i128 492 88..91 '100': i128
484 102..103 'd': i128 493 101..102 'd': i128
485 112..114 '-a': i128 494 111..113 '-a': i128
486 113..114 'a': i128 495 112..113 'a': i128
487 124..125 'e': i32 496 123..124 'e': i32
488 128..132 '-100': i32 497 127..131 '-100': i32
489 129..132 '100': i32 498 128..131 '100': i32
490 142..143 'f': bool 499 141..142 'f': bool
491 146..153 '!!!true': bool 500 145..152 '!!!true': bool
492 147..153 '!!true': bool 501 146..152 '!!true': bool
493 148..153 '!true': bool 502 147..152 '!true': bool
494 149..153 'true': bool 503 148..152 'true': bool
495 163..164 'g': i32 504 162..163 'g': i32
496 167..170 '!42': i32 505 166..169 '!42': i32
497 168..170 '42': i32 506 167..169 '42': i32
498 180..181 'h': u32 507 179..180 'h': u32
499 184..190 '!10u32': u32 508 183..189 '!10u32': u32
500 185..190 '10u32': u32 509 184..189 '10u32': u32
501 200..201 'j': i128 510 199..200 'j': i128
502 204..206 '!a': i128 511 203..205 '!a': i128
503 205..206 'a': i128 512 204..205 'a': i128
504 212..217 '-3.14': f64 513 211..216 '-3.14': f64
505 213..217 '3.14': f64 514 212..216 '3.14': f64
506 223..225 '!3': i32 515 222..224 '!3': i32
507 224..225 '3': i32 516 223..224 '3': i32
508 231..233 '-x': {unknown} 517 230..232 '-x': {unknown}
509 232..233 'x': SomeType 518 231..232 'x': SomeType
510 239..241 '!x': {unknown} 519 238..240 '!x': {unknown}
511 240..241 'x': SomeType 520 239..240 'x': SomeType
512 247..255 '-"hello"': {unknown} 521 246..254 '-"hello"': {unknown}
513 248..255 '"hello"': &str 522 247..254 '"hello"': &str
514 261..269 '!"hello"': {unknown} 523 260..268 '!"hello"': {unknown}
515 262..269 '"hello"': &str 524 261..268 '"hello"': &str
516 "### 525 "###
517 ); 526 );
518} 527}
@@ -535,26 +544,26 @@ fn test() -> &mut &f64 {
535} 544}
536"#), 545"#),
537 @r###" 546 @r###"
538 14..15 'x': u32 547 13..14 'x': u32
539 22..24 '{}': () 548 21..23 '{}': ()
540 78..231 '{ ...t &c }': &mut &f64 549 77..230 '{ ...t &c }': &mut &f64
541 88..89 'a': u32 550 87..88 'a': u32
542 92..108 'unknow...nction': {unknown} 551 91..107 'unknow...nction': {unknown}
543 92..110 'unknow...tion()': u32 552 91..109 'unknow...tion()': u32
544 116..125 'takes_u32': fn takes_u32(u32) 553 115..124 'takes_u32': fn takes_u32(u32)
545 116..128 'takes_u32(a)': () 554 115..127 'takes_u32(a)': ()
546 126..127 'a': u32 555 125..126 'a': u32
547 138..139 'b': i32 556 137..138 'b': i32
548 142..158 'unknow...nction': {unknown} 557 141..157 'unknow...nction': {unknown}
549 142..160 'unknow...tion()': i32 558 141..159 'unknow...tion()': i32
550 166..184 'S { i3...d: b }': S 559 165..183 'S { i3...d: b }': S
551 181..182 'b': i32 560 180..181 'b': i32
552 194..195 'c': f64 561 193..194 'c': f64
553 198..214 'unknow...nction': {unknown} 562 197..213 'unknow...nction': {unknown}
554 198..216 'unknow...tion()': f64 563 197..215 'unknow...tion()': f64
555 222..229 '&mut &c': &mut &f64 564 221..228 '&mut &c': &mut &f64
556 227..229 '&c': &f64 565 226..228 '&c': &f64
557 228..229 'c': f64 566 227..228 'c': f64
558 "### 567 "###
559 ); 568 );
560} 569}
@@ -581,16 +590,16 @@ impl S {
581} 590}
582"#), 591"#),
583 @r###" 592 @r###"
584 34..38 'self': &S 593 33..37 'self': &S
585 40..61 '{ ... }': () 594 39..60 '{ ... }': ()
586 50..54 'self': &S 595 49..53 'self': &S
587 75..79 'self': &S 596 74..78 'self': &S
588 88..109 '{ ... }': () 597 87..108 '{ ... }': ()
589 98..102 'self': &S 598 97..101 'self': &S
590 133..153 '{ ... }': S 599 132..152 '{ ... }': S
591 143..147 'S {}': S 600 142..146 'S {}': S
592 177..200 '{ ... }': S 601 176..199 '{ ... }': S
593 187..194 'Self {}': S 602 186..193 'Self {}': S
594 "### 603 "###
595 ); 604 );
596} 605}
@@ -624,17 +633,17 @@ impl E {
624} 633}
625"#), 634"#),
626 @r###" 635 @r###"
627 87..108 '{ ... }': () 636 86..107 '{ ... }': ()
628 97..101 'Self': S1 637 96..100 'Self': S1
629 135..159 '{ ... }': () 638 134..158 '{ ... }': ()
630 145..149 'Self': S2(isize) -> S2 639 144..148 'Self': S2(isize) -> S2
631 145..152 'Self(1)': S2 640 144..151 'Self(1)': S2
632 150..151 '1': isize 641 149..150 '1': isize
633 185..231 '{ ... }': () 642 184..230 '{ ... }': ()
634 195..203 'Self::V1': E 643 194..202 'Self::V1': E
635 213..221 'Self::V2': V2(u32) -> E 644 212..220 'Self::V2': V2(u32) -> E
636 213..224 'Self::V2(1)': E 645 212..223 'Self::V2(1)': E
637 222..223 '1': u32 646 221..222 '1': u32
638 "### 647 "###
639 ); 648 );
640} 649}
@@ -664,56 +673,56 @@ fn test() -> bool {
664} 673}
665"#), 674"#),
666 @r###" 675 @r###"
667 6..7 'x': bool 676 5..6 'x': bool
668 22..34 '{ 0i32 }': i32 677 21..33 '{ 0i32 }': i32
669 28..32 '0i32': i32 678 27..31 '0i32': i32
670 54..370 '{ ... < 3 }': bool 679 53..369 '{ ... < 3 }': bool
671 64..65 'x': bool 680 63..64 'x': bool
672 68..69 'a': bool 681 67..68 'a': bool
673 68..74 'a && b': bool 682 67..73 'a && b': bool
674 73..74 'b': bool 683 72..73 'b': bool
675 84..85 'y': bool 684 83..84 'y': bool
676 88..92 'true': bool 685 87..91 'true': bool
677 88..101 'true || false': bool 686 87..100 'true || false': bool
678 96..101 'false': bool 687 95..100 'false': bool
679 111..112 'z': bool 688 110..111 'z': bool
680 115..116 'x': bool 689 114..115 'x': bool
681 115..121 'x == y': bool 690 114..120 'x == y': bool
682 120..121 'y': bool 691 119..120 'y': bool
683 131..132 't': bool 692 130..131 't': bool
684 135..136 'x': bool 693 134..135 'x': bool
685 135..141 'x != y': bool 694 134..140 'x != y': bool
686 140..141 'y': bool 695 139..140 'y': bool
687 151..162 'minus_forty': isize 696 150..161 'minus_forty': isize
688 172..180 '-40isize': isize 697 171..179 '-40isize': isize
689 173..180 '40isize': isize 698 172..179 '40isize': isize
690 190..191 'h': bool 699 189..190 'h': bool
691 194..205 'minus_forty': isize 700 193..204 'minus_forty': isize
692 194..216 'minus_...ONST_2': bool 701 193..215 'minus_...ONST_2': bool
693 209..216 'CONST_2': isize 702 208..215 'CONST_2': isize
694 226..227 'c': i32 703 225..226 'c': i32
695 230..231 'f': fn f(bool) -> i32 704 229..230 'f': fn f(bool) -> i32
696 230..239 'f(z || y)': i32 705 229..238 'f(z || y)': i32
697 230..243 'f(z || y) + 5': i32 706 229..242 'f(z || y) + 5': i32
698 232..233 'z': bool 707 231..232 'z': bool
699 232..238 'z || y': bool 708 231..237 'z || y': bool
700 237..238 'y': bool 709 236..237 'y': bool
701 242..243 '5': i32 710 241..242 '5': i32
702 253..254 'd': {unknown} 711 252..253 'd': {unknown}
703 257..258 'b': {unknown} 712 256..257 'b': {unknown}
704 268..269 'g': () 713 267..268 'g': ()
705 272..283 'minus_forty': isize 714 271..282 'minus_forty': isize
706 272..288 'minus_...y ^= i': () 715 271..287 'minus_...y ^= i': ()
707 287..288 'i': isize 716 286..287 'i': isize
708 298..301 'ten': usize 717 297..300 'ten': usize
709 311..313 '10': usize 718 310..312 '10': usize
710 323..336 'ten_is_eleven': bool 719 322..335 'ten_is_eleven': bool
711 339..342 'ten': usize 720 338..341 'ten': usize
712 339..354 'ten == some_num': bool 721 338..353 'ten == some_num': bool
713 346..354 'some_num': usize 722 345..353 'some_num': usize
714 361..364 'ten': usize 723 360..363 'ten': usize
715 361..368 'ten < 3': bool 724 360..367 'ten < 3': bool
716 367..368 '3': usize 725 366..367 '3': usize
717 "### 726 "###
718 ); 727 );
719} 728}
@@ -728,13 +737,13 @@ fn test() {
728} 737}
729"#), 738"#),
730 @r###" 739 @r###"
731 11..48 '{ ...5u8; }': () 740 10..47 '{ ...5u8; }': ()
732 17..21 '1u32': u32 741 16..20 '1u32': u32
733 17..28 '1u32 << 5u8': u32 742 16..27 '1u32 << 5u8': u32
734 25..28 '5u8': u8 743 24..27 '5u8': u8
735 34..38 '1u32': u32 744 33..37 '1u32': u32
736 34..45 '1u32 >> 5u8': u32 745 33..44 '1u32 >> 5u8': u32
737 42..45 '5u8': u8 746 41..44 '5u8': u8
738 "### 747 "###
739 ); 748 );
740} 749}
@@ -767,49 +776,49 @@ fn test2(a1: *const A, a2: *mut A) {
767} 776}
768"#), 777"#),
769 @r###" 778 @r###"
770 44..45 'a': A 779 43..44 'a': A
771 50..213 '{ ...5.b; }': () 780 49..212 '{ ...5.b; }': ()
772 60..62 'a1': A 781 59..61 'a1': A
773 65..66 'a': A 782 64..65 'a': A
774 72..74 'a1': A 783 71..73 'a1': A
775 72..76 'a1.b': B 784 71..75 'a1.b': B
776 86..88 'a2': &A 785 85..87 'a2': &A
777 91..93 '&a': &A 786 90..92 '&a': &A
778 92..93 'a': A 787 91..92 'a': A
779 99..101 'a2': &A 788 98..100 'a2': &A
780 99..103 'a2.b': B 789 98..102 'a2.b': B
781 113..115 'a3': &mut A 790 112..114 'a3': &mut A
782 118..124 '&mut a': &mut A 791 117..123 '&mut a': &mut A
783 123..124 'a': A 792 122..123 'a': A
784 130..132 'a3': &mut A 793 129..131 'a3': &mut A
785 130..134 'a3.b': B 794 129..133 'a3.b': B
786 144..146 'a4': &&&&&&&A 795 143..145 'a4': &&&&&&&A
787 149..157 '&&&&&&&a': &&&&&&&A 796 148..156 '&&&&&&&a': &&&&&&&A
788 150..157 '&&&&&&a': &&&&&&A 797 149..156 '&&&&&&a': &&&&&&A
789 151..157 '&&&&&a': &&&&&A 798 150..156 '&&&&&a': &&&&&A
790 152..157 '&&&&a': &&&&A 799 151..156 '&&&&a': &&&&A
791 153..157 '&&&a': &&&A 800 152..156 '&&&a': &&&A
792 154..157 '&&a': &&A 801 153..156 '&&a': &&A
793 155..157 '&a': &A 802 154..156 '&a': &A
794 156..157 'a': A 803 155..156 'a': A
795 163..165 'a4': &&&&&&&A 804 162..164 'a4': &&&&&&&A
796 163..167 'a4.b': B 805 162..166 'a4.b': B
797 177..179 'a5': &mut &&mut &&mut A 806 176..178 'a5': &mut &&mut &&mut A
798 182..200 '&mut &...&mut a': &mut &&mut &&mut A 807 181..199 '&mut &...&mut a': &mut &&mut &&mut A
799 187..200 '&&mut &&mut a': &&mut &&mut A 808 186..199 '&&mut &&mut a': &&mut &&mut A
800 188..200 '&mut &&mut a': &mut &&mut A 809 187..199 '&mut &&mut a': &mut &&mut A
801 193..200 '&&mut a': &&mut A 810 192..199 '&&mut a': &&mut A
802 194..200 '&mut a': &mut A 811 193..199 '&mut a': &mut A
803 199..200 'a': A 812 198..199 'a': A
804 206..208 'a5': &mut &&mut &&mut A 813 205..207 'a5': &mut &&mut &&mut A
805 206..210 'a5.b': B 814 205..209 'a5.b': B
806 224..226 'a1': *const A 815 223..225 'a1': *const A
807 238..240 'a2': *mut A 816 237..239 'a2': *mut A
808 250..273 '{ ...2.b; }': () 817 249..272 '{ ...2.b; }': ()
809 256..258 'a1': *const A 818 255..257 'a1': *const A
810 256..260 'a1.b': B 819 255..259 'a1.b': B
811 266..268 'a2': *mut A 820 265..267 'a2': *mut A
812 266..270 'a2.b': B 821 265..269 'a2.b': B
813 "### 822 "###
814 ); 823 );
815} 824}
@@ -846,30 +855,30 @@ fn test() {
846} 855}
847"#), 856"#),
848 @r###" 857 @r###"
849 68..72 'self': &Self 858 67..71 'self': &Self
850 139..143 'self': &A<T> 859 138..142 'self': &A<T>
851 151..174 '{ ... }': &T 860 150..173 '{ ... }': &T
852 161..168 '&self.0': &T 861 160..167 '&self.0': &T
853 162..166 'self': &A<T> 862 161..165 'self': &A<T>
854 162..168 'self.0': T 863 161..167 'self.0': T
855 255..259 'self': &B<T> 864 254..258 'self': &B<T>
856 278..301 '{ ... }': &T 865 277..300 '{ ... }': &T
857 288..295 '&self.0': &T 866 287..294 '&self.0': &T
858 289..293 'self': &B<T> 867 288..292 'self': &B<T>
859 289..295 'self.0': T 868 288..294 'self.0': T
860 315..353 '{ ...))); }': () 869 314..352 '{ ...))); }': ()
861 325..326 't': &i32 870 324..325 't': &i32
862 329..335 'A::foo': fn foo<i32>(&A<i32>) -> &i32 871 328..334 'A::foo': fn foo<i32>(&A<i32>) -> &i32
863 329..350 'A::foo...42))))': &i32 872 328..349 'A::foo...42))))': &i32
864 336..349 '&&B(B(A(42)))': &&B<B<A<i32>>> 873 335..348 '&&B(B(A(42)))': &&B<B<A<i32>>>
865 337..349 '&B(B(A(42)))': &B<B<A<i32>>> 874 336..348 '&B(B(A(42)))': &B<B<A<i32>>>
866 338..339 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> 875 337..338 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>>
867 338..349 'B(B(A(42)))': B<B<A<i32>>> 876 337..348 'B(B(A(42)))': B<B<A<i32>>>
868 340..341 'B': B<A<i32>>(A<i32>) -> B<A<i32>> 877 339..340 'B': B<A<i32>>(A<i32>) -> B<A<i32>>
869 340..348 'B(A(42))': B<A<i32>> 878 339..347 'B(A(42))': B<A<i32>>
870 342..343 'A': A<i32>(i32) -> A<i32> 879 341..342 'A': A<i32>(i32) -> A<i32>
871 342..347 'A(42)': A<i32> 880 341..346 'A(42)': A<i32>
872 344..346 '42': i32 881 343..345 '42': i32
873 "### 882 "###
874 ); 883 );
875} 884}
@@ -906,34 +915,34 @@ fn test(a: A<i32>) {
906} 915}
907"#), 916"#),
908 @r###" 917 @r###"
909 68..72 'self': &Self 918 67..71 'self': &Self
910 144..148 'self': &A<T> 919 143..147 'self': &A<T>
911 150..151 'x': &A<T> 920 149..150 'x': &A<T>
912 166..187 '{ ... }': &T 921 165..186 '{ ... }': &T
913 176..181 '&*x.0': &T 922 175..180 '&*x.0': &T
914 177..181 '*x.0': T 923 176..180 '*x.0': T
915 178..179 'x': &A<T> 924 177..178 'x': &A<T>
916 178..181 'x.0': *mut T 925 177..180 'x.0': *mut T
917 268..272 'self': &B<T> 926 267..271 'self': &B<T>
918 291..314 '{ ... }': &T 927 290..313 '{ ... }': &T
919 301..308 '&self.0': &T 928 300..307 '&self.0': &T
920 302..306 'self': &B<T> 929 301..305 'self': &B<T>
921 302..308 'self.0': T 930 301..307 'self.0': T
922 326..327 'a': A<i32> 931 325..326 'a': A<i32>
923 337..383 '{ ...))); }': () 932 336..382 '{ ...))); }': ()
924 347..348 't': &i32 933 346..347 't': &i32
925 351..352 'A': A<i32>(*mut i32) -> A<i32> 934 350..351 'A': A<i32>(*mut i32) -> A<i32>
926 351..365 'A(0 as *mut _)': A<i32> 935 350..364 'A(0 as *mut _)': A<i32>
927 351..380 'A(0 as...B(a)))': &i32 936 350..379 'A(0 as...B(a)))': &i32
928 353..354 '0': i32 937 352..353 '0': i32
929 353..364 '0 as *mut _': *mut i32 938 352..363 '0 as *mut _': *mut i32
930 370..379 '&&B(B(a))': &&B<B<A<i32>>> 939 369..378 '&&B(B(a))': &&B<B<A<i32>>>
931 371..379 '&B(B(a))': &B<B<A<i32>>> 940 370..378 '&B(B(a))': &B<B<A<i32>>>
932 372..373 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>> 941 371..372 'B': B<B<A<i32>>>(B<A<i32>>) -> B<B<A<i32>>>
933 372..379 'B(B(a))': B<B<A<i32>>> 942 371..378 'B(B(a))': B<B<A<i32>>>
934 374..375 'B': B<A<i32>>(A<i32>) -> B<A<i32>> 943 373..374 'B': B<A<i32>>(A<i32>) -> B<A<i32>>
935 374..378 'B(a)': B<A<i32>> 944 373..377 'B(a)': B<A<i32>>
936 376..377 'a': A<i32> 945 375..376 'a': A<i32>
937 "### 946 "###
938 ); 947 );
939} 948}
@@ -952,16 +961,16 @@ fn main(foo: Foo) {
952} 961}
953"#), 962"#),
954 @r###" 963 @r###"
955 35..38 'foo': Foo 964 34..37 'foo': Foo
956 45..109 '{ ... } }': () 965 44..108 '{ ... } }': ()
957 51..107 'if tru... }': () 966 50..106 'if tru... }': ()
958 54..58 'true': bool 967 53..57 'true': bool
959 59..67 '{ }': () 968 58..66 '{ }': ()
960 73..107 'if fal... }': i32 969 72..106 'if fal... }': i32
961 76..81 'false': bool 970 75..80 'false': bool
962 82..107 '{ ... }': i32 971 81..106 '{ ... }': i32
963 92..95 'foo': Foo 972 91..94 'foo': Foo
964 92..101 'foo.field': i32 973 91..100 'foo.field': i32
965 "### 974 "###
966 ) 975 )
967} 976}
@@ -993,38 +1002,38 @@ fn foo() {
993 }; 1002 };
994}"#), 1003}"#),
995 @r###" 1004 @r###"
996 10..323 '{ ... }; }': () 1005 9..322 '{ ... }; }': ()
997 20..23 '_x1': i32 1006 19..22 '_x1': i32
998 26..80 'if tru... }': i32 1007 25..79 'if tru... }': i32
999 29..33 'true': bool 1008 28..32 'true': bool
1000 34..51 '{ ... }': i32 1009 33..50 '{ ... }': i32
1001 44..45 '1': i32 1010 43..44 '1': i32
1002 57..80 '{ ... }': i32 1011 56..79 '{ ... }': i32
1003 67..73 'return': ! 1012 66..72 'return': !
1004 90..93 '_x2': i32 1013 89..92 '_x2': i32
1005 96..149 'if tru... }': i32 1014 95..148 'if tru... }': i32
1006 99..103 'true': bool 1015 98..102 'true': bool
1007 104..121 '{ ... }': i32 1016 103..120 '{ ... }': i32
1008 114..115 '2': i32 1017 113..114 '2': i32
1009 127..149 '{ ... }': ! 1018 126..148 '{ ... }': !
1010 137..143 'return': ! 1019 136..142 'return': !
1011 159..162 '_x3': i32 1020 158..161 '_x3': i32
1012 165..247 'match ... }': i32 1021 164..246 'match ... }': i32
1013 171..175 'true': bool 1022 170..174 'true': bool
1014 186..190 'true': bool 1023 185..189 'true': bool
1015 186..190 'true': bool 1024 185..189 'true': bool
1016 194..195 '3': i32 1025 193..194 '3': i32
1017 205..206 '_': bool 1026 204..205 '_': bool
1018 210..241 '{ ... }': i32 1027 209..240 '{ ... }': i32
1019 224..230 'return': ! 1028 223..229 'return': !
1020 257..260 '_x4': i32 1029 256..259 '_x4': i32
1021 263..320 'match ... }': i32 1030 262..319 'match ... }': i32
1022 269..273 'true': bool 1031 268..272 'true': bool
1023 284..288 'true': bool 1032 283..287 'true': bool
1024 284..288 'true': bool 1033 283..287 'true': bool
1025 292..293 '4': i32 1034 291..292 '4': i32
1026 303..304 '_': bool 1035 302..303 '_': bool
1027 308..314 'return': ! 1036 307..313 'return': !
1028 "### 1037 "###
1029 ) 1038 )
1030} 1039}
@@ -1052,24 +1061,24 @@ fn test(a: A) {
1052} 1061}
1053"#), 1062"#),
1054 @r###" 1063 @r###"
1055 32..36 'self': A 1064 31..35 'self': A
1056 38..39 'x': u32 1065 37..38 'x': u32
1057 53..55 '{}': () 1066 52..54 '{}': ()
1058 103..107 'self': &A 1067 102..106 'self': &A
1059 109..110 'x': u64 1068 108..109 'x': u64
1060 124..126 '{}': () 1069 123..125 '{}': ()
1061 144..145 'a': A 1070 143..144 'a': A
1062 150..198 '{ ...(1); }': () 1071 149..197 '{ ...(1); }': ()
1063 156..157 'a': A 1072 155..156 'a': A
1064 156..164 'a.foo(1)': i32 1073 155..163 'a.foo(1)': i32
1065 162..163 '1': u32 1074 161..162 '1': u32
1066 170..181 '(&a).bar(1)': i64 1075 169..180 '(&a).bar(1)': i64
1067 171..173 '&a': &A 1076 170..172 '&a': &A
1068 172..173 'a': A 1077 171..172 'a': A
1069 179..180 '1': u64 1078 178..179 '1': u64
1070 187..188 'a': A 1079 186..187 'a': A
1071 187..195 'a.bar(1)': i64 1080 186..194 'a.bar(1)': i64
1072 193..194 '1': u64 1081 192..193 '1': u64
1073 "### 1082 "###
1074 ); 1083 );
1075} 1084}
@@ -1088,11 +1097,11 @@ fn test() {
1088} 1097}
1089"#), 1098"#),
1090 @r###" 1099 @r###"
1091 40..44 'self': &str 1100 39..43 'self': &str
1092 53..55 '{}': () 1101 52..54 '{}': ()
1093 69..89 '{ ...o(); }': () 1102 68..88 '{ ...o(); }': ()
1094 75..80 '"foo"': &str 1103 74..79 '"foo"': &str
1095 75..86 '"foo".foo()': i32 1104 74..85 '"foo".foo()': i32
1096 "### 1105 "###
1097 ); 1106 );
1098} 1107}
@@ -1111,33 +1120,33 @@ fn test(x: &str, y: isize) {
1111} 1120}
1112"#), 1121"#),
1113 @r###" 1122 @r###"
1114 9..10 'x': &str 1123 8..9 'x': &str
1115 18..19 'y': isize 1124 17..18 'y': isize
1116 28..170 '{ ...d"); }': () 1125 27..169 '{ ...d"); }': ()
1117 38..39 'a': (u32, &str) 1126 37..38 'a': (u32, &str)
1118 55..63 '(1, "a")': (u32, &str) 1127 54..62 '(1, "a")': (u32, &str)
1119 56..57 '1': u32 1128 55..56 '1': u32
1120 59..62 '"a"': &str 1129 58..61 '"a"': &str
1121 73..74 'b': ((u32, &str), &str) 1130 72..73 'b': ((u32, &str), &str)
1122 77..83 '(a, x)': ((u32, &str), &str) 1131 76..82 '(a, x)': ((u32, &str), &str)
1123 78..79 'a': (u32, &str) 1132 77..78 'a': (u32, &str)
1124 81..82 'x': &str 1133 80..81 'x': &str
1125 93..94 'c': (isize, &str) 1134 92..93 'c': (isize, &str)
1126 97..103 '(y, x)': (isize, &str) 1135 96..102 '(y, x)': (isize, &str)
1127 98..99 'y': isize 1136 97..98 'y': isize
1128 101..102 'x': &str 1137 100..101 'x': &str
1129 113..114 'd': ((isize, &str), &str) 1138 112..113 'd': ((isize, &str), &str)
1130 117..123 '(c, x)': ((isize, &str), &str) 1139 116..122 '(c, x)': ((isize, &str), &str)
1131 118..119 'c': (isize, &str) 1140 117..118 'c': (isize, &str)
1132 121..122 'x': &str 1141 120..121 'x': &str
1133 133..134 'e': (i32, &str) 1142 132..133 'e': (i32, &str)
1134 137..145 '(1, "e")': (i32, &str) 1143 136..144 '(1, "e")': (i32, &str)
1135 138..139 '1': i32 1144 137..138 '1': i32
1136 141..144 '"e"': &str 1145 140..143 '"e"': &str
1137 155..156 'f': ((i32, &str), &str) 1146 154..155 'f': ((i32, &str), &str)
1138 159..167 '(e, "d")': ((i32, &str), &str) 1147 158..166 '(e, "d")': ((i32, &str), &str)
1139 160..161 'e': (i32, &str) 1148 159..160 'e': (i32, &str)
1140 163..166 '"d"': &str 1149 162..165 '"d"': &str
1141 "### 1150 "###
1142 ); 1151 );
1143} 1152}
@@ -1165,58 +1174,58 @@ fn test(x: &str, y: isize) {
1165} 1174}
1166"#), 1175"#),
1167 @r###" 1176 @r###"
1168 9..10 'x': &str 1177 8..9 'x': &str
1169 18..19 'y': isize 1178 17..18 'y': isize
1170 28..293 '{ ... []; }': () 1179 27..292 '{ ... []; }': ()
1171 38..39 'a': [&str; _] 1180 37..38 'a': [&str; _]
1172 42..45 '[x]': [&str; _] 1181 41..44 '[x]': [&str; _]
1173 43..44 'x': &str 1182 42..43 'x': &str
1174 55..56 'b': [[&str; _]; _] 1183 54..55 'b': [[&str; _]; _]
1175 59..65 '[a, a]': [[&str; _]; _] 1184 58..64 '[a, a]': [[&str; _]; _]
1176 60..61 'a': [&str; _] 1185 59..60 'a': [&str; _]
1177 63..64 'a': [&str; _] 1186 62..63 'a': [&str; _]
1178 75..76 'c': [[[&str; _]; _]; _] 1187 74..75 'c': [[[&str; _]; _]; _]
1179 79..85 '[b, b]': [[[&str; _]; _]; _] 1188 78..84 '[b, b]': [[[&str; _]; _]; _]
1180 80..81 'b': [[&str; _]; _] 1189 79..80 'b': [[&str; _]; _]
1181 83..84 'b': [[&str; _]; _] 1190 82..83 'b': [[&str; _]; _]
1182 96..97 'd': [isize; _] 1191 95..96 'd': [isize; _]
1183 100..112 '[y, 1, 2, 3]': [isize; _] 1192 99..111 '[y, 1, 2, 3]': [isize; _]
1184 101..102 'y': isize 1193 100..101 'y': isize
1185 104..105 '1': isize 1194 103..104 '1': isize
1186 107..108 '2': isize 1195 106..107 '2': isize
1187 110..111 '3': isize 1196 109..110 '3': isize
1188 122..123 'd': [isize; _] 1197 121..122 'd': [isize; _]
1189 126..138 '[1, y, 2, 3]': [isize; _] 1198 125..137 '[1, y, 2, 3]': [isize; _]
1190 127..128 '1': isize 1199 126..127 '1': isize
1191 130..131 'y': isize 1200 129..130 'y': isize
1192 133..134 '2': isize 1201 132..133 '2': isize
1193 136..137 '3': isize 1202 135..136 '3': isize
1194 148..149 'e': [isize; _] 1203 147..148 'e': [isize; _]
1195 152..155 '[y]': [isize; _] 1204 151..154 '[y]': [isize; _]
1196 153..154 'y': isize 1205 152..153 'y': isize
1197 165..166 'f': [[isize; _]; _] 1206 164..165 'f': [[isize; _]; _]
1198 169..175 '[d, d]': [[isize; _]; _] 1207 168..174 '[d, d]': [[isize; _]; _]
1199 170..171 'd': [isize; _] 1208 169..170 'd': [isize; _]
1200 173..174 'd': [isize; _] 1209 172..173 'd': [isize; _]
1201 185..186 'g': [[isize; _]; _] 1210 184..185 'g': [[isize; _]; _]
1202 189..195 '[e, e]': [[isize; _]; _] 1211 188..194 '[e, e]': [[isize; _]; _]
1203 190..191 'e': [isize; _] 1212 189..190 'e': [isize; _]
1204 193..194 'e': [isize; _] 1213 192..193 'e': [isize; _]
1205 206..207 'h': [i32; _] 1214 205..206 'h': [i32; _]
1206 210..216 '[1, 2]': [i32; _] 1215 209..215 '[1, 2]': [i32; _]
1207 211..212 '1': i32 1216 210..211 '1': i32
1208 214..215 '2': i32 1217 213..214 '2': i32
1209 226..227 'i': [&str; _] 1218 225..226 'i': [&str; _]
1210 230..240 '["a", "b"]': [&str; _] 1219 229..239 '["a", "b"]': [&str; _]
1211 231..234 '"a"': &str 1220 230..233 '"a"': &str
1212 236..239 '"b"': &str 1221 235..238 '"b"': &str
1213 251..252 'b': [[&str; _]; _] 1222 250..251 'b': [[&str; _]; _]
1214 255..265 '[a, ["b"]]': [[&str; _]; _] 1223 254..264 '[a, ["b"]]': [[&str; _]; _]
1215 256..257 'a': [&str; _] 1224 255..256 'a': [&str; _]
1216 259..264 '["b"]': [&str; _] 1225 258..263 '["b"]': [&str; _]
1217 260..263 '"b"': &str 1226 259..262 '"b"': &str
1218 275..276 'x': [u8; _] 1227 274..275 'x': [u8; _]
1219 288..290 '[]': [u8; _] 1228 287..289 '[]': [u8; _]
1220 "### 1229 "###
1221 ); 1230 );
1222} 1231}
@@ -1238,21 +1247,21 @@ fn test(a1: A<u32>, i: i32) {
1238} 1247}
1239"#), 1248"#),
1240 @r###" 1249 @r###"
1241 36..38 'a1': A<u32> 1250 35..37 'a1': A<u32>
1242 48..49 'i': i32 1251 47..48 'i': i32
1243 56..147 '{ ...3.x; }': () 1252 55..146 '{ ...3.x; }': ()
1244 62..64 'a1': A<u32> 1253 61..63 'a1': A<u32>
1245 62..66 'a1.x': u32 1254 61..65 'a1.x': u32
1246 76..78 'a2': A<i32> 1255 75..77 'a2': A<i32>
1247 81..91 'A { x: i }': A<i32> 1256 80..90 'A { x: i }': A<i32>
1248 88..89 'i': i32 1257 87..88 'i': i32
1249 97..99 'a2': A<i32> 1258 96..98 'a2': A<i32>
1250 97..101 'a2.x': i32 1259 96..100 'a2.x': i32
1251 111..113 'a3': A<i128> 1260 110..112 'a3': A<i128>
1252 116..134 'A::<i1...x: 1 }': A<i128> 1261 115..133 'A::<i1...x: 1 }': A<i128>
1253 131..132 '1': i128 1262 130..131 '1': i128
1254 140..142 'a3': A<i128> 1263 139..141 'a3': A<i128>
1255 140..144 'a3.x': i128 1264 139..143 'a3.x': i128
1256 "### 1265 "###
1257 ); 1266 );
1258} 1267}
@@ -1275,22 +1284,22 @@ fn test() {
1275} 1284}
1276"#), 1285"#),
1277 @r###" 1286 @r###"
1278 76..184 '{ ...one; }': () 1287 75..183 '{ ...one; }': ()
1279 82..83 'A': A<i32>(i32) -> A<i32> 1288 81..82 'A': A<i32>(i32) -> A<i32>
1280 82..87 'A(42)': A<i32> 1289 81..86 'A(42)': A<i32>
1281 84..86 '42': i32 1290 83..85 '42': i32
1282 93..94 'A': A<u128>(u128) -> A<u128> 1291 92..93 'A': A<u128>(u128) -> A<u128>
1283 93..102 'A(42u128)': A<u128> 1292 92..101 'A(42u128)': A<u128>
1284 95..101 '42u128': u128 1293 94..100 '42u128': u128
1285 108..112 'Some': Some<&str>(&str) -> Option<&str> 1294 107..111 'Some': Some<&str>(&str) -> Option<&str>
1286 108..117 'Some("x")': Option<&str> 1295 107..116 'Some("x")': Option<&str>
1287 113..116 '"x"': &str 1296 112..115 '"x"': &str
1288 123..135 'Option::Some': Some<&str>(&str) -> Option<&str> 1297 122..134 'Option::Some': Some<&str>(&str) -> Option<&str>
1289 123..140 'Option...e("x")': Option<&str> 1298 122..139 'Option...e("x")': Option<&str>
1290 136..139 '"x"': &str 1299 135..138 '"x"': &str
1291 146..150 'None': Option<{unknown}> 1300 145..149 'None': Option<{unknown}>
1292 160..161 'x': Option<i64> 1301 159..160 'x': Option<i64>
1293 177..181 'None': Option<i64> 1302 176..180 'None': Option<i64>
1294 "### 1303 "###
1295 ); 1304 );
1296} 1305}
@@ -1308,20 +1317,20 @@ fn test() {
1308} 1317}
1309"#), 1318"#),
1310 @r###" 1319 @r###"
1311 10..11 't': T 1320 9..10 't': T
1312 21..26 '{ t }': T 1321 20..25 '{ t }': T
1313 23..24 't': T 1322 22..23 't': T
1314 38..98 '{ ...(1); }': () 1323 37..97 '{ ...(1); }': ()
1315 44..46 'id': fn id<u32>(u32) -> u32 1324 43..45 'id': fn id<u32>(u32) -> u32
1316 44..52 'id(1u32)': u32 1325 43..51 'id(1u32)': u32
1317 47..51 '1u32': u32 1326 46..50 '1u32': u32
1318 58..68 'id::<i128>': fn id<i128>(i128) -> i128 1327 57..67 'id::<i128>': fn id<i128>(i128) -> i128
1319 58..71 'id::<i128>(1)': i128 1328 57..70 'id::<i128>(1)': i128
1320 69..70 '1': i128 1329 68..69 '1': i128
1321 81..82 'x': u64 1330 80..81 'x': u64
1322 90..92 'id': fn id<u64>(u64) -> u64 1331 89..91 'id': fn id<u64>(u64) -> u64
1323 90..95 'id(1)': u64 1332 89..94 'id(1)': u64
1324 93..94 '1': u64 1333 92..93 '1': u64
1325 "### 1334 "###
1326 ); 1335 );
1327} 1336}
@@ -1355,38 +1364,38 @@ fn test() -> i128 {
1355} 1364}
1356"#), 1365"#),
1357 @r###" 1366 @r###"
1358 74..78 'self': A<X, Y> 1367 73..77 'self': A<X, Y>
1359 85..107 '{ ... }': X 1368 84..106 '{ ... }': X
1360 95..99 'self': A<X, Y> 1369 94..98 'self': A<X, Y>
1361 95..101 'self.x': X 1370 94..100 'self.x': X
1362 117..121 'self': A<X, Y> 1371 116..120 'self': A<X, Y>
1363 128..150 '{ ... }': Y 1372 127..149 '{ ... }': Y
1364 138..142 'self': A<X, Y> 1373 137..141 'self': A<X, Y>
1365 138..144 'self.y': Y 1374 137..143 'self.y': Y
1366 163..167 'self': A<X, Y> 1375 162..166 'self': A<X, Y>
1367 169..170 't': T 1376 168..169 't': T
1368 188..223 '{ ... }': (X, Y, T) 1377 187..222 '{ ... }': (X, Y, T)
1369 198..217 '(self.....y, t)': (X, Y, T) 1378 197..216 '(self.....y, t)': (X, Y, T)
1370 199..203 'self': A<X, Y> 1379 198..202 'self': A<X, Y>
1371 199..205 'self.x': X 1380 198..204 'self.x': X
1372 207..211 'self': A<X, Y> 1381 206..210 'self': A<X, Y>
1373 207..213 'self.y': Y 1382 206..212 'self.y': Y
1374 215..216 't': T 1383 214..215 't': T
1375 245..342 '{ ...(1); }': () 1384 244..341 '{ ...(1); }': ()
1376 255..256 'a': A<u64, i64> 1385 254..255 'a': A<u64, i64>
1377 259..281 'A { x:...1i64 }': A<u64, i64> 1386 258..280 'A { x:...1i64 }': A<u64, i64>
1378 266..270 '1u64': u64 1387 265..269 '1u64': u64
1379 275..279 '1i64': i64 1388 274..278 '1i64': i64
1380 287..288 'a': A<u64, i64> 1389 286..287 'a': A<u64, i64>
1381 287..292 'a.x()': u64 1390 286..291 'a.x()': u64
1382 298..299 'a': A<u64, i64> 1391 297..298 'a': A<u64, i64>
1383 298..303 'a.y()': i64 1392 297..302 'a.y()': i64
1384 309..310 'a': A<u64, i64> 1393 308..309 'a': A<u64, i64>
1385 309..319 'a.z(1i128)': (u64, i64, i128) 1394 308..318 'a.z(1i128)': (u64, i64, i128)
1386 313..318 '1i128': i128 1395 312..317 '1i128': i128
1387 325..326 'a': A<u64, i64> 1396 324..325 'a': A<u64, i64>
1388 325..339 'a.z::<u128>(1)': (u64, i64, u128) 1397 324..338 'a.z::<u128>(1)': (u64, i64, u128)
1389 337..338 '1': u128 1398 336..337 '1': u128
1390 "### 1399 "###
1391 ); 1400 );
1392} 1401}
@@ -1408,15 +1417,15 @@ fn test(o: Option<u32>) {
1408} 1417}
1409"#), 1418"#),
1410 @r###" 1419 @r###"
1411 78..82 'self': &Option<T> 1420 77..81 'self': &Option<T>
1412 98..100 '{}': () 1421 97..99 '{}': ()
1413 111..112 'o': Option<u32> 1422 110..111 'o': Option<u32>
1414 127..165 '{ ...f(); }': () 1423 126..164 '{ ...f(); }': ()
1415 133..146 '(&o).as_ref()': Option<&u32> 1424 132..145 '(&o).as_ref()': Option<&u32>
1416 134..136 '&o': &Option<u32> 1425 133..135 '&o': &Option<u32>
1417 135..136 'o': Option<u32> 1426 134..135 'o': Option<u32>
1418 152..153 'o': Option<u32> 1427 151..152 'o': Option<u32>
1419 152..162 'o.as_ref()': Option<&u32> 1428 151..161 'o.as_ref()': Option<&u32>
1420 "### 1429 "###
1421 ); 1430 );
1422} 1431}
@@ -1445,35 +1454,35 @@ fn test() -> i128 {
1445} 1454}
1446"#), 1455"#),
1447 @r###" 1456 @r###"
1448 53..57 'self': A<T2> 1457 52..56 'self': A<T2>
1449 65..87 '{ ... }': T2 1458 64..86 '{ ... }': T2
1450 75..79 'self': A<T2> 1459 74..78 'self': A<T2>
1451 75..81 'self.x': T2 1460 74..80 'self.x': T2
1452 99..100 't': T 1461 98..99 't': T
1453 110..115 '{ t }': T 1462 109..114 '{ t }': T
1454 112..113 't': T 1463 111..112 't': T
1455 135..261 '{ ....x() }': i128 1464 134..260 '{ ....x() }': i128
1456 146..147 'x': i128 1465 145..146 'x': i128
1457 150..151 '1': i128 1466 149..150 '1': i128
1458 162..163 'y': i128 1467 161..162 'y': i128
1459 166..168 'id': fn id<i128>(i128) -> i128 1468 165..167 'id': fn id<i128>(i128) -> i128
1460 166..171 'id(x)': i128 1469 165..170 'id(x)': i128
1461 169..170 'x': i128 1470 168..169 'x': i128
1462 182..183 'a': A<i128> 1471 181..182 'a': A<i128>
1463 186..200 'A { x: id(y) }': A<i128> 1472 185..199 'A { x: id(y) }': A<i128>
1464 193..195 'id': fn id<i128>(i128) -> i128 1473 192..194 'id': fn id<i128>(i128) -> i128
1465 193..198 'id(y)': i128 1474 192..197 'id(y)': i128
1466 196..197 'y': i128 1475 195..196 'y': i128
1467 211..212 'z': i128 1476 210..211 'z': i128
1468 215..217 'id': fn id<i128>(i128) -> i128 1477 214..216 'id': fn id<i128>(i128) -> i128
1469 215..222 'id(a.x)': i128 1478 214..221 'id(a.x)': i128
1470 218..219 'a': A<i128> 1479 217..218 'a': A<i128>
1471 218..221 'a.x': i128 1480 217..220 'a.x': i128
1472 233..234 'b': A<i128> 1481 232..233 'b': A<i128>
1473 237..247 'A { x: z }': A<i128> 1482 236..246 'A { x: z }': A<i128>
1474 244..245 'z': i128 1483 243..244 'z': i128
1475 254..255 'b': A<i128> 1484 253..254 'b': A<i128>
1476 254..259 'b.x()': i128 1485 253..258 'b.x()': i128
1477 "### 1486 "###
1478 ); 1487 );
1479} 1488}
@@ -1511,16 +1520,16 @@ fn test() {
1511} 1520}
1512"#), 1521"#),
1513 @r###" 1522 @r###"
1514 52..53 '1': u32 1523 51..52 '1': u32
1515 105..106 '2': u32 1524 104..105 '2': u32
1516 213..214 '5': u32 1525 212..213 '5': u32
1517 229..307 '{ ...:ID; }': () 1526 228..306 '{ ...:ID; }': ()
1518 239..240 'x': u32 1527 238..239 'x': u32
1519 243..254 'Struct::FOO': u32 1528 242..253 'Struct::FOO': u32
1520 264..265 'y': u32 1529 263..264 'y': u32
1521 268..277 'Enum::BAR': u32 1530 267..276 'Enum::BAR': u32
1522 287..288 'z': u32 1531 286..287 'z': u32
1523 291..304 'TraitTest::ID': u32 1532 290..303 'TraitTest::ID': u32
1524 "### 1533 "###
1525 ); 1534 );
1526} 1535}
@@ -1543,22 +1552,22 @@ fn test(x: Foo, y: Bar<&str>, z: Baz<i8, u8>) {
1543} 1552}
1544"#), 1553"#),
1545 @r###" 1554 @r###"
1546 116..117 'x': A<u32, i128> 1555 115..116 'x': A<u32, i128>
1547 124..125 'y': A<&str, u128> 1556 123..124 'y': A<&str, u128>
1548 138..139 'z': A<u8, i8> 1557 137..138 'z': A<u8, i8>
1549 154..211 '{ ...z.y; }': () 1558 153..210 '{ ...z.y; }': ()
1550 160..161 'x': A<u32, i128> 1559 159..160 'x': A<u32, i128>
1551 160..163 'x.x': u32 1560 159..162 'x.x': u32
1552 169..170 'x': A<u32, i128> 1561 168..169 'x': A<u32, i128>
1553 169..172 'x.y': i128 1562 168..171 'x.y': i128
1554 178..179 'y': A<&str, u128> 1563 177..178 'y': A<&str, u128>
1555 178..181 'y.x': &str 1564 177..180 'y.x': &str
1556 187..188 'y': A<&str, u128> 1565 186..187 'y': A<&str, u128>
1557 187..190 'y.y': u128 1566 186..189 'y.y': u128
1558 196..197 'z': A<u8, i8> 1567 195..196 'z': A<u8, i8>
1559 196..199 'z.x': u8 1568 195..198 'z.x': u8
1560 205..206 'z': A<u8, i8> 1569 204..205 'z': A<u8, i8>
1561 205..208 'z.y': i8 1570 204..207 'z.y': i8
1562 "### 1571 "###
1563 ) 1572 )
1564} 1573}
@@ -1573,8 +1582,8 @@ type Bar = A<Bar>;
1573fn test(x: Foo) {} 1582fn test(x: Foo) {}
1574"#), 1583"#),
1575 @r###" 1584 @r###"
1576 59..60 'x': {unknown} 1585 58..59 'x': {unknown}
1577 67..69 '{}': () 1586 66..68 '{}': ()
1578 "### 1587 "###
1579 ) 1588 )
1580} 1589}
@@ -1599,26 +1608,26 @@ fn test() {
1599} 1608}
1600"#), 1609"#),
1601 @r###" 1610 @r###"
1602 10..11 'x': T 1611 9..10 'x': T
1603 21..30 '{ x }': T 1612 20..29 '{ x }': T
1604 27..28 'x': T 1613 26..27 'x': T
1605 44..45 'x': &T 1614 43..44 'x': &T
1606 56..66 '{ *x }': T 1615 55..65 '{ *x }': T
1607 62..64 '*x': T 1616 61..63 '*x': T
1608 63..64 'x': &T 1617 62..63 'x': &T
1609 78..158 '{ ...(1); }': () 1618 77..157 '{ ...(1); }': ()
1610 88..89 'y': u32 1619 87..88 'y': u32
1611 92..97 '10u32': u32 1620 91..96 '10u32': u32
1612 103..105 'id': fn id<u32>(u32) -> u32 1621 102..104 'id': fn id<u32>(u32) -> u32
1613 103..108 'id(y)': u32 1622 102..107 'id(y)': u32
1614 106..107 'y': u32 1623 105..106 'y': u32
1615 118..119 'x': bool 1624 117..118 'x': bool
1616 128..133 'clone': fn clone<bool>(&bool) -> bool 1625 127..132 'clone': fn clone<bool>(&bool) -> bool
1617 128..136 'clone(z)': bool 1626 127..135 'clone(z)': bool
1618 134..135 'z': &bool 1627 133..134 'z': &bool
1619 142..152 'id::<i128>': fn id<i128>(i128) -> i128 1628 141..151 'id::<i128>': fn id<i128>(i128) -> i128
1620 142..155 'id::<i128>(1)': i128 1629 141..154 'id::<i128>(1)': i128
1621 153..154 '1': i128 1630 152..153 '1': i128
1622 "### 1631 "###
1623 ); 1632 );
1624} 1633}
@@ -1638,16 +1647,16 @@ fn test() {
1638} 1647}
1639"#), 1648"#),
1640 @r###" 1649 @r###"
1641 49..50 '0': u32 1650 48..49 '0': u32
1642 80..83 '101': u32 1651 79..82 '101': u32
1643 95..213 '{ ...NST; }': () 1652 94..212 '{ ...NST; }': ()
1644 138..139 'x': u32 1653 137..138 'x': u32
1645 142..153 'LOCAL_CONST': u32 1654 141..152 'LOCAL_CONST': u32
1646 163..164 'z': u32 1655 162..163 'z': u32
1647 167..179 'GLOBAL_CONST': u32 1656 166..178 'GLOBAL_CONST': u32
1648 189..191 'id': u32 1657 188..190 'id': u32
1649 194..210 'Foo::A..._CONST': u32 1658 193..209 'Foo::A..._CONST': u32
1650 126..128 '99': u32 1659 125..127 '99': u32
1651 "### 1660 "###
1652 ); 1661 );
1653} 1662}
@@ -1668,28 +1677,27 @@ fn test() {
1668} 1677}
1669"#), 1678"#),
1670 @r###" 1679 @r###"
1671 29..32 '101': u32 1680 28..31 '101': u32
1672 70..73 '101': u32 1681 69..72 '101': u32
1673 85..280 '{ ...MUT; }': () 1682 84..279 '{ ...MUT; }': ()
1674 173..174 'x': u32 1683 172..173 'x': u32
1675 177..189 'LOCAL_STATIC': u32 1684 176..188 'LOCAL_STATIC': u32
1676 199..200 'y': u32 1685 198..199 'y': u32
1677 203..219 'LOCAL_...IC_MUT': u32 1686 202..218 'LOCAL_...IC_MUT': u32
1678 229..230 'z': u32 1687 228..229 'z': u32
1679 233..246 'GLOBAL_STATIC': u32 1688 232..245 'GLOBAL_STATIC': u32
1680 256..257 'w': u32 1689 255..256 'w': u32
1681 260..277 'GLOBAL...IC_MUT': u32 1690 259..276 'GLOBAL...IC_MUT': u32
1682 118..120 '99': u32 1691 117..119 '99': u32
1683 161..163 '99': u32 1692 160..162 '99': u32
1684 "### 1693 "###
1685 ); 1694 );
1686} 1695}
1687 1696
1688#[test] 1697#[test]
1689fn shadowing_primitive() { 1698fn shadowing_primitive() {
1690 let t = type_at( 1699 check_types(
1691 r#" 1700 r#"
1692//- /main.rs
1693struct i32; 1701struct i32;
1694struct Foo; 1702struct Foo;
1695 1703
@@ -1697,15 +1705,15 @@ impl i32 { fn foo(&self) -> Foo { Foo } }
1697 1705
1698fn main() { 1706fn main() {
1699 let x: i32 = i32; 1707 let x: i32 = i32;
1700 x.foo()<|>; 1708 x.foo();
1709 //^ Foo
1701}"#, 1710}"#,
1702 ); 1711 );
1703 assert_eq!(t, "Foo");
1704} 1712}
1705 1713
1706#[test] 1714#[test]
1707fn not_shadowing_primitive_by_module() { 1715fn not_shadowing_primitive_by_module() {
1708 let t = type_at( 1716 check_types(
1709 r#" 1717 r#"
1710//- /str.rs 1718//- /str.rs
1711fn foo() {} 1719fn foo() {}
@@ -1715,15 +1723,15 @@ mod str;
1715fn foo() -> &'static str { "" } 1723fn foo() -> &'static str { "" }
1716 1724
1717fn main() { 1725fn main() {
1718 foo()<|>; 1726 foo();
1727 //^ &str
1719}"#, 1728}"#,
1720 ); 1729 );
1721 assert_eq!(t, "&str");
1722} 1730}
1723 1731
1724#[test] 1732#[test]
1725fn not_shadowing_module_by_primitive() { 1733fn not_shadowing_module_by_primitive() {
1726 let t = type_at( 1734 check_types(
1727 r#" 1735 r#"
1728//- /str.rs 1736//- /str.rs
1729fn foo() -> u32 {0} 1737fn foo() -> u32 {0}
@@ -1733,10 +1741,38 @@ mod str;
1733fn foo() -> &'static str { "" } 1741fn foo() -> &'static str { "" }
1734 1742
1735fn main() { 1743fn main() {
1736 str::foo()<|>; 1744 str::foo();
1745 //^ u32
1746}"#,
1747 );
1748}
1749
1750// This test is actually testing the shadowing behavior within ra_hir_def. It
1751// lives here because the testing infrastructure in ra_hir_def isn't currently
1752// capable of asserting the necessary conditions.
1753#[test]
1754fn should_be_shadowing_imports() {
1755 check_types(
1756 r#"
1757mod a {
1758 pub fn foo() -> i8 {0}
1759 pub struct foo { a: i8 }
1760}
1761mod b { pub fn foo () -> u8 {0} }
1762mod c { pub struct foo { a: u8 } }
1763mod d {
1764 pub use super::a::*;
1765 pub use super::c::foo;
1766 pub use super::b::foo;
1767}
1768
1769fn main() {
1770 d::foo();
1771 //^ u8
1772 d::foo{a:0};
1773 //^ u8
1737}"#, 1774}"#,
1738 ); 1775 );
1739 assert_eq!(t, "u32");
1740} 1776}
1741 1777
1742#[test] 1778#[test]
@@ -1748,12 +1784,12 @@ fn foo() -> u32 {
1748} 1784}
1749"#), 1785"#),
1750 @r###" 1786 @r###"
1751 17..59 '{ ...; }; }': () 1787 16..58 '{ ...; }; }': ()
1752 27..28 'x': || -> usize 1788 26..27 'x': || -> usize
1753 31..56 '|| -> ...n 1; }': || -> usize 1789 30..55 '|| -> ...n 1; }': || -> usize
1754 43..56 '{ return 1; }': usize 1790 42..55 '{ return 1; }': usize
1755 45..53 'return 1': ! 1791 44..52 'return 1': !
1756 52..53 '1': usize 1792 51..52 '1': usize
1757 "### 1793 "###
1758 ); 1794 );
1759} 1795}
@@ -1767,11 +1803,11 @@ fn foo() -> u32 {
1767} 1803}
1768"#), 1804"#),
1769 @r###" 1805 @r###"
1770 17..48 '{ ...; }; }': () 1806 16..47 '{ ...; }; }': ()
1771 27..28 'x': || -> () 1807 26..27 'x': || -> ()
1772 31..45 '|| { return; }': || -> () 1808 30..44 '|| { return; }': || -> ()
1773 34..45 '{ return; }': () 1809 33..44 '{ return; }': ()
1774 36..42 'return': ! 1810 35..41 'return': !
1775 "### 1811 "###
1776 ); 1812 );
1777} 1813}
@@ -1785,11 +1821,11 @@ fn foo() -> u32 {
1785} 1821}
1786"#), 1822"#),
1787 @r###" 1823 @r###"
1788 17..47 '{ ..." }; }': () 1824 16..46 '{ ..." }; }': ()
1789 27..28 'x': || -> &str 1825 26..27 'x': || -> &str
1790 31..44 '|| { "test" }': || -> &str 1826 30..43 '|| { "test" }': || -> &str
1791 34..44 '{ "test" }': &str 1827 33..43 '{ "test" }': &str
1792 36..42 '"test"': &str 1828 35..41 '"test"': &str
1793 "### 1829 "###
1794 ); 1830 );
1795} 1831}
@@ -1808,14 +1844,14 @@ fn main() {
1808} 1844}
1809"#), 1845"#),
1810 @r###" 1846 @r###"
1811 48..121 '{ ...hod; }': () 1847 47..120 '{ ...hod; }': ()
1812 58..64 'vtable': Vtable 1848 57..63 'vtable': Vtable
1813 67..91 'Vtable...| {} }': Vtable 1849 66..90 'Vtable...| {} }': Vtable
1814 84..89 '|| {}': || -> () 1850 83..88 '|| {}': || -> ()
1815 87..89 '{}': () 1851 86..88 '{}': ()
1816 101..102 'm': fn() 1852 100..101 'm': fn()
1817 105..111 'vtable': Vtable 1853 104..110 'vtable': Vtable
1818 105..118 'vtable.method': fn() 1854 104..117 'vtable.method': fn()
1819 "### 1855 "###
1820 ); 1856 );
1821} 1857}
@@ -1832,22 +1868,23 @@ fn main() {
1832} 1868}
1833"#), 1869"#),
1834 @r###" 1870 @r###"
1835 11..131 '{ ...2 }; }': () 1871 10..130 '{ ...2 }; }': ()
1836 21..22 'x': i32 1872 20..21 'x': i32
1837 32..38 '{ 92 }': i32 1873 24..37 'unsafe { 92 }': i32
1838 34..36 '92': i32 1874 31..37 '{ 92 }': i32
1839 48..49 'y': {unknown} 1875 33..35 '92': i32
1840 58..80 '{ asyn...wait }': {unknown} 1876 47..48 'y': {unknown}
1841 60..78 'async ....await': {unknown} 1877 57..79 '{ asyn...wait }': {unknown}
1842 66..72 '{ () }': () 1878 59..77 'async ....await': {unknown}
1843 68..70 '()': () 1879 65..71 '{ () }': ()
1844 90..91 'z': {unknown} 1880 67..69 '()': ()
1845 94..104 'try { () }': {unknown} 1881 89..90 'z': {unknown}
1846 98..104 '{ () }': () 1882 93..103 'try { () }': {unknown}
1847 100..102 '()': () 1883 97..103 '{ () }': ()
1848 114..115 't': i32 1884 99..101 '()': ()
1849 122..128 '{ 92 }': i32 1885 113..114 't': i32
1850 124..126 '92': i32 1886 121..127 '{ 92 }': i32
1887 123..125 '92': i32
1851 "### 1888 "###
1852 ) 1889 )
1853} 1890}
@@ -1867,16 +1904,16 @@ fn test() {
1867} 1904}
1868"#), 1905"#),
1869 @r###" 1906 @r###"
1870 60..130 '{ ... } }': () 1907 59..129 '{ ... } }': ()
1871 70..77 'mut end': Option<bool> 1908 69..76 'mut end': Option<bool>
1872 80..84 'None': Option<bool> 1909 79..83 'None': Option<bool>
1873 90..128 'loop {... }': ! 1910 89..127 'loop {... }': !
1874 95..128 '{ ... }': () 1911 94..127 '{ ... }': ()
1875 105..108 'end': Option<bool> 1912 104..107 'end': Option<bool>
1876 105..121 'end = ...(true)': () 1913 104..120 'end = ...(true)': ()
1877 111..115 'Some': Some<bool>(bool) -> Option<bool> 1914 110..114 'Some': Some<bool>(bool) -> Option<bool>
1878 111..121 'Some(true)': Option<bool> 1915 110..120 'Some(true)': Option<bool>
1879 116..120 'true': bool 1916 115..119 'true': bool
1880 "### 1917 "###
1881 ); 1918 );
1882} 1919}
@@ -1899,19 +1936,19 @@ fn test() {
1899} 1936}
1900"#), 1937"#),
1901 @r###" 1938 @r###"
1902 60..169 '{ ... }; }': () 1939 59..168 '{ ... }; }': ()
1903 70..71 'x': Option<bool> 1940 69..70 'x': Option<bool>
1904 74..166 'loop {... }': Option<bool> 1941 73..165 'loop {... }': Option<bool>
1905 79..166 '{ ... }': () 1942 78..165 '{ ... }': ()
1906 89..133 'if fal... }': () 1943 88..132 'if fal... }': ()
1907 92..97 'false': bool 1944 91..96 'false': bool
1908 98..133 '{ ... }': () 1945 97..132 '{ ... }': ()
1909 112..122 'break None': ! 1946 111..121 'break None': !
1910 118..122 'None': Option<bool> 1947 117..121 'None': Option<bool>
1911 143..159 'break ...(true)': ! 1948 142..158 'break ...(true)': !
1912 149..153 'Some': Some<bool>(bool) -> Option<bool> 1949 148..152 'Some': Some<bool>(bool) -> Option<bool>
1913 149..159 'Some(true)': Option<bool> 1950 148..158 'Some(true)': Option<bool>
1914 154..158 'true': bool 1951 153..157 'true': bool
1915 "### 1952 "###
1916 ); 1953 );
1917} 1954}
@@ -1932,14 +1969,14 @@ fn test() {
1932} 1969}
1933"#), 1970"#),
1934 @r###" 1971 @r###"
1935 60..137 '{ ... }; }': () 1972 59..136 '{ ... }; }': ()
1936 70..71 'x': () 1973 69..70 'x': ()
1937 74..134 'loop {... }': () 1974 73..133 'loop {... }': ()
1938 79..134 '{ ... }': () 1975 78..133 '{ ... }': ()
1939 89..128 'if fal... }': () 1976 88..127 'if fal... }': ()
1940 92..97 'false': bool 1977 91..96 'false': bool
1941 98..128 '{ ... }': () 1978 97..127 '{ ... }': ()
1942 112..117 'break': ! 1979 111..116 'break': !
1943 "### 1980 "###
1944 ); 1981 );
1945} 1982}
@@ -1964,36 +2001,189 @@ fn foo() {
1964} 2001}
1965"#), 2002"#),
1966 @r###" 2003 @r###"
1967 10..336 '{ ... }; }': () 2004 9..335 '{ ... }; }': ()
1968 20..22 '_x': || -> bool 2005 19..21 '_x': || -> bool
1969 25..333 '|| 'ou... }': || -> bool 2006 24..332 '|| 'ou... }': || -> bool
1970 28..333 ''outer... }': bool 2007 27..332 ''outer... }': bool
1971 41..333 '{ ... }': () 2008 40..332 '{ ... }': ()
1972 55..60 'inner': i8 2009 54..59 'inner': i8
1973 63..301 ''inner... }': i8 2010 62..300 ''inner... }': i8
1974 76..301 '{ ... }': () 2011 75..300 '{ ... }': ()
1975 94..95 'i': bool 2012 93..94 'i': bool
1976 98..114 'Defaul...efault': {unknown} 2013 97..113 'Defaul...efault': {unknown}
1977 98..116 'Defaul...ault()': bool 2014 97..115 'Defaul...ault()': bool
1978 130..270 'if (br... }': () 2015 129..269 'if (br... }': ()
1979 134..148 'break 'outer i': ! 2016 133..147 'break 'outer i': !
1980 147..148 'i': bool 2017 146..147 'i': bool
1981 150..209 '{ ... }': () 2018 149..208 '{ ... }': ()
1982 168..194 'loop {...5i8; }': ! 2019 167..193 'loop {...5i8; }': !
1983 173..194 '{ brea...5i8; }': () 2020 172..193 '{ brea...5i8; }': ()
1984 175..191 'break ...er 5i8': ! 2021 174..190 'break ...er 5i8': !
1985 188..191 '5i8': i8 2022 187..190 '5i8': i8
1986 215..270 'if tru... }': () 2023 214..269 'if tru... }': ()
1987 218..222 'true': bool 2024 217..221 'true': bool
1988 223..270 '{ ... }': () 2025 222..269 '{ ... }': ()
1989 241..255 'break 'inner 6': ! 2026 240..254 'break 'inner 6': !
1990 254..255 '6': i8 2027 253..254 '6': i8
1991 283..290 'break 7': ! 2028 282..289 'break 7': !
1992 289..290 '7': i8 2029 288..289 '7': i8
1993 311..326 'break inner < 8': ! 2030 310..325 'break inner < 8': !
1994 317..322 'inner': i8 2031 316..321 'inner': i8
1995 317..326 'inner < 8': bool 2032 316..325 'inner < 8': bool
1996 325..326 '8': i8 2033 324..325 '8': i8
2034 "###
2035 );
2036}
2037
2038#[test]
2039fn generic_default() {
2040 assert_snapshot!(
2041 infer(r#"
2042struct Thing<T = ()> { t: T }
2043enum OtherThing<T = ()> {
2044 One { t: T },
2045 Two(T),
2046}
2047
2048fn test(t1: Thing, t2: OtherThing, t3: Thing<i32>, t4: OtherThing<i32>) {
2049 t1.t;
2050 t3.t;
2051 match t2 {
2052 OtherThing::One { t } => { t; },
2053 OtherThing::Two(t) => { t; },
2054 }
2055 match t4 {
2056 OtherThing::One { t } => { t; },
2057 OtherThing::Two(t) => { t; },
2058 }
2059}
2060"#),
2061 @r###"
2062 97..99 't1': Thing<()>
2063 108..110 't2': OtherThing<()>
2064 124..126 't3': Thing<i32>
2065 140..142 't4': OtherThing<i32>
2066 161..384 '{ ... } }': ()
2067 167..169 't1': Thing<()>
2068 167..171 't1.t': ()
2069 177..179 't3': Thing<i32>
2070 177..181 't3.t': i32
2071 187..282 'match ... }': ()
2072 193..195 't2': OtherThing<()>
2073 206..227 'OtherT... { t }': OtherThing<()>
2074 224..225 't': ()
2075 231..237 '{ t; }': ()
2076 233..234 't': ()
2077 247..265 'OtherT...Two(t)': OtherThing<()>
2078 263..264 't': ()
2079 269..275 '{ t; }': ()
2080 271..272 't': ()
2081 287..382 'match ... }': ()
2082 293..295 't4': OtherThing<i32>
2083 306..327 'OtherT... { t }': OtherThing<i32>
2084 324..325 't': i32
2085 331..337 '{ t; }': ()
2086 333..334 't': i32
2087 347..365 'OtherT...Two(t)': OtherThing<i32>
2088 363..364 't': i32
2089 369..375 '{ t; }': ()
2090 371..372 't': i32
2091 "###
2092 );
2093}
2094
2095#[test]
2096fn generic_default_in_struct_literal() {
2097 assert_snapshot!(
2098 infer(r#"
2099struct Thing<T = ()> { t: T }
2100enum OtherThing<T = ()> {
2101 One { t: T },
2102 Two(T),
2103}
2104
2105fn test() {
2106 let x = Thing { t: loop {} };
2107 let y = Thing { t: () };
2108 let z = Thing { t: 1i32 };
2109 if let Thing { t } = z {
2110 t;
2111 }
2112
2113 let a = OtherThing::One { t: 1i32 };
2114 let b = OtherThing::Two(1i32);
2115}
2116"#),
2117 @r###"
2118 99..319 '{ ...32); }': ()
2119 109..110 'x': Thing<!>
2120 113..133 'Thing ...p {} }': Thing<!>
2121 124..131 'loop {}': !
2122 129..131 '{}': ()
2123 143..144 'y': Thing<()>
2124 147..162 'Thing { t: () }': Thing<()>
2125 158..160 '()': ()
2126 172..173 'z': Thing<i32>
2127 176..193 'Thing ...1i32 }': Thing<i32>
2128 187..191 '1i32': i32
2129 199..240 'if let... }': ()
2130 206..217 'Thing { t }': Thing<i32>
2131 214..215 't': i32
2132 220..221 'z': Thing<i32>
2133 222..240 '{ ... }': ()
2134 232..233 't': i32
2135 250..251 'a': OtherThing<i32>
2136 254..281 'OtherT...1i32 }': OtherThing<i32>
2137 275..279 '1i32': i32
2138 291..292 'b': OtherThing<i32>
2139 295..310 'OtherThing::Two': Two<i32>(i32) -> OtherThing<i32>
2140 295..316 'OtherT...(1i32)': OtherThing<i32>
2141 311..315 '1i32': i32
2142 "###
2143 );
2144}
2145
2146#[test]
2147fn generic_default_depending_on_other_type_arg() {
2148 assert_snapshot!(
2149 infer(r#"
2150struct Thing<T = u128, F = fn() -> T> { t: T }
2151
2152fn test(t1: Thing<u32>, t2: Thing) {
2153 t1;
2154 t2;
2155 Thing::<_> { t: 1u32 };
2156}
2157"#),
2158 // FIXME: the {unknown} is a bug
2159 @r###"
2160 56..58 't1': Thing<u32, fn() -> u32>
2161 72..74 't2': Thing<u128, fn() -> u128>
2162 83..130 '{ ...2 }; }': ()
2163 89..91 't1': Thing<u32, fn() -> u32>
2164 97..99 't2': Thing<u128, fn() -> u128>
2165 105..127 'Thing:...1u32 }': Thing<u32, fn() -> {unknown}>
2166 121..125 '1u32': u32
2167 "###
2168 );
2169}
2170
2171#[test]
2172fn generic_default_depending_on_other_type_arg_forward() {
2173 assert_snapshot!(
2174 infer(r#"
2175struct Thing<F = fn() -> T, T = u128> { t: T }
2176
2177fn test(t1: Thing) {
2178 t1;
2179}
2180"#),
2181 // the {unknown} here is intentional, as defaults are not allowed to
2182 // refer to type parameters coming later
2183 @r###"
2184 56..58 't1': Thing<fn() -> {unknown}, u128>
2185 67..78 '{ t1; }': ()
2186 73..75 't1': Thing<fn() -> {unknown}, u128>
1997 "### 2187 "###
1998 ); 2188 );
1999} 2189}
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index e8778d419..27737fa94 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -1,17 +1,13 @@
1use insta::assert_snapshot; 1use insta::assert_snapshot;
2use ra_db::fixture::WithFixture;
3use test_utils::mark; 2use test_utils::mark;
4 3
5use crate::test_db::TestDB; 4use super::{check_types, infer, infer_with_mismatches};
6
7use super::{infer, infer_with_mismatches, type_at, type_at_pos};
8 5
9#[test] 6#[test]
10fn infer_await() { 7fn infer_await() {
11 let (db, pos) = TestDB::with_position( 8 check_types(
12 r#" 9 r#"
13//- /main.rs crate:main deps:std 10//- /main.rs crate:main deps:core
14
15struct IntFuture; 11struct IntFuture;
16 12
17impl Future for IntFuture { 13impl Future for IntFuture {
@@ -21,10 +17,10 @@ impl Future for IntFuture {
21fn test() { 17fn test() {
22 let r = IntFuture; 18 let r = IntFuture;
23 let v = r.await; 19 let v = r.await;
24 v<|>; 20 v;
25} 21} //^ u64
26 22
27//- /std.rs crate:std 23//- /core.rs crate:core
28#[prelude_import] use future::*; 24#[prelude_import] use future::*;
29mod future { 25mod future {
30 #[lang = "future_trait"] 26 #[lang = "future_trait"]
@@ -32,18 +28,15 @@ mod future {
32 type Output; 28 type Output;
33 } 29 }
34} 30}
35
36"#, 31"#,
37 ); 32 );
38 assert_eq!("u64", type_at_pos(&db, pos));
39} 33}
40 34
41#[test] 35#[test]
42fn infer_async() { 36fn infer_async() {
43 let (db, pos) = TestDB::with_position( 37 check_types(
44 r#" 38 r#"
45//- /main.rs crate:main deps:std 39//- /main.rs crate:main deps:core
46
47async fn foo() -> u64 { 40async fn foo() -> u64 {
48 128 41 128
49} 42}
@@ -51,10 +44,10 @@ async fn foo() -> u64 {
51fn test() { 44fn test() {
52 let r = foo(); 45 let r = foo();
53 let v = r.await; 46 let v = r.await;
54 v<|>; 47 v;
55} 48} //^ u64
56 49
57//- /std.rs crate:std 50//- /core.rs crate:core
58#[prelude_import] use future::*; 51#[prelude_import] use future::*;
59mod future { 52mod future {
60 #[lang = "future_trait"] 53 #[lang = "future_trait"]
@@ -62,28 +55,25 @@ mod future {
62 type Output; 55 type Output;
63 } 56 }
64} 57}
65
66"#, 58"#,
67 ); 59 );
68 assert_eq!("u64", type_at_pos(&db, pos));
69} 60}
70 61
71#[test] 62#[test]
72fn infer_desugar_async() { 63fn infer_desugar_async() {
73 let (db, pos) = TestDB::with_position( 64 check_types(
74 r#" 65 r#"
75//- /main.rs crate:main deps:std 66//- /main.rs crate:main deps:core
76
77async fn foo() -> u64 { 67async fn foo() -> u64 {
78 128 68 128
79} 69}
80 70
81fn test() { 71fn test() {
82 let r = foo(); 72 let r = foo();
83 r<|>; 73 r;
84} 74} //^ impl Future<Output = u64>
85 75
86//- /std.rs crate:std 76//- /core.rs crate:core
87#[prelude_import] use future::*; 77#[prelude_import] use future::*;
88mod future { 78mod future {
89 trait Future { 79 trait Future {
@@ -93,23 +83,20 @@ mod future {
93 83
94"#, 84"#,
95 ); 85 );
96 assert_eq!("impl Future<Output = u64>", type_at_pos(&db, pos));
97} 86}
98 87
99#[test] 88#[test]
100fn infer_try() { 89fn infer_try() {
101 let (db, pos) = TestDB::with_position( 90 check_types(
102 r#" 91 r#"
103//- /main.rs crate:main deps:std 92//- /main.rs crate:main deps:core
104
105fn test() { 93fn test() {
106 let r: Result<i32, u64> = Result::Ok(1); 94 let r: Result<i32, u64> = Result::Ok(1);
107 let v = r?; 95 let v = r?;
108 v<|>; 96 v;
109} 97} //^ i32
110
111//- /std.rs crate:std
112 98
99//- /core.rs crate:core
113#[prelude_import] use ops::*; 100#[prelude_import] use ops::*;
114mod ops { 101mod ops {
115 trait Try { 102 trait Try {
@@ -130,30 +117,26 @@ mod result {
130 type Error = E; 117 type Error = E;
131 } 118 }
132} 119}
133
134"#, 120"#,
135 ); 121 );
136 assert_eq!("i32", type_at_pos(&db, pos));
137} 122}
138 123
139#[test] 124#[test]
140fn infer_for_loop() { 125fn infer_for_loop() {
141 let (db, pos) = TestDB::with_position( 126 check_types(
142 r#" 127 r#"
143//- /main.rs crate:main deps:std 128//- /main.rs crate:main deps:core,alloc
144 129use alloc::collections::Vec;
145use std::collections::Vec;
146 130
147fn test() { 131fn test() {
148 let v = Vec::new(); 132 let v = Vec::new();
149 v.push("foo"); 133 v.push("foo");
150 for x in v { 134 for x in v {
151 x<|>; 135 x;
152 } 136 } //^ &str
153} 137}
154 138
155//- /std.rs crate:std 139//- /core.rs crate:core
156
157#[prelude_import] use iter::*; 140#[prelude_import] use iter::*;
158mod iter { 141mod iter {
159 trait IntoIterator { 142 trait IntoIterator {
@@ -161,6 +144,7 @@ mod iter {
161 } 144 }
162} 145}
163 146
147//- /alloc.rs crate:alloc deps:core
164mod collections { 148mod collections {
165 struct Vec<T> {} 149 struct Vec<T> {}
166 impl<T> Vec<T> { 150 impl<T> Vec<T> {
@@ -168,21 +152,19 @@ mod collections {
168 fn push(&mut self, t: T) { } 152 fn push(&mut self, t: T) { }
169 } 153 }
170 154
171 impl<T> crate::iter::IntoIterator for Vec<T> { 155 impl<T> IntoIterator for Vec<T> {
172 type Item=T; 156 type Item=T;
173 } 157 }
174} 158}
175"#, 159"#,
176 ); 160 );
177 assert_eq!("&str", type_at_pos(&db, pos));
178} 161}
179 162
180#[test] 163#[test]
181fn infer_ops_neg() { 164fn infer_ops_neg() {
182 let (db, pos) = TestDB::with_position( 165 check_types(
183 r#" 166 r#"
184//- /main.rs crate:main deps:std 167//- /main.rs crate:main deps:std
185
186struct Bar; 168struct Bar;
187struct Foo; 169struct Foo;
188 170
@@ -193,11 +175,10 @@ impl std::ops::Neg for Bar {
193fn test() { 175fn test() {
194 let a = Bar; 176 let a = Bar;
195 let b = -a; 177 let b = -a;
196 b<|>; 178 b;
197} 179} //^ Foo
198 180
199//- /std.rs crate:std 181//- /std.rs crate:std
200
201#[prelude_import] use ops::*; 182#[prelude_import] use ops::*;
202mod ops { 183mod ops {
203 #[lang = "neg"] 184 #[lang = "neg"]
@@ -207,15 +188,13 @@ mod ops {
207} 188}
208"#, 189"#,
209 ); 190 );
210 assert_eq!("Foo", type_at_pos(&db, pos));
211} 191}
212 192
213#[test] 193#[test]
214fn infer_ops_not() { 194fn infer_ops_not() {
215 let (db, pos) = TestDB::with_position( 195 check_types(
216 r#" 196 r#"
217//- /main.rs crate:main deps:std 197//- /main.rs crate:main deps:std
218
219struct Bar; 198struct Bar;
220struct Foo; 199struct Foo;
221 200
@@ -226,11 +205,10 @@ impl std::ops::Not for Bar {
226fn test() { 205fn test() {
227 let a = Bar; 206 let a = Bar;
228 let b = !a; 207 let b = !a;
229 b<|>; 208 b;
230} 209} //^ Foo
231 210
232//- /std.rs crate:std 211//- /std.rs crate:std
233
234#[prelude_import] use ops::*; 212#[prelude_import] use ops::*;
235mod ops { 213mod ops {
236 #[lang = "not"] 214 #[lang = "not"]
@@ -240,7 +218,6 @@ mod ops {
240} 218}
241"#, 219"#,
242 ); 220 );
243 assert_eq!("Foo", type_at_pos(&db, pos));
244} 221}
245 222
246#[test] 223#[test]
@@ -257,16 +234,16 @@ fn test() {
257} 234}
258"#), 235"#),
259 @r###" 236 @r###"
260 86..87 't': T 237 85..86 't': T
261 92..94 '{}': () 238 91..93 '{}': ()
262 105..144 '{ ...(s); }': () 239 104..143 '{ ...(s); }': ()
263 115..116 's': S<u32> 240 114..115 's': S<u32>
264 119..120 'S': S<u32>(u32) -> S<u32> 241 118..119 'S': S<u32>(u32) -> S<u32>
265 119..129 'S(unknown)': S<u32> 242 118..128 'S(unknown)': S<u32>
266 121..128 'unknown': u32 243 120..127 'unknown': u32
267 135..138 'foo': fn foo<S<u32>>(S<u32>) 244 134..137 'foo': fn foo<S<u32>>(S<u32>)
268 135..141 'foo(s)': () 245 134..140 'foo(s)': ()
269 139..140 's': S<u32> 246 138..139 's': S<u32>
270 "### 247 "###
271 ); 248 );
272} 249}
@@ -285,17 +262,17 @@ fn test() {
285} 262}
286"#), 263"#),
287 @r###" 264 @r###"
288 87..88 't': T 265 86..87 't': T
289 98..100 '{}': () 266 97..99 '{}': ()
290 111..163 '{ ...(s); }': () 267 110..162 '{ ...(s); }': ()
291 121..122 's': S<u32> 268 120..121 's': S<u32>
292 125..126 'S': S<u32>(u32) -> S<u32> 269 124..125 'S': S<u32>(u32) -> S<u32>
293 125..135 'S(unknown)': S<u32> 270 124..134 'S(unknown)': S<u32>
294 127..134 'unknown': u32 271 126..133 'unknown': u32
295 145..146 'x': u32 272 144..145 'x': u32
296 154..157 'foo': fn foo<u32, S<u32>>(S<u32>) -> u32 273 153..156 'foo': fn foo<u32, S<u32>>(S<u32>) -> u32
297 154..160 'foo(s)': u32 274 153..159 'foo(s)': u32
298 158..159 's': S<u32> 275 157..158 's': S<u32>
299 "### 276 "###
300 ); 277 );
301} 278}
@@ -313,12 +290,12 @@ trait Trait {
313} 290}
314"#), 291"#),
315 @r###" 292 @r###"
316 27..31 'self': &Self 293 26..30 'self': &Self
317 53..57 'self': &Self 294 52..56 'self': &Self
318 62..97 '{ ... }': () 295 61..96 '{ ... }': ()
319 76..77 'x': i64 296 75..76 'x': i64
320 80..84 'self': &Self 297 79..83 'self': &Self
321 80..90 'self.foo()': i64 298 79..89 'self.foo()': i64
322 "### 299 "###
323 ); 300 );
324} 301}
@@ -337,12 +314,12 @@ trait Trait: SuperTrait {
337} 314}
338"#), 315"#),
339 @r###" 316 @r###"
340 32..36 'self': &Self 317 31..35 'self': &Self
341 86..90 'self': &Self 318 85..89 'self': &Self
342 95..130 '{ ... }': () 319 94..129 '{ ... }': ()
343 109..110 'x': i64 320 108..109 'x': i64
344 113..117 'self': &Self 321 112..116 'self': &Self
345 113..123 'self.foo()': i64 322 112..122 'self.foo()': i64
346 "### 323 "###
347 ); 324 );
348} 325}
@@ -364,15 +341,15 @@ fn test<T: Iterable>() {
364} 341}
365"#), 342"#),
366 @r###" 343 @r###"
367 108..261 '{ ...ter; }': () 344 107..260 '{ ...ter; }': ()
368 118..119 'x': u32 345 117..118 'x': u32
369 145..146 '1': u32 346 144..145 '1': u32
370 156..157 'y': Iterable::Item<T> 347 155..156 'y': Iterable::Item<T>
371 183..192 'no_matter': Iterable::Item<T> 348 182..191 'no_matter': Iterable::Item<T>
372 202..203 'z': Iterable::Item<T> 349 201..202 'z': Iterable::Item<T>
373 215..224 'no_matter': Iterable::Item<T> 350 214..223 'no_matter': Iterable::Item<T>
374 234..235 'a': Iterable::Item<T> 351 233..234 'a': Iterable::Item<T>
375 249..258 'no_matter': Iterable::Item<T> 352 248..257 'no_matter': Iterable::Item<T>
376 "### 353 "###
377 ); 354 );
378} 355}
@@ -396,25 +373,25 @@ fn test() {
396} 373}
397"#), 374"#),
398 @r###" 375 @r###"
399 106..107 't': T 376 105..106 't': T
400 123..125 '{}': () 377 122..124 '{}': ()
401 147..148 't': T 378 146..147 't': T
402 178..180 '{}': () 379 177..179 '{}': ()
403 202..203 't': T 380 201..202 't': T
404 221..223 '{}': () 381 220..222 '{}': ()
405 234..300 '{ ...(S); }': () 382 233..299 '{ ...(S); }': ()
406 244..245 'x': u32 383 243..244 'x': u32
407 248..252 'foo1': fn foo1<S>(S) -> <S as Iterable>::Item 384 247..251 'foo1': fn foo1<S>(S) -> <S as Iterable>::Item
408 248..255 'foo1(S)': u32 385 247..254 'foo1(S)': u32
409 253..254 'S': S 386 252..253 'S': S
410 265..266 'y': u32 387 264..265 'y': u32
411 269..273 'foo2': fn foo2<S>(S) -> <S as Iterable>::Item 388 268..272 'foo2': fn foo2<S>(S) -> <S as Iterable>::Item
412 269..276 'foo2(S)': u32 389 268..275 'foo2(S)': u32
413 274..275 'S': S 390 273..274 'S': S
414 286..287 'z': u32 391 285..286 'z': u32
415 290..294 'foo3': fn foo3<S>(S) -> <S as Iterable>::Item 392 289..293 'foo3': fn foo3<S>(S) -> <S as Iterable>::Item
416 290..297 'foo3(S)': u32 393 289..296 'foo3(S)': u32
417 295..296 'S': S 394 294..295 'S': S
418 "### 395 "###
419 ); 396 );
420} 397}
@@ -431,9 +408,9 @@ fn test<T: Iterable<Item=u32>>() {
431} 408}
432"#), 409"#),
433 @r###" 410 @r###"
434 67..100 '{ ...own; }': () 411 66..99 '{ ...own; }': ()
435 77..78 'y': u32 412 76..77 'y': u32
436 90..97 'unknown': u32 413 89..96 'unknown': u32
437 "### 414 "###
438 ); 415 );
439} 416}
@@ -446,13 +423,13 @@ const A: u32 = 1 + 1;
446static B: u64 = { let x = 1; x }; 423static B: u64 = { let x = 1; x };
447"#), 424"#),
448 @r###" 425 @r###"
449 16..17 '1': u32 426 15..16 '1': u32
450 16..21 '1 + 1': u32 427 15..20 '1 + 1': u32
451 20..21 '1': u32 428 19..20 '1': u32
452 39..55 '{ let ...1; x }': u64 429 38..54 '{ let ...1; x }': u64
453 45..46 'x': u64 430 44..45 'x': u64
454 49..50 '1': u64 431 48..49 '1': u64
455 52..53 'x': u64 432 51..52 'x': u64
456 "### 433 "###
457 ); 434 );
458} 435}
@@ -469,17 +446,17 @@ fn test() -> u64 {
469} 446}
470"#), 447"#),
471 @r###" 448 @r###"
472 38..87 '{ ... a.1 }': u64 449 37..86 '{ ... a.1 }': u64
473 48..49 'a': S 450 47..48 'a': S
474 52..53 'S': S(i32, u64) -> S 451 51..52 'S': S(i32, u64) -> S
475 52..59 'S(4, 6)': S 452 51..58 'S(4, 6)': S
476 54..55 '4': i32 453 53..54 '4': i32
477 57..58 '6': u64 454 56..57 '6': u64
478 69..70 'b': i32 455 68..69 'b': i32
479 73..74 'a': S 456 72..73 'a': S
480 73..76 'a.0': i32 457 72..75 'a.0': i32
481 82..83 'a': S 458 81..82 'a': S
482 82..85 'a.1': u64 459 81..84 'a.1': u64
483 "### 460 "###
484 ); 461 );
485} 462}
@@ -496,24 +473,24 @@ fn test() -> u64 {
496} 473}
497"#), 474"#),
498 @r###" 475 @r###"
499 44..102 '{ ...0(2) }': u64 476 43..101 '{ ...0(2) }': u64
500 54..55 'a': S 477 53..54 'a': S
501 58..59 'S': S(fn(u32) -> u64) -> S 478 57..58 'S': S(fn(u32) -> u64) -> S
502 58..68 'S(|i| 2*i)': S 479 57..67 'S(|i| 2*i)': S
503 60..67 '|i| 2*i': |u32| -> u64 480 59..66 '|i| 2*i': |u32| -> u64
504 61..62 'i': u32 481 60..61 'i': u32
505 64..65 '2': u32 482 63..64 '2': u32
506 64..67 '2*i': u32 483 63..66 '2*i': u32
507 66..67 'i': u32 484 65..66 'i': u32
508 78..79 'b': u64 485 77..78 'b': u64
509 82..83 'a': S 486 81..82 'a': S
510 82..85 'a.0': fn(u32) -> u64 487 81..84 'a.0': fn(u32) -> u64
511 82..88 'a.0(4)': u64 488 81..87 'a.0(4)': u64
512 86..87 '4': u32 489 85..86 '4': u32
513 94..95 'a': S 490 93..94 'a': S
514 94..97 'a.0': fn(u32) -> u64 491 93..96 'a.0': fn(u32) -> u64
515 94..100 'a.0(2)': u64 492 93..99 'a.0(2)': u64
516 98..99 '2': u32 493 97..98 '2': u32
517 "### 494 "###
518 ); 495 );
519} 496}
@@ -535,10 +512,9 @@ fn indexing_arrays() {
535 512
536#[test] 513#[test]
537fn infer_ops_index() { 514fn infer_ops_index() {
538 let (db, pos) = TestDB::with_position( 515 check_types(
539 r#" 516 r#"
540//- /main.rs crate:main deps:std 517//- /main.rs crate:main deps:std
541
542struct Bar; 518struct Bar;
543struct Foo; 519struct Foo;
544 520
@@ -549,11 +525,46 @@ impl std::ops::Index<u32> for Bar {
549fn test() { 525fn test() {
550 let a = Bar; 526 let a = Bar;
551 let b = a[1u32]; 527 let b = a[1u32];
552 b<|>; 528 b;
553} 529} //^ Foo
554 530
555//- /std.rs crate:std 531//- /std.rs crate:std
532#[prelude_import] use ops::*;
533mod ops {
534 #[lang = "index"]
535 pub trait Index<Idx> {
536 type Output;
537 }
538}
539"#,
540 );
541}
542
543#[test]
544fn infer_ops_index_int() {
545 check_types(
546 r#"
547//- /main.rs crate:main deps:std
548struct Bar;
549struct Foo;
550
551impl std::ops::Index<u32> for Bar {
552 type Output = Foo;
553}
554
555struct Range;
556impl std::ops::Index<Range> for Bar {
557 type Output = Bar;
558}
559
560fn test() {
561 let a = Bar;
562 let b = a[1];
563 b;
564 //^ Foo
565}
556 566
567//- /std.rs crate:std
557#[prelude_import] use ops::*; 568#[prelude_import] use ops::*;
558mod ops { 569mod ops {
559 #[lang = "index"] 570 #[lang = "index"]
@@ -563,19 +574,18 @@ mod ops {
563} 574}
564"#, 575"#,
565 ); 576 );
566 assert_eq!("Foo", type_at_pos(&db, pos));
567} 577}
568 578
569#[test] 579#[test]
570fn infer_ops_index_autoderef() { 580fn infer_ops_index_autoderef() {
571 let (db, pos) = TestDB::with_position( 581 check_types(
572 r#" 582 r#"
573//- /main.rs crate:main deps:std 583//- /main.rs crate:main deps:std
574fn test() { 584fn test() {
575 let a = &[1u32, 2, 3]; 585 let a = &[1u32, 2, 3];
576 let b = a[1u32]; 586 let b = a[1u32];
577 b<|>; 587 b;
578} 588} //^ u32
579 589
580//- /std.rs crate:std 590//- /std.rs crate:std
581impl<T> ops::Index<u32> for [T] { 591impl<T> ops::Index<u32> for [T] {
@@ -591,14 +601,12 @@ mod ops {
591} 601}
592"#, 602"#,
593 ); 603 );
594 assert_eq!("u32", type_at_pos(&db, pos));
595} 604}
596 605
597#[test] 606#[test]
598fn deref_trait() { 607fn deref_trait() {
599 let t = type_at( 608 check_types(
600 r#" 609 r#"
601//- /main.rs
602#[lang = "deref"] 610#[lang = "deref"]
603trait Deref { 611trait Deref {
604 type Target; 612 type Target;
@@ -616,16 +624,15 @@ impl S {
616} 624}
617 625
618fn test(s: Arc<S>) { 626fn test(s: Arc<S>) {
619 (*s, s.foo())<|>; 627 (*s, s.foo());
620} 628} //^ (S, u128)
621"#, 629"#,
622 ); 630 );
623 assert_eq!(t, "(S, u128)");
624} 631}
625 632
626#[test] 633#[test]
627fn deref_trait_with_inference_var() { 634fn deref_trait_with_inference_var() {
628 let t = type_at( 635 check_types(
629 r#" 636 r#"
630//- /main.rs 637//- /main.rs
631#[lang = "deref"] 638#[lang = "deref"]
@@ -645,19 +652,18 @@ fn foo(a: Arc<S>) {}
645 652
646fn test() { 653fn test() {
647 let a = new_arc(); 654 let a = new_arc();
648 let b = (*a)<|>; 655 let b = (*a);
656 //^ S
649 foo(a); 657 foo(a);
650} 658}
651"#, 659"#,
652 ); 660 );
653 assert_eq!(t, "S");
654} 661}
655 662
656#[test] 663#[test]
657fn deref_trait_infinite_recursion() { 664fn deref_trait_infinite_recursion() {
658 let t = type_at( 665 check_types(
659 r#" 666 r#"
660//- /main.rs
661#[lang = "deref"] 667#[lang = "deref"]
662trait Deref { 668trait Deref {
663 type Target; 669 type Target;
@@ -671,18 +677,16 @@ impl Deref for S {
671} 677}
672 678
673fn test(s: S) { 679fn test(s: S) {
674 s.foo()<|>; 680 s.foo();
675} 681} //^ {unknown}
676"#, 682"#,
677 ); 683 );
678 assert_eq!(t, "{unknown}");
679} 684}
680 685
681#[test] 686#[test]
682fn deref_trait_with_question_mark_size() { 687fn deref_trait_with_question_mark_size() {
683 let t = type_at( 688 check_types(
684 r#" 689 r#"
685//- /main.rs
686#[lang = "deref"] 690#[lang = "deref"]
687trait Deref { 691trait Deref {
688 type Target; 692 type Target;
@@ -700,18 +704,16 @@ impl S {
700} 704}
701 705
702fn test(s: Arc<S>) { 706fn test(s: Arc<S>) {
703 (*s, s.foo())<|>; 707 (*s, s.foo());
704} 708} //^ (S, u128)
705"#, 709"#,
706 ); 710 );
707 assert_eq!(t, "(S, u128)");
708} 711}
709 712
710#[test] 713#[test]
711fn obligation_from_function_clause() { 714fn obligation_from_function_clause() {
712 let t = type_at( 715 check_types(
713 r#" 716 r#"
714//- /main.rs
715struct S; 717struct S;
716 718
717trait Trait<T> {} 719trait Trait<T> {}
@@ -720,16 +722,15 @@ impl Trait<u32> for S {}
720fn foo<T: Trait<U>, U>(t: T) -> U {} 722fn foo<T: Trait<U>, U>(t: T) -> U {}
721 723
722fn test(s: S) { 724fn test(s: S) {
723 foo(s)<|>; 725 (foo(s));
724} 726} //^ u32
725"#, 727"#,
726 ); 728 );
727 assert_eq!(t, "u32");
728} 729}
729 730
730#[test] 731#[test]
731fn obligation_from_method_clause() { 732fn obligation_from_method_clause() {
732 let t = type_at( 733 check_types(
733 r#" 734 r#"
734//- /main.rs 735//- /main.rs
735struct S; 736struct S;
@@ -743,18 +744,16 @@ impl O {
743} 744}
744 745
745fn test() { 746fn test() {
746 O.foo(S)<|>; 747 O.foo(S);
747} 748} //^ isize
748"#, 749"#,
749 ); 750 );
750 assert_eq!(t, "isize");
751} 751}
752 752
753#[test] 753#[test]
754fn obligation_from_self_method_clause() { 754fn obligation_from_self_method_clause() {
755 let t = type_at( 755 check_types(
756 r#" 756 r#"
757//- /main.rs
758struct S; 757struct S;
759 758
760trait Trait<T> {} 759trait Trait<T> {}
@@ -765,18 +764,16 @@ impl S {
765} 764}
766 765
767fn test() { 766fn test() {
768 S.foo()<|>; 767 S.foo();
769} 768} //^ i64
770"#, 769"#,
771 ); 770 );
772 assert_eq!(t, "i64");
773} 771}
774 772
775#[test] 773#[test]
776fn obligation_from_impl_clause() { 774fn obligation_from_impl_clause() {
777 let t = type_at( 775 check_types(
778 r#" 776 r#"
779//- /main.rs
780struct S; 777struct S;
781 778
782trait Trait<T> {} 779trait Trait<T> {}
@@ -788,32 +785,30 @@ impl<U, T: Trait<U>> O<T> {
788} 785}
789 786
790fn test(o: O<S>) { 787fn test(o: O<S>) {
791 o.foo()<|>; 788 o.foo();
792} 789} //^ &str
793"#, 790"#,
794 ); 791 );
795 assert_eq!(t, "&str");
796} 792}
797 793
798#[test] 794#[test]
799fn generic_param_env_1() { 795fn generic_param_env_1() {
800 let t = type_at( 796 check_types(
801 r#" 797 r#"
802//- /main.rs
803trait Clone {} 798trait Clone {}
804trait Trait { fn foo(self) -> u128; } 799trait Trait { fn foo(self) -> u128; }
805struct S; 800struct S;
806impl Clone for S {} 801impl Clone for S {}
807impl<T> Trait for T where T: Clone {} 802impl<T> Trait for T where T: Clone {}
808fn test<T: Clone>(t: T) { t.foo()<|>; } 803fn test<T: Clone>(t: T) { t.foo(); }
804 //^ u128
809"#, 805"#,
810 ); 806 );
811 assert_eq!(t, "u128");
812} 807}
813 808
814#[test] 809#[test]
815fn generic_param_env_1_not_met() { 810fn generic_param_env_1_not_met() {
816 let t = type_at( 811 check_types(
817 r#" 812 r#"
818//- /main.rs 813//- /main.rs
819trait Clone {} 814trait Clone {}
@@ -821,45 +816,42 @@ trait Trait { fn foo(self) -> u128; }
821struct S; 816struct S;
822impl Clone for S {} 817impl Clone for S {}
823impl<T> Trait for T where T: Clone {} 818impl<T> Trait for T where T: Clone {}
824fn test<T>(t: T) { t.foo()<|>; } 819fn test<T>(t: T) { t.foo(); }
820 //^ {unknown}
825"#, 821"#,
826 ); 822 );
827 assert_eq!(t, "{unknown}");
828} 823}
829 824
830#[test] 825#[test]
831fn generic_param_env_2() { 826fn generic_param_env_2() {
832 let t = type_at( 827 check_types(
833 r#" 828 r#"
834//- /main.rs
835trait Trait { fn foo(self) -> u128; } 829trait Trait { fn foo(self) -> u128; }
836struct S; 830struct S;
837impl Trait for S {} 831impl Trait for S {}
838fn test<T: Trait>(t: T) { t.foo()<|>; } 832fn test<T: Trait>(t: T) { t.foo(); }
833 //^ u128
839"#, 834"#,
840 ); 835 );
841 assert_eq!(t, "u128");
842} 836}
843 837
844#[test] 838#[test]
845fn generic_param_env_2_not_met() { 839fn generic_param_env_2_not_met() {
846 let t = type_at( 840 check_types(
847 r#" 841 r#"
848//- /main.rs
849trait Trait { fn foo(self) -> u128; } 842trait Trait { fn foo(self) -> u128; }
850struct S; 843struct S;
851impl Trait for S {} 844impl Trait for S {}
852fn test<T>(t: T) { t.foo()<|>; } 845fn test<T>(t: T) { t.foo(); }
846 //^ {unknown}
853"#, 847"#,
854 ); 848 );
855 assert_eq!(t, "{unknown}");
856} 849}
857 850
858#[test] 851#[test]
859fn generic_param_env_deref() { 852fn generic_param_env_deref() {
860 let t = type_at( 853 check_types(
861 r#" 854 r#"
862//- /main.rs
863#[lang = "deref"] 855#[lang = "deref"]
864trait Deref { 856trait Deref {
865 type Target; 857 type Target;
@@ -868,17 +860,17 @@ trait Trait {}
868impl<T> Deref for T where T: Trait { 860impl<T> Deref for T where T: Trait {
869 type Target = i128; 861 type Target = i128;
870} 862}
871fn test<T: Trait>(t: T) { (*t)<|>; } 863fn test<T: Trait>(t: T) { (*t); }
864 //^ i128
872"#, 865"#,
873 ); 866 );
874 assert_eq!(t, "i128");
875} 867}
876 868
877#[test] 869#[test]
878fn associated_type_placeholder() { 870fn associated_type_placeholder() {
879 let t = type_at( 871 // inside the generic function, the associated type gets normalized to a placeholder `ApplL::Out<T>` [https://rust-lang.github.io/rustc-guide/traits/associated-types.html#placeholder-associated-types].
872 check_types(
880 r#" 873 r#"
881//- /main.rs
882pub trait ApplyL { 874pub trait ApplyL {
883 type Out; 875 type Out;
884} 876}
@@ -891,19 +883,16 @@ impl<T> ApplyL for RefMutL<T> {
891 883
892fn test<T: ApplyL>() { 884fn test<T: ApplyL>() {
893 let y: <RefMutL<T> as ApplyL>::Out = no_matter; 885 let y: <RefMutL<T> as ApplyL>::Out = no_matter;
894 y<|>; 886 y;
895} 887} //^ ApplyL::Out<T>
896"#, 888"#,
897 ); 889 );
898 // inside the generic function, the associated type gets normalized to a placeholder `ApplL::Out<T>` [https://rust-lang.github.io/rustc-guide/traits/associated-types.html#placeholder-associated-types].
899 assert_eq!(t, "ApplyL::Out<T>");
900} 890}
901 891
902#[test] 892#[test]
903fn associated_type_placeholder_2() { 893fn associated_type_placeholder_2() {
904 let t = type_at( 894 check_types(
905 r#" 895 r#"
906//- /main.rs
907pub trait ApplyL { 896pub trait ApplyL {
908 type Out; 897 type Out;
909} 898}
@@ -911,11 +900,10 @@ fn foo<T: ApplyL>(t: T) -> <T as ApplyL>::Out;
911 900
912fn test<T: ApplyL>(t: T) { 901fn test<T: ApplyL>(t: T) {
913 let y = foo(t); 902 let y = foo(t);
914 y<|>; 903 y;
915} 904} //^ ApplyL::Out<T>
916"#, 905"#,
917 ); 906 );
918 assert_eq!(t, "ApplyL::Out<T>");
919} 907}
920 908
921#[test] 909#[test]
@@ -944,34 +932,34 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u32>) {
944} 932}
945"#, true), 933"#, true),
946 @r###" 934 @r###"
947 30..34 'self': &Self 935 29..33 'self': &Self
948 55..59 'self': &Self 936 54..58 'self': &Self
949 78..79 'x': impl Trait<u16> 937 77..78 'x': impl Trait<u16>
950 98..100 '{}': () 938 97..99 '{}': ()
951 155..156 'x': impl Trait<u64> 939 154..155 'x': impl Trait<u64>
952 175..176 'y': &impl Trait<u32> 940 174..175 'y': &impl Trait<u32>
953 196..324 '{ ...2(); }': () 941 195..323 '{ ...2(); }': ()
954 202..203 'x': impl Trait<u64> 942 201..202 'x': impl Trait<u64>
955 209..210 'y': &impl Trait<u32> 943 208..209 'y': &impl Trait<u32>
956 220..221 'z': S<u16> 944 219..220 'z': S<u16>
957 224..225 'S': S<u16>(u16) -> S<u16> 945 223..224 'S': S<u16>(u16) -> S<u16>
958 224..228 'S(1)': S<u16> 946 223..227 'S(1)': S<u16>
959 226..227 '1': u16 947 225..226 '1': u16
960 234..237 'bar': fn bar(S<u16>) 948 233..236 'bar': fn bar(S<u16>)
961 234..240 'bar(z)': () 949 233..239 'bar(z)': ()
962 238..239 'z': S<u16> 950 237..238 'z': S<u16>
963 246..247 'x': impl Trait<u64> 951 245..246 'x': impl Trait<u64>
964 246..253 'x.foo()': u64 952 245..252 'x.foo()': u64
965 259..260 'y': &impl Trait<u32> 953 258..259 'y': &impl Trait<u32>
966 259..266 'y.foo()': u32 954 258..265 'y.foo()': u32
967 272..273 'z': S<u16> 955 271..272 'z': S<u16>
968 272..279 'z.foo()': u16 956 271..278 'z.foo()': u16
969 285..286 'x': impl Trait<u64> 957 284..285 'x': impl Trait<u64>
970 285..293 'x.foo2()': i64 958 284..292 'x.foo2()': i64
971 299..300 'y': &impl Trait<u32> 959 298..299 'y': &impl Trait<u32>
972 299..307 'y.foo2()': i64 960 298..306 'y.foo2()': i64
973 313..314 'z': S<u16> 961 312..313 'z': S<u16>
974 313..321 'z.foo2()': i64 962 312..320 'z.foo2()': i64
975 "### 963 "###
976 ); 964 );
977} 965}
@@ -1005,39 +993,39 @@ fn test() {
1005} 993}
1006"#, true), 994"#, true),
1007 @r###" 995 @r###"
1008 156..157 'x': impl Trait 996 155..156 'x': impl Trait
1009 176..187 '{ loop {} }': T 997 175..186 '{ loop {} }': T
1010 178..185 'loop {}': ! 998 177..184 'loop {}': !
1011 183..185 '{}': () 999 182..184 '{}': ()
1012 200..201 'x': impl Trait 1000 199..200 'x': impl Trait
1013 220..231 '{ loop {} }': T 1001 219..230 '{ loop {} }': T
1014 222..229 'loop {}': ! 1002 221..228 'loop {}': !
1015 227..229 '{}': () 1003 226..228 '{}': ()
1016 301..510 '{ ... i32 }': () 1004 300..509 '{ ... i32 }': ()
1017 307..315 'Foo::bar': fn bar<{unknown}, {unknown}>(S) -> {unknown} 1005 306..314 'Foo::bar': fn bar<{unknown}, {unknown}>(S) -> {unknown}
1018 307..318 'Foo::bar(S)': {unknown} 1006 306..317 'Foo::bar(S)': {unknown}
1019 316..317 'S': S 1007 315..316 'S': S
1020 324..339 '<F as Foo>::bar': fn bar<F, {unknown}>(S) -> {unknown} 1008 323..338 '<F as Foo>::bar': fn bar<F, {unknown}>(S) -> {unknown}
1021 324..342 '<F as ...bar(S)': {unknown} 1009 323..341 '<F as ...bar(S)': {unknown}
1022 340..341 'S': S 1010 339..340 'S': S
1023 348..354 'F::bar': fn bar<F, {unknown}>(S) -> {unknown} 1011 347..353 'F::bar': fn bar<F, {unknown}>(S) -> {unknown}
1024 348..357 'F::bar(S)': {unknown} 1012 347..356 'F::bar(S)': {unknown}
1025 355..356 'S': S 1013 354..355 'S': S
1026 363..378 'Foo::bar::<u32>': fn bar<{unknown}, u32>(S) -> u32 1014 362..377 'Foo::bar::<u32>': fn bar<{unknown}, u32>(S) -> u32
1027 363..381 'Foo::b...32>(S)': u32 1015 362..380 'Foo::b...32>(S)': u32
1028 379..380 'S': S 1016 378..379 'S': S
1029 387..409 '<F as ...:<u32>': fn bar<F, u32>(S) -> u32 1017 386..408 '<F as ...:<u32>': fn bar<F, u32>(S) -> u32
1030 387..412 '<F as ...32>(S)': u32 1018 386..411 '<F as ...32>(S)': u32
1031 410..411 'S': S 1019 409..410 'S': S
1032 419..422 'foo': fn foo<{unknown}>(S) -> {unknown} 1020 418..421 'foo': fn foo<{unknown}>(S) -> {unknown}
1033 419..425 'foo(S)': {unknown} 1021 418..424 'foo(S)': {unknown}
1034 423..424 'S': S 1022 422..423 'S': S
1035 431..441 'foo::<u32>': fn foo<u32>(S) -> u32 1023 430..440 'foo::<u32>': fn foo<u32>(S) -> u32
1036 431..444 'foo::<u32>(S)': u32 1024 430..443 'foo::<u32>(S)': u32
1037 442..443 'S': S 1025 441..442 'S': S
1038 450..465 'foo::<u32, i32>': fn foo<u32>(S) -> u32 1026 449..464 'foo::<u32, i32>': fn foo<u32>(S) -> u32
1039 450..468 'foo::<...32>(S)': u32 1027 449..467 'foo::<...32>(S)': u32
1040 466..467 'S': S 1028 465..466 'S': S
1041 "### 1029 "###
1042 ); 1030 );
1043} 1031}
@@ -1062,24 +1050,24 @@ fn test() {
1062} 1050}
1063"#, true), 1051"#, true),
1064 @r###" 1052 @r###"
1065 88..92 'self': F<T> 1053 87..91 'self': F<T>
1066 94..95 'x': impl Trait 1054 93..94 'x': impl Trait
1067 119..130 '{ loop {} }': (T, U) 1055 118..129 '{ loop {} }': (T, U)
1068 121..128 'loop {}': ! 1056 120..127 'loop {}': !
1069 126..128 '{}': () 1057 125..127 '{}': ()
1070 144..284 '{ ...ored }': () 1058 143..283 '{ ...ored }': ()
1071 150..151 'F': F<{unknown}> 1059 149..150 'F': F<{unknown}>
1072 150..158 'F.foo(S)': ({unknown}, {unknown}) 1060 149..157 'F.foo(S)': ({unknown}, {unknown})
1073 156..157 'S': S 1061 155..156 'S': S
1074 164..172 'F::<u32>': F<u32> 1062 163..171 'F::<u32>': F<u32>
1075 164..179 'F::<u32>.foo(S)': (u32, {unknown}) 1063 163..178 'F::<u32>.foo(S)': (u32, {unknown})
1076 177..178 'S': S 1064 176..177 'S': S
1077 185..193 'F::<u32>': F<u32> 1065 184..192 'F::<u32>': F<u32>
1078 185..207 'F::<u3...32>(S)': (u32, i32) 1066 184..206 'F::<u3...32>(S)': (u32, i32)
1079 205..206 'S': S 1067 204..205 'S': S
1080 213..221 'F::<u32>': F<u32> 1068 212..220 'F::<u32>': F<u32>
1081 213..240 'F::<u3...32>(S)': (u32, i32) 1069 212..239 'F::<u3...32>(S)': (u32, i32)
1082 238..239 'S': S 1070 237..238 'S': S
1083 "### 1071 "###
1084 ); 1072 );
1085} 1073}
@@ -1098,19 +1086,18 @@ fn test() {
1098} 1086}
1099"#, true), 1087"#, true),
1100 @r###" 1088 @r###"
1101 23..24 'x': impl Trait 1089 22..23 'x': impl Trait
1102 38..49 '{ loop {} }': () 1090 37..48 '{ loop {} }': ()
1103 40..47 'loop {}': ! 1091 39..46 'loop {}': !
1104 45..47 '{}': () 1092 44..46 '{}': ()
1105 91..124 '{ ...foo; }': () 1093 90..123 '{ ...foo; }': ()
1106 101..102 'f': fn(S) 1094 100..101 'f': fn(S)
1107 118..121 'foo': fn foo(S) 1095 117..120 'foo': fn foo(S)
1108 "### 1096 "###
1109 ); 1097 );
1110} 1098}
1111 1099
1112#[test] 1100#[test]
1113#[ignore]
1114fn impl_trait() { 1101fn impl_trait() {
1115 assert_snapshot!( 1102 assert_snapshot!(
1116 infer(r#" 1103 infer(r#"
@@ -1133,29 +1120,118 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u64>) {
1133} 1120}
1134"#), 1121"#),
1135 @r###" 1122 @r###"
1136 30..34 'self': &Self 1123 29..33 'self': &Self
1137 55..59 'self': &Self 1124 54..58 'self': &Self
1138 99..101 '{}': () 1125 98..100 '{}': ()
1139 111..112 'x': impl Trait<u64> 1126 110..111 'x': impl Trait<u64>
1140 131..132 'y': &impl Trait<u64> 1127 130..131 'y': &impl Trait<u64>
1141 152..269 '{ ...2(); }': () 1128 151..268 '{ ...2(); }': ()
1142 158..159 'x': impl Trait<u64> 1129 157..158 'x': impl Trait<u64>
1143 165..166 'y': &impl Trait<u64> 1130 164..165 'y': &impl Trait<u64>
1144 176..177 'z': impl Trait<u64> 1131 175..176 'z': impl Trait<u64>
1145 180..183 'bar': fn bar() -> impl Trait<u64> 1132 179..182 'bar': fn bar() -> impl Trait<u64>
1146 180..185 'bar()': impl Trait<u64> 1133 179..184 'bar()': impl Trait<u64>
1147 191..192 'x': impl Trait<u64> 1134 190..191 'x': impl Trait<u64>
1148 191..198 'x.foo()': u64 1135 190..197 'x.foo()': u64
1149 204..205 'y': &impl Trait<u64> 1136 203..204 'y': &impl Trait<u64>
1150 204..211 'y.foo()': u64 1137 203..210 'y.foo()': u64
1151 217..218 'z': impl Trait<u64> 1138 216..217 'z': impl Trait<u64>
1152 217..224 'z.foo()': u64 1139 216..223 'z.foo()': u64
1153 230..231 'x': impl Trait<u64> 1140 229..230 'x': impl Trait<u64>
1154 230..238 'x.foo2()': i64 1141 229..237 'x.foo2()': i64
1155 244..245 'y': &impl Trait<u64> 1142 243..244 'y': &impl Trait<u64>
1156 244..252 'y.foo2()': i64 1143 243..251 'y.foo2()': i64
1157 258..259 'z': impl Trait<u64> 1144 257..258 'z': impl Trait<u64>
1158 258..266 'z.foo2()': i64 1145 257..265 'z.foo2()': i64
1146 "###
1147 );
1148}
1149
1150#[test]
1151fn simple_return_pos_impl_trait() {
1152 mark::check!(lower_rpit);
1153 assert_snapshot!(
1154 infer(r#"
1155trait Trait<T> {
1156 fn foo(&self) -> T;
1157}
1158fn bar() -> impl Trait<u64> { loop {} }
1159
1160fn test() {
1161 let a = bar();
1162 a.foo();
1163}
1164"#),
1165 @r###"
1166 29..33 'self': &Self
1167 71..82 '{ loop {} }': !
1168 73..80 'loop {}': !
1169 78..80 '{}': ()
1170 94..129 '{ ...o(); }': ()
1171 104..105 'a': impl Trait<u64>
1172 108..111 'bar': fn bar() -> impl Trait<u64>
1173 108..113 'bar()': impl Trait<u64>
1174 119..120 'a': impl Trait<u64>
1175 119..126 'a.foo()': u64
1176 "###
1177 );
1178}
1179
1180#[test]
1181fn more_return_pos_impl_trait() {
1182 assert_snapshot!(
1183 infer(r#"
1184trait Iterator {
1185 type Item;
1186 fn next(&mut self) -> Self::Item;
1187}
1188trait Trait<T> {
1189 fn foo(&self) -> T;
1190}
1191fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>) { loop {} }
1192fn baz<T>(t: T) -> (impl Iterator<Item = impl Trait<T>>, impl Trait<T>) { loop {} }
1193
1194fn test() {
1195 let (a, b) = bar();
1196 a.next().foo();
1197 b.foo();
1198 let (c, d) = baz(1u128);
1199 c.next().foo();
1200 d.foo();
1201}
1202"#),
1203 @r###"
1204 49..53 'self': &mut Self
1205 101..105 'self': &Self
1206 184..195 '{ loop {} }': ({unknown}, {unknown})
1207 186..193 'loop {}': !
1208 191..193 '{}': ()
1209 206..207 't': T
1210 268..279 '{ loop {} }': ({unknown}, {unknown})
1211 270..277 'loop {}': !
1212 275..277 '{}': ()
1213 291..413 '{ ...o(); }': ()
1214 301..307 '(a, b)': (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
1215 302..303 'a': impl Iterator<Item = impl Trait<u32>>
1216 305..306 'b': impl Trait<u64>
1217 310..313 'bar': fn bar() -> (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
1218 310..315 'bar()': (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
1219 321..322 'a': impl Iterator<Item = impl Trait<u32>>
1220 321..329 'a.next()': impl Trait<u32>
1221 321..335 'a.next().foo()': u32
1222 341..342 'b': impl Trait<u64>
1223 341..348 'b.foo()': u64
1224 358..364 '(c, d)': (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
1225 359..360 'c': impl Iterator<Item = impl Trait<u128>>
1226 362..363 'd': impl Trait<u128>
1227 367..370 'baz': fn baz<u128>(u128) -> (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
1228 367..377 'baz(1u128)': (impl Iterator<Item = impl Trait<u128>>, impl Trait<u128>)
1229 371..376 '1u128': u128
1230 383..384 'c': impl Iterator<Item = impl Trait<u128>>
1231 383..391 'c.next()': impl Trait<u128>
1232 383..397 'c.next().foo()': u128
1233 403..404 'd': impl Trait<u128>
1234 403..410 'd.foo()': u128
1159 "### 1235 "###
1160 ); 1236 );
1161} 1237}
@@ -1183,29 +1259,29 @@ fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) {
1183} 1259}
1184"#), 1260"#),
1185 @r###" 1261 @r###"
1186 30..34 'self': &Self 1262 29..33 'self': &Self
1187 55..59 'self': &Self 1263 54..58 'self': &Self
1188 98..100 '{}': () 1264 97..99 '{}': ()
1189 110..111 'x': dyn Trait<u64> 1265 109..110 'x': dyn Trait<u64>
1190 129..130 'y': &dyn Trait<u64> 1266 128..129 'y': &dyn Trait<u64>
1191 149..266 '{ ...2(); }': () 1267 148..265 '{ ...2(); }': ()
1192 155..156 'x': dyn Trait<u64> 1268 154..155 'x': dyn Trait<u64>
1193 162..163 'y': &dyn Trait<u64> 1269 161..162 'y': &dyn Trait<u64>
1194 173..174 'z': dyn Trait<u64> 1270 172..173 'z': dyn Trait<u64>
1195 177..180 'bar': fn bar() -> dyn Trait<u64> 1271 176..179 'bar': fn bar() -> dyn Trait<u64>
1196 177..182 'bar()': dyn Trait<u64> 1272 176..181 'bar()': dyn Trait<u64>
1197 188..189 'x': dyn Trait<u64> 1273 187..188 'x': dyn Trait<u64>
1198 188..195 'x.foo()': u64 1274 187..194 'x.foo()': u64
1199 201..202 'y': &dyn Trait<u64> 1275 200..201 'y': &dyn Trait<u64>
1200 201..208 'y.foo()': u64 1276 200..207 'y.foo()': u64
1201 214..215 'z': dyn Trait<u64> 1277 213..214 'z': dyn Trait<u64>
1202 214..221 'z.foo()': u64 1278 213..220 'z.foo()': u64
1203 227..228 'x': dyn Trait<u64> 1279 226..227 'x': dyn Trait<u64>
1204 227..235 'x.foo2()': i64 1280 226..234 'x.foo2()': i64
1205 241..242 'y': &dyn Trait<u64> 1281 240..241 'y': &dyn Trait<u64>
1206 241..249 'y.foo2()': i64 1282 240..248 'y.foo2()': i64
1207 255..256 'z': dyn Trait<u64> 1283 254..255 'z': dyn Trait<u64>
1208 255..263 'z.foo2()': i64 1284 254..262 'z.foo2()': i64
1209 "### 1285 "###
1210 ); 1286 );
1211} 1287}
@@ -1231,17 +1307,17 @@ fn test(s: S<u32, i32>) {
1231} 1307}
1232"#), 1308"#),
1233 @r###" 1309 @r###"
1234 33..37 'self': &Self 1310 32..36 'self': &Self
1235 103..107 'self': &S<T, U> 1311 102..106 'self': &S<T, U>
1236 129..140 '{ loop {} }': &dyn Trait<T, U> 1312 128..139 '{ loop {} }': &dyn Trait<T, U>
1237 131..138 'loop {}': ! 1313 130..137 'loop {}': !
1238 136..138 '{}': () 1314 135..137 '{}': ()
1239 176..180 'self': &Self 1315 175..179 'self': &Self
1240 252..253 's': S<u32, i32> 1316 251..252 's': S<u32, i32>
1241 268..290 '{ ...z(); }': () 1317 267..289 '{ ...z(); }': ()
1242 274..275 's': S<u32, i32> 1318 273..274 's': S<u32, i32>
1243 274..281 's.bar()': &dyn Trait<u32, i32> 1319 273..280 's.bar()': &dyn Trait<u32, i32>
1244 274..287 's.bar().baz()': (u32, i32) 1320 273..286 's.bar().baz()': (u32, i32)
1245 "### 1321 "###
1246 ); 1322 );
1247} 1323}
@@ -1265,22 +1341,22 @@ fn test(x: Trait, y: &Trait) -> u64 {
1265} 1341}
1266"#), 1342"#),
1267 @r###" 1343 @r###"
1268 27..31 'self': &Self 1344 26..30 'self': &Self
1269 61..63 '{}': () 1345 60..62 '{}': ()
1270 73..74 'x': dyn Trait 1346 72..73 'x': dyn Trait
1271 83..84 'y': &dyn Trait 1347 82..83 'y': &dyn Trait
1272 101..176 '{ ...o(); }': () 1348 100..175 '{ ...o(); }': ()
1273 107..108 'x': dyn Trait 1349 106..107 'x': dyn Trait
1274 114..115 'y': &dyn Trait 1350 113..114 'y': &dyn Trait
1275 125..126 'z': dyn Trait 1351 124..125 'z': dyn Trait
1276 129..132 'bar': fn bar() -> dyn Trait 1352 128..131 'bar': fn bar() -> dyn Trait
1277 129..134 'bar()': dyn Trait 1353 128..133 'bar()': dyn Trait
1278 140..141 'x': dyn Trait 1354 139..140 'x': dyn Trait
1279 140..147 'x.foo()': u64 1355 139..146 'x.foo()': u64
1280 153..154 'y': &dyn Trait 1356 152..153 'y': &dyn Trait
1281 153..160 'y.foo()': u64 1357 152..159 'y.foo()': u64
1282 166..167 'z': dyn Trait 1358 165..166 'z': dyn Trait
1283 166..173 'z.foo()': u64 1359 165..172 'z.foo()': u64
1284 "### 1360 "###
1285 ); 1361 );
1286} 1362}
@@ -1294,13 +1370,13 @@ fn test(a: impl Trait + 'lifetime, b: impl 'lifetime, c: impl (Trait), d: impl (
1294} 1370}
1295"#), 1371"#),
1296 @r###" 1372 @r###"
1297 24..25 'a': impl Trait + {error} 1373 23..24 'a': impl Trait + {error}
1298 51..52 'b': impl {error} 1374 50..51 'b': impl {error}
1299 70..71 'c': impl Trait 1375 69..70 'c': impl Trait
1300 87..88 'd': impl {error} 1376 86..87 'd': impl {error}
1301 108..109 'e': impl {error} 1377 107..108 'e': impl {error}
1302 124..125 'f': impl Trait + {error} 1378 123..124 'f': impl Trait + {error}
1303 148..151 '{ }': () 1379 147..150 '{ }': ()
1304 "### 1380 "###
1305 ); 1381 );
1306} 1382}
@@ -1308,19 +1384,17 @@ fn test(a: impl Trait + 'lifetime, b: impl 'lifetime, c: impl (Trait), d: impl (
1308#[test] 1384#[test]
1309#[ignore] 1385#[ignore]
1310fn error_bound_chalk() { 1386fn error_bound_chalk() {
1311 let t = type_at( 1387 check_types(
1312 r#" 1388 r#"
1313//- /main.rs
1314trait Trait { 1389trait Trait {
1315 fn foo(&self) -> u32 {} 1390 fn foo(&self) -> u32 {}
1316} 1391}
1317 1392
1318fn test(x: (impl Trait + UnknownTrait)) { 1393fn test(x: (impl Trait + UnknownTrait)) {
1319 x.foo()<|>; 1394 x.foo();
1320} 1395} //^ u32
1321"#, 1396"#,
1322 ); 1397 );
1323 assert_eq!(t, "u32");
1324} 1398}
1325 1399
1326#[test] 1400#[test]
@@ -1349,48 +1423,48 @@ fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) {
1349} 1423}
1350"#), 1424"#),
1351 @r###" 1425 @r###"
1352 50..51 't': T 1426 49..50 't': T
1353 78..80 '{}': () 1427 77..79 '{}': ()
1354 112..113 't': T 1428 111..112 't': T
1355 123..125 '{}': () 1429 122..124 '{}': ()
1356 155..156 't': T 1430 154..155 't': T
1357 166..169 '{t}': T 1431 165..168 '{t}': T
1358 167..168 't': T 1432 166..167 't': T
1359 257..258 'x': T 1433 256..257 'x': T
1360 263..264 'y': impl Trait<Type = i64> 1434 262..263 'y': impl Trait<Type = i64>
1361 290..398 '{ ...r>); }': () 1435 289..397 '{ ...r>); }': ()
1362 296..299 'get': fn get<T>(T) -> <T as Trait>::Type 1436 295..298 'get': fn get<T>(T) -> <T as Trait>::Type
1363 296..302 'get(x)': u32 1437 295..301 'get(x)': u32
1364 300..301 'x': T 1438 299..300 'x': T
1365 308..312 'get2': fn get2<u32, T>(T) -> u32 1439 307..311 'get2': fn get2<u32, T>(T) -> u32
1366 308..315 'get2(x)': u32 1440 307..314 'get2(x)': u32
1367 313..314 'x': T 1441 312..313 'x': T
1368 321..324 'get': fn get<impl Trait<Type = i64>>(impl Trait<Type = i64>) -> <impl Trait<Type = i64> as Trait>::Type 1442 320..323 'get': fn get<impl Trait<Type = i64>>(impl Trait<Type = i64>) -> <impl Trait<Type = i64> as Trait>::Type
1369 321..327 'get(y)': i64 1443 320..326 'get(y)': i64
1370 325..326 'y': impl Trait<Type = i64> 1444 324..325 'y': impl Trait<Type = i64>
1371 333..337 'get2': fn get2<i64, impl Trait<Type = i64>>(impl Trait<Type = i64>) -> i64 1445 332..336 'get2': fn get2<i64, impl Trait<Type = i64>>(impl Trait<Type = i64>) -> i64
1372 333..340 'get2(y)': i64 1446 332..339 'get2(y)': i64
1373 338..339 'y': impl Trait<Type = i64> 1447 337..338 'y': impl Trait<Type = i64>
1374 346..349 'get': fn get<S<u64>>(S<u64>) -> <S<u64> as Trait>::Type 1448 345..348 'get': fn get<S<u64>>(S<u64>) -> <S<u64> as Trait>::Type
1375 346..357 'get(set(S))': u64 1449 345..356 'get(set(S))': u64
1376 350..353 'set': fn set<S<u64>>(S<u64>) -> S<u64> 1450 349..352 'set': fn set<S<u64>>(S<u64>) -> S<u64>
1377 350..356 'set(S)': S<u64> 1451 349..355 'set(S)': S<u64>
1378 354..355 'S': S<u64> 1452 353..354 'S': S<u64>
1379 363..367 'get2': fn get2<u64, S<u64>>(S<u64>) -> u64 1453 362..366 'get2': fn get2<u64, S<u64>>(S<u64>) -> u64
1380 363..375 'get2(set(S))': u64 1454 362..374 'get2(set(S))': u64
1381 368..371 'set': fn set<S<u64>>(S<u64>) -> S<u64> 1455 367..370 'set': fn set<S<u64>>(S<u64>) -> S<u64>
1382 368..374 'set(S)': S<u64> 1456 367..373 'set(S)': S<u64>
1383 372..373 'S': S<u64> 1457 371..372 'S': S<u64>
1384 381..385 'get2': fn get2<str, S<str>>(S<str>) -> str 1458 380..384 'get2': fn get2<str, S<str>>(S<str>) -> str
1385 381..395 'get2(S::<str>)': str 1459 380..394 'get2(S::<str>)': str
1386 386..394 'S::<str>': S<str> 1460 385..393 'S::<str>': S<str>
1387 "### 1461 "###
1388 ); 1462 );
1389} 1463}
1390 1464
1391#[test] 1465#[test]
1392fn impl_trait_assoc_binding_projection_bug() { 1466fn impl_trait_assoc_binding_projection_bug() {
1393 let (db, pos) = TestDB::with_position( 1467 check_types(
1394 r#" 1468 r#"
1395//- /main.rs crate:main deps:std 1469//- /main.rs crate:main deps:std
1396pub trait Language { 1470pub trait Language {
@@ -1409,8 +1483,8 @@ trait Clone {
1409 1483
1410fn api_walkthrough() { 1484fn api_walkthrough() {
1411 for node in foo() { 1485 for node in foo() {
1412 node.clone()<|>; 1486 node.clone();
1413 } 1487 } //^ {unknown}
1414} 1488}
1415 1489
1416//- /std.rs crate:std 1490//- /std.rs crate:std
@@ -1428,7 +1502,6 @@ mod iter {
1428} 1502}
1429"#, 1503"#,
1430 ); 1504 );
1431 assert_eq!("{unknown}", type_at_pos(&db, pos));
1432} 1505}
1433 1506
1434#[test] 1507#[test]
@@ -1448,20 +1521,19 @@ fn test<T: Trait1<Type = u32>>(x: T) {
1448} 1521}
1449"#), 1522"#),
1450 @r###" 1523 @r###"
1451 62..66 'self': Self 1524 61..65 'self': Self
1452 164..165 'x': T 1525 163..164 'x': T
1453 170..186 '{ ...o(); }': () 1526 169..185 '{ ...o(); }': ()
1454 176..177 'x': T 1527 175..176 'x': T
1455 176..183 'x.foo()': u32 1528 175..182 'x.foo()': u32
1456 "### 1529 "###
1457 ); 1530 );
1458} 1531}
1459 1532
1460#[test] 1533#[test]
1461fn where_clause_trait_in_scope_for_method_resolution() { 1534fn where_clause_trait_in_scope_for_method_resolution() {
1462 let t = type_at( 1535 check_types(
1463 r#" 1536 r#"
1464//- /main.rs
1465mod foo { 1537mod foo {
1466 trait Trait { 1538 trait Trait {
1467 fn foo(&self) -> u32 {} 1539 fn foo(&self) -> u32 {}
@@ -1469,11 +1541,10 @@ mod foo {
1469} 1541}
1470 1542
1471fn test<T: foo::Trait>(x: T) { 1543fn test<T: foo::Trait>(x: T) {
1472 x.foo()<|>; 1544 x.foo();
1473} 1545} //^ u32
1474"#, 1546"#,
1475 ); 1547 );
1476 assert_eq!(t, "u32");
1477} 1548}
1478 1549
1479#[test] 1550#[test]
@@ -1494,15 +1565,15 @@ fn test<T: Trait1, U: Trait2>(x: T, y: U) {
1494} 1565}
1495"#), 1566"#),
1496 @r###" 1567 @r###"
1497 50..54 'self': &Self 1568 49..53 'self': &Self
1498 63..65 '{}': () 1569 62..64 '{}': ()
1499 182..183 'x': T 1570 181..182 'x': T
1500 188..189 'y': U 1571 187..188 'y': U
1501 194..223 '{ ...o(); }': () 1572 193..222 '{ ...o(); }': ()
1502 200..201 'x': T 1573 199..200 'x': T
1503 200..207 'x.foo()': u32 1574 199..206 'x.foo()': u32
1504 213..214 'y': U 1575 212..213 'y': U
1505 213..220 'y.foo()': u32 1576 212..219 'y.foo()': u32
1506 "### 1577 "###
1507 ); 1578 );
1508} 1579}
@@ -1523,12 +1594,12 @@ fn test(x: &impl Trait1) {
1523} 1594}
1524"#), 1595"#),
1525 @r###" 1596 @r###"
1526 50..54 'self': &Self 1597 49..53 'self': &Self
1527 63..65 '{}': () 1598 62..64 '{}': ()
1528 116..117 'x': &impl Trait1 1599 115..116 'x': &impl Trait1
1529 133..149 '{ ...o(); }': () 1600 132..148 '{ ...o(); }': ()
1530 139..140 'x': &impl Trait1 1601 138..139 'x': &impl Trait1
1531 139..146 'x.foo()': u32 1602 138..145 'x.foo()': u32
1532 "### 1603 "###
1533 ); 1604 );
1534} 1605}
@@ -1546,10 +1617,10 @@ fn test<T: A>(x: T) {
1546} 1617}
1547"#), 1618"#),
1548 @r###" 1619 @r###"
1549 44..45 'x': T 1620 43..44 'x': T
1550 50..66 '{ ...o(); }': () 1621 49..65 '{ ...o(); }': ()
1551 56..57 'x': T 1622 55..56 'x': T
1552 56..63 'x.foo()': {unknown} 1623 55..62 'x.foo()': {unknown}
1553 "### 1624 "###
1554 ); 1625 );
1555} 1626}
@@ -1573,17 +1644,17 @@ fn test() {
1573} 1644}
1574"#), 1645"#),
1575 @r###" 1646 @r###"
1576 103..104 't': T 1647 102..103 't': T
1577 114..116 '{}': () 1648 113..115 '{}': ()
1578 146..147 't': T 1649 145..146 't': T
1579 157..160 '{t}': T 1650 156..159 '{t}': T
1580 158..159 't': T 1651 157..158 't': T
1581 259..280 '{ ...S)); }': () 1652 258..279 '{ ...S)); }': ()
1582 265..269 'get2': fn get2<u64, S<u64>>(S<u64>) -> u64 1653 264..268 'get2': fn get2<u64, S<u64>>(S<u64>) -> u64
1583 265..277 'get2(set(S))': u64 1654 264..276 'get2(set(S))': u64
1584 270..273 'set': fn set<S<u64>>(S<u64>) -> S<u64> 1655 269..272 'set': fn set<S<u64>>(S<u64>) -> S<u64>
1585 270..276 'set(S)': S<u64> 1656 269..275 'set(S)': S<u64>
1586 274..275 'S': S<u64> 1657 273..274 'S': S<u64>
1587 "### 1658 "###
1588 ); 1659 );
1589} 1660}
@@ -1603,15 +1674,15 @@ fn test<F: FnOnce(u32, u64) -> u128>(f: F) {
1603} 1674}
1604"#), 1675"#),
1605 @r###" 1676 @r###"
1606 57..61 'self': Self 1677 56..60 'self': Self
1607 63..67 'args': Args 1678 62..66 'args': Args
1608 150..151 'f': F 1679 149..150 'f': F
1609 156..184 '{ ...2)); }': () 1680 155..183 '{ ...2)); }': ()
1610 162..163 'f': F 1681 161..162 'f': F
1611 162..181 'f.call...1, 2))': u128 1682 161..180 'f.call...1, 2))': u128
1612 174..180 '(1, 2)': (u32, u64) 1683 173..179 '(1, 2)': (u32, u64)
1613 175..176 '1': u32 1684 174..175 '1': u32
1614 178..179 '2': u64 1685 177..178 '2': u64
1615 "### 1686 "###
1616 ); 1687 );
1617} 1688}
@@ -1652,24 +1723,24 @@ fn test() {
1652} 1723}
1653"#), 1724"#),
1654 @r###" 1725 @r###"
165575..79 'self': Self 1726 74..78 'self': Self
165681..85 'args': Args 1727 80..84 'args': Args
1657140..144 'self': &Self 1728 139..143 'self': &Self
1658244..248 'self': &Bar<F> 1729 243..247 'self': &Bar<F>
1659261..263 '{}': () 1730 260..262 '{}': ()
1660347..351 'self': Opt<T> 1731 346..350 'self': Opt<T>
1661353..354 'f': F 1732 352..353 'f': F
1662369..371 '{}': () 1733 368..370 '{}': ()
1663385..501 '{ ...(f); }': () 1734 384..500 '{ ...(f); }': ()
1664395..398 'bar': Bar<fn(u8) -> u32> 1735 394..397 'bar': Bar<fn(u8) -> u32>
1665424..427 'bar': Bar<fn(u8) -> u32> 1736 423..426 'bar': Bar<fn(u8) -> u32>
1666424..433 'bar.foo()': {unknown} 1737 423..432 'bar.foo()': (u8, u32)
1667444..447 'opt': Opt<u8> 1738 443..446 'opt': Opt<u8>
1668466..467 'f': fn(u8) -> u32 1739 465..466 'f': fn(u8) -> u32
1669488..491 'opt': Opt<u8> 1740 487..490 'opt': Opt<u8>
1670488..498 'opt.map(f)': Opt<FnOnce::Output<fn(u8) -> u32, (u8,)>> 1741 487..497 'opt.map(f)': Opt<u32>
1671496..497 'f': fn(u8) -> u32 1742 495..496 'f': fn(u8) -> u32
1672"### 1743 "###
1673 ); 1744 );
1674} 1745}
1675 1746
@@ -1718,33 +1789,33 @@ fn test() {
1718} 1789}
1719"#), 1790"#),
1720 @r###" 1791 @r###"
172165..69 'self': &Self 1792 64..68 'self': &Self
1722166..170 'self': Self 1793 165..169 'self': Self
1723172..176 'args': Args 1794 171..175 'args': Args
1724240..244 'self': &Foo 1795 239..243 'self': &Foo
1725255..257 '{}': () 1796 254..256 '{}': ()
1726335..336 'f': F 1797 334..335 'f': F
1727355..357 '{}': () 1798 354..356 '{}': ()
1728444..690 '{ ...o(); }': () 1799 443..689 '{ ...o(); }': ()
1729454..459 'lazy1': Lazy<Foo, fn() -> T> 1800 453..458 'lazy1': Lazy<Foo, || -> Foo>
1730476..485 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T> 1801 475..484 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
1731476..493 'Lazy::...| Foo)': Lazy<Foo, fn() -> T> 1802 475..492 'Lazy::...| Foo)': Lazy<Foo, || -> Foo>
1732486..492 '|| Foo': || -> T 1803 485..491 '|| Foo': || -> Foo
1733489..492 'Foo': Foo 1804 488..491 'Foo': Foo
1734503..505 'r1': {unknown} 1805 502..504 'r1': usize
1735508..513 'lazy1': Lazy<Foo, fn() -> T> 1806 507..512 'lazy1': Lazy<Foo, || -> Foo>
1736508..519 'lazy1.foo()': {unknown} 1807 507..518 'lazy1.foo()': usize
1737561..576 'make_foo_fn_ptr': fn() -> Foo 1808 560..575 'make_foo_fn_ptr': fn() -> Foo
1738592..603 'make_foo_fn': fn make_foo_fn() -> Foo 1809 591..602 'make_foo_fn': fn make_foo_fn() -> Foo
1739613..618 'lazy2': Lazy<Foo, fn() -> T> 1810 612..617 'lazy2': Lazy<Foo, fn() -> Foo>
1740635..644 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T> 1811 634..643 'Lazy::new': fn new<Foo, fn() -> Foo>(fn() -> Foo) -> Lazy<Foo, fn() -> Foo>
1741635..661 'Lazy::...n_ptr)': Lazy<Foo, fn() -> T> 1812 634..660 'Lazy::...n_ptr)': Lazy<Foo, fn() -> Foo>
1742645..660 'make_foo_fn_ptr': fn() -> Foo 1813 644..659 'make_foo_fn_ptr': fn() -> Foo
1743671..673 'r2': {unknown} 1814 670..672 'r2': usize
1744676..681 'lazy2': Lazy<Foo, fn() -> T> 1815 675..680 'lazy2': Lazy<Foo, fn() -> Foo>
1745676..687 'lazy2.foo()': {unknown} 1816 675..686 'lazy2.foo()': usize
1746550..552 '{}': () 1817 549..551 '{}': ()
1747"### 1818 "###
1748 ); 1819 );
1749} 1820}
1750 1821
@@ -1770,32 +1841,32 @@ fn test() {
1770} 1841}
1771"#), 1842"#),
1772 @r###" 1843 @r###"
1773 148..152 'self': Option<T> 1844 147..151 'self': Option<T>
1774 154..155 'f': F 1845 153..154 'f': F
1775 173..175 '{}': () 1846 172..174 '{}': ()
1776 189..308 '{ ... 1); }': () 1847 188..307 '{ ... 1); }': ()
1777 199..200 'x': Option<u32> 1848 198..199 'x': Option<u32>
1778 203..215 'Option::Some': Some<u32>(u32) -> Option<u32> 1849 202..214 'Option::Some': Some<u32>(u32) -> Option<u32>
1779 203..221 'Option...(1u32)': Option<u32> 1850 202..220 'Option...(1u32)': Option<u32>
1780 216..220 '1u32': u32 1851 215..219 '1u32': u32
1781 227..228 'x': Option<u32> 1852 226..227 'x': Option<u32>
1782 227..243 'x.map(...v + 1)': Option<u32> 1853 226..242 'x.map(...v + 1)': Option<u32>
1783 233..242 '|v| v + 1': |u32| -> u32 1854 232..241 '|v| v + 1': |u32| -> u32
1784 234..235 'v': u32 1855 233..234 'v': u32
1785 237..238 'v': u32 1856 236..237 'v': u32
1786 237..242 'v + 1': u32 1857 236..241 'v + 1': u32
1787 241..242 '1': u32 1858 240..241 '1': u32
1788 249..250 'x': Option<u32> 1859 248..249 'x': Option<u32>
1789 249..265 'x.map(... 1u64)': Option<u64> 1860 248..264 'x.map(... 1u64)': Option<u64>
1790 255..264 '|_v| 1u64': |u32| -> u64 1861 254..263 '|_v| 1u64': |u32| -> u64
1791 256..258 '_v': u32 1862 255..257 '_v': u32
1792 260..264 '1u64': u64 1863 259..263 '1u64': u64
1793 275..276 'y': Option<i64> 1864 274..275 'y': Option<i64>
1794 292..293 'x': Option<u32> 1865 291..292 'x': Option<u32>
1795 292..305 'x.map(|_v| 1)': Option<i64> 1866 291..304 'x.map(|_v| 1)': Option<i64>
1796 298..304 '|_v| 1': |u32| -> i64 1867 297..303 '|_v| 1': |u32| -> i64
1797 299..301 '_v': u32 1868 298..300 '_v': u32
1798 303..304 '1': i64 1869 302..303 '1': i64
1799 "### 1870 "###
1800 ); 1871 );
1801} 1872}
@@ -1816,26 +1887,26 @@ fn test<F: FnOnce(u32) -> u64>(f: F) {
1816} 1887}
1817"#), 1888"#),
1818 @r###" 1889 @r###"
1819 73..74 'f': F 1890 72..73 'f': F
1820 79..155 '{ ...+ v; }': () 1891 78..154 '{ ...+ v; }': ()
1821 85..86 'f': F 1892 84..85 'f': F
1822 85..89 'f(1)': {unknown} 1893 84..88 'f(1)': {unknown}
1823 87..88 '1': i32 1894 86..87 '1': i32
1824 99..100 'g': |u64| -> i32 1895 98..99 'g': |u64| -> i32
1825 103..112 '|v| v + 1': |u64| -> i32 1896 102..111 '|v| v + 1': |u64| -> i32
1826 104..105 'v': u64 1897 103..104 'v': u64
1827 107..108 'v': u64 1898 106..107 'v': u64
1828 107..112 'v + 1': i32 1899 106..111 'v + 1': i32
1829 111..112 '1': i32 1900 110..111 '1': i32
1830 118..119 'g': |u64| -> i32 1901 117..118 'g': |u64| -> i32
1831 118..125 'g(1u64)': i32 1902 117..124 'g(1u64)': i32
1832 120..124 '1u64': u64 1903 119..123 '1u64': u64
1833 135..136 'h': |u128| -> u128 1904 134..135 'h': |u128| -> u128
1834 139..152 '|v| 1u128 + v': |u128| -> u128 1905 138..151 '|v| 1u128 + v': |u128| -> u128
1835 140..141 'v': u128 1906 139..140 'v': u128
1836 143..148 '1u128': u128 1907 142..147 '1u128': u128
1837 143..152 '1u128 + v': u128 1908 142..151 '1u128 + v': u128
1838 151..152 'v': u128 1909 150..151 'v': u128
1839 "### 1910 "###
1840 ); 1911 );
1841} 1912}
@@ -1868,61 +1939,83 @@ fn test() {
1868} 1939}
1869"#), 1940"#),
1870 @r###" 1941 @r###"
1871 95..96 'x': T 1942 94..95 'x': T
1872 101..102 'f': F 1943 100..101 'f': F
1873 112..114 '{}': () 1944 111..113 '{}': ()
1874 148..149 'f': F 1945 147..148 'f': F
1875 154..155 'x': T 1946 153..154 'x': T
1876 165..167 '{}': () 1947 164..166 '{}': ()
1877 202..206 'self': S 1948 201..205 'self': S
1878 254..258 'self': S 1949 253..257 'self': S
1879 260..261 'x': T 1950 259..260 'x': T
1880 266..267 'f': F 1951 265..266 'f': F
1881 277..279 '{}': () 1952 276..278 '{}': ()
1882 317..321 'self': S 1953 316..320 'self': S
1883 323..324 'f': F 1954 322..323 'f': F
1884 329..330 'x': T 1955 328..329 'x': T
1885 340..342 '{}': () 1956 339..341 '{}': ()
1886 356..515 '{ ... S); }': () 1957 355..514 '{ ... S); }': ()
1887 366..368 'x1': u64 1958 365..367 'x1': u64
1888 371..375 'foo1': fn foo1<S, u64, |S| -> u64>(S, |S| -> u64) -> u64 1959 370..374 'foo1': fn foo1<S, u64, |S| -> u64>(S, |S| -> u64) -> u64
1889 371..394 'foo1(S...hod())': u64 1960 370..393 'foo1(S...hod())': u64
1890 376..377 'S': S 1961 375..376 'S': S
1891 379..393 '|s| s.method()': |S| -> u64 1962 378..392 '|s| s.method()': |S| -> u64
1892 380..381 's': S 1963 379..380 's': S
1893 383..384 's': S 1964 382..383 's': S
1894 383..393 's.method()': u64 1965 382..392 's.method()': u64
1895 404..406 'x2': u64 1966 403..405 'x2': u64
1896 409..413 'foo2': fn foo2<S, u64, |S| -> u64>(|S| -> u64, S) -> u64 1967 408..412 'foo2': fn foo2<S, u64, |S| -> u64>(|S| -> u64, S) -> u64
1897 409..432 'foo2(|...(), S)': u64 1968 408..431 'foo2(|...(), S)': u64
1898 414..428 '|s| s.method()': |S| -> u64 1969 413..427 '|s| s.method()': |S| -> u64
1899 415..416 's': S 1970 414..415 's': S
1900 418..419 's': S 1971 417..418 's': S
1901 418..428 's.method()': u64 1972 417..427 's.method()': u64
1902 430..431 'S': S 1973 429..430 'S': S
1903 442..444 'x3': u64 1974 441..443 'x3': u64
1904 447..448 'S': S 1975 446..447 'S': S
1905 447..472 'S.foo1...hod())': u64 1976 446..471 'S.foo1...hod())': u64
1906 454..455 'S': S 1977 453..454 'S': S
1907 457..471 '|s| s.method()': |S| -> u64 1978 456..470 '|s| s.method()': |S| -> u64
1908 458..459 's': S 1979 457..458 's': S
1909 461..462 's': S 1980 460..461 's': S
1910 461..471 's.method()': u64 1981 460..470 's.method()': u64
1911 482..484 'x4': u64 1982 481..483 'x4': u64
1912 487..488 'S': S 1983 486..487 'S': S
1913 487..512 'S.foo2...(), S)': u64 1984 486..511 'S.foo2...(), S)': u64
1914 494..508 '|s| s.method()': |S| -> u64 1985 493..507 '|s| s.method()': |S| -> u64
1915 495..496 's': S 1986 494..495 's': S
1916 498..499 's': S 1987 497..498 's': S
1917 498..508 's.method()': u64 1988 497..507 's.method()': u64
1918 510..511 'S': S 1989 509..510 'S': S
1919 "### 1990 "###
1920 ); 1991 );
1921} 1992}
1922 1993
1923#[test] 1994#[test]
1995fn fn_item_fn_trait() {
1996 check_types(
1997 r#"
1998#[lang = "fn_once"]
1999trait FnOnce<Args> {
2000 type Output;
2001}
2002
2003struct S;
2004
2005fn foo() -> S {}
2006
2007fn takes_closure<U, F: FnOnce() -> U>(f: F) -> U { f() }
2008
2009fn test() {
2010 takes_closure(foo);
2011} //^^^^^^^^^^^^^^^^^^ S
2012"#,
2013 );
2014}
2015
2016#[test]
1924fn unselected_projection_in_trait_env_1() { 2017fn unselected_projection_in_trait_env_1() {
1925 let t = type_at( 2018 check_types(
1926 r#" 2019 r#"
1927//- /main.rs 2020//- /main.rs
1928trait Trait { 2021trait Trait {
@@ -1935,18 +2028,16 @@ trait Trait2 {
1935 2028
1936fn test<T: Trait>() where T::Item: Trait2 { 2029fn test<T: Trait>() where T::Item: Trait2 {
1937 let x: T::Item = no_matter; 2030 let x: T::Item = no_matter;
1938 x.foo()<|>; 2031 x.foo();
1939} 2032} //^ u32
1940"#, 2033"#,
1941 ); 2034 );
1942 assert_eq!(t, "u32");
1943} 2035}
1944 2036
1945#[test] 2037#[test]
1946fn unselected_projection_in_trait_env_2() { 2038fn unselected_projection_in_trait_env_2() {
1947 let t = type_at( 2039 check_types(
1948 r#" 2040 r#"
1949//- /main.rs
1950trait Trait<T> { 2041trait Trait<T> {
1951 type Item; 2042 type Item;
1952} 2043}
@@ -1957,11 +2048,10 @@ trait Trait2 {
1957 2048
1958fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> { 2049fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> {
1959 let x: T::Item = no_matter; 2050 let x: T::Item = no_matter;
1960 x.foo()<|>; 2051 x.foo();
1961} 2052} //^ u32
1962"#, 2053"#,
1963 ); 2054 );
1964 assert_eq!(t, "u32");
1965} 2055}
1966 2056
1967#[test] 2057#[test]
@@ -1990,26 +2080,25 @@ impl Trait for S2 {
1990} 2080}
1991"#, 2081"#,
1992 ), @r###" 2082 ), @r###"
1993 54..58 'self': &Self 2083 40..44 'self': &Self
1994 60..61 'x': Trait::Item<Self> 2084 46..47 'x': Trait::Item<Self>
1995 140..144 'self': &S 2085 126..130 'self': &S
1996 146..147 'x': u32 2086 132..133 'x': u32
1997 161..175 '{ let y = x; }': () 2087 147..161 '{ let y = x; }': ()
1998 167..168 'y': u32 2088 153..154 'y': u32
1999 171..172 'x': u32 2089 157..158 'x': u32
2000 242..246 'self': &S2 2090 228..232 'self': &S2
2001 248..249 'x': i32 2091 234..235 'x': i32
2002 265..279 '{ let y = x; }': () 2092 251..265 '{ let y = x; }': ()
2003 271..272 'y': i32 2093 257..258 'y': i32
2004 275..276 'x': i32 2094 261..262 'x': i32
2005 "###); 2095 "###);
2006} 2096}
2007 2097
2008#[test] 2098#[test]
2009fn unselected_projection_on_trait_self() { 2099fn unselected_projection_on_trait_self() {
2010 let t = type_at( 2100 check_types(
2011 r#" 2101 r#"
2012//- /main.rs
2013trait Trait { 2102trait Trait {
2014 type Item; 2103 type Item;
2015 2104
@@ -2022,18 +2111,16 @@ impl Trait for S {
2022} 2111}
2023 2112
2024fn test() { 2113fn test() {
2025 S.f()<|>; 2114 S.f();
2026} 2115} //^ u32
2027"#, 2116"#,
2028 ); 2117 );
2029 assert_eq!(t, "u32");
2030} 2118}
2031 2119
2032#[test] 2120#[test]
2033fn unselected_projection_chalk_fold() { 2121fn unselected_projection_chalk_fold() {
2034 let t = type_at( 2122 check_types(
2035 r#" 2123 r#"
2036//- /main.rs
2037trait Interner {} 2124trait Interner {}
2038trait Fold<I: Interner, TI = I> { 2125trait Fold<I: Interner, TI = I> {
2039 type Result; 2126 type Result;
@@ -2052,18 +2139,16 @@ where
2052} 2139}
2053 2140
2054fn foo<I: Interner>(interner: &I, t: Ty<I>) { 2141fn foo<I: Interner>(interner: &I, t: Ty<I>) {
2055 fold(interner, t)<|>; 2142 fold(interner, t);
2056} 2143} //^ Ty<I>
2057"#, 2144"#,
2058 ); 2145 );
2059 assert_eq!(t, "Ty<I>");
2060} 2146}
2061 2147
2062#[test] 2148#[test]
2063fn trait_impl_self_ty() { 2149fn trait_impl_self_ty() {
2064 let t = type_at( 2150 check_types(
2065 r#" 2151 r#"
2066//- /main.rs
2067trait Trait<T> { 2152trait Trait<T> {
2068 fn foo(&self); 2153 fn foo(&self);
2069} 2154}
@@ -2073,18 +2158,16 @@ struct S;
2073impl Trait<Self> for S {} 2158impl Trait<Self> for S {}
2074 2159
2075fn test() { 2160fn test() {
2076 S.foo()<|>; 2161 S.foo();
2077} 2162} //^ ()
2078"#, 2163"#,
2079 ); 2164 );
2080 assert_eq!(t, "()");
2081} 2165}
2082 2166
2083#[test] 2167#[test]
2084fn trait_impl_self_ty_cycle() { 2168fn trait_impl_self_ty_cycle() {
2085 let t = type_at( 2169 check_types(
2086 r#" 2170 r#"
2087//- /main.rs
2088trait Trait { 2171trait Trait {
2089 fn foo(&self); 2172 fn foo(&self);
2090} 2173}
@@ -2094,18 +2177,17 @@ struct S<T>;
2094impl Trait for S<Self> {} 2177impl Trait for S<Self> {}
2095 2178
2096fn test() { 2179fn test() {
2097 S.foo()<|>; 2180 S.foo();
2098} 2181} //^ {unknown}
2099"#, 2182"#,
2100 ); 2183 );
2101 assert_eq!(t, "{unknown}");
2102} 2184}
2103 2185
2104#[test] 2186#[test]
2105fn unselected_projection_in_trait_env_cycle_1() { 2187fn unselected_projection_in_trait_env_cycle_1() {
2106 let t = type_at( 2188 // this is a legitimate cycle
2189 check_types(
2107 r#" 2190 r#"
2108//- /main.rs
2109trait Trait { 2191trait Trait {
2110 type Item; 2192 type Item;
2111} 2193}
@@ -2113,17 +2195,16 @@ trait Trait {
2113trait Trait2<T> {} 2195trait Trait2<T> {}
2114 2196
2115fn test<T: Trait>() where T: Trait2<T::Item> { 2197fn test<T: Trait>() where T: Trait2<T::Item> {
2116 let x: T::Item = no_matter<|>; 2198 let x: T::Item = no_matter;
2117} 2199} //^ {unknown}
2118"#, 2200"#,
2119 ); 2201 );
2120 // this is a legitimate cycle
2121 assert_eq!(t, "{unknown}");
2122} 2202}
2123 2203
2124#[test] 2204#[test]
2125fn unselected_projection_in_trait_env_cycle_2() { 2205fn unselected_projection_in_trait_env_cycle_2() {
2126 let t = type_at( 2206 // this is a legitimate cycle
2207 check_types(
2127 r#" 2208 r#"
2128//- /main.rs 2209//- /main.rs
2129trait Trait<T> { 2210trait Trait<T> {
@@ -2131,19 +2212,16 @@ trait Trait<T> {
2131} 2212}
2132 2213
2133fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> { 2214fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> {
2134 let x: T::Item = no_matter<|>; 2215 let x: T::Item = no_matter;
2135} 2216} //^ {unknown}
2136"#, 2217"#,
2137 ); 2218 );
2138 // this is a legitimate cycle
2139 assert_eq!(t, "{unknown}");
2140} 2219}
2141 2220
2142#[test] 2221#[test]
2143fn inline_assoc_type_bounds_1() { 2222fn inline_assoc_type_bounds_1() {
2144 let t = type_at( 2223 check_types(
2145 r#" 2224 r#"
2146//- /main.rs
2147trait Iterator { 2225trait Iterator {
2148 type Item; 2226 type Item;
2149} 2227}
@@ -2159,29 +2237,26 @@ impl<T: Iterator> Iterator for S<T> {
2159 2237
2160fn test<I: Iterator<Item: OtherTrait<u32>>>() { 2238fn test<I: Iterator<Item: OtherTrait<u32>>>() {
2161 let x: <S<I> as Iterator>::Item; 2239 let x: <S<I> as Iterator>::Item;
2162 x.foo()<|>; 2240 x.foo();
2163} 2241} //^ u32
2164"#, 2242"#,
2165 ); 2243 );
2166 assert_eq!(t, "u32");
2167} 2244}
2168 2245
2169#[test] 2246#[test]
2170fn inline_assoc_type_bounds_2() { 2247fn inline_assoc_type_bounds_2() {
2171 let t = type_at( 2248 check_types(
2172 r#" 2249 r#"
2173//- /main.rs
2174trait Iterator { 2250trait Iterator {
2175 type Item; 2251 type Item;
2176} 2252}
2177 2253
2178fn test<I: Iterator<Item: Iterator<Item = u32>>>() { 2254fn test<I: Iterator<Item: Iterator<Item = u32>>>() {
2179 let x: <<I as Iterator>::Item as Iterator>::Item; 2255 let x: <<I as Iterator>::Item as Iterator>::Item;
2180 x<|>; 2256 x;
2181} 2257} //^ u32
2182"#, 2258"#,
2183 ); 2259 );
2184 assert_eq!(t, "u32");
2185} 2260}
2186 2261
2187#[test] 2262#[test]
@@ -2241,15 +2316,15 @@ impl TokenStream for Rustc {
2241} 2316}
2242"#), 2317"#),
2243 @r###" 2318 @r###"
2244 1062..1073 '{ loop {} }': T 2319 1061..1072 '{ loop {} }': T
2245 1064..1071 'loop {}': ! 2320 1063..1070 'loop {}': !
2246 1069..1071 '{}': () 2321 1068..1070 '{}': ()
2247 1137..1200 '{ ... }': T 2322 1136..1199 '{ ... }': T
2248 1151..1156 'group': G 2323 1150..1155 'group': G
2249 1172..1176 'make': fn make<G>() -> G 2324 1171..1175 'make': fn make<G>() -> G
2250 1172..1178 'make()': G 2325 1171..1177 'make()': G
2251 1188..1192 'make': fn make<T>() -> T 2326 1187..1191 'make': fn make<T>() -> T
2252 1188..1194 'make()': T 2327 1187..1193 'make()': T
2253 "### 2328 "###
2254 ); 2329 );
2255} 2330}
@@ -2276,37 +2351,37 @@ fn test() -> impl Trait<i32> {
2276} 2351}
2277"#, true), 2352"#, true),
2278 @r###" 2353 @r###"
2279 27..28 'x': impl Trait<u32> 2354 26..27 'x': impl Trait<u32>
2280 47..58 '{ loop {} }': () 2355 46..57 '{ loop {} }': ()
2281 49..56 'loop {}': ! 2356 48..55 'loop {}': !
2282 54..56 '{}': () 2357 53..55 '{}': ()
2283 69..70 'x': impl Trait<T> 2358 68..69 'x': impl Trait<T>
2284 92..103 '{ loop {} }': T 2359 91..102 '{ loop {} }': T
2285 94..101 'loop {}': ! 2360 93..100 'loop {}': !
2286 99..101 '{}': () 2361 98..100 '{}': ()
2287 172..183 '{ loop {} }': T 2362 171..182 '{ loop {} }': T
2288 174..181 'loop {}': ! 2363 173..180 'loop {}': !
2289 179..181 '{}': () 2364 178..180 '{}': ()
2290 214..310 '{ ...t()) }': S<{unknown}> 2365 213..309 '{ ...t()) }': S<{unknown}>
2291 224..226 's1': S<u32> 2366 223..225 's1': S<u32>
2292 229..230 'S': S<u32>(u32) -> S<u32> 2367 228..229 'S': S<u32>(u32) -> S<u32>
2293 229..241 'S(default())': S<u32> 2368 228..240 'S(default())': S<u32>
2294 231..238 'default': fn default<u32>() -> u32 2369 230..237 'default': fn default<u32>() -> u32
2295 231..240 'default()': u32 2370 230..239 'default()': u32
2296 247..250 'foo': fn foo(S<u32>) 2371 246..249 'foo': fn foo(S<u32>)
2297 247..254 'foo(s1)': () 2372 246..253 'foo(s1)': ()
2298 251..253 's1': S<u32> 2373 250..252 's1': S<u32>
2299 264..265 'x': i32 2374 263..264 'x': i32
2300 273..276 'bar': fn bar<i32>(S<i32>) -> i32 2375 272..275 'bar': fn bar<i32>(S<i32>) -> i32
2301 273..290 'bar(S(...lt()))': i32 2376 272..289 'bar(S(...lt()))': i32
2302 277..278 'S': S<i32>(i32) -> S<i32> 2377 276..277 'S': S<i32>(i32) -> S<i32>
2303 277..289 'S(default())': S<i32> 2378 276..288 'S(default())': S<i32>
2304 279..286 'default': fn default<i32>() -> i32 2379 278..285 'default': fn default<i32>() -> i32
2305 279..288 'default()': i32 2380 278..287 'default()': i32
2306 296..297 'S': S<{unknown}>({unknown}) -> S<{unknown}> 2381 295..296 'S': S<{unknown}>({unknown}) -> S<{unknown}>
2307 296..308 'S(default())': S<{unknown}> 2382 295..307 'S(default())': S<{unknown}>
2308 298..305 'default': fn default<{unknown}>() -> {unknown} 2383 297..304 'default': fn default<{unknown}>() -> {unknown}
2309 298..307 'default()': {unknown} 2384 297..306 'default()': {unknown}
2310 "### 2385 "###
2311 ); 2386 );
2312} 2387}
@@ -2340,24 +2415,23 @@ fn main() {
2340} 2415}
2341"#), 2416"#),
2342 @r###" 2417 @r###"
2343 147..149 '_v': F 2418 133..135 '_v': F
2344 192..195 '{ }': () 2419 178..181 '{ }': ()
2345 207..238 '{ ... }); }': () 2420 193..224 '{ ... }); }': ()
2346 213..223 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ()) 2421 199..209 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ())
2347 213..235 'f::<()... z; })': () 2422 199..221 'f::<()... z; })': ()
2348 224..234 '|z| { z; }': |&()| -> () 2423 210..220 '|z| { z; }': |&()| -> ()
2349 225..226 'z': &() 2424 211..212 'z': &()
2350 228..234 '{ z; }': () 2425 214..220 '{ z; }': ()
2351 230..231 'z': &() 2426 216..217 'z': &()
2352 "### 2427 "###
2353 ); 2428 );
2354} 2429}
2355 2430
2356#[test] 2431#[test]
2357fn associated_type_bound() { 2432fn associated_type_bound() {
2358 let t = type_at( 2433 check_types(
2359 r#" 2434 r#"
2360//- /main.rs
2361pub trait Trait { 2435pub trait Trait {
2362 type Item: OtherTrait<u32>; 2436 type Item: OtherTrait<u32>;
2363} 2437}
@@ -2373,18 +2447,16 @@ impl<T: Trait> Trait for S<T> {
2373 2447
2374fn test<T: Trait>() { 2448fn test<T: Trait>() {
2375 let y: <S<T> as Trait>::Item = no_matter; 2449 let y: <S<T> as Trait>::Item = no_matter;
2376 y.foo()<|>; 2450 y.foo();
2377} 2451} //^ u32
2378"#, 2452"#,
2379 ); 2453 );
2380 assert_eq!(t, "u32");
2381} 2454}
2382 2455
2383#[test] 2456#[test]
2384fn dyn_trait_through_chalk() { 2457fn dyn_trait_through_chalk() {
2385 let t = type_at( 2458 check_types(
2386 r#" 2459 r#"
2387//- /main.rs
2388struct Box<T> {} 2460struct Box<T> {}
2389#[lang = "deref"] 2461#[lang = "deref"]
2390trait Deref { 2462trait Deref {
@@ -2398,18 +2470,16 @@ trait Trait {
2398} 2470}
2399 2471
2400fn test(x: Box<dyn Trait>) { 2472fn test(x: Box<dyn Trait>) {
2401 x.foo()<|>; 2473 x.foo();
2402} 2474} //^ ()
2403"#, 2475"#,
2404 ); 2476 );
2405 assert_eq!(t, "()");
2406} 2477}
2407 2478
2408#[test] 2479#[test]
2409fn string_to_owned() { 2480fn string_to_owned() {
2410 let t = type_at( 2481 check_types(
2411 r#" 2482 r#"
2412//- /main.rs
2413struct String {} 2483struct String {}
2414pub trait ToOwned { 2484pub trait ToOwned {
2415 type Owned; 2485 type Owned;
@@ -2419,11 +2489,10 @@ impl ToOwned for str {
2419 type Owned = String; 2489 type Owned = String;
2420} 2490}
2421fn test() { 2491fn test() {
2422 "foo".to_owned()<|>; 2492 "foo".to_owned();
2423} 2493} //^ String
2424"#, 2494"#,
2425 ); 2495 );
2426 assert_eq!(t, "String");
2427} 2496}
2428 2497
2429#[test] 2498#[test]
@@ -2501,55 +2570,54 @@ fn main() {
2501} 2570}
2502"#), 2571"#),
2503 @r###" 2572 @r###"
2504 240..244 'self': Self 2573 226..230 'self': Self
2505 246..247 'f': F 2574 232..233 'f': F
2506 331..342 '{ loop {} }': FilterMap<Self, F> 2575 317..328 '{ loop {} }': FilterMap<Self, F>
2507 333..340 'loop {}': ! 2576 319..326 'loop {}': !
2508 338..340 '{}': () 2577 324..326 '{}': ()
2509 363..367 'self': Self 2578 349..353 'self': Self
2510 369..370 'f': F 2579 355..356 'f': F
2511 419..430 '{ loop {} }': () 2580 405..416 '{ loop {} }': ()
2512 421..428 'loop {}': ! 2581 407..414 'loop {}': !
2513 426..428 '{}': () 2582 412..414 '{}': ()
2514 539..543 'self': Self 2583 525..529 'self': Self
2515 868..872 'self': I 2584 854..858 'self': I
2516 879..899 '{ ... }': I 2585 865..885 '{ ... }': I
2517 889..893 'self': I 2586 875..879 'self': I
2518 958..969 '{ loop {} }': Vec<T> 2587 944..955 '{ loop {} }': Vec<T>
2519 960..967 'loop {}': ! 2588 946..953 'loop {}': !
2520 965..967 '{}': () 2589 951..953 '{}': ()
2521 1156..1287 '{ ... }); }': () 2590 1142..1273 '{ ... }); }': ()
2522 1162..1177 'Vec::<i32>::new': fn new<i32>() -> Vec<i32> 2591 1148..1163 'Vec::<i32>::new': fn new<i32>() -> Vec<i32>
2523 1162..1179 'Vec::<...:new()': Vec<i32> 2592 1148..1165 'Vec::<...:new()': Vec<i32>
2524 1162..1191 'Vec::<...iter()': IntoIter<i32> 2593 1148..1177 'Vec::<...iter()': IntoIter<i32>
2525 1162..1256 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>> 2594 1148..1242 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>>
2526 1162..1284 'Vec::<... y; })': () 2595 1148..1270 'Vec::<... y; })': ()
2527 1210..1255 '|x| if...None }': |i32| -> Option<u32> 2596 1196..1241 '|x| if...None }': |i32| -> Option<u32>
2528 1211..1212 'x': i32 2597 1197..1198 'x': i32
2529 1214..1255 'if x >...None }': Option<u32> 2598 1200..1241 'if x >...None }': Option<u32>
2530 1217..1218 'x': i32 2599 1203..1204 'x': i32
2531 1217..1222 'x > 0': bool 2600 1203..1208 'x > 0': bool
2532 1221..1222 '0': i32 2601 1207..1208 '0': i32
2533 1223..1241 '{ Some...u32) }': Option<u32> 2602 1209..1227 '{ Some...u32) }': Option<u32>
2534 1225..1229 'Some': Some<u32>(u32) -> Option<u32> 2603 1211..1215 'Some': Some<u32>(u32) -> Option<u32>
2535 1225..1239 'Some(x as u32)': Option<u32> 2604 1211..1225 'Some(x as u32)': Option<u32>
2536 1230..1231 'x': i32 2605 1216..1217 'x': i32
2537 1230..1238 'x as u32': u32 2606 1216..1224 'x as u32': u32
2538 1247..1255 '{ None }': Option<u32> 2607 1233..1241 '{ None }': Option<u32>
2539 1249..1253 'None': Option<u32> 2608 1235..1239 'None': Option<u32>
2540 1273..1283 '|y| { y; }': |u32| -> () 2609 1259..1269 '|y| { y; }': |u32| -> ()
2541 1274..1275 'y': u32 2610 1260..1261 'y': u32
2542 1277..1283 '{ y; }': () 2611 1263..1269 '{ y; }': ()
2543 1279..1280 'y': u32 2612 1265..1266 'y': u32
2544 "### 2613 "###
2545 ); 2614 );
2546} 2615}
2547 2616
2548#[test] 2617#[test]
2549fn nested_assoc() { 2618fn nested_assoc() {
2550 let t = type_at( 2619 check_types(
2551 r#" 2620 r#"
2552//- /main.rs
2553struct Bar; 2621struct Bar;
2554struct Foo; 2622struct Foo;
2555 2623
@@ -2572,11 +2640,10 @@ impl<T:A> B for T {
2572} 2640}
2573 2641
2574fn main() { 2642fn main() {
2575 Bar::foo()<|>; 2643 Bar::foo();
2576} 2644} //^ Foo
2577"#, 2645"#,
2578 ); 2646 );
2579 assert_eq!(t, "Foo");
2580} 2647}
2581 2648
2582#[test] 2649#[test]
@@ -2592,13 +2659,13 @@ fn test(x: &dyn Foo) {
2592} 2659}
2593"#, true), 2660"#, true),
2594 @r###" 2661 @r###"
2595 22..23 'x': &dyn Foo 2662 21..22 'x': &dyn Foo
2596 35..37 '{}': () 2663 34..36 '{}': ()
2597 47..48 'x': &dyn Foo 2664 46..47 'x': &dyn Foo
2598 60..75 '{ foo(x); }': () 2665 59..74 '{ foo(x); }': ()
2599 66..69 'foo': fn foo(&dyn Foo) 2666 65..68 'foo': fn foo(&dyn Foo)
2600 66..72 'foo(x)': () 2667 65..71 'foo(x)': ()
2601 70..71 'x': &dyn Foo 2668 69..70 'x': &dyn Foo
2602 "### 2669 "###
2603 ); 2670 );
2604} 2671}
@@ -2625,20 +2692,20 @@ fn test() {
2625} 2692}
2626"#, true), 2693"#, true),
2627 @r###" 2694 @r###"
2628 111..115 'self': &Self 2695 110..114 'self': &Self
2629 167..268 '{ ...t(); }': () 2696 166..267 '{ ...t(); }': ()
2630 173..179 'IsCopy': IsCopy 2697 172..178 'IsCopy': IsCopy
2631 173..186 'IsCopy.test()': bool 2698 172..185 'IsCopy.test()': bool
2632 192..199 'NotCopy': NotCopy 2699 191..198 'NotCopy': NotCopy
2633 192..206 'NotCopy.test()': {unknown} 2700 191..205 'NotCopy.test()': {unknown}
2634 212..228 '(IsCop...sCopy)': (IsCopy, IsCopy) 2701 211..227 '(IsCop...sCopy)': (IsCopy, IsCopy)
2635 212..235 '(IsCop...test()': bool 2702 211..234 '(IsCop...test()': bool
2636 213..219 'IsCopy': IsCopy 2703 212..218 'IsCopy': IsCopy
2637 221..227 'IsCopy': IsCopy 2704 220..226 'IsCopy': IsCopy
2638 241..258 '(IsCop...tCopy)': (IsCopy, NotCopy) 2705 240..257 '(IsCop...tCopy)': (IsCopy, NotCopy)
2639 241..265 '(IsCop...test()': {unknown} 2706 240..264 '(IsCop...test()': {unknown}
2640 242..248 'IsCopy': IsCopy 2707 241..247 'IsCopy': IsCopy
2641 250..257 'NotCopy': NotCopy 2708 249..256 'NotCopy': NotCopy
2642 "### 2709 "###
2643 ); 2710 );
2644} 2711}
@@ -2666,20 +2733,20 @@ fn test() {
2666} 2733}
2667"#, true), 2734"#, true),
2668 @r###" 2735 @r###"
2669 42..44 '{}': () 2736 41..43 '{}': ()
2670 61..62 'T': {unknown} 2737 60..61 'T': {unknown}
2671 69..71 '{}': () 2738 68..70 '{}': ()
2672 69..71: expected T, got () 2739 68..70: expected T, got ()
2673 146..150 'self': &Self 2740 145..149 'self': &Self
2674 202..282 '{ ...t(); }': () 2741 201..281 '{ ...t(); }': ()
2675 208..211 'foo': fn foo() 2742 207..210 'foo': fn foo()
2676 208..218 'foo.test()': bool 2743 207..217 'foo.test()': bool
2677 224..227 'bar': fn bar<{unknown}>({unknown}) -> {unknown} 2744 223..226 'bar': fn bar<{unknown}>({unknown}) -> {unknown}
2678 224..234 'bar.test()': bool 2745 223..233 'bar.test()': bool
2679 240..246 'Struct': Struct(usize) -> Struct 2746 239..245 'Struct': Struct(usize) -> Struct
2680 240..253 'Struct.test()': bool 2747 239..252 'Struct.test()': bool
2681 259..272 'Enum::Variant': Variant(usize) -> Enum 2748 258..271 'Enum::Variant': Variant(usize) -> Enum
2682 259..279 'Enum::...test()': bool 2749 258..278 'Enum::...test()': bool
2683 "### 2750 "###
2684 ); 2751 );
2685} 2752}
@@ -2701,17 +2768,17 @@ fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) {
2701} 2768}
2702"#, true), 2769"#, true),
2703 @r###" 2770 @r###"
2704 55..59 'self': &Self 2771 54..58 'self': &Self
2705 109..111 'f1': fn() 2772 108..110 'f1': fn()
2706 119..121 'f2': fn(usize) -> u8 2773 118..120 'f2': fn(usize) -> u8
2707 140..142 'f3': fn(u8, u8) -> &u8 2774 139..141 'f3': fn(u8, u8) -> &u8
2708 163..211 '{ ...t(); }': () 2775 162..210 '{ ...t(); }': ()
2709 169..171 'f1': fn() 2776 168..170 'f1': fn()
2710 169..178 'f1.test()': bool 2777 168..177 'f1.test()': bool
2711 184..186 'f2': fn(usize) -> u8 2778 183..185 'f2': fn(usize) -> u8
2712 184..193 'f2.test()': bool 2779 183..192 'f2.test()': bool
2713 199..201 'f3': fn(u8, u8) -> &u8 2780 198..200 'f3': fn(u8, u8) -> &u8
2714 199..208 'f3.test()': bool 2781 198..207 'f3.test()': bool
2715 "### 2782 "###
2716 ); 2783 );
2717} 2784}
@@ -2734,36 +2801,36 @@ fn test() {
2734} 2801}
2735"#, true), 2802"#, true),
2736 @r###" 2803 @r###"
2737 57..61 'self': &Self 2804 56..60 'self': &Self
2738 114..229 '{ ...ized }': () 2805 113..228 '{ ...ized }': ()
2739 120..123 '1u8': u8 2806 119..122 '1u8': u8
2740 120..130 '1u8.test()': bool 2807 119..129 '1u8.test()': bool
2741 136..151 '(*"foo").test()': {unknown} 2808 135..150 '(*"foo").test()': {unknown}
2742 137..143 '*"foo"': str 2809 136..142 '*"foo"': str
2743 138..143 '"foo"': &str 2810 137..142 '"foo"': &str
2744 170..180 '(1u8, 1u8)': (u8, u8) 2811 169..179 '(1u8, 1u8)': (u8, u8)
2745 170..187 '(1u8, ...test()': bool 2812 169..186 '(1u8, ...test()': bool
2746 171..174 '1u8': u8 2813 170..173 '1u8': u8
2747 176..179 '1u8': u8 2814 175..178 '1u8': u8
2748 193..206 '(1u8, *"foo")': (u8, str) 2815 192..205 '(1u8, *"foo")': (u8, str)
2749 193..213 '(1u8, ...test()': {unknown} 2816 192..212 '(1u8, ...test()': {unknown}
2750 194..197 '1u8': u8 2817 193..196 '1u8': u8
2751 199..205 '*"foo"': str 2818 198..204 '*"foo"': str
2752 200..205 '"foo"': &str 2819 199..204 '"foo"': &str
2753 "### 2820 "###
2754 ); 2821 );
2755} 2822}
2756 2823
2757#[test] 2824#[test]
2758fn integer_range_iterate() { 2825fn integer_range_iterate() {
2759 let t = type_at( 2826 check_types(
2760 r#" 2827 r#"
2761//- /main.rs crate:main deps:std 2828//- /main.rs crate:main deps:core
2762fn test() { 2829fn test() {
2763 for x in 0..100 { x<|>; } 2830 for x in 0..100 { x; }
2764} 2831} //^ i32
2765 2832
2766//- /std.rs crate:std 2833//- /core.rs crate:core
2767pub mod ops { 2834pub mod ops {
2768 pub struct Range<Idx> { 2835 pub struct Range<Idx> {
2769 pub start: Idx, 2836 pub start: Idx,
@@ -2796,5 +2863,235 @@ impl<A: Step> iter::Iterator for ops::Range<A> {
2796} 2863}
2797"#, 2864"#,
2798 ); 2865 );
2799 assert_eq!(t, "i32"); 2866}
2867
2868#[test]
2869fn infer_closure_arg() {
2870 assert_snapshot!(
2871 infer(
2872 r#"
2873 //- /lib.rs
2874
2875 enum Option<T> {
2876 None,
2877 Some(T)
2878 }
2879
2880 fn foo() {
2881 let s = Option::None;
2882 let f = |x: Option<i32>| {};
2883 (&f)(s)
2884 }
2885 "#
2886 ),
2887 @r###"
2888 52..126 '{ ...)(s) }': ()
2889 62..63 's': Option<i32>
2890 66..78 'Option::None': Option<i32>
2891 88..89 'f': |Option<i32>| -> ()
2892 92..111 '|x: Op...2>| {}': |Option<i32>| -> ()
2893 93..94 'x': Option<i32>
2894 109..111 '{}': ()
2895 117..124 '(&f)(s)': ()
2896 118..120 '&f': &|Option<i32>| -> ()
2897 119..120 'f': |Option<i32>| -> ()
2898 122..123 's': Option<i32>
2899 "###
2900 );
2901}
2902
2903#[test]
2904fn infer_fn_trait_arg() {
2905 assert_snapshot!(
2906 infer(
2907 r#"
2908 //- /lib.rs deps:std
2909
2910 #[lang = "fn_once"]
2911 pub trait FnOnce<Args> {
2912 type Output;
2913
2914 extern "rust-call" fn call_once(&self, args: Args) -> Self::Output;
2915 }
2916
2917 #[lang = "fn"]
2918 pub trait Fn<Args>:FnOnce<Args> {
2919 extern "rust-call" fn call(&self, args: Args) -> Self::Output;
2920 }
2921
2922 enum Option<T> {
2923 None,
2924 Some(T)
2925 }
2926
2927 fn foo<F, T>(f: F) -> T
2928 where
2929 F: Fn(Option<i32>) -> T,
2930 {
2931 let s = None;
2932 f(s)
2933 }
2934 "#
2935 ),
2936 @r###"
2937 101..105 'self': &Self
2938 107..111 'args': Args
2939 220..224 'self': &Self
2940 226..230 'args': Args
2941 313..314 'f': F
2942 359..389 '{ ...f(s) }': T
2943 369..370 's': Option<i32>
2944 373..377 'None': Option<i32>
2945 383..384 'f': F
2946 383..387 'f(s)': T
2947 385..386 's': Option<i32>
2948 "###
2949 );
2950}
2951
2952#[test]
2953fn infer_box_fn_arg() {
2954 assert_snapshot!(
2955 infer(
2956 r#"
2957 //- /lib.rs deps:std
2958
2959 #[lang = "fn_once"]
2960 pub trait FnOnce<Args> {
2961 type Output;
2962
2963 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
2964 }
2965
2966 #[lang = "deref"]
2967 pub trait Deref {
2968 type Target: ?Sized;
2969
2970 fn deref(&self) -> &Self::Target;
2971 }
2972
2973 #[lang = "owned_box"]
2974 pub struct Box<T: ?Sized> {
2975 inner: *mut T,
2976 }
2977
2978 impl<T: ?Sized> Deref for Box<T> {
2979 type Target = T;
2980
2981 fn deref(&self) -> &T {
2982 &self.inner
2983 }
2984 }
2985
2986 enum Option<T> {
2987 None,
2988 Some(T)
2989 }
2990
2991 fn foo() {
2992 let s = Option::None;
2993 let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {});
2994 f(&s)
2995 }
2996 "#
2997 ),
2998 @r###"
2999 100..104 'self': Self
3000 106..110 'args': Args
3001 214..218 'self': &Self
3002 384..388 'self': &Box<T>
3003 396..423 '{ ... }': &T
3004 406..417 '&self.inner': &*mut T
3005 407..411 'self': &Box<T>
3006 407..417 'self.inner': *mut T
3007 478..575 '{ ...(&s) }': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)>
3008 488..489 's': Option<i32>
3009 492..504 'Option::None': Option<i32>
3010 514..515 'f': Box<dyn FnOnce<(&Option<i32>,)>>
3011 549..562 'box (|ps| {})': Box<|{unknown}| -> ()>
3012 554..561 '|ps| {}': |{unknown}| -> ()
3013 555..557 'ps': {unknown}
3014 559..561 '{}': ()
3015 568..569 'f': Box<dyn FnOnce<(&Option<i32>,)>>
3016 568..573 'f(&s)': FnOnce::Output<dyn FnOnce<(&Option<i32>,)>, (&Option<i32>,)>
3017 570..572 '&s': &Option<i32>
3018 571..572 's': Option<i32>
3019 "###
3020 );
3021}
3022
3023#[test]
3024fn infer_dyn_fn_output() {
3025 check_types(
3026 r#"
3027#[lang = "fn_once"]
3028pub trait FnOnce<Args> {
3029 type Output;
3030 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
3031}
3032
3033#[lang = "fn"]
3034pub trait Fn<Args>: FnOnce<Args> {
3035 extern "rust-call" fn call(&self, args: Args) -> Self::Output;
3036}
3037
3038fn foo() {
3039 let f: &dyn Fn() -> i32;
3040 f();
3041 //^^^ i32
3042}"#,
3043 );
3044}
3045
3046#[test]
3047fn infer_dyn_fn_once_output() {
3048 check_types(
3049 r#"
3050#[lang = "fn_once"]
3051pub trait FnOnce<Args> {
3052 type Output;
3053 extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
3054}
3055
3056fn foo() {
3057 let f: dyn FnOnce() -> i32;
3058 f();
3059 //^^^ i32
3060}"#,
3061 );
3062}
3063
3064#[test]
3065fn variable_kinds_1() {
3066 check_types(
3067 r#"
3068trait Trait<T> { fn get(self, t: T) -> T; }
3069struct S;
3070impl Trait<u128> for S {}
3071impl Trait<f32> for S {}
3072fn test() {
3073 S.get(1);
3074 //^^^^^^^^ u128
3075 S.get(1.);
3076 //^^^^^^^^ f32
3077}
3078 "#,
3079 );
3080}
3081
3082#[test]
3083fn variable_kinds_2() {
3084 check_types(
3085 r#"
3086trait Trait { fn get(self) -> Self; }
3087impl Trait for u128 {}
3088impl Trait for f32 {}
3089fn test() {
3090 1.get();
3091 //^^^^^^^ u128
3092 (1.).get();
3093 //^^^^^^^^^^ f32
3094}
3095 "#,
3096 );
2800} 3097}
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs
index 6bc6d474c..3f6d2cf35 100644
--- a/crates/ra_hir_ty/src/traits.rs
+++ b/crates/ra_hir_ty/src/traits.rs
@@ -1,20 +1,19 @@
1//! Trait solving using Chalk. 1//! Trait solving using Chalk.
2use std::{panic, sync::Arc}; 2use std::sync::Arc;
3 3
4use chalk_ir::cast::Cast; 4use chalk_ir::cast::Cast;
5use hir_def::{expr::ExprId, DefWithBodyId, ImplId, TraitId, TypeAliasId}; 5use chalk_solve::Solver;
6use ra_db::{impl_intern_key, salsa, CrateId}; 6use hir_def::{lang_item::LangItemTarget, TraitId};
7use ra_db::CrateId;
7use ra_prof::profile; 8use ra_prof::profile;
8use rustc_hash::FxHashSet;
9 9
10use crate::{db::HirDatabase, method_resolution::TyFingerprint, DebruijnIndex}; 10use crate::{db::HirDatabase, DebruijnIndex, Substs};
11 11
12use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; 12use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};
13 13
14use self::chalk::{from_chalk, Interner, ToChalk}; 14use self::chalk::{from_chalk, Interner, ToChalk};
15 15
16pub(crate) mod chalk; 16pub(crate) mod chalk;
17mod builtin;
18 17
19// This controls the maximum size of types Chalk considers. If we set this too 18// This controls the maximum size of types Chalk considers. If we set this too
20// high, we can run into slow edge cases; if we set it too low, Chalk won't 19// high, we can run into slow edge cases; if we set it too low, Chalk won't
@@ -31,37 +30,10 @@ struct ChalkContext<'a> {
31 krate: CrateId, 30 krate: CrateId,
32} 31}
33 32
34fn create_chalk_solver() -> chalk_solve::Solver<Interner> { 33fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
35 let solver_choice = chalk_solve::SolverChoice::recursive(); 34 let overflow_depth = 100;
36 solver_choice.into_solver() 35 let caching_enabled = true;
37} 36 chalk_recursive::RecursiveSolver::new(overflow_depth, caching_enabled)
38
39/// Collects impls for the given trait in the whole dependency tree of `krate`.
40pub(crate) fn impls_for_trait_query(
41 db: &dyn HirDatabase,
42 krate: CrateId,
43 trait_: TraitId,
44 self_ty_fp: Option<TyFingerprint>,
45) -> Arc<[ImplId]> {
46 // FIXME: We could be a lot smarter here - because of the orphan rules and
47 // the fact that the trait and the self type need to be in the dependency
48 // tree of a crate somewhere for an impl to exist, we could skip looking in
49 // a lot of crates completely
50 let mut impls = FxHashSet::default();
51 // We call the query recursively here. On the one hand, this means we can
52 // reuse results from queries for different crates; on the other hand, this
53 // will only ever get called for a few crates near the root of the tree (the
54 // ones the user is editing), so this may actually be a waste of memory. I'm
55 // doing it like this mainly for simplicity for now.
56 for dep in &db.crate_graph()[krate].dependencies {
57 impls.extend(db.impls_for_trait(dep.crate_id, trait_, self_ty_fp).iter());
58 }
59 let crate_impl_defs = db.impls_in_crate(krate);
60 match self_ty_fp {
61 Some(fp) => impls.extend(crate_impl_defs.lookup_impl_defs_for_trait_and_ty(trait_, fp)),
62 None => impls.extend(crate_impl_defs.lookup_impl_defs_for_trait(trait_)),
63 }
64 impls.into_iter().collect()
65} 37}
66 38
67/// A set of clauses that we assume to be true. E.g. if we are inside this function: 39/// A set of clauses that we assume to be true. E.g. if we are inside this function:
@@ -217,15 +189,7 @@ fn solution_from_chalk(
217 solution: chalk_solve::Solution<Interner>, 189 solution: chalk_solve::Solution<Interner>,
218) -> Solution { 190) -> Solution {
219 let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<Interner>>| { 191 let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<Interner>>| {
220 let value = subst 192 let result = from_chalk(db, subst);
221 .value
222 .iter(&Interner)
223 .map(|p| match p.ty(&Interner) {
224 Some(ty) => from_chalk(db, ty.clone()),
225 None => unimplemented!(),
226 })
227 .collect();
228 let result = Canonical { value, num_vars: subst.binders.len(&Interner) };
229 SolutionVariables(result) 193 SolutionVariables(result)
230 }; 194 };
231 match solution { 195 match solution {
@@ -249,7 +213,7 @@ fn solution_from_chalk(
249} 213}
250 214
251#[derive(Clone, Debug, PartialEq, Eq)] 215#[derive(Clone, Debug, PartialEq, Eq)]
252pub struct SolutionVariables(pub Canonical<Vec<Ty>>); 216pub struct SolutionVariables(pub Canonical<Substs>);
253 217
254#[derive(Clone, Debug, PartialEq, Eq)] 218#[derive(Clone, Debug, PartialEq, Eq)]
255/// A (possible) solution for a proposed goal. 219/// A (possible) solution for a proposed goal.
@@ -298,53 +262,12 @@ impl FnTrait {
298 FnTrait::Fn => "fn", 262 FnTrait::Fn => "fn",
299 } 263 }
300 } 264 }
301}
302 265
303#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 266 pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
304pub struct ClosureFnTraitImplData { 267 let target = db.lang_item(krate, self.lang_item_name().into())?;
305 def: DefWithBodyId, 268 match target {
306 expr: ExprId, 269 LangItemTarget::TraitId(t) => Some(t),
307 fn_trait: FnTrait, 270 _ => None,
308} 271 }
309 272 }
310#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
311pub struct UnsizeToSuperTraitObjectData {
312 trait_: TraitId,
313 super_trait: TraitId,
314}
315
316/// An impl. Usually this comes from an impl block, but some built-in types get
317/// synthetic impls.
318#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
319pub enum Impl {
320 /// A normal impl from an impl block.
321 ImplDef(ImplId),
322 /// Closure types implement the Fn traits synthetically.
323 ClosureFnTraitImpl(ClosureFnTraitImplData),
324 /// [T; n]: Unsize<[T]>
325 UnsizeArray,
326 /// T: Unsize<dyn Trait> where T: Trait
327 UnsizeToTraitObject(TraitId),
328 /// dyn Trait: Unsize<dyn SuperTrait> if Trait: SuperTrait
329 UnsizeToSuperTraitObject(UnsizeToSuperTraitObjectData),
330}
331/// This exists just for Chalk, because our ImplIds are only unique per module.
332#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
333pub struct GlobalImplId(salsa::InternId);
334impl_intern_key!(GlobalImplId);
335
336/// An associated type value. Usually this comes from a `type` declaration
337/// inside an impl block, but for built-in impls we have to synthesize it.
338/// (We only need this because Chalk wants a unique ID for each of these.)
339#[derive(Debug, Clone, PartialEq, Eq, Hash)]
340pub enum AssocTyValue {
341 /// A normal assoc type value from an impl block.
342 TypeAlias(TypeAliasId),
343 /// The output type of the Fn trait implementation.
344 ClosureFnTraitImplOutput(ClosureFnTraitImplData),
345} 273}
346/// This exists just for Chalk, because it needs a unique ID for each associated
347/// type value in an impl (even synthetic ones).
348#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
349pub struct AssocTyValueId(salsa::InternId);
350impl_intern_key!(AssocTyValueId);
diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs
deleted file mode 100644
index 88a422d2c..000000000
--- a/crates/ra_hir_ty/src/traits/builtin.rs
+++ /dev/null
@@ -1,377 +0,0 @@
1//! This module provides the built-in trait implementations, e.g. to make
2//! closures implement `Fn`.
3use hir_def::{expr::Expr, lang_item::LangItemTarget, TraitId, TypeAliasId};
4use hir_expand::name::name;
5use ra_db::CrateId;
6
7use super::{AssocTyValue, Impl, UnsizeToSuperTraitObjectData};
8use crate::{
9 db::HirDatabase,
10 utils::{all_super_traits, generics},
11 ApplicationTy, Binders, BoundVar, DebruijnIndex, GenericPredicate, Substs, TraitRef, Ty,
12 TypeCtor, TypeWalk,
13};
14
15pub(super) struct BuiltinImplData {
16 pub num_vars: usize,
17 pub trait_ref: TraitRef,
18 pub where_clauses: Vec<super::GenericPredicate>,
19 pub assoc_ty_values: Vec<AssocTyValue>,
20}
21
22pub(super) struct BuiltinImplAssocTyValueData {
23 pub impl_: Impl,
24 pub assoc_ty_id: TypeAliasId,
25 pub num_vars: usize,
26 pub value: Ty,
27}
28
29pub(super) fn get_builtin_impls(
30 db: &dyn HirDatabase,
31 krate: CrateId,
32 ty: &Ty,
33 // The first argument for the trait, if present
34 arg: &Option<Ty>,
35 trait_: TraitId,
36 mut callback: impl FnMut(Impl),
37) {
38 // Note: since impl_datum needs to be infallible, we need to make sure here
39 // that we have all prerequisites to build the respective impls.
40 if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Closure { def, expr }, .. }) = ty {
41 for &fn_trait in [super::FnTrait::FnOnce, super::FnTrait::FnMut, super::FnTrait::Fn].iter()
42 {
43 if let Some(actual_trait) = get_fn_trait(db, krate, fn_trait) {
44 if trait_ == actual_trait {
45 let impl_ = super::ClosureFnTraitImplData { def: *def, expr: *expr, fn_trait };
46 if check_closure_fn_trait_impl_prerequisites(db, krate, impl_) {
47 callback(Impl::ClosureFnTraitImpl(impl_));
48 }
49 }
50 }
51 }
52 }
53
54 let unsize_trait = get_unsize_trait(db, krate);
55 if let Some(actual_trait) = unsize_trait {
56 if trait_ == actual_trait {
57 get_builtin_unsize_impls(db, krate, ty, arg, callback);
58 }
59 }
60}
61
62fn get_builtin_unsize_impls(
63 db: &dyn HirDatabase,
64 krate: CrateId,
65 ty: &Ty,
66 // The first argument for the trait, if present
67 arg: &Option<Ty>,
68 mut callback: impl FnMut(Impl),
69) {
70 if !check_unsize_impl_prerequisites(db, krate) {
71 return;
72 }
73
74 if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Array, .. }) = ty {
75 callback(Impl::UnsizeArray);
76 return; // array is unsized, the rest of the impls shouldn't apply
77 }
78
79 if let Some(target_trait) = arg.as_ref().and_then(|t| t.dyn_trait_ref()) {
80 // FIXME what about more complicated dyn tys with marker traits?
81 if let Some(trait_ref) = ty.dyn_trait_ref() {
82 if trait_ref.trait_ != target_trait.trait_ {
83 let super_traits = all_super_traits(db.upcast(), trait_ref.trait_);
84 if super_traits.contains(&target_trait.trait_) {
85 callback(Impl::UnsizeToSuperTraitObject(UnsizeToSuperTraitObjectData {
86 trait_: trait_ref.trait_,
87 super_trait: target_trait.trait_,
88 }));
89 }
90 }
91 } else {
92 // FIXME only for sized types
93 callback(Impl::UnsizeToTraitObject(target_trait.trait_));
94 }
95 }
96}
97
98pub(super) fn impl_datum(db: &dyn HirDatabase, krate: CrateId, impl_: Impl) -> BuiltinImplData {
99 match impl_ {
100 Impl::ImplDef(_) => unreachable!(),
101 Impl::ClosureFnTraitImpl(data) => closure_fn_trait_impl_datum(db, krate, data),
102 Impl::UnsizeArray => array_unsize_impl_datum(db, krate),
103 Impl::UnsizeToTraitObject(trait_) => trait_object_unsize_impl_datum(db, krate, trait_),
104 Impl::UnsizeToSuperTraitObject(data) => {
105 super_trait_object_unsize_impl_datum(db, krate, data)
106 }
107 }
108}
109
110pub(super) fn associated_ty_value(
111 db: &dyn HirDatabase,
112 krate: CrateId,
113 data: AssocTyValue,
114) -> BuiltinImplAssocTyValueData {
115 match data {
116 AssocTyValue::TypeAlias(_) => unreachable!(),
117 AssocTyValue::ClosureFnTraitImplOutput(data) => {
118 closure_fn_trait_output_assoc_ty_value(db, krate, data)
119 }
120 }
121}
122
123// Closure Fn trait impls
124
125fn check_closure_fn_trait_impl_prerequisites(
126 db: &dyn HirDatabase,
127 krate: CrateId,
128 data: super::ClosureFnTraitImplData,
129) -> bool {
130 // the respective Fn/FnOnce/FnMut trait needs to exist
131 if get_fn_trait(db, krate, data.fn_trait).is_none() {
132 return false;
133 }
134
135 // FIXME: there are more assumptions that we should probably check here:
136 // the traits having no type params, FnOnce being a supertrait
137
138 // the FnOnce trait needs to exist and have an assoc type named Output
139 let fn_once_trait = match get_fn_trait(db, krate, super::FnTrait::FnOnce) {
140 Some(t) => t,
141 None => return false,
142 };
143 db.trait_data(fn_once_trait).associated_type_by_name(&name![Output]).is_some()
144}
145
146fn closure_fn_trait_impl_datum(
147 db: &dyn HirDatabase,
148 krate: CrateId,
149 data: super::ClosureFnTraitImplData,
150) -> BuiltinImplData {
151 // for some closure |X, Y| -> Z:
152 // impl<T, U, V> Fn<(T, U)> for closure<fn(T, U) -> V> { Output = V }
153
154 let trait_ = get_fn_trait(db, krate, data.fn_trait) // get corresponding fn trait
155 // the existence of the Fn trait has been checked before
156 .expect("fn trait for closure impl missing");
157
158 let num_args: u16 = match &db.body(data.def)[data.expr] {
159 Expr::Lambda { args, .. } => args.len() as u16,
160 _ => {
161 log::warn!("closure for closure type {:?} not found", data);
162 0
163 }
164 };
165
166 let arg_ty = Ty::apply(
167 TypeCtor::Tuple { cardinality: num_args },
168 Substs::builder(num_args as usize)
169 .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0)
170 .build(),
171 );
172 let sig_ty = Ty::apply(
173 TypeCtor::FnPtr { num_args },
174 Substs::builder(num_args as usize + 1)
175 .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0)
176 .build(),
177 );
178
179 let self_ty = Ty::apply_one(TypeCtor::Closure { def: data.def, expr: data.expr }, sig_ty);
180
181 let trait_ref = TraitRef {
182 trait_,
183 substs: Substs::build_for_def(db, trait_).push(self_ty).push(arg_ty).build(),
184 };
185
186 let output_ty_id = AssocTyValue::ClosureFnTraitImplOutput(data);
187
188 BuiltinImplData {
189 num_vars: num_args as usize + 1,
190 trait_ref,
191 where_clauses: Vec::new(),
192 assoc_ty_values: vec![output_ty_id],
193 }
194}
195
196fn closure_fn_trait_output_assoc_ty_value(
197 db: &dyn HirDatabase,
198 krate: CrateId,
199 data: super::ClosureFnTraitImplData,
200) -> BuiltinImplAssocTyValueData {
201 let impl_ = Impl::ClosureFnTraitImpl(data);
202
203 let num_args: u16 = match &db.body(data.def)[data.expr] {
204 Expr::Lambda { args, .. } => args.len() as u16,
205 _ => {
206 log::warn!("closure for closure type {:?} not found", data);
207 0
208 }
209 };
210
211 let output_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, num_args.into()));
212
213 let fn_once_trait =
214 get_fn_trait(db, krate, super::FnTrait::FnOnce).expect("assoc ty value should not exist");
215
216 let output_ty_id = db
217 .trait_data(fn_once_trait)
218 .associated_type_by_name(&name![Output])
219 .expect("assoc ty value should not exist");
220
221 BuiltinImplAssocTyValueData {
222 impl_,
223 assoc_ty_id: output_ty_id,
224 num_vars: num_args as usize + 1,
225 value: output_ty,
226 }
227}
228
229// Array unsizing
230
231fn check_unsize_impl_prerequisites(db: &dyn HirDatabase, krate: CrateId) -> bool {
232 // the Unsize trait needs to exist and have two type parameters (Self and T)
233 let unsize_trait = match get_unsize_trait(db, krate) {
234 Some(t) => t,
235 None => return false,
236 };
237 let generic_params = generics(db.upcast(), unsize_trait.into());
238 generic_params.len() == 2
239}
240
241fn array_unsize_impl_datum(db: &dyn HirDatabase, krate: CrateId) -> BuiltinImplData {
242 // impl<T> Unsize<[T]> for [T; _]
243 // (this can be a single impl because we don't distinguish array sizes currently)
244
245 let trait_ = get_unsize_trait(db, krate) // get unsize trait
246 // the existence of the Unsize trait has been checked before
247 .expect("Unsize trait missing");
248
249 let var = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
250 let substs = Substs::builder(2)
251 .push(Ty::apply_one(TypeCtor::Array, var.clone()))
252 .push(Ty::apply_one(TypeCtor::Slice, var))
253 .build();
254
255 let trait_ref = TraitRef { trait_, substs };
256
257 BuiltinImplData {
258 num_vars: 1,
259 trait_ref,
260 where_clauses: Vec::new(),
261 assoc_ty_values: Vec::new(),
262 }
263}
264
265// Trait object unsizing
266
267fn trait_object_unsize_impl_datum(
268 db: &dyn HirDatabase,
269 krate: CrateId,
270 trait_: TraitId,
271) -> BuiltinImplData {
272 // impl<T, T1, ...> Unsize<dyn Trait<T1, ...>> for T where T: Trait<T1, ...>
273
274 let unsize_trait = get_unsize_trait(db, krate) // get unsize trait
275 // the existence of the Unsize trait has been checked before
276 .expect("Unsize trait missing");
277
278 let self_ty = Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0));
279
280 let target_substs = Substs::build_for_def(db, trait_)
281 .push(Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)))
282 .fill_with_bound_vars(DebruijnIndex::ONE, 1)
283 .build();
284 let num_vars = target_substs.len();
285 let target_trait_ref = TraitRef { trait_, substs: target_substs };
286 let target_bounds = vec![GenericPredicate::Implemented(target_trait_ref)];
287
288 let self_substs =
289 Substs::build_for_def(db, trait_).fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build();
290 let self_trait_ref = TraitRef { trait_, substs: self_substs };
291 let where_clauses = vec![GenericPredicate::Implemented(self_trait_ref)];
292
293 let impl_substs = Substs::builder(2).push(self_ty).push(Ty::Dyn(target_bounds.into())).build();
294
295 let trait_ref = TraitRef { trait_: unsize_trait, substs: impl_substs };
296
297 BuiltinImplData { num_vars, trait_ref, where_clauses, assoc_ty_values: Vec::new() }
298}
299
300fn super_trait_object_unsize_impl_datum(
301 db: &dyn HirDatabase,
302 krate: CrateId,
303 data: UnsizeToSuperTraitObjectData,
304) -> BuiltinImplData {
305 // impl<T1, ...> Unsize<dyn SuperTrait> for dyn Trait<T1, ...>
306
307 let unsize_trait = get_unsize_trait(db, krate) // get unsize trait
308 // the existence of the Unsize trait has been checked before
309 .expect("Unsize trait missing");
310
311 let self_substs = Substs::build_for_def(db, data.trait_)
312 .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0)
313 .build();
314 let self_trait_ref = TraitRef { trait_: data.trait_, substs: self_substs.clone() };
315
316 let num_vars = self_substs.len() - 1;
317
318 // we need to go from our trait to the super trait, substituting type parameters
319 let path = crate::utils::find_super_trait_path(db.upcast(), data.trait_, data.super_trait);
320
321 let mut current_trait_ref = self_trait_ref.clone();
322 for t in path.into_iter().skip(1) {
323 let bounds = db.generic_predicates(current_trait_ref.trait_.into());
324 let super_trait_ref = bounds
325 .iter()
326 .find_map(|b| match &b.value {
327 GenericPredicate::Implemented(tr)
328 if tr.trait_ == t
329 && tr.substs[0]
330 == Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)) =>
331 {
332 Some(Binders { value: tr, num_binders: b.num_binders })
333 }
334 _ => None,
335 })
336 .expect("trait bound for known super trait not found");
337 current_trait_ref = super_trait_ref.cloned().subst(&current_trait_ref.substs);
338 }
339
340 // We need to renumber the variables a bit now: from ^0.0, ^0.1, ^0.2, ...
341 // to ^0.0, ^1.0, ^1.1. The reason for this is that the first variable comes
342 // from the dyn Trait binder, while the other variables come from the impl.
343 let new_substs = Substs::builder(num_vars + 1)
344 .push(Ty::Bound(BoundVar::new(DebruijnIndex::INNERMOST, 0)))
345 .fill_with_bound_vars(DebruijnIndex::ONE, 0)
346 .build();
347
348 let self_bounds =
349 vec![GenericPredicate::Implemented(self_trait_ref.subst_bound_vars(&new_substs))];
350 let super_bounds =
351 vec![GenericPredicate::Implemented(current_trait_ref.subst_bound_vars(&new_substs))];
352
353 let substs = Substs::builder(2)
354 .push(Ty::Dyn(self_bounds.into()))
355 .push(Ty::Dyn(super_bounds.into()))
356 .build();
357
358 let trait_ref = TraitRef { trait_: unsize_trait, substs };
359
360 BuiltinImplData { num_vars, trait_ref, where_clauses: Vec::new(), assoc_ty_values: Vec::new() }
361}
362
363fn get_fn_trait(db: &dyn HirDatabase, krate: CrateId, fn_trait: super::FnTrait) -> Option<TraitId> {
364 let target = db.lang_item(krate, fn_trait.lang_item_name().into())?;
365 match target {
366 LangItemTarget::TraitId(t) => Some(t),
367 _ => None,
368 }
369}
370
371fn get_unsize_trait(db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
372 let target = db.lang_item(krate, "unsize".into())?;
373 match target {
374 LangItemTarget::TraitId(t) => Some(t),
375 _ => None,
376 }
377}
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index 61de3cc30..78d0bc43b 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -3,8 +3,8 @@ use std::sync::Arc;
3 3
4use log::debug; 4use log::debug;
5 5
6use chalk_ir::{fold::shift::Shift, GenericArg, TypeName}; 6use chalk_ir::{fold::shift::Shift, CanonicalVarKinds, GenericArg, TypeName};
7use chalk_solve::rust_ir::{self, WellKnownTrait}; 7use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
8 8
9use hir_def::{ 9use hir_def::{
10 lang_item::{lang_attr, LangItemTarget}, 10 lang_item::{lang_attr, LangItemTarget},
@@ -12,12 +12,17 @@ use hir_def::{
12}; 12};
13use ra_db::{salsa::InternKey, CrateId}; 13use ra_db::{salsa::InternKey, CrateId};
14 14
15use super::{builtin, AssocTyValue, ChalkContext, Impl}; 15use super::ChalkContext;
16use crate::{ 16use crate::{
17 db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics, 17 db::HirDatabase,
18 CallableDef, DebruijnIndex, GenericPredicate, Substs, Ty, TypeCtor, 18 display::HirDisplay,
19 method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
20 utils::generics,
21 CallableDefId, DebruijnIndex, FnSig, GenericPredicate, Substs, Ty, TypeCtor,
22};
23use mapping::{
24 convert_where_clauses, generic_predicate_to_inline_bound, make_binders, TypeAliasAsValue,
19}; 25};
20use mapping::{convert_where_clauses, generic_predicate_to_inline_bound, make_binders};
21 26
22pub use self::interner::*; 27pub use self::interner::*;
23 28
@@ -48,6 +53,9 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
48 fn adt_datum(&self, struct_id: AdtId) -> Arc<StructDatum> { 53 fn adt_datum(&self, struct_id: AdtId) -> Arc<StructDatum> {
49 self.db.struct_datum(self.krate, struct_id) 54 self.db.struct_datum(self.krate, struct_id)
50 } 55 }
56 fn adt_repr(&self, _struct_id: AdtId) -> rust_ir::AdtRepr {
57 unreachable!()
58 }
51 fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> { 59 fn impl_datum(&self, impl_id: ImplId) -> Arc<ImplDatum> {
52 self.db.impl_datum(self.krate, impl_id) 60 self.db.impl_datum(self.krate, impl_id)
53 } 61 }
@@ -63,32 +71,57 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
63 &self, 71 &self,
64 trait_id: TraitId, 72 trait_id: TraitId,
65 parameters: &[GenericArg<Interner>], 73 parameters: &[GenericArg<Interner>],
74 binders: &CanonicalVarKinds<Interner>,
66 ) -> Vec<ImplId> { 75 ) -> Vec<ImplId> {
67 debug!("impls_for_trait {:?}", trait_id); 76 debug!("impls_for_trait {:?}", trait_id);
68 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); 77 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id);
69 78
70 let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref(&Interner).clone()); 79 let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref(&Interner).clone());
71 80
81 fn binder_kind(ty: &Ty, binders: &CanonicalVarKinds<Interner>) -> Option<chalk_ir::TyKind> {
82 if let Ty::Bound(bv) = ty {
83 let binders = binders.as_slice(&Interner);
84 if bv.debruijn == DebruijnIndex::INNERMOST {
85 if let chalk_ir::VariableKind::Ty(tk) = binders[bv.index].kind {
86 return Some(tk);
87 }
88 }
89 }
90 None
91 }
92
72 let self_ty_fp = TyFingerprint::for_impl(&ty); 93 let self_ty_fp = TyFingerprint::for_impl(&ty);
94 let fps: &[TyFingerprint] = match binder_kind(&ty, binders) {
95 Some(chalk_ir::TyKind::Integer) => &ALL_INT_FPS,
96 Some(chalk_ir::TyKind::Float) => &ALL_FLOAT_FPS,
97 _ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
98 };
73 99
74 // Note: Since we're using impls_for_trait, only impls where the trait 100 // Note: Since we're using impls_for_trait, only impls where the trait
75 // can be resolved should ever reach Chalk. `impl_datum` relies on that 101 // can be resolved should ever reach Chalk. `impl_datum` relies on that
76 // and will panic if the trait can't be resolved. 102 // and will panic if the trait can't be resolved.
77 let mut result: Vec<_> = self 103 let in_deps = self.db.trait_impls_in_deps(self.krate);
78 .db 104 let in_self = self.db.trait_impls_in_crate(self.krate);
79 .impls_for_trait(self.krate, trait_, self_ty_fp) 105 let impl_maps = [in_deps, in_self];
80 .iter() 106
81 .copied() 107 let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
82 .map(Impl::ImplDef) 108
83 .map(|impl_| impl_.to_chalk(self.db)) 109 let result: Vec<_> = if fps.is_empty() {
84 .collect(); 110 debug!("Unrestricted search for {:?} impls...", trait_);
85 111 impl_maps
86 let arg: Option<Ty> = 112 .iter()
87 parameters.get(1).map(|p| from_chalk(self.db, p.assert_ty_ref(&Interner).clone())); 113 .flat_map(|crate_impl_defs| crate_impl_defs.for_trait(trait_).map(id_to_chalk))
88 114 .collect()
89 builtin::get_builtin_impls(self.db, self.krate, &ty, &arg, trait_, |i| { 115 } else {
90 result.push(i.to_chalk(self.db)) 116 impl_maps
91 }); 117 .iter()
118 .flat_map(|crate_impl_defs| {
119 fps.iter().flat_map(move |fp| {
120 crate_impl_defs.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
121 })
122 })
123 .collect()
124 };
92 125
93 debug!("impls_for_trait returned {} impls", result.len()); 126 debug!("impls_for_trait returned {} impls", result.len());
94 result 127 result
@@ -100,6 +133,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
100 fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> { 133 fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc<AssociatedTyValue> {
101 self.db.associated_ty_value(self.krate, id) 134 self.db.associated_ty_value(self.krate, id)
102 } 135 }
136
103 fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> { 137 fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<Interner>> {
104 vec![] 138 vec![]
105 } 139 }
@@ -115,8 +149,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
115 well_known_trait: rust_ir::WellKnownTrait, 149 well_known_trait: rust_ir::WellKnownTrait,
116 ) -> Option<chalk_ir::TraitId<Interner>> { 150 ) -> Option<chalk_ir::TraitId<Interner>> {
117 let lang_attr = lang_attr_from_well_known_trait(well_known_trait); 151 let lang_attr = lang_attr_from_well_known_trait(well_known_trait);
118 let lang_items = self.db.crate_lang_items(self.krate); 152 let trait_ = match self.db.lang_item(self.krate, lang_attr.into()) {
119 let trait_ = match lang_items.target(lang_attr) {
120 Some(LangItemTarget::TraitId(trait_)) => trait_, 153 Some(LangItemTarget::TraitId(trait_)) => trait_,
121 _ => return None, 154 _ => return None,
122 }; 155 };
@@ -130,11 +163,34 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
130 self.db.program_clauses_for_chalk_env(self.krate, environment.clone()) 163 self.db.program_clauses_for_chalk_env(self.krate, environment.clone())
131 } 164 }
132 165
133 fn opaque_ty_data( 166 fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId<Interner>) -> Arc<OpaqueTyDatum> {
134 &self, 167 let interned_id = crate::db::InternedOpaqueTyId::from(id);
135 _id: chalk_ir::OpaqueTyId<Interner>, 168 let full_id = self.db.lookup_intern_impl_trait_id(interned_id);
136 ) -> Arc<rust_ir::OpaqueTyDatum<Interner>> { 169 let (func, idx) = match full_id {
137 unimplemented!() 170 crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => (func, idx),
171 };
172 let datas =
173 self.db.return_type_impl_traits(func).expect("impl trait id without impl traits");
174 let data = &datas.value.impl_traits[idx as usize];
175 let bound = OpaqueTyDatumBound {
176 bounds: make_binders(
177 data.bounds
178 .value
179 .iter()
180 .cloned()
181 .filter(|b| !b.is_error())
182 .map(|b| b.to_chalk(self.db))
183 .collect(),
184 1,
185 ),
186 };
187 let num_vars = datas.num_binders;
188 Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound: make_binders(bound, num_vars) })
189 }
190
191 fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> {
192 // FIXME: actually provide the hidden type; it is relevant for auto traits
193 Ty::Unknown.to_chalk(self.db)
138 } 194 }
139 195
140 fn force_impl_for( 196 fn force_impl_for(
@@ -151,8 +207,61 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
151 true 207 true
152 } 208 }
153 209
154 fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId<Interner>) -> chalk_ir::Ty<Interner> { 210 fn closure_kind(
155 Ty::Unknown.to_chalk(self.db) 211 &self,
212 _closure_id: chalk_ir::ClosureId<Interner>,
213 _substs: &chalk_ir::Substitution<Interner>,
214 ) -> rust_ir::ClosureKind {
215 // Fn is the closure kind that implements all three traits
216 rust_ir::ClosureKind::Fn
217 }
218 fn closure_inputs_and_output(
219 &self,
220 _closure_id: chalk_ir::ClosureId<Interner>,
221 substs: &chalk_ir::Substitution<Interner>,
222 ) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> {
223 let sig_ty: Ty =
224 from_chalk(self.db, substs.at(&Interner, 0).assert_ty_ref(&Interner).clone());
225 let sig = FnSig::from_fn_ptr_substs(
226 &sig_ty.substs().expect("first closure param should be fn ptr"),
227 false,
228 );
229 let io = rust_ir::FnDefInputsAndOutputDatum {
230 argument_types: sig.params().iter().map(|ty| ty.clone().to_chalk(self.db)).collect(),
231 return_type: sig.ret().clone().to_chalk(self.db),
232 };
233 make_binders(io.shifted_in(&Interner), 0)
234 }
235 fn closure_upvars(
236 &self,
237 _closure_id: chalk_ir::ClosureId<Interner>,
238 _substs: &chalk_ir::Substitution<Interner>,
239 ) -> chalk_ir::Binders<chalk_ir::Ty<Interner>> {
240 let ty = Ty::unit().to_chalk(self.db);
241 make_binders(ty, 0)
242 }
243 fn closure_fn_substitution(
244 &self,
245 _closure_id: chalk_ir::ClosureId<Interner>,
246 _substs: &chalk_ir::Substitution<Interner>,
247 ) -> chalk_ir::Substitution<Interner> {
248 Substs::empty().to_chalk(self.db)
249 }
250
251 fn trait_name(&self, _trait_id: chalk_ir::TraitId<Interner>) -> String {
252 unimplemented!()
253 }
254 fn adt_name(&self, _struct_id: chalk_ir::AdtId<Interner>) -> String {
255 unimplemented!()
256 }
257 fn assoc_type_name(&self, _assoc_ty_id: chalk_ir::AssocTypeId<Interner>) -> String {
258 unimplemented!()
259 }
260 fn opaque_type_name(&self, _opaque_ty_id: chalk_ir::OpaqueTyId<Interner>) -> String {
261 unimplemented!()
262 }
263 fn fn_def_name(&self, _fn_def_id: chalk_ir::FnDefId<Interner>) -> String {
264 unimplemented!()
156 } 265 }
157} 266}
158 267
@@ -218,7 +327,7 @@ pub(crate) fn trait_datum_query(
218 upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate, 327 upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate,
219 non_enumerable: true, 328 non_enumerable: true,
220 coinductive: false, // only relevant for Chalk testing 329 coinductive: false, // only relevant for Chalk testing
221 // FIXME set these flags correctly 330 // FIXME: set these flags correctly
222 marker: false, 331 marker: false,
223 fundamental: false, 332 fundamental: false,
224 }; 333 };
@@ -240,20 +349,28 @@ pub(crate) fn trait_datum_query(
240 349
241fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> { 350fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> {
242 Some(match name { 351 Some(match name {
243 "sized" => WellKnownTrait::SizedTrait, 352 "sized" => WellKnownTrait::Sized,
244 "copy" => WellKnownTrait::CopyTrait, 353 "copy" => WellKnownTrait::Copy,
245 "clone" => WellKnownTrait::CloneTrait, 354 "clone" => WellKnownTrait::Clone,
246 "drop" => WellKnownTrait::DropTrait, 355 "drop" => WellKnownTrait::Drop,
356 "fn_once" => WellKnownTrait::FnOnce,
357 "fn_mut" => WellKnownTrait::FnMut,
358 "fn" => WellKnownTrait::Fn,
359 "unsize" => WellKnownTrait::Unsize,
247 _ => return None, 360 _ => return None,
248 }) 361 })
249} 362}
250 363
251fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str { 364fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str {
252 match attr { 365 match attr {
253 WellKnownTrait::SizedTrait => "sized", 366 WellKnownTrait::Sized => "sized",
254 WellKnownTrait::CopyTrait => "copy", 367 WellKnownTrait::Copy => "copy",
255 WellKnownTrait::CloneTrait => "clone", 368 WellKnownTrait::Clone => "clone",
256 WellKnownTrait::DropTrait => "drop", 369 WellKnownTrait::Drop => "drop",
370 WellKnownTrait::FnOnce => "fn_once",
371 WellKnownTrait::FnMut => "fn_mut",
372 WellKnownTrait::Fn => "fn",
373 WellKnownTrait::Unsize => "unsize",
257 } 374 }
258} 375}
259 376
@@ -277,15 +394,22 @@ pub(crate) fn struct_datum_query(
277 .unwrap_or_else(Vec::new); 394 .unwrap_or_else(Vec::new);
278 let flags = rust_ir::AdtFlags { 395 let flags = rust_ir::AdtFlags {
279 upstream, 396 upstream,
280 // FIXME set fundamental flag correctly 397 // FIXME set fundamental and phantom_data flags correctly
281 fundamental: false, 398 fundamental: false,
399 phantom_data: false,
282 }; 400 };
283 let struct_datum_bound = rust_ir::AdtDatumBound { 401 // FIXME provide enum variants properly (for auto traits)
284 fields: Vec::new(), // FIXME add fields (only relevant for auto traits) 402 let variant = rust_ir::AdtVariantDatum {
285 where_clauses, 403 fields: Vec::new(), // FIXME add fields (only relevant for auto traits),
404 };
405 let struct_datum_bound = rust_ir::AdtDatumBound { variants: vec![variant], where_clauses };
406 let struct_datum = StructDatum {
407 // FIXME set ADT kind
408 kind: rust_ir::AdtKind::Struct,
409 id: struct_id,
410 binders: make_binders(struct_datum_bound, num_params),
411 flags,
286 }; 412 };
287 let struct_datum =
288 StructDatum { id: struct_id, binders: make_binders(struct_datum_bound, num_params), flags };
289 Arc::new(struct_datum) 413 Arc::new(struct_datum)
290} 414}
291 415
@@ -296,11 +420,8 @@ pub(crate) fn impl_datum_query(
296) -> Arc<ImplDatum> { 420) -> Arc<ImplDatum> {
297 let _p = ra_prof::profile("impl_datum"); 421 let _p = ra_prof::profile("impl_datum");
298 debug!("impl_datum {:?}", impl_id); 422 debug!("impl_datum {:?}", impl_id);
299 let impl_: Impl = from_chalk(db, impl_id); 423 let impl_: hir_def::ImplId = from_chalk(db, impl_id);
300 match impl_ { 424 impl_def_datum(db, krate, impl_id, impl_)
301 Impl::ImplDef(impl_def) => impl_def_datum(db, krate, impl_id, impl_def),
302 _ => Arc::new(builtin::impl_datum(db, krate, impl_).to_chalk(db)),
303 }
304} 425}
305 426
306fn impl_def_datum( 427fn impl_def_datum(
@@ -351,7 +472,7 @@ fn impl_def_datum(
351 let name = &db.type_alias_data(type_alias).name; 472 let name = &db.type_alias_data(type_alias).name;
352 trait_data.associated_type_by_name(name).is_some() 473 trait_data.associated_type_by_name(name).is_some()
353 }) 474 })
354 .map(|type_alias| AssocTyValue::TypeAlias(type_alias).to_chalk(db)) 475 .map(|type_alias| TypeAliasAsValue(type_alias).to_chalk(db))
355 .collect(); 476 .collect();
356 debug!("impl_datum: {:?}", impl_datum_bound); 477 debug!("impl_datum: {:?}", impl_datum_bound);
357 let impl_datum = ImplDatum { 478 let impl_datum = ImplDatum {
@@ -368,13 +489,8 @@ pub(crate) fn associated_ty_value_query(
368 krate: CrateId, 489 krate: CrateId,
369 id: AssociatedTyValueId, 490 id: AssociatedTyValueId,
370) -> Arc<AssociatedTyValue> { 491) -> Arc<AssociatedTyValue> {
371 let data: AssocTyValue = from_chalk(db, id); 492 let type_alias: TypeAliasAsValue = from_chalk(db, id);
372 match data { 493 type_alias_associated_ty_value(db, krate, type_alias.0)
373 AssocTyValue::TypeAlias(type_alias) => {
374 type_alias_associated_ty_value(db, krate, type_alias)
375 }
376 _ => Arc::new(builtin::associated_ty_value(db, krate, data).to_chalk(db)),
377 }
378} 494}
379 495
380fn type_alias_associated_ty_value( 496fn type_alias_associated_ty_value(
@@ -397,7 +513,7 @@ fn type_alias_associated_ty_value(
397 let ty = db.ty(type_alias.into()); 513 let ty = db.ty(type_alias.into());
398 let value_bound = rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) }; 514 let value_bound = rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) };
399 let value = rust_ir::AssociatedTyValue { 515 let value = rust_ir::AssociatedTyValue {
400 impl_id: Impl::ImplDef(impl_id).to_chalk(db), 516 impl_id: impl_id.to_chalk(db),
401 associated_ty_id: assoc_ty.to_chalk(db), 517 associated_ty_id: assoc_ty.to_chalk(db),
402 value: make_binders(value_bound, ty.num_binders), 518 value: make_binders(value_bound, ty.num_binders),
403 }; 519 };
@@ -409,65 +525,65 @@ pub(crate) fn fn_def_datum_query(
409 _krate: CrateId, 525 _krate: CrateId,
410 fn_def_id: FnDefId, 526 fn_def_id: FnDefId,
411) -> Arc<FnDefDatum> { 527) -> Arc<FnDefDatum> {
412 let callable_def: CallableDef = from_chalk(db, fn_def_id); 528 let callable_def: CallableDefId = from_chalk(db, fn_def_id);
413 let generic_params = generics(db.upcast(), callable_def.into()); 529 let generic_params = generics(db.upcast(), callable_def.into());
414 let sig = db.callable_item_signature(callable_def); 530 let sig = db.callable_item_signature(callable_def);
415 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); 531 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
416 let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars); 532 let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars);
417 let bound = rust_ir::FnDefDatumBound { 533 let bound = rust_ir::FnDefDatumBound {
418 // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway 534 // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway
419 argument_types: sig.value.params().iter().map(|ty| ty.clone().to_chalk(db)).collect(), 535 inputs_and_output: make_binders(
420 return_type: sig.value.ret().clone().to_chalk(db), 536 rust_ir::FnDefInputsAndOutputDatum {
537 argument_types: sig
538 .value
539 .params()
540 .iter()
541 .map(|ty| ty.clone().to_chalk(db))
542 .collect(),
543 return_type: sig.value.ret().clone().to_chalk(db),
544 }
545 .shifted_in(&Interner),
546 0,
547 ),
421 where_clauses, 548 where_clauses,
422 }; 549 };
423 let datum = FnDefDatum { id: fn_def_id, binders: make_binders(bound, sig.num_binders) }; 550 let datum =
551 FnDefDatum { id: fn_def_id, binders: make_binders(bound, sig.num_binders), abi: () };
424 Arc::new(datum) 552 Arc::new(datum)
425} 553}
426 554
427impl From<AdtId> for crate::TypeCtorId { 555impl From<FnDefId> for crate::db::InternedCallableDefId {
428 fn from(struct_id: AdtId) -> Self {
429 struct_id.0
430 }
431}
432
433impl From<crate::TypeCtorId> for AdtId {
434 fn from(type_ctor_id: crate::TypeCtorId) -> Self {
435 chalk_ir::AdtId(type_ctor_id)
436 }
437}
438
439impl From<FnDefId> for crate::CallableDefId {
440 fn from(fn_def_id: FnDefId) -> Self { 556 fn from(fn_def_id: FnDefId) -> Self {
441 InternKey::from_intern_id(fn_def_id.0) 557 InternKey::from_intern_id(fn_def_id.0)
442 } 558 }
443} 559}
444 560
445impl From<crate::CallableDefId> for FnDefId { 561impl From<crate::db::InternedCallableDefId> for FnDefId {
446 fn from(callable_def_id: crate::CallableDefId) -> Self { 562 fn from(callable_def_id: crate::db::InternedCallableDefId) -> Self {
447 chalk_ir::FnDefId(callable_def_id.as_intern_id()) 563 chalk_ir::FnDefId(callable_def_id.as_intern_id())
448 } 564 }
449} 565}
450 566
451impl From<ImplId> for crate::traits::GlobalImplId { 567impl From<OpaqueTyId> for crate::db::InternedOpaqueTyId {
452 fn from(impl_id: ImplId) -> Self { 568 fn from(id: OpaqueTyId) -> Self {
453 InternKey::from_intern_id(impl_id.0) 569 InternKey::from_intern_id(id.0)
454 } 570 }
455} 571}
456 572
457impl From<crate::traits::GlobalImplId> for ImplId { 573impl From<crate::db::InternedOpaqueTyId> for OpaqueTyId {
458 fn from(impl_id: crate::traits::GlobalImplId) -> Self { 574 fn from(id: crate::db::InternedOpaqueTyId) -> Self {
459 chalk_ir::ImplId(impl_id.as_intern_id()) 575 chalk_ir::OpaqueTyId(id.as_intern_id())
460 } 576 }
461} 577}
462 578
463impl From<rust_ir::AssociatedTyValueId<Interner>> for crate::traits::AssocTyValueId { 579impl From<chalk_ir::ClosureId<Interner>> for crate::db::ClosureId {
464 fn from(id: rust_ir::AssociatedTyValueId<Interner>) -> Self { 580 fn from(id: chalk_ir::ClosureId<Interner>) -> Self {
465 Self::from_intern_id(id.0) 581 Self::from_intern_id(id.0)
466 } 582 }
467} 583}
468 584
469impl From<crate::traits::AssocTyValueId> for rust_ir::AssociatedTyValueId<Interner> { 585impl From<crate::db::ClosureId> for chalk_ir::ClosureId<Interner> {
470 fn from(assoc_ty_value_id: crate::traits::AssocTyValueId) -> Self { 586 fn from(id: crate::db::ClosureId) -> Self {
471 rust_ir::AssociatedTyValueId(assoc_ty_value_id.as_intern_id()) 587 chalk_ir::ClosureId(id.as_intern_id())
472 } 588 }
473} 589}
diff --git a/crates/ra_hir_ty/src/traits/chalk/interner.rs b/crates/ra_hir_ty/src/traits/chalk/interner.rs
index e27074ba6..8d4c51a8f 100644
--- a/crates/ra_hir_ty/src/traits/chalk/interner.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/interner.rs
@@ -22,6 +22,8 @@ pub type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId<Interne
22pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>; 22pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue<Interner>;
23pub type FnDefId = chalk_ir::FnDefId<Interner>; 23pub type FnDefId = chalk_ir::FnDefId<Interner>;
24pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; 24pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>;
25pub type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>;
26pub type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>;
25 27
26impl chalk_ir::interner::Interner for Interner { 28impl chalk_ir::interner::Interner for Interner {
27 type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc? 29 type InternedType = Box<chalk_ir::TyData<Self>>; // FIXME use Arc?
@@ -37,9 +39,11 @@ impl chalk_ir::interner::Interner for Interner {
37 type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>; 39 type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>;
38 type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>; 40 type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>;
39 type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>; 41 type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>;
42 type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>;
40 type DefId = InternId; 43 type DefId = InternId;
41 type InternedAdtId = crate::TypeCtorId; 44 type InternedAdtId = hir_def::AdtId;
42 type Identifier = TypeAliasId; 45 type Identifier = TypeAliasId;
46 type FnAbi = ();
43 47
44 fn debug_adt_id(type_kind_id: AdtId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { 48 fn debug_adt_id(type_kind_id: AdtId, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
45 tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt))) 49 tls::with_current_program(|prog| Some(prog?.debug_struct_id(type_kind_id, fmt)))
@@ -346,6 +350,32 @@ impl chalk_ir::interner::Interner for Interner {
346 ) -> &'a [chalk_ir::CanonicalVarKind<Self>] { 350 ) -> &'a [chalk_ir::CanonicalVarKind<Self>] {
347 &canonical_var_kinds 351 &canonical_var_kinds
348 } 352 }
353
354 fn intern_constraints<E>(
355 &self,
356 data: impl IntoIterator<Item = Result<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>, E>>,
357 ) -> Result<Self::InternedConstraints, E> {
358 data.into_iter().collect()
359 }
360
361 fn constraints_data<'a>(
362 &self,
363 constraints: &'a Self::InternedConstraints,
364 ) -> &'a [chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] {
365 constraints
366 }
367 fn debug_closure_id(
368 _fn_def_id: chalk_ir::ClosureId<Self>,
369 _fmt: &mut fmt::Formatter<'_>,
370 ) -> Option<fmt::Result> {
371 None
372 }
373 fn debug_constraints(
374 _clauses: &chalk_ir::Constraints<Self>,
375 _fmt: &mut fmt::Formatter<'_>,
376 ) -> Option<fmt::Result> {
377 None
378 }
349} 379}
350 380
351impl chalk_ir::interner::HasInterner for Interner { 381impl chalk_ir::interner::HasInterner for Interner {
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
index 5f6daf842..09d8347ca 100644
--- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
@@ -14,10 +14,10 @@ use ra_db::salsa::InternKey;
14 14
15use crate::{ 15use crate::{
16 db::HirDatabase, 16 db::HirDatabase,
17 primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain}, 17 primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness},
18 traits::{builtin, AssocTyValue, Canonical, Impl, Obligation}, 18 traits::{Canonical, Obligation},
19 ApplicationTy, CallableDef, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy, 19 ApplicationTy, CallableDefId, GenericPredicate, InEnvironment, OpaqueTy, OpaqueTyId,
20 Substs, TraitEnvironment, TraitRef, Ty, TypeCtor, 20 ProjectionPredicate, ProjectionTy, Substs, TraitEnvironment, TraitRef, Ty, TyKind, TypeCtor,
21}; 21};
22 22
23use super::interner::*; 23use super::interner::*;
@@ -29,7 +29,9 @@ impl ToChalk for Ty {
29 match self { 29 match self {
30 Ty::Apply(apply_ty) => match apply_ty.ctor { 30 Ty::Apply(apply_ty) => match apply_ty.ctor {
31 TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters), 31 TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters),
32 TypeCtor::FnPtr { num_args: _ } => { 32 TypeCtor::Array => array_to_chalk(db, apply_ty.parameters),
33 TypeCtor::FnPtr { num_args: _, is_varargs: _ } => {
34 // FIXME: handle is_varargs
33 let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner); 35 let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner);
34 chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: 0, substitution }) 36 chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: 0, substitution })
35 .intern(&Interner) 37 .intern(&Interner)
@@ -61,14 +63,26 @@ impl ToChalk for Ty {
61 Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx).intern(&Interner), 63 Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx).intern(&Interner),
62 Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"), 64 Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"),
63 Ty::Dyn(predicates) => { 65 Ty::Dyn(predicates) => {
64 let where_clauses = chalk_ir::QuantifiedWhereClauses::from( 66 let where_clauses = chalk_ir::QuantifiedWhereClauses::from_iter(
65 &Interner, 67 &Interner,
66 predicates.iter().filter(|p| !p.is_error()).cloned().map(|p| p.to_chalk(db)), 68 predicates.iter().filter(|p| !p.is_error()).cloned().map(|p| p.to_chalk(db)),
67 ); 69 );
68 let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) }; 70 let bounded_ty = chalk_ir::DynTy {
71 bounds: make_binders(where_clauses, 1),
72 lifetime: FAKE_PLACEHOLDER.to_lifetime(&Interner),
73 };
69 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) 74 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner)
70 } 75 }
71 Ty::Opaque(_) | Ty::Unknown => { 76 Ty::Opaque(opaque_ty) => {
77 let opaque_ty_id = opaque_ty.opaque_ty_id.to_chalk(db);
78 let substitution = opaque_ty.parameters.to_chalk(db);
79 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy {
80 opaque_ty_id,
81 substitution,
82 }))
83 .intern(&Interner)
84 }
85 Ty::Unknown => {
72 let substitution = chalk_ir::Substitution::empty(&Interner); 86 let substitution = chalk_ir::Substitution::empty(&Interner);
73 let name = TypeName::Error; 87 let name = TypeName::Error;
74 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner) 88 chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
@@ -80,6 +94,7 @@ impl ToChalk for Ty {
80 chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name { 94 chalk_ir::TyData::Apply(apply_ty) => match apply_ty.name {
81 TypeName::Error => Ty::Unknown, 95 TypeName::Error => Ty::Unknown,
82 TypeName::Ref(m) => ref_from_chalk(db, m, apply_ty.substitution), 96 TypeName::Ref(m) => ref_from_chalk(db, m, apply_ty.substitution),
97 TypeName::Array => array_from_chalk(db, apply_ty.substitution),
83 _ => { 98 _ => {
84 let ctor = from_chalk(db, apply_ty.name); 99 let ctor = from_chalk(db, apply_ty.name);
85 let parameters = from_chalk(db, apply_ty.substitution); 100 let parameters = from_chalk(db, apply_ty.substitution);
@@ -98,11 +113,22 @@ impl ToChalk for Ty {
98 let parameters = from_chalk(db, proj.substitution); 113 let parameters = from_chalk(db, proj.substitution);
99 Ty::Projection(ProjectionTy { associated_ty, parameters }) 114 Ty::Projection(ProjectionTy { associated_ty, parameters })
100 } 115 }
101 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(), 116 chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(opaque_ty)) => {
102 chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => { 117 let impl_trait_id = from_chalk(db, opaque_ty.opaque_ty_id);
103 let parameters: Substs = from_chalk(db, substitution); 118 let parameters = from_chalk(db, opaque_ty.substitution);
119 Ty::Opaque(OpaqueTy { opaque_ty_id: impl_trait_id, parameters })
120 }
121 chalk_ir::TyData::Function(chalk_ir::Fn { num_binders, substitution }) => {
122 assert_eq!(num_binders, 0);
123 let parameters: Substs = from_chalk(
124 db,
125 substitution.shifted_out(&Interner).expect("fn ptr should have no binders"),
126 );
104 Ty::Apply(ApplicationTy { 127 Ty::Apply(ApplicationTy {
105 ctor: TypeCtor::FnPtr { num_args: (parameters.len() - 1) as u16 }, 128 ctor: TypeCtor::FnPtr {
129 num_args: (parameters.len() - 1) as u16,
130 is_varargs: false,
131 },
106 parameters, 132 parameters,
107 }) 133 })
108 } 134 }
@@ -122,7 +148,7 @@ impl ToChalk for Ty {
122 } 148 }
123} 149}
124 150
125const LIFETIME_PLACEHOLDER: PlaceholderIndex = 151const FAKE_PLACEHOLDER: PlaceholderIndex =
126 PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::MAX }; 152 PlaceholderIndex { ui: UniverseIndex::ROOT, idx: usize::MAX };
127 153
128/// We currently don't model lifetimes, but Chalk does. So, we have to insert a 154/// We currently don't model lifetimes, but Chalk does. So, we have to insert a
@@ -133,10 +159,10 @@ fn ref_to_chalk(
133 subst: Substs, 159 subst: Substs,
134) -> chalk_ir::Ty<Interner> { 160) -> chalk_ir::Ty<Interner> {
135 let arg = subst[0].clone().to_chalk(db); 161 let arg = subst[0].clone().to_chalk(db);
136 let lifetime = LIFETIME_PLACEHOLDER.to_lifetime(&Interner); 162 let lifetime = FAKE_PLACEHOLDER.to_lifetime(&Interner);
137 chalk_ir::ApplicationTy { 163 chalk_ir::ApplicationTy {
138 name: TypeName::Ref(mutability.to_chalk(db)), 164 name: TypeName::Ref(mutability.to_chalk(db)),
139 substitution: chalk_ir::Substitution::from( 165 substitution: chalk_ir::Substitution::from_iter(
140 &Interner, 166 &Interner,
141 vec![lifetime.cast(&Interner), arg.cast(&Interner)], 167 vec![lifetime.cast(&Interner), arg.cast(&Interner)],
142 ), 168 ),
@@ -157,11 +183,40 @@ fn ref_from_chalk(
157 Ty::apply(TypeCtor::Ref(from_chalk(db, mutability)), Substs(tys)) 183 Ty::apply(TypeCtor::Ref(from_chalk(db, mutability)), Substs(tys))
158} 184}
159 185
186/// We currently don't model constants, but Chalk does. So, we have to insert a
187/// fake constant here, because Chalks built-in logic may expect it to be there.
188fn array_to_chalk(db: &dyn HirDatabase, subst: Substs) -> chalk_ir::Ty<Interner> {
189 let arg = subst[0].clone().to_chalk(db);
190 let usize_ty = chalk_ir::ApplicationTy {
191 name: TypeName::Scalar(Scalar::Uint(chalk_ir::UintTy::Usize)),
192 substitution: chalk_ir::Substitution::empty(&Interner),
193 }
194 .intern(&Interner);
195 let const_ = FAKE_PLACEHOLDER.to_const(&Interner, usize_ty);
196 chalk_ir::ApplicationTy {
197 name: TypeName::Array,
198 substitution: chalk_ir::Substitution::from_iter(
199 &Interner,
200 vec![arg.cast(&Interner), const_.cast(&Interner)],
201 ),
202 }
203 .intern(&Interner)
204}
205
206/// Here we remove the const from the type we got from Chalk.
207fn array_from_chalk(db: &dyn HirDatabase, subst: chalk_ir::Substitution<Interner>) -> Ty {
208 let tys = subst
209 .iter(&Interner)
210 .filter_map(|p| Some(from_chalk(db, p.ty(&Interner)?.clone())))
211 .collect();
212 Ty::apply(TypeCtor::Array, Substs(tys))
213}
214
160impl ToChalk for Substs { 215impl ToChalk for Substs {
161 type Chalk = chalk_ir::Substitution<Interner>; 216 type Chalk = chalk_ir::Substitution<Interner>;
162 217
163 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Substitution<Interner> { 218 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Substitution<Interner> {
164 chalk_ir::Substitution::from(&Interner, self.iter().map(|ty| ty.clone().to_chalk(db))) 219 chalk_ir::Substitution::from_iter(&Interner, self.iter().map(|ty| ty.clone().to_chalk(db)))
165 } 220 }
166 221
167 fn from_chalk(db: &dyn HirDatabase, parameters: chalk_ir::Substitution<Interner>) -> Substs { 222 fn from_chalk(db: &dyn HirDatabase, parameters: chalk_ir::Substitution<Interner>) -> Substs {
@@ -204,6 +259,21 @@ impl ToChalk for hir_def::TraitId {
204 } 259 }
205} 260}
206 261
262impl ToChalk for OpaqueTyId {
263 type Chalk = chalk_ir::OpaqueTyId<Interner>;
264
265 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::OpaqueTyId<Interner> {
266 db.intern_impl_trait_id(self).into()
267 }
268
269 fn from_chalk(
270 db: &dyn HirDatabase,
271 opaque_ty_id: chalk_ir::OpaqueTyId<Interner>,
272 ) -> OpaqueTyId {
273 db.lookup_intern_impl_trait_id(opaque_ty_id.into())
274 }
275}
276
207impl ToChalk for TypeCtor { 277impl ToChalk for TypeCtor {
208 type Chalk = TypeName<Interner>; 278 type Chalk = TypeName<Interner>;
209 279
@@ -214,19 +284,25 @@ impl ToChalk for TypeCtor {
214 TypeName::AssociatedType(type_id) 284 TypeName::AssociatedType(type_id)
215 } 285 }
216 286
287 TypeCtor::OpaqueType(impl_trait_id) => {
288 let id = impl_trait_id.to_chalk(db);
289 TypeName::OpaqueType(id)
290 }
291
217 TypeCtor::Bool => TypeName::Scalar(Scalar::Bool), 292 TypeCtor::Bool => TypeName::Scalar(Scalar::Bool),
218 TypeCtor::Char => TypeName::Scalar(Scalar::Char), 293 TypeCtor::Char => TypeName::Scalar(Scalar::Char),
219 TypeCtor::Int(Uncertain::Known(int_ty)) => TypeName::Scalar(int_ty_to_chalk(int_ty)), 294 TypeCtor::Int(int_ty) => TypeName::Scalar(int_ty_to_chalk(int_ty)),
220 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) => { 295 TypeCtor::Float(FloatTy { bitness: FloatBitness::X32 }) => {
221 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) 296 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32))
222 } 297 }
223 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) => { 298 TypeCtor::Float(FloatTy { bitness: FloatBitness::X64 }) => {
224 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) 299 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64))
225 } 300 }
226 301
227 TypeCtor::Tuple { cardinality } => TypeName::Tuple(cardinality.into()), 302 TypeCtor::Tuple { cardinality } => TypeName::Tuple(cardinality.into()),
228 TypeCtor::RawPtr(mutability) => TypeName::Raw(mutability.to_chalk(db)), 303 TypeCtor::RawPtr(mutability) => TypeName::Raw(mutability.to_chalk(db)),
229 TypeCtor::Slice => TypeName::Slice, 304 TypeCtor::Slice => TypeName::Slice,
305 TypeCtor::Array => TypeName::Array,
230 TypeCtor::Ref(mutability) => TypeName::Ref(mutability.to_chalk(db)), 306 TypeCtor::Ref(mutability) => TypeName::Ref(mutability.to_chalk(db)),
231 TypeCtor::Str => TypeName::Str, 307 TypeCtor::Str => TypeName::Str,
232 TypeCtor::FnDef(callable_def) => { 308 TypeCtor::FnDef(callable_def) => {
@@ -235,40 +311,44 @@ impl ToChalk for TypeCtor {
235 } 311 }
236 TypeCtor::Never => TypeName::Never, 312 TypeCtor::Never => TypeName::Never,
237 313
238 TypeCtor::Int(Uncertain::Unknown) 314 TypeCtor::Closure { def, expr } => {
239 | TypeCtor::Float(Uncertain::Unknown) 315 let closure_id = db.intern_closure((def, expr));
240 | TypeCtor::Adt(_) 316 TypeName::Closure(closure_id.into())
241 | TypeCtor::Array 317 }
242 | TypeCtor::FnPtr { .. } 318
243 | TypeCtor::Closure { .. } => { 319 TypeCtor::Adt(adt_id) => TypeName::Adt(chalk_ir::AdtId(adt_id)),
244 // other TypeCtors get interned and turned into a chalk StructId 320
245 let struct_id = db.intern_type_ctor(self).into(); 321 TypeCtor::FnPtr { .. } => {
246 TypeName::Adt(struct_id) 322 // This should not be reached, since Chalk doesn't represent
323 // function pointers with TypeName
324 unreachable!()
247 } 325 }
248 } 326 }
249 } 327 }
250 328
251 fn from_chalk(db: &dyn HirDatabase, type_name: TypeName<Interner>) -> TypeCtor { 329 fn from_chalk(db: &dyn HirDatabase, type_name: TypeName<Interner>) -> TypeCtor {
252 match type_name { 330 match type_name {
253 TypeName::Adt(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), 331 TypeName::Adt(struct_id) => TypeCtor::Adt(struct_id.0),
254 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), 332 TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
255 TypeName::OpaqueType(_) => unreachable!(), 333 TypeName::OpaqueType(opaque_type_id) => {
334 TypeCtor::OpaqueType(from_chalk(db, opaque_type_id))
335 }
256 336
257 TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool, 337 TypeName::Scalar(Scalar::Bool) => TypeCtor::Bool,
258 TypeName::Scalar(Scalar::Char) => TypeCtor::Char, 338 TypeName::Scalar(Scalar::Char) => TypeCtor::Char,
259 TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(Uncertain::Known(IntTy { 339 TypeName::Scalar(Scalar::Int(int_ty)) => TypeCtor::Int(IntTy {
260 signedness: Signedness::Signed, 340 signedness: Signedness::Signed,
261 bitness: bitness_from_chalk_int(int_ty), 341 bitness: bitness_from_chalk_int(int_ty),
262 })), 342 }),
263 TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(Uncertain::Known(IntTy { 343 TypeName::Scalar(Scalar::Uint(uint_ty)) => TypeCtor::Int(IntTy {
264 signedness: Signedness::Unsigned, 344 signedness: Signedness::Unsigned,
265 bitness: bitness_from_chalk_uint(uint_ty), 345 bitness: bitness_from_chalk_uint(uint_ty),
266 })), 346 }),
267 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => { 347 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F32)) => {
268 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X32 })) 348 TypeCtor::Float(FloatTy { bitness: FloatBitness::X32 })
269 } 349 }
270 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => { 350 TypeName::Scalar(Scalar::Float(chalk_ir::FloatTy::F64)) => {
271 TypeCtor::Float(Uncertain::Known(FloatTy { bitness: FloatBitness::X64 })) 351 TypeCtor::Float(FloatTy { bitness: FloatBitness::X64 })
272 } 352 }
273 TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 }, 353 TypeName::Tuple(cardinality) => TypeCtor::Tuple { cardinality: cardinality as u16 },
274 TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)), 354 TypeName::Raw(mutability) => TypeCtor::RawPtr(from_chalk(db, mutability)),
@@ -281,8 +361,15 @@ impl ToChalk for TypeCtor {
281 let callable_def = from_chalk(db, fn_def_id); 361 let callable_def = from_chalk(db, fn_def_id);
282 TypeCtor::FnDef(callable_def) 362 TypeCtor::FnDef(callable_def)
283 } 363 }
364 TypeName::Array => TypeCtor::Array,
284 365
285 TypeName::Array | TypeName::Error => { 366 TypeName::Closure(id) => {
367 let id: crate::db::ClosureId = id.into();
368 let (def, expr) = db.lookup_intern_closure(id);
369 TypeCtor::Closure { def, expr }
370 }
371
372 TypeName::Error => {
286 // this should not be reached, since we don't represent TypeName::Error with TypeCtor 373 // this should not be reached, since we don't represent TypeName::Error with TypeCtor
287 unreachable!() 374 unreachable!()
288 } 375 }
@@ -355,26 +442,26 @@ impl ToChalk for Mutability {
355 } 442 }
356} 443}
357 444
358impl ToChalk for Impl { 445impl ToChalk for hir_def::ImplId {
359 type Chalk = ImplId; 446 type Chalk = ImplId;
360 447
361 fn to_chalk(self, db: &dyn HirDatabase) -> ImplId { 448 fn to_chalk(self, _db: &dyn HirDatabase) -> ImplId {
362 db.intern_chalk_impl(self).into() 449 chalk_ir::ImplId(self.as_intern_id())
363 } 450 }
364 451
365 fn from_chalk(db: &dyn HirDatabase, impl_id: ImplId) -> Impl { 452 fn from_chalk(_db: &dyn HirDatabase, impl_id: ImplId) -> hir_def::ImplId {
366 db.lookup_intern_chalk_impl(impl_id.into()) 453 InternKey::from_intern_id(impl_id.0)
367 } 454 }
368} 455}
369 456
370impl ToChalk for CallableDef { 457impl ToChalk for CallableDefId {
371 type Chalk = FnDefId; 458 type Chalk = FnDefId;
372 459
373 fn to_chalk(self, db: &dyn HirDatabase) -> FnDefId { 460 fn to_chalk(self, db: &dyn HirDatabase) -> FnDefId {
374 db.intern_callable_def(self).into() 461 db.intern_callable_def(self).into()
375 } 462 }
376 463
377 fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDef { 464 fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDefId {
378 db.lookup_intern_callable_def(fn_def_id.into()) 465 db.lookup_intern_callable_def(fn_def_id.into())
379 } 466 }
380} 467}
@@ -391,15 +478,20 @@ impl ToChalk for TypeAliasId {
391 } 478 }
392} 479}
393 480
394impl ToChalk for AssocTyValue { 481pub struct TypeAliasAsValue(pub TypeAliasId);
482
483impl ToChalk for TypeAliasAsValue {
395 type Chalk = AssociatedTyValueId; 484 type Chalk = AssociatedTyValueId;
396 485
397 fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValueId { 486 fn to_chalk(self, _db: &dyn HirDatabase) -> AssociatedTyValueId {
398 db.intern_assoc_ty_value(self).into() 487 rust_ir::AssociatedTyValueId(self.0.as_intern_id())
399 } 488 }
400 489
401 fn from_chalk(db: &dyn HirDatabase, assoc_ty_value_id: AssociatedTyValueId) -> AssocTyValue { 490 fn from_chalk(
402 db.lookup_intern_assoc_ty_value(assoc_ty_value_id.into()) 491 _db: &dyn HirDatabase,
492 assoc_ty_value_id: AssociatedTyValueId,
493 ) -> TypeAliasAsValue {
494 TypeAliasAsValue(TypeAliasId::from_intern_id(assoc_ty_value_id.0))
403 } 495 }
404} 496}
405 497
@@ -447,6 +539,16 @@ impl ToChalk for GenericPredicate {
447 let ty = from_chalk(db, projection_eq.ty); 539 let ty = from_chalk(db, projection_eq.ty);
448 GenericPredicate::Projection(ProjectionPredicate { projection_ty, ty }) 540 GenericPredicate::Projection(ProjectionPredicate { projection_ty, ty })
449 } 541 }
542
543 chalk_ir::WhereClause::LifetimeOutlives(_) => {
544 // we shouldn't get these from Chalk
545 panic!("encountered LifetimeOutlives from Chalk")
546 }
547
548 chalk_ir::WhereClause::TypeOutlives(_) => {
549 // we shouldn't get these from Chalk
550 panic!("encountered TypeOutlives from Chalk")
551 }
450 } 552 }
451 } 553 }
452} 554}
@@ -510,22 +612,42 @@ where
510 type Chalk = chalk_ir::Canonical<T::Chalk>; 612 type Chalk = chalk_ir::Canonical<T::Chalk>;
511 613
512 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> { 614 fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical<T::Chalk> {
513 let parameter = chalk_ir::CanonicalVarKind::new( 615 let kinds = self
514 chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General), 616 .kinds
515 chalk_ir::UniverseIndex::ROOT, 617 .iter()
516 ); 618 .map(|k| match k {
619 TyKind::General => chalk_ir::TyKind::General,
620 TyKind::Integer => chalk_ir::TyKind::Integer,
621 TyKind::Float => chalk_ir::TyKind::Float,
622 })
623 .map(|tk| {
624 chalk_ir::CanonicalVarKind::new(
625 chalk_ir::VariableKind::Ty(tk),
626 chalk_ir::UniverseIndex::ROOT,
627 )
628 });
517 let value = self.value.to_chalk(db); 629 let value = self.value.to_chalk(db);
518 chalk_ir::Canonical { 630 chalk_ir::Canonical {
519 value, 631 value,
520 binders: chalk_ir::CanonicalVarKinds::from(&Interner, vec![parameter; self.num_vars]), 632 binders: chalk_ir::CanonicalVarKinds::from_iter(&Interner, kinds),
521 } 633 }
522 } 634 }
523 635
524 fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> { 636 fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical<T::Chalk>) -> Canonical<T> {
525 Canonical { 637 let kinds = canonical
526 num_vars: canonical.binders.len(&Interner), 638 .binders
527 value: from_chalk(db, canonical.value), 639 .iter(&Interner)
528 } 640 .map(|k| match k.kind {
641 chalk_ir::VariableKind::Ty(tk) => match tk {
642 chalk_ir::TyKind::General => TyKind::General,
643 chalk_ir::TyKind::Integer => TyKind::Integer,
644 chalk_ir::TyKind::Float => TyKind::Float,
645 },
646 chalk_ir::VariableKind::Lifetime => panic!("unexpected lifetime from Chalk"),
647 chalk_ir::VariableKind::Const(_) => panic!("unexpected const from Chalk"),
648 })
649 .collect();
650 Canonical { kinds, value: from_chalk(db, canonical.value) }
529 } 651 }
530} 652}
531 653
@@ -578,58 +700,12 @@ where
578 } 700 }
579} 701}
580 702
581impl ToChalk for builtin::BuiltinImplData {
582 type Chalk = ImplDatum;
583
584 fn to_chalk(self, db: &dyn HirDatabase) -> ImplDatum {
585 let impl_type = rust_ir::ImplType::External;
586 let where_clauses = self.where_clauses.into_iter().map(|w| w.to_chalk(db)).collect();
587
588 let impl_datum_bound =
589 rust_ir::ImplDatumBound { trait_ref: self.trait_ref.to_chalk(db), where_clauses };
590 let associated_ty_value_ids =
591 self.assoc_ty_values.into_iter().map(|v| v.to_chalk(db)).collect();
592 rust_ir::ImplDatum {
593 binders: make_binders(impl_datum_bound, self.num_vars),
594 impl_type,
595 polarity: rust_ir::Polarity::Positive,
596 associated_ty_value_ids,
597 }
598 }
599
600 fn from_chalk(_db: &dyn HirDatabase, _data: ImplDatum) -> Self {
601 unimplemented!()
602 }
603}
604
605impl ToChalk for builtin::BuiltinImplAssocTyValueData {
606 type Chalk = AssociatedTyValue;
607
608 fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValue {
609 let ty = self.value.to_chalk(db);
610 let value_bound = rust_ir::AssociatedTyValueBound { ty };
611
612 rust_ir::AssociatedTyValue {
613 associated_ty_id: self.assoc_ty_id.to_chalk(db),
614 impl_id: self.impl_.to_chalk(db),
615 value: make_binders(value_bound, self.num_vars),
616 }
617 }
618
619 fn from_chalk(
620 _db: &dyn HirDatabase,
621 _data: AssociatedTyValue,
622 ) -> builtin::BuiltinImplAssocTyValueData {
623 unimplemented!()
624 }
625}
626
627pub(super) fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> 703pub(super) fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T>
628where 704where
629 T: HasInterner<Interner = Interner>, 705 T: HasInterner<Interner = Interner>,
630{ 706{
631 chalk_ir::Binders::new( 707 chalk_ir::Binders::new(
632 chalk_ir::VariableKinds::from( 708 chalk_ir::VariableKinds::from_iter(
633 &Interner, 709 &Interner,
634 std::iter::repeat(chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General)).take(num_vars), 710 std::iter::repeat(chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General)).take(num_vars),
635 ), 711 ),
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs
index d88828c7c..db915625c 100644
--- a/crates/ra_hir_ty/src/traits/chalk/tls.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs
@@ -5,12 +5,12 @@ use chalk_ir::{AliasTy, GenericArg, Goal, Goals, Lifetime, ProgramClauseImplicat
5use itertools::Itertools; 5use itertools::Itertools;
6 6
7use super::{from_chalk, Interner}; 7use super::{from_chalk, Interner};
8use crate::{db::HirDatabase, CallableDef, TypeCtor}; 8use crate::{db::HirDatabase, CallableDefId, TypeCtor};
9use hir_def::{AdtId, AssocContainerId, DefWithBodyId, Lookup, TypeAliasId}; 9use hir_def::{AdtId, AssocContainerId, DefWithBodyId, Lookup, TypeAliasId};
10 10
11pub use unsafe_tls::{set_current_program, with_current_program}; 11pub use unsafe_tls::{set_current_program, with_current_program};
12 12
13pub struct DebugContext<'a>(&'a (dyn HirDatabase + 'a)); 13pub struct DebugContext<'a>(&'a dyn HirDatabase);
14 14
15impl DebugContext<'_> { 15impl DebugContext<'_> {
16 pub fn debug_struct_id( 16 pub fn debug_struct_id(
@@ -38,16 +38,16 @@ impl DebugContext<'_> {
38 } 38 }
39 TypeCtor::FnDef(def) => { 39 TypeCtor::FnDef(def) => {
40 let name = match def { 40 let name = match def {
41 CallableDef::FunctionId(ff) => self.0.function_data(ff).name.clone(), 41 CallableDefId::FunctionId(ff) => self.0.function_data(ff).name.clone(),
42 CallableDef::StructId(s) => self.0.struct_data(s).name.clone(), 42 CallableDefId::StructId(s) => self.0.struct_data(s).name.clone(),
43 CallableDef::EnumVariantId(e) => { 43 CallableDefId::EnumVariantId(e) => {
44 let enum_data = self.0.enum_data(e.parent); 44 let enum_data = self.0.enum_data(e.parent);
45 enum_data.variants[e.local_id].name.clone() 45 enum_data.variants[e.local_id].name.clone()
46 } 46 }
47 }; 47 };
48 match def { 48 match def {
49 CallableDef::FunctionId(_) => write!(f, "{{fn {}}}", name)?, 49 CallableDefId::FunctionId(_) => write!(f, "{{fn {}}}", name)?,
50 CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => { 50 CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {
51 write!(f, "{{ctor {}}}", name)? 51 write!(f, "{{ctor {}}}", name)?
52 } 52 }
53 } 53 }
@@ -69,6 +69,11 @@ impl DebugContext<'_> {
69 let name = self.0.type_alias_data(type_alias).name.clone(); 69 let name = self.0.type_alias_data(type_alias).name.clone();
70 write!(f, "{}::{}", trait_name, name)?; 70 write!(f, "{}::{}", trait_name, name)?;
71 } 71 }
72 TypeCtor::OpaqueType(opaque_ty_id) => match opaque_ty_id {
73 crate::OpaqueTyId::ReturnTypeImplTrait(func, idx) => {
74 write!(f, "{{impl trait {} of {:?}}}", idx, func)?;
75 }
76 },
72 TypeCtor::Closure { def, expr } => { 77 TypeCtor::Closure { def, expr } => {
73 write!(f, "{{closure {:?} in ", expr.into_raw())?; 78 write!(f, "{{closure {:?} in ", expr.into_raw())?;
74 match def { 79 match def {
@@ -152,7 +157,7 @@ impl DebugContext<'_> {
152 _ => panic!("associated type not in trait"), 157 _ => panic!("associated type not in trait"),
153 }; 158 };
154 let trait_data = self.0.trait_data(trait_); 159 let trait_data = self.0.trait_data(trait_);
155 let params = projection_ty.substitution.parameters(&Interner); 160 let params = projection_ty.substitution.as_slice(&Interner);
156 write!(fmt, "<{:?} as {}", &params[0], trait_data.name,)?; 161 write!(fmt, "<{:?} as {}", &params[0], trait_data.name,)?;
157 if params.len() > 1 { 162 if params.len() > 1 {
158 write!( 163 write!(
@@ -250,18 +255,18 @@ impl DebugContext<'_> {
250 fn_def_id: chalk_ir::FnDefId<Interner>, 255 fn_def_id: chalk_ir::FnDefId<Interner>,
251 fmt: &mut fmt::Formatter<'_>, 256 fmt: &mut fmt::Formatter<'_>,
252 ) -> Result<(), fmt::Error> { 257 ) -> Result<(), fmt::Error> {
253 let def: CallableDef = from_chalk(self.0, fn_def_id); 258 let def: CallableDefId = from_chalk(self.0, fn_def_id);
254 let name = match def { 259 let name = match def {
255 CallableDef::FunctionId(ff) => self.0.function_data(ff).name.clone(), 260 CallableDefId::FunctionId(ff) => self.0.function_data(ff).name.clone(),
256 CallableDef::StructId(s) => self.0.struct_data(s).name.clone(), 261 CallableDefId::StructId(s) => self.0.struct_data(s).name.clone(),
257 CallableDef::EnumVariantId(e) => { 262 CallableDefId::EnumVariantId(e) => {
258 let enum_data = self.0.enum_data(e.parent); 263 let enum_data = self.0.enum_data(e.parent);
259 enum_data.variants[e.local_id].name.clone() 264 enum_data.variants[e.local_id].name.clone()
260 } 265 }
261 }; 266 };
262 match def { 267 match def {
263 CallableDef::FunctionId(_) => write!(fmt, "{{fn {}}}", name), 268 CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name),
264 CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => { 269 CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {
265 write!(fmt, "{{ctor {}}}", name) 270 write!(fmt, "{{ctor {}}}", name)
266 } 271 }
267 } 272 }
diff --git a/crates/ra_hir_ty/src/utils.rs b/crates/ra_hir_ty/src/utils.rs
index f98350bf9..e3e244268 100644
--- a/crates/ra_hir_ty/src/utils.rs
+++ b/crates/ra_hir_ty/src/utils.rs
@@ -110,46 +110,15 @@ pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) ->
110 result 110 result
111} 111}
112 112
113/// Finds a path from a trait to one of its super traits. Returns an empty
114/// vector if there is no path.
115pub(super) fn find_super_trait_path(
116 db: &dyn DefDatabase,
117 trait_: TraitId,
118 super_trait: TraitId,
119) -> Vec<TraitId> {
120 let mut result = Vec::with_capacity(2);
121 result.push(trait_);
122 return if go(db, super_trait, &mut result) { result } else { Vec::new() };
123
124 fn go(db: &dyn DefDatabase, super_trait: TraitId, path: &mut Vec<TraitId>) -> bool {
125 let trait_ = *path.last().unwrap();
126 if trait_ == super_trait {
127 return true;
128 }
129
130 for tt in direct_super_traits(db, trait_) {
131 if path.contains(&tt) {
132 continue;
133 }
134 path.push(tt);
135 if go(db, super_trait, path) {
136 return true;
137 } else {
138 path.pop();
139 }
140 }
141 false
142 }
143}
144
145pub(super) fn associated_type_by_name_including_super_traits( 113pub(super) fn associated_type_by_name_including_super_traits(
146 db: &dyn DefDatabase, 114 db: &dyn HirDatabase,
147 trait_: TraitId, 115 trait_ref: TraitRef,
148 name: &Name, 116 name: &Name,
149) -> Option<TypeAliasId> { 117) -> Option<(TraitRef, TypeAliasId)> {
150 all_super_traits(db, trait_) 118 all_super_trait_refs(db, trait_ref).into_iter().find_map(|t| {
151 .into_iter() 119 let assoc_type = db.trait_data(t.trait_).associated_type_by_name(name)?;
152 .find_map(|t| db.trait_data(t).associated_type_by_name(name)) 120 Some((t, assoc_type))
121 })
153} 122}
154 123
155pub(super) fn variant_data(db: &dyn DefDatabase, var: VariantId) -> Arc<VariantData> { 124pub(super) fn variant_data(db: &dyn DefDatabase, var: VariantId) -> Arc<VariantData> {
@@ -176,6 +145,7 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
176 Generics { def, params: db.generic_params(def), parent_generics } 145 Generics { def, params: db.generic_params(def), parent_generics }
177} 146}
178 147
148#[derive(Debug)]
179pub(crate) struct Generics { 149pub(crate) struct Generics {
180 def: GenericDefId, 150 def: GenericDefId,
181 pub(crate) params: Arc<GenericParams>, 151 pub(crate) params: Arc<GenericParams>,
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index 05c940605..6f8107491 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_ide" 3name = "ra_ide"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
@@ -29,10 +30,11 @@ ra_fmt = { path = "../ra_fmt" }
29ra_prof = { path = "../ra_prof" } 30ra_prof = { path = "../ra_prof" }
30test_utils = { path = "../test_utils" } 31test_utils = { path = "../test_utils" }
31ra_assists = { path = "../ra_assists" } 32ra_assists = { path = "../ra_assists" }
33ra_ssr = { path = "../ra_ssr" }
32 34
33# ra_ide should depend only on the top-level `hir` package. if you need 35# ra_ide should depend only on the top-level `hir` package. if you need
34# something from some `hir_xxx` subpackage, reexport the API via `hir`. 36# something from some `hir_xxx` subpackage, reexport the API via `hir`.
35hir = { path = "../ra_hir", package = "ra_hir" } 37hir = { path = "../ra_hir", package = "ra_hir" }
36 38
37[dev-dependencies] 39[dev-dependencies]
38insta = "0.16.0" 40expect = { path = "../expect" }
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs
index defd8176f..cb7e62cd6 100644
--- a/crates/ra_ide/src/call_hierarchy.rs
+++ b/crates/ra_ide/src/call_hierarchy.rs
@@ -39,10 +39,11 @@ pub(crate) fn call_hierarchy(
39 39
40pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> { 40pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
41 let sema = Semantics::new(db); 41 let sema = Semantics::new(db);
42
42 // 1. Find all refs 43 // 1. Find all refs
43 // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply. 44 // 2. Loop through refs and determine unique fndef. This will become our `from: CallHierarchyItem,` in the reply.
44 // 3. Add ranges relative to the start of the fndef. 45 // 3. Add ranges relative to the start of the fndef.
45 let refs = references::find_all_refs(db, position, None)?; 46 let refs = references::find_all_refs(&sema, position, None)?;
46 47
47 let mut calls = CallLocations::default(); 48 let mut calls = CallLocations::default();
48 49
@@ -96,7 +97,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
96 //FIXME: Type::as_callable is broken 97 //FIXME: Type::as_callable is broken
97 let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?; 98 let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?;
98 match callable_def { 99 match callable_def {
99 hir::CallableDef::FunctionId(it) => { 100 hir::CallableDefId::FunctionId(it) => {
100 let fn_def: hir::Function = it.into(); 101 let fn_def: hir::Function = it.into();
101 let nav = fn_def.to_nav(db); 102 let nav = fn_def.to_nav(db);
102 Some(nav) 103 Some(nav)
@@ -145,12 +146,12 @@ mod tests {
145 use crate::mock_analysis::analysis_and_position; 146 use crate::mock_analysis::analysis_and_position;
146 147
147 fn check_hierarchy( 148 fn check_hierarchy(
148 fixture: &str, 149 ra_fixture: &str,
149 expected: &str, 150 expected: &str,
150 expected_incoming: &[&str], 151 expected_incoming: &[&str],
151 expected_outgoing: &[&str], 152 expected_outgoing: &[&str],
152 ) { 153 ) {
153 let (analysis, pos) = analysis_and_position(fixture); 154 let (analysis, pos) = analysis_and_position(ra_fixture);
154 155
155 let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info; 156 let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info;
156 assert_eq!(navs.len(), 1); 157 assert_eq!(navs.len(), 1);
@@ -177,12 +178,12 @@ mod tests {
177 fn test_call_hierarchy_on_ref() { 178 fn test_call_hierarchy_on_ref() {
178 check_hierarchy( 179 check_hierarchy(
179 r#" 180 r#"
180 //- /lib.rs 181//- /lib.rs
181 fn callee() {} 182fn callee() {}
182 fn caller() { 183fn caller() {
183 call<|>ee(); 184 call<|>ee();
184 } 185}
185 "#, 186"#,
186 "callee FN_DEF FileId(1) 0..14 3..9", 187 "callee FN_DEF FileId(1) 0..14 3..9",
187 &["caller FN_DEF FileId(1) 15..44 18..24 : [33..39]"], 188 &["caller FN_DEF FileId(1) 15..44 18..24 : [33..39]"],
188 &[], 189 &[],
@@ -193,12 +194,12 @@ mod tests {
193 fn test_call_hierarchy_on_def() { 194 fn test_call_hierarchy_on_def() {
194 check_hierarchy( 195 check_hierarchy(
195 r#" 196 r#"
196 //- /lib.rs 197//- /lib.rs
197 fn call<|>ee() {} 198fn call<|>ee() {}
198 fn caller() { 199fn caller() {
199 callee(); 200 callee();
200 } 201}
201 "#, 202"#,
202 "callee FN_DEF FileId(1) 0..14 3..9", 203 "callee FN_DEF FileId(1) 0..14 3..9",
203 &["caller FN_DEF FileId(1) 15..44 18..24 : [33..39]"], 204 &["caller FN_DEF FileId(1) 15..44 18..24 : [33..39]"],
204 &[], 205 &[],
@@ -209,13 +210,13 @@ mod tests {
209 fn test_call_hierarchy_in_same_fn() { 210 fn test_call_hierarchy_in_same_fn() {
210 check_hierarchy( 211 check_hierarchy(
211 r#" 212 r#"
212 //- /lib.rs 213//- /lib.rs
213 fn callee() {} 214fn callee() {}
214 fn caller() { 215fn caller() {
215 call<|>ee(); 216 call<|>ee();
216 callee(); 217 callee();
217 } 218}
218 "#, 219"#,
219 "callee FN_DEF FileId(1) 0..14 3..9", 220 "callee FN_DEF FileId(1) 0..14 3..9",
220 &["caller FN_DEF FileId(1) 15..58 18..24 : [33..39, 47..53]"], 221 &["caller FN_DEF FileId(1) 15..58 18..24 : [33..39, 47..53]"],
221 &[], 222 &[],
@@ -226,20 +227,20 @@ mod tests {
226 fn test_call_hierarchy_in_different_fn() { 227 fn test_call_hierarchy_in_different_fn() {
227 check_hierarchy( 228 check_hierarchy(
228 r#" 229 r#"
229 //- /lib.rs 230//- /lib.rs
230 fn callee() {} 231fn callee() {}
231 fn caller1() { 232fn caller1() {
232 call<|>ee(); 233 call<|>ee();
233 } 234}
234 235
235 fn caller2() { 236fn caller2() {
236 callee(); 237 callee();
237 } 238}
238 "#, 239"#,
239 "callee FN_DEF FileId(1) 0..14 3..9", 240 "callee FN_DEF FileId(1) 0..14 3..9",
240 &[ 241 &[
241 "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]", 242 "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]",
242 "caller2 FN_DEF FileId(1) 46..76 49..56 : [65..71]", 243 "caller2 FN_DEF FileId(1) 47..77 50..57 : [66..72]",
243 ], 244 ],
244 &[], 245 &[],
245 ); 246 );
@@ -249,26 +250,26 @@ mod tests {
249 fn test_call_hierarchy_in_tests_mod() { 250 fn test_call_hierarchy_in_tests_mod() {
250 check_hierarchy( 251 check_hierarchy(
251 r#" 252 r#"
252 //- /lib.rs cfg:test 253//- /lib.rs cfg:test
253 fn callee() {} 254fn callee() {}
254 fn caller1() { 255fn caller1() {
255 call<|>ee(); 256 call<|>ee();
256 } 257}
257 258
258 #[cfg(test)] 259#[cfg(test)]
259 mod tests { 260mod tests {
260 use super::*; 261 use super::*;
261 262
262 #[test] 263 #[test]
263 fn test_caller() { 264 fn test_caller() {
264 callee(); 265 callee();
265 } 266 }
266 } 267}
267 "#, 268"#,
268 "callee FN_DEF FileId(1) 0..14 3..9", 269 "callee FN_DEF FileId(1) 0..14 3..9",
269 &[ 270 &[
270 "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]", 271 "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]",
271 "test_caller FN_DEF FileId(1) 93..147 108..119 : [132..138]", 272 "test_caller FN_DEF FileId(1) 95..149 110..121 : [134..140]",
272 ], 273 ],
273 &[], 274 &[],
274 ); 275 );
@@ -278,19 +279,19 @@ mod tests {
278 fn test_call_hierarchy_in_different_files() { 279 fn test_call_hierarchy_in_different_files() {
279 check_hierarchy( 280 check_hierarchy(
280 r#" 281 r#"
281 //- /lib.rs 282//- /lib.rs
282 mod foo; 283mod foo;
283 use foo::callee; 284use foo::callee;
284 285
285 fn caller() { 286fn caller() {
286 call<|>ee(); 287 call<|>ee();
287 } 288}
288 289
289 //- /foo/mod.rs 290//- /foo/mod.rs
290 pub fn callee() {} 291pub fn callee() {}
291 "#, 292"#,
292 "callee FN_DEF FileId(2) 0..18 7..13", 293 "callee FN_DEF FileId(2) 0..18 7..13",
293 &["caller FN_DEF FileId(1) 26..55 29..35 : [44..50]"], 294 &["caller FN_DEF FileId(1) 27..56 30..36 : [45..51]"],
294 &[], 295 &[],
295 ); 296 );
296 } 297 }
@@ -299,13 +300,13 @@ mod tests {
299 fn test_call_hierarchy_outgoing() { 300 fn test_call_hierarchy_outgoing() {
300 check_hierarchy( 301 check_hierarchy(
301 r#" 302 r#"
302 //- /lib.rs 303//- /lib.rs
303 fn callee() {} 304fn callee() {}
304 fn call<|>er() { 305fn call<|>er() {
305 callee(); 306 callee();
306 callee(); 307 callee();
307 } 308}
308 "#, 309"#,
309 "caller FN_DEF FileId(1) 15..58 18..24", 310 "caller FN_DEF FileId(1) 15..58 18..24",
310 &[], 311 &[],
311 &["callee FN_DEF FileId(1) 0..14 3..9 : [33..39, 47..53]"], 312 &["callee FN_DEF FileId(1) 0..14 3..9 : [33..39, 47..53]"],
@@ -316,20 +317,20 @@ mod tests {
316 fn test_call_hierarchy_outgoing_in_different_files() { 317 fn test_call_hierarchy_outgoing_in_different_files() {
317 check_hierarchy( 318 check_hierarchy(
318 r#" 319 r#"
319 //- /lib.rs 320//- /lib.rs
320 mod foo; 321mod foo;
321 use foo::callee; 322use foo::callee;
322 323
323 fn call<|>er() { 324fn call<|>er() {
324 callee(); 325 callee();
325 } 326}
326 327
327 //- /foo/mod.rs 328//- /foo/mod.rs
328 pub fn callee() {} 329pub fn callee() {}
329 "#, 330"#,
330 "caller FN_DEF FileId(1) 26..55 29..35", 331 "caller FN_DEF FileId(1) 27..56 30..36",
331 &[], 332 &[],
332 &["callee FN_DEF FileId(2) 0..18 7..13 : [44..50]"], 333 &["callee FN_DEF FileId(2) 0..18 7..13 : [45..51]"],
333 ); 334 );
334 } 335 }
335 336
@@ -337,22 +338,59 @@ mod tests {
337 fn test_call_hierarchy_incoming_outgoing() { 338 fn test_call_hierarchy_incoming_outgoing() {
338 check_hierarchy( 339 check_hierarchy(
339 r#" 340 r#"
340 //- /lib.rs 341//- /lib.rs
341 fn caller1() { 342fn caller1() {
342 call<|>er2(); 343 call<|>er2();
343 } 344}
344 345
345 fn caller2() { 346fn caller2() {
346 caller3(); 347 caller3();
347 } 348}
348 349
349 fn caller3() { 350fn caller3() {
350 351
351 } 352}
352 "#, 353"#,
353 "caller2 FN_DEF FileId(1) 32..63 35..42", 354 "caller2 FN_DEF FileId(1) 33..64 36..43",
354 &["caller1 FN_DEF FileId(1) 0..31 3..10 : [19..26]"], 355 &["caller1 FN_DEF FileId(1) 0..31 3..10 : [19..26]"],
355 &["caller3 FN_DEF FileId(1) 64..80 67..74 : [51..58]"], 356 &["caller3 FN_DEF FileId(1) 66..83 69..76 : [52..59]"],
357 );
358 }
359
360 #[test]
361 fn test_call_hierarchy_issue_5103() {
362 check_hierarchy(
363 r#"
364fn a() {
365 b()
366}
367
368fn b() {}
369
370fn main() {
371 a<|>()
372}
373"#,
374 "a FN_DEF FileId(1) 0..18 3..4",
375 &["main FN_DEF FileId(1) 31..52 34..38 : [47..48]"],
376 &["b FN_DEF FileId(1) 20..29 23..24 : [13..14]"],
377 );
378
379 check_hierarchy(
380 r#"
381fn a() {
382 b<|>()
383}
384
385fn b() {}
386
387fn main() {
388 a()
389}
390"#,
391 "b FN_DEF FileId(1) 20..29 23..24",
392 &["a FN_DEF FileId(1) 0..18 3..4 : [13..14]"],
393 &[],
356 ); 394 );
357 } 395 }
358} 396}
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index aa039e6fc..1fe1c21de 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -7,7 +7,15 @@ use ra_syntax::{
7}; 7};
8use test_utils::mark; 8use test_utils::mark;
9 9
10use crate::{CallInfo, FilePosition, FunctionSignature}; 10use crate::{FilePosition, FunctionSignature};
11
12/// Contains information about a call site. Specifically the
13/// `FunctionSignature`and current parameter.
14#[derive(Debug)]
15pub struct CallInfo {
16 pub signature: FunctionSignature,
17 pub active_parameter: Option<usize>,
18}
11 19
12/// Computes parameter information for the given call expression. 20/// Computes parameter information for the given call expression.
13pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> { 21pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
@@ -40,43 +48,40 @@ fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Op
40 // Find the calling expression and it's NameRef 48 // Find the calling expression and it's NameRef
41 let calling_node = FnCallNode::with_node(&token.parent())?; 49 let calling_node = FnCallNode::with_node(&token.parent())?;
42 50
43 let (mut call_info, has_self) = match &calling_node { 51 let signature = match &calling_node {
44 FnCallNode::CallExpr(call) => { 52 FnCallNode::CallExpr(call) => {
45 //FIXME: Type::as_callable is broken 53 //FIXME: Type::as_callable is broken
46 let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?; 54 let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?;
47 match callable_def { 55 match callable_def {
48 hir::CallableDef::FunctionId(it) => { 56 hir::CallableDefId::FunctionId(it) => {
49 let fn_def = it.into(); 57 let fn_def = it.into();
50 (CallInfo::with_fn(sema.db, fn_def), fn_def.has_self_param(sema.db)) 58 FunctionSignature::from_hir(sema.db, fn_def)
51 } 59 }
52 hir::CallableDef::StructId(it) => { 60 hir::CallableDefId::StructId(it) => {
53 (CallInfo::with_struct(sema.db, it.into())?, false) 61 FunctionSignature::from_struct(sema.db, it.into())?
54 } 62 }
55 hir::CallableDef::EnumVariantId(it) => { 63 hir::CallableDefId::EnumVariantId(it) => {
56 (CallInfo::with_enum_variant(sema.db, it.into())?, false) 64 FunctionSignature::from_enum_variant(sema.db, it.into())?
57 } 65 }
58 } 66 }
59 } 67 }
60 FnCallNode::MethodCallExpr(method_call) => { 68 FnCallNode::MethodCallExpr(method_call) => {
61 let function = sema.resolve_method_call(&method_call)?; 69 let function = sema.resolve_method_call(&method_call)?;
62 (CallInfo::with_fn(sema.db, function), function.has_self_param(sema.db)) 70 FunctionSignature::from_hir(sema.db, function)
63 } 71 }
64 FnCallNode::MacroCallExpr(macro_call) => { 72 FnCallNode::MacroCallExpr(macro_call) => {
65 let macro_def = sema.resolve_macro_call(&macro_call)?; 73 let macro_def = sema.resolve_macro_call(&macro_call)?;
66 (CallInfo::with_macro(sema.db, macro_def)?, false) 74 FunctionSignature::from_macro(sema.db, macro_def)?
67 } 75 }
68 }; 76 };
69 77
70 // If we have a calling expression let's find which argument we are on 78 // If we have a calling expression let's find which argument we are on
71 let num_params = call_info.parameters().len(); 79 let num_params = signature.parameters.len();
72 80
73 match num_params { 81 let active_parameter = match num_params {
74 0 => (), 82 0 => None,
75 1 => { 83 1 if signature.has_self_param => None,
76 if !has_self { 84 1 => Some(0),
77 call_info.active_parameter = Some(0);
78 }
79 }
80 _ => { 85 _ => {
81 if let Some(arg_list) = calling_node.arg_list() { 86 if let Some(arg_list) = calling_node.arg_list() {
82 // Number of arguments specified at the call site 87 // Number of arguments specified at the call site
@@ -93,22 +98,24 @@ fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Op
93 arg_list 98 arg_list
94 .args() 99 .args()
95 .take_while(|arg| { 100 .take_while(|arg| {
96 arg.syntax().text_range().end() < token.text_range().start() 101 arg.syntax().text_range().end() <= token.text_range().start()
97 }) 102 })
98 .count(), 103 .count(),
99 ); 104 );
100 105
101 // If we are in a method account for `self` 106 // If we are in a method account for `self`
102 if has_self { 107 if signature.has_self_param {
103 param += 1; 108 param += 1;
104 } 109 }
105 110
106 call_info.active_parameter = Some(param); 111 Some(param)
112 } else {
113 None
107 } 114 }
108 } 115 }
109 } 116 };
110 117
111 Some(call_info) 118 Some(CallInfo { signature, active_parameter })
112} 119}
113 120
114#[derive(Debug)] 121#[derive(Debug)]
@@ -181,201 +188,192 @@ impl CallInfo {
181 let res = ActiveParameter { ty, name }; 188 let res = ActiveParameter { ty, name };
182 Some(res) 189 Some(res)
183 } 190 }
184
185 fn with_fn(db: &RootDatabase, function: hir::Function) -> Self {
186 let signature = FunctionSignature::from_hir(db, function);
187
188 CallInfo { signature, active_parameter: None }
189 }
190
191 fn with_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
192 let signature = FunctionSignature::from_struct(db, st)?;
193
194 Some(CallInfo { signature, active_parameter: None })
195 }
196
197 fn with_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
198 let signature = FunctionSignature::from_enum_variant(db, variant)?;
199
200 Some(CallInfo { signature, active_parameter: None })
201 }
202
203 fn with_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
204 let signature = FunctionSignature::from_macro(db, macro_def)?;
205
206 Some(CallInfo { signature, active_parameter: None })
207 }
208
209 fn parameters(&self) -> &[String] {
210 &self.signature.parameters
211 }
212} 191}
213 192
214#[cfg(test)] 193#[cfg(test)]
215mod tests { 194mod tests {
195 use expect::{expect, Expect};
216 use test_utils::mark; 196 use test_utils::mark;
217 197
218 use crate::mock_analysis::single_file_with_position; 198 use crate::mock_analysis::analysis_and_position;
219
220 use super::*;
221
222 // These are only used when testing
223 impl CallInfo {
224 fn doc(&self) -> Option<hir::Documentation> {
225 self.signature.doc.clone()
226 }
227
228 fn label(&self) -> String {
229 self.signature.to_string()
230 }
231 }
232
233 fn call_info_helper(text: &str) -> Option<CallInfo> {
234 let (analysis, position) = single_file_with_position(text);
235 analysis.call_info(position).unwrap()
236 }
237
238 fn call_info(text: &str) -> CallInfo {
239 let info = call_info_helper(text);
240 assert!(info.is_some());
241 info.unwrap()
242 }
243 199
244 fn no_call_info(text: &str) { 200 fn check(ra_fixture: &str, expect: Expect) {
245 let info = call_info_helper(text); 201 let (analysis, position) = analysis_and_position(ra_fixture);
246 assert!(info.is_none()); 202 let call_info = analysis.call_info(position).unwrap();
203 let actual = match call_info {
204 Some(call_info) => {
205 let docs = match &call_info.signature.doc {
206 None => "".to_string(),
207 Some(docs) => format!("{}\n------\n", docs.as_str()),
208 };
209 let params = call_info
210 .signature
211 .parameters
212 .iter()
213 .enumerate()
214 .map(|(i, param)| {
215 if Some(i) == call_info.active_parameter {
216 format!("<{}>", param)
217 } else {
218 param.clone()
219 }
220 })
221 .collect::<Vec<_>>()
222 .join(", ");
223 format!("{}{}\n({})\n", docs, call_info.signature, params)
224 }
225 None => String::new(),
226 };
227 expect.assert_eq(&actual);
247 } 228 }
248 229
249 #[test] 230 #[test]
250 fn test_fn_signature_two_args_firstx() { 231 fn test_fn_signature_two_args() {
251 let info = call_info( 232 check(
252 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 233 r#"
253fn bar() { foo(<|>3, ); }"#, 234fn foo(x: u32, y: u32) -> u32 {x + y}
235fn bar() { foo(<|>3, ); }
236"#,
237 expect![[r#"
238 fn foo(x: u32, y: u32) -> u32
239 (<x: u32>, y: u32)
240 "#]],
254 ); 241 );
255 242 check(
256 assert_eq!(info.parameters(), ["x: u32", "y: u32"]); 243 r#"
257 assert_eq!(info.active_parameter, Some(0)); 244fn foo(x: u32, y: u32) -> u32 {x + y}
258 } 245fn bar() { foo(3<|>, ); }
259 246"#,
260 #[test] 247 expect![[r#"
261 fn test_fn_signature_two_args_second() { 248 fn foo(x: u32, y: u32) -> u32
262 let info = call_info( 249 (<x: u32>, y: u32)
263 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 250 "#]],
264fn bar() { foo(3, <|>); }"#, 251 );
252 check(
253 r#"
254fn foo(x: u32, y: u32) -> u32 {x + y}
255fn bar() { foo(3,<|> ); }
256"#,
257 expect![[r#"
258 fn foo(x: u32, y: u32) -> u32
259 (x: u32, <y: u32>)
260 "#]],
261 );
262 check(
263 r#"
264fn foo(x: u32, y: u32) -> u32 {x + y}
265fn bar() { foo(3, <|>); }
266"#,
267 expect![[r#"
268 fn foo(x: u32, y: u32) -> u32
269 (x: u32, <y: u32>)
270 "#]],
265 ); 271 );
266
267 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
268 assert_eq!(info.active_parameter, Some(1));
269 } 272 }
270 273
271 #[test] 274 #[test]
272 fn test_fn_signature_two_args_empty() { 275 fn test_fn_signature_two_args_empty() {
273 let info = call_info( 276 check(
274 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 277 r#"
275fn bar() { foo(<|>); }"#, 278fn foo(x: u32, y: u32) -> u32 {x + y}
279fn bar() { foo(<|>); }
280"#,
281 expect![[r#"
282 fn foo(x: u32, y: u32) -> u32
283 (<x: u32>, y: u32)
284 "#]],
276 ); 285 );
277
278 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
279 assert_eq!(info.active_parameter, Some(0));
280 } 286 }
281 287
282 #[test] 288 #[test]
283 fn test_fn_signature_two_args_first_generics() { 289 fn test_fn_signature_two_args_first_generics() {
284 let info = call_info( 290 check(
285 r#"fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 where T: Copy + Display, U: Debug {x + y}
286fn bar() { foo(<|>3, ); }"#,
287 );
288
289 assert_eq!(info.parameters(), ["x: T", "y: U"]);
290 assert_eq!(
291 info.label(),
292 r#" 291 r#"
293fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 292fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
294where T: Copy + Display, 293 where T: Copy + Display, U: Debug
295 U: Debug 294{ x + y }
296 "# 295
297 .trim() 296fn bar() { foo(<|>3, ); }
297"#,
298 expect![[r#"
299 fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
300 where T: Copy + Display,
301 U: Debug
302 (<x: T>, y: U)
303 "#]],
298 ); 304 );
299 assert_eq!(info.active_parameter, Some(0));
300 } 305 }
301 306
302 #[test] 307 #[test]
303 fn test_fn_signature_no_params() { 308 fn test_fn_signature_no_params() {
304 let info = call_info( 309 check(
305 r#"fn foo<T>() -> T where T: Copy + Display {}
306fn bar() { foo(<|>); }"#,
307 );
308
309 assert!(info.parameters().is_empty());
310 assert_eq!(
311 info.label(),
312 r#" 310 r#"
313fn foo<T>() -> T 311fn foo<T>() -> T where T: Copy + Display {}
314where T: Copy + Display 312fn bar() { foo(<|>); }
315 "# 313"#,
316 .trim() 314 expect![[r#"
315 fn foo<T>() -> T
316 where T: Copy + Display
317 ()
318 "#]],
317 ); 319 );
318 assert!(info.active_parameter.is_none());
319 } 320 }
320 321
321 #[test] 322 #[test]
322 fn test_fn_signature_for_impl() { 323 fn test_fn_signature_for_impl() {
323 let info = call_info( 324 check(
324 r#"struct F; impl F { pub fn new() { F{}} } 325 r#"
325fn bar() {let _ : F = F::new(<|>);}"#, 326struct F; impl F { pub fn new() { F{}} }
327fn bar() {let _ : F = F::new(<|>);}
328"#,
329 expect![[r#"
330 pub fn new()
331 ()
332 "#]],
326 ); 333 );
327
328 assert!(info.parameters().is_empty());
329 assert_eq!(info.active_parameter, None);
330 } 334 }
331 335
332 #[test] 336 #[test]
333 fn test_fn_signature_for_method_self() { 337 fn test_fn_signature_for_method_self() {
334 let info = call_info( 338 check(
335 r#"struct F; 339 r#"
336impl F { 340struct S;
337 pub fn new() -> F{ 341impl S { pub fn do_it(&self) {} }
338 F{}
339 }
340
341 pub fn do_it(&self) {}
342}
343 342
344fn bar() { 343fn bar() {
345 let f : F = F::new(); 344 let s: S = S;
346 f.do_it(<|>); 345 s.do_it(<|>);
347}"#, 346}
347"#,
348 expect![[r#"
349 pub fn do_it(&self)
350 (&self)
351 "#]],
348 ); 352 );
349
350 assert_eq!(info.parameters(), ["&self"]);
351 assert_eq!(info.active_parameter, None);
352 } 353 }
353 354
354 #[test] 355 #[test]
355 fn test_fn_signature_for_method_with_arg() { 356 fn test_fn_signature_for_method_with_arg() {
356 let info = call_info( 357 check(
357 r#"struct F; 358 r#"
358impl F { 359struct S;
359 pub fn new() -> F{ 360impl S { pub fn do_it(&self, x: i32) {} }
360 F{}
361 }
362
363 pub fn do_it(&self, x: i32) {}
364}
365 361
366fn bar() { 362fn bar() {
367 let f : F = F::new(); 363 let s: S = S;
368 f.do_it(<|>); 364 s.do_it(<|>);
369}"#, 365}
366"#,
367 expect![[r#"
368 pub fn do_it(&self, x: i32)
369 (&self, <x: i32>)
370 "#]],
370 ); 371 );
371
372 assert_eq!(info.parameters(), ["&self", "x: i32"]);
373 assert_eq!(info.active_parameter, Some(1));
374 } 372 }
375 373
376 #[test] 374 #[test]
377 fn test_fn_signature_with_docs_simple() { 375 fn test_fn_signature_with_docs_simple() {
378 let info = call_info( 376 check(
379 r#" 377 r#"
380/// test 378/// test
381// non-doc-comment 379// non-doc-comment
@@ -387,17 +385,18 @@ fn bar() {
387 let _ = foo(<|>); 385 let _ = foo(<|>);
388} 386}
389"#, 387"#,
388 expect![[r#"
389 test
390 ------
391 fn foo(j: u32) -> u32
392 (<j: u32>)
393 "#]],
390 ); 394 );
391
392 assert_eq!(info.parameters(), ["j: u32"]);
393 assert_eq!(info.active_parameter, Some(0));
394 assert_eq!(info.label(), "fn foo(j: u32) -> u32");
395 assert_eq!(info.doc().map(|it| it.into()), Some("test".to_string()));
396 } 395 }
397 396
398 #[test] 397 #[test]
399 fn test_fn_signature_with_docs() { 398 fn test_fn_signature_with_docs() {
400 let info = call_info( 399 check(
401 r#" 400 r#"
402/// Adds one to the number given. 401/// Adds one to the number given.
403/// 402///
@@ -415,31 +414,26 @@ pub fn add_one(x: i32) -> i32 {
415pub fn do() { 414pub fn do() {
416 add_one(<|> 415 add_one(<|>
417}"#, 416}"#,
418 ); 417 expect![[r##"
418 Adds one to the number given.
419 419
420 assert_eq!(info.parameters(), ["x: i32"]); 420 # Examples
421 assert_eq!(info.active_parameter, Some(0));
422 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
423 assert_eq!(
424 info.doc().map(|it| it.into()),
425 Some(
426 r#"Adds one to the number given.
427 421
428# Examples 422 ```
423 let five = 5;
429 424
430``` 425 assert_eq!(6, my_crate::add_one(5));
431let five = 5; 426 ```
432 427 ------
433assert_eq!(6, my_crate::add_one(5)); 428 pub fn add_one(x: i32) -> i32
434```"# 429 (<x: i32>)
435 .to_string() 430 "##]],
436 )
437 ); 431 );
438 } 432 }
439 433
440 #[test] 434 #[test]
441 fn test_fn_signature_with_docs_impl() { 435 fn test_fn_signature_with_docs_impl() {
442 let info = call_info( 436 check(
443 r#" 437 r#"
444struct addr; 438struct addr;
445impl addr { 439impl addr {
@@ -460,32 +454,28 @@ impl addr {
460pub fn do_it() { 454pub fn do_it() {
461 addr {}; 455 addr {};
462 addr::add_one(<|>); 456 addr::add_one(<|>);
463}"#, 457}
464 ); 458"#,
465 459 expect![[r##"
466 assert_eq!(info.parameters(), ["x: i32"]); 460 Adds one to the number given.
467 assert_eq!(info.active_parameter, Some(0));
468 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
469 assert_eq!(
470 info.doc().map(|it| it.into()),
471 Some(
472 r#"Adds one to the number given.
473 461
474# Examples 462 # Examples
475 463
476``` 464 ```
477let five = 5; 465 let five = 5;
478 466
479assert_eq!(6, my_crate::add_one(5)); 467 assert_eq!(6, my_crate::add_one(5));
480```"# 468 ```
481 .to_string() 469 ------
482 ) 470 pub fn add_one(x: i32) -> i32
471 (<x: i32>)
472 "##]],
483 ); 473 );
484 } 474 }
485 475
486 #[test] 476 #[test]
487 fn test_fn_signature_with_docs_from_actix() { 477 fn test_fn_signature_with_docs_from_actix() {
488 let info = call_info( 478 check(
489 r#" 479 r#"
490struct WriteHandler<E>; 480struct WriteHandler<E>;
491 481
@@ -509,101 +499,102 @@ impl<E> WriteHandler<E> {
509pub fn foo(mut r: WriteHandler<()>) { 499pub fn foo(mut r: WriteHandler<()>) {
510 r.finished(<|>); 500 r.finished(<|>);
511} 501}
512
513"#, 502"#,
514 ); 503 expect![[r#"
515 504 Method is called when writer finishes.
516 assert_eq!(info.label(), "fn finished(&mut self, ctx: &mut Self::Context)".to_string()); 505
517 assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]); 506 By default this method stops actor's `Context`.
518 assert_eq!(info.active_parameter, Some(1)); 507 ------
519 assert_eq!( 508 fn finished(&mut self, ctx: &mut Self::Context)
520 info.doc().map(|it| it.into()), 509 (&mut self, <ctx: &mut Self::Context>)
521 Some( 510 "#]],
522 r#"Method is called when writer finishes.
523
524By default this method stops actor's `Context`."#
525 .to_string()
526 )
527 ); 511 );
528 } 512 }
529 513
530 #[test] 514 #[test]
531 fn call_info_bad_offset() { 515 fn call_info_bad_offset() {
532 mark::check!(call_info_bad_offset); 516 mark::check!(call_info_bad_offset);
533 let (analysis, position) = single_file_with_position( 517 check(
534 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 518 r#"
535 fn bar() { foo <|> (3, ); }"#, 519fn foo(x: u32, y: u32) -> u32 {x + y}
520fn bar() { foo <|> (3, ); }
521"#,
522 expect![[""]],
536 ); 523 );
537 let call_info = analysis.call_info(position).unwrap();
538 assert!(call_info.is_none());
539 } 524 }
540 525
541 #[test] 526 #[test]
542 fn test_nested_method_in_lamba() { 527 fn test_nested_method_in_lambda() {
543 let info = call_info( 528 check(
544 r#"struct Foo; 529 r#"
545 530struct Foo;
546impl Foo { 531impl Foo { fn bar(&self, _: u32) { } }
547 fn bar(&self, _: u32) { }
548}
549 532
550fn bar(_: u32) { } 533fn bar(_: u32) { }
551 534
552fn main() { 535fn main() {
553 let foo = Foo; 536 let foo = Foo;
554 std::thread::spawn(move || foo.bar(<|>)); 537 std::thread::spawn(move || foo.bar(<|>));
555}"#, 538}
539"#,
540 expect![[r#"
541 fn bar(&self, _: u32)
542 (&self, <_: u32>)
543 "#]],
556 ); 544 );
557
558 assert_eq!(info.parameters(), ["&self", "_: u32"]);
559 assert_eq!(info.active_parameter, Some(1));
560 assert_eq!(info.label(), "fn bar(&self, _: u32)");
561 } 545 }
562 546
563 #[test] 547 #[test]
564 fn works_for_tuple_structs() { 548 fn works_for_tuple_structs() {
565 let info = call_info( 549 check(
566 r#" 550 r#"
567/// A cool tuple struct 551/// A cool tuple struct
568struct TS(u32, i32); 552struct TS(u32, i32);
569fn main() { 553fn main() {
570 let s = TS(0, <|>); 554 let s = TS(0, <|>);
571}"#, 555}
556"#,
557 expect![[r#"
558 A cool tuple struct
559 ------
560 struct TS(u32, i32) -> TS
561 (u32, <i32>)
562 "#]],
572 ); 563 );
573
574 assert_eq!(info.label(), "struct TS(u32, i32) -> TS");
575 assert_eq!(info.doc().map(|it| it.into()), Some("A cool tuple struct".to_string()));
576 assert_eq!(info.active_parameter, Some(1));
577 } 564 }
578 565
579 #[test] 566 #[test]
580 fn generic_struct() { 567 fn generic_struct() {
581 let info = call_info( 568 check(
582 r#" 569 r#"
583struct TS<T>(T); 570struct TS<T>(T);
584fn main() { 571fn main() {
585 let s = TS(<|>); 572 let s = TS(<|>);
586}"#, 573}
574"#,
575 expect![[r#"
576 struct TS<T>(T) -> TS
577 (<T>)
578 "#]],
587 ); 579 );
588
589 assert_eq!(info.label(), "struct TS<T>(T) -> TS");
590 assert_eq!(info.active_parameter, Some(0));
591 } 580 }
592 581
593 #[test] 582 #[test]
594 fn cant_call_named_structs() { 583 fn cant_call_named_structs() {
595 no_call_info( 584 check(
596 r#" 585 r#"
597struct TS { x: u32, y: i32 } 586struct TS { x: u32, y: i32 }
598fn main() { 587fn main() {
599 let s = TS(<|>); 588 let s = TS(<|>);
600}"#, 589}
590"#,
591 expect![[""]],
601 ); 592 );
602 } 593 }
603 594
604 #[test] 595 #[test]
605 fn works_for_enum_variants() { 596 fn works_for_enum_variants() {
606 let info = call_info( 597 check(
607 r#" 598 r#"
608enum E { 599enum E {
609 /// A Variant 600 /// A Variant
@@ -617,17 +608,19 @@ enum E {
617fn main() { 608fn main() {
618 let a = E::A(<|>); 609 let a = E::A(<|>);
619} 610}
620 "#, 611"#,
612 expect![[r#"
613 A Variant
614 ------
615 E::A(0: i32)
616 (<0: i32>)
617 "#]],
621 ); 618 );
622
623 assert_eq!(info.label(), "E::A(0: i32)");
624 assert_eq!(info.doc().map(|it| it.into()), Some("A Variant".to_string()));
625 assert_eq!(info.active_parameter, Some(0));
626 } 619 }
627 620
628 #[test] 621 #[test]
629 fn cant_call_enum_records() { 622 fn cant_call_enum_records() {
630 no_call_info( 623 check(
631 r#" 624 r#"
632enum E { 625enum E {
633 /// A Variant 626 /// A Variant
@@ -641,13 +634,14 @@ enum E {
641fn main() { 634fn main() {
642 let a = E::C(<|>); 635 let a = E::C(<|>);
643} 636}
644 "#, 637"#,
638 expect![[""]],
645 ); 639 );
646 } 640 }
647 641
648 #[test] 642 #[test]
649 fn fn_signature_for_macro() { 643 fn fn_signature_for_macro() {
650 let info = call_info( 644 check(
651 r#" 645 r#"
652/// empty macro 646/// empty macro
653macro_rules! foo { 647macro_rules! foo {
@@ -657,31 +651,30 @@ macro_rules! foo {
657fn f() { 651fn f() {
658 foo!(<|>); 652 foo!(<|>);
659} 653}
660 "#, 654"#,
655 expect![[r#"
656 empty macro
657 ------
658 foo!()
659 ()
660 "#]],
661 ); 661 );
662
663 assert_eq!(info.label(), "foo!()");
664 assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string()));
665 } 662 }
666 663
667 #[test] 664 #[test]
668 fn fn_signature_for_call_in_macro() { 665 fn fn_signature_for_call_in_macro() {
669 let info = call_info( 666 check(
670 r#" 667 r#"
671 macro_rules! id { 668macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
672 ($($tt:tt)*) => { $($tt)* } 669fn foo() { }
673 } 670id! {
674 fn foo() { 671 fn bar() { foo(<|>); }
675 672}
676 } 673"#,
677 id! { 674 expect![[r#"
678 fn bar() { 675 fn foo()
679 foo(<|>); 676 ()
680 } 677 "#]],
681 }
682 "#,
683 ); 678 );
684
685 assert_eq!(info.label(), "fn foo()");
686 } 679 }
687} 680}
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index d890b69d2..f3a5e9573 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -2,6 +2,9 @@ mod completion_config;
2mod completion_item; 2mod completion_item;
3mod completion_context; 3mod completion_context;
4mod presentation; 4mod presentation;
5mod patterns;
6#[cfg(test)]
7mod test_utils;
5 8
6mod complete_attribute; 9mod complete_attribute;
7mod complete_dot; 10mod complete_dot;
@@ -15,8 +18,6 @@ mod complete_unqualified_path;
15mod complete_postfix; 18mod complete_postfix;
16mod complete_macro_in_item_position; 19mod complete_macro_in_item_position;
17mod complete_trait_impl; 20mod complete_trait_impl;
18#[cfg(test)]
19mod test_utils;
20 21
21use ra_ide_db::RootDatabase; 22use ra_ide_db::RootDatabase;
22 23
@@ -62,11 +63,11 @@ pub use crate::completion::{
62// There also snippet completions: 63// There also snippet completions:
63// 64//
64// .Expressions 65// .Expressions
65// - `pd` -> `println!("{:?}")` 66// - `pd` -> `eprintln!(" = {:?}", );")`
66// - `ppd` -> `println!("{:#?}")` 67// - `ppd` -> `eprintln!(" = {:#?}", );`
67// 68//
68// .Items 69// .Items
69// - `tfn` -> `#[test] fn f(){}` 70// - `tfn` -> `#[test] fn feature(){}`
70// - `tmod` -> 71// - `tmod` ->
71// ```rust 72// ```rust
72// #[cfg(test)] 73// #[cfg(test)]
@@ -74,7 +75,7 @@ pub use crate::completion::{
74// use super::*; 75// use super::*;
75// 76//
76// #[test] 77// #[test]
77// fn test_fn() {} 78// fn test_name() {}
78// } 79// }
79// ``` 80// ```
80 81
@@ -125,3 +126,81 @@ pub(crate) fn completions(
125 126
126 Some(acc) 127 Some(acc)
127} 128}
129
130#[cfg(test)]
131mod tests {
132 use crate::completion::completion_config::CompletionConfig;
133 use crate::mock_analysis::analysis_and_position;
134
135 struct DetailAndDocumentation<'a> {
136 detail: &'a str,
137 documentation: &'a str,
138 }
139
140 fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
141 let (analysis, position) = analysis_and_position(ra_fixture);
142 let config = CompletionConfig::default();
143 let completions = analysis.completions(&config, position).unwrap().unwrap();
144 for item in completions {
145 if item.detail() == Some(expected.detail) {
146 let opt = item.documentation();
147 let doc = opt.as_ref().map(|it| it.as_str());
148 assert_eq!(doc, Some(expected.documentation));
149 return;
150 }
151 }
152 panic!("completion detail not found: {}", expected.detail)
153 }
154
155 #[test]
156 fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
157 check_detail_and_documentation(
158 r#"
159 //- /lib.rs
160 macro_rules! bar {
161 () => {
162 struct Bar;
163 impl Bar {
164 #[doc = "Do the foo"]
165 fn foo(&self) {}
166 }
167 }
168 }
169
170 bar!();
171
172 fn foo() {
173 let bar = Bar;
174 bar.fo<|>;
175 }
176 "#,
177 DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
178 );
179 }
180
181 #[test]
182 fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
183 check_detail_and_documentation(
184 r#"
185 //- /lib.rs
186 macro_rules! bar {
187 () => {
188 struct Bar;
189 impl Bar {
190 /// Do the foo
191 fn foo(&self) {}
192 }
193 }
194 }
195
196 bar!();
197
198 fn foo() {
199 let bar = Bar;
200 bar.fo<|>;
201 }
202 "#,
203 DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
204 );
205 }
206}
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
index fb3f0b743..d268c92be 100644
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -20,6 +20,7 @@ pub(super) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
20 { 20 {
21 complete_derive(acc, ctx, token_tree) 21 complete_derive(acc, ctx, token_tree)
22 } 22 }
23 (_, Some(ast::AttrInput::TokenTree(_token_tree))) => {}
23 _ => complete_attribute_start(acc, ctx, attribute), 24 _ => complete_attribute_start(acc, ctx, attribute),
24 } 25 }
25 Some(()) 26 Some(())
@@ -34,6 +35,10 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
34 ) 35 )
35 .kind(CompletionItemKind::Attribute); 36 .kind(CompletionItemKind::Attribute);
36 37
38 if let Some(lookup) = attr_completion.lookup {
39 item = item.lookup_by(lookup);
40 }
41
37 match (attr_completion.snippet, ctx.config.snippet_cap) { 42 match (attr_completion.snippet, ctx.config.snippet_cap) {
38 (Some(snippet), Some(cap)) => { 43 (Some(snippet), Some(cap)) => {
39 item = item.insert_snippet(cap, snippet); 44 item = item.insert_snippet(cap, snippet);
@@ -41,7 +46,7 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
41 _ => {} 46 _ => {}
42 } 47 }
43 48
44 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.should_be_inner { 49 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
45 acc.add(item); 50 acc.add(item);
46 } 51 }
47 } 52 }
@@ -49,85 +54,74 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
49 54
50struct AttrCompletion { 55struct AttrCompletion {
51 label: &'static str, 56 label: &'static str,
57 lookup: Option<&'static str>,
58 snippet: Option<&'static str>,
59 prefer_inner: bool,
60}
61
62impl AttrCompletion {
63 const fn prefer_inner(self) -> AttrCompletion {
64 AttrCompletion { prefer_inner: true, ..self }
65 }
66}
67
68const fn attr(
69 label: &'static str,
70 lookup: Option<&'static str>,
52 snippet: Option<&'static str>, 71 snippet: Option<&'static str>,
53 should_be_inner: bool, 72) -> AttrCompletion {
73 AttrCompletion { label, lookup, snippet, prefer_inner: false }
54} 74}
55 75
56const ATTRIBUTES: &[AttrCompletion] = &[ 76const ATTRIBUTES: &[AttrCompletion] = &[
57 AttrCompletion { label: "allow", snippet: Some("allow(${0:lint})"), should_be_inner: false }, 77 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
58 AttrCompletion { 78 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
59 label: "cfg_attr", 79 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
60 snippet: Some("cfg_attr(${1:predicate}, ${0:attr})"), 80 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
61 should_be_inner: false, 81 attr(r#"deprecated = "…""#, Some("deprecated"), Some(r#"deprecated = "${0:reason}""#)),
62 }, 82 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
63 AttrCompletion { label: "cfg", snippet: Some("cfg(${0:predicate})"), should_be_inner: false }, 83 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
64 AttrCompletion { label: "deny", snippet: Some("deny(${0:lint})"), should_be_inner: false }, 84 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
65 AttrCompletion { 85 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
66 label: "deprecated",
67 snippet: Some(r#"deprecated = "${0:reason}""#),
68 should_be_inner: false,
69 },
70 AttrCompletion {
71 label: "derive",
72 snippet: Some(r#"derive(${0:Debug})"#),
73 should_be_inner: false,
74 },
75 AttrCompletion { label: "doc", snippet: Some(r#"doc = "${0:docs}""#), should_be_inner: false },
76 AttrCompletion { label: "feature", snippet: Some("feature(${0:flag})"), should_be_inner: true },
77 AttrCompletion { label: "forbid", snippet: Some("forbid(${0:lint})"), should_be_inner: false },
78 // FIXME: resolve through macro resolution? 86 // FIXME: resolve through macro resolution?
79 AttrCompletion { label: "global_allocator", snippet: None, should_be_inner: true }, 87 attr("global_allocator", None, None).prefer_inner(),
80 AttrCompletion { label: "ignore", snippet: Some("ignore(${0:lint})"), should_be_inner: false }, 88 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
81 AttrCompletion { label: "inline", snippet: Some("inline(${0:lint})"), should_be_inner: false }, 89 attr("inline(…)", Some("inline"), Some("inline(${0:lint})")),
82 AttrCompletion { 90 attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
83 label: "link_name", 91 attr("link", None, None),
84 snippet: Some(r#"link_name = "${0:symbol_name}""#), 92 attr("macro_export", None, None),
85 should_be_inner: false, 93 attr("macro_use", None, None),
86 }, 94 attr(r#"must_use = "…""#, Some("must_use"), Some(r#"must_use = "${0:reason}""#)),
87 AttrCompletion { label: "link", snippet: None, should_be_inner: false }, 95 attr("no_mangle", None, None),
88 AttrCompletion { label: "macro_export", snippet: None, should_be_inner: false }, 96 attr("no_std", None, None).prefer_inner(),
89 AttrCompletion { label: "macro_use", snippet: None, should_be_inner: false }, 97 attr("non_exhaustive", None, None),
90 AttrCompletion { 98 attr("panic_handler", None, None).prefer_inner(),
91 label: "must_use", 99 attr("path = \"…\"", Some("path"), Some("path =\"${0:path}\"")),
92 snippet: Some(r#"must_use = "${0:reason}""#), 100 attr("proc_macro", None, None),
93 should_be_inner: false, 101 attr("proc_macro_attribute", None, None),
94 }, 102 attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
95 AttrCompletion { label: "no_mangle", snippet: None, should_be_inner: false }, 103 attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
96 AttrCompletion { label: "no_std", snippet: None, should_be_inner: true }, 104 .prefer_inner(),
97 AttrCompletion { label: "non_exhaustive", snippet: None, should_be_inner: false }, 105 attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
98 AttrCompletion { label: "panic_handler", snippet: None, should_be_inner: true }, 106 attr(
99 AttrCompletion { label: "path", snippet: Some("path =\"${0:path}\""), should_be_inner: false }, 107 "should_panic(…)",
100 AttrCompletion { label: "proc_macro", snippet: None, should_be_inner: false }, 108 Some("should_panic"),
101 AttrCompletion { label: "proc_macro_attribute", snippet: None, should_be_inner: false }, 109 Some(r#"should_panic(expected = "${0:reason}")"#),
102 AttrCompletion { 110 ),
103 label: "proc_macro_derive", 111 attr(
104 snippet: Some("proc_macro_derive(${0:Trait})"), 112 r#"target_feature = "…""#,
105 should_be_inner: false, 113 Some("target_feature"),
106 }, 114 Some("target_feature = \"${0:feature}\""),
107 AttrCompletion { 115 ),
108 label: "recursion_limit", 116 attr("test", None, None),
109 snippet: Some("recursion_limit = ${0:128}"), 117 attr("used", None, None),
110 should_be_inner: true, 118 attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
111 }, 119 attr(
112 AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false }, 120 r#"windows_subsystem = "…""#,
113 AttrCompletion { 121 Some("windows_subsystem"),
114 label: "should_panic", 122 Some(r#"windows_subsystem = "${0:subsystem}""#),
115 snippet: Some(r#"should_panic(expected = "${0:reason}")"#), 123 )
116 should_be_inner: false, 124 .prefer_inner(),
117 },
118 AttrCompletion {
119 label: "target_feature",
120 snippet: Some("target_feature = \"${0:feature}\""),
121 should_be_inner: false,
122 },
123 AttrCompletion { label: "test", snippet: None, should_be_inner: false },
124 AttrCompletion { label: "used", snippet: None, should_be_inner: false },
125 AttrCompletion { label: "warn", snippet: Some("warn(${0:lint})"), should_be_inner: false },
126 AttrCompletion {
127 label: "windows_subsystem",
128 snippet: Some(r#"windows_subsystem = "${0:subsystem}""#),
129 should_be_inner: true,
130 },
131]; 125];
132 126
133fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { 127fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) {
@@ -201,7 +195,7 @@ fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>,
201 195
202fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { 196fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
203 let mut result = FxHashSet::default(); 197 let mut result = FxHashSet::default();
204 ctx.scope().process_all_names(&mut |name, scope_def| { 198 ctx.scope.process_all_names(&mut |name, scope_def| {
205 if let hir::ScopeDef::MacroDef(mac) = scope_def { 199 if let hir::ScopeDef::MacroDef(mac) = scope_def {
206 if mac.is_derive_macro() { 200 if mac.is_derive_macro() {
207 result.insert(name.to_string()); 201 result.insert(name.to_string());
@@ -232,624 +226,147 @@ const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
232 226
233#[cfg(test)] 227#[cfg(test)]
234mod tests { 228mod tests {
235 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 229 use expect::{expect, Expect};
236 use insta::assert_debug_snapshot; 230
231 use crate::completion::{test_utils::completion_list, CompletionKind};
237 232
238 fn do_attr_completion(code: &str) -> Vec<CompletionItem> { 233 fn check(ra_fixture: &str, expect: Expect) {
239 do_completion(code, CompletionKind::Attribute) 234 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
235 expect.assert_eq(&actual);
240 } 236 }
241 237
242 #[test] 238 #[test]
243 fn empty_derive_completion() { 239 fn empty_derive_completion() {
244 assert_debug_snapshot!( 240 check(
245 do_attr_completion( 241 r#"
246 r" 242#[derive(<|>)]
247 #[derive(<|>)] 243struct Test {}
248 struct Test {} 244 "#,
249 ", 245 expect![[r#"
250 ), 246 at Clone
251 @r###" 247 at Copy, Clone
252 [ 248 at Debug
253 CompletionItem { 249 at Default
254 label: "Clone", 250 at Eq, PartialEq
255 source_range: 30..30, 251 at Hash
256 delete: 30..30, 252 at Ord, PartialOrd, Eq, PartialEq
257 insert: "Clone", 253 at PartialEq
258 kind: Attribute, 254 at PartialOrd, PartialEq
259 }, 255 "#]],
260 CompletionItem {
261 label: "Copy, Clone",
262 source_range: 30..30,
263 delete: 30..30,
264 insert: "Copy, Clone",
265 kind: Attribute,
266 },
267 CompletionItem {
268 label: "Debug",
269 source_range: 30..30,
270 delete: 30..30,
271 insert: "Debug",
272 kind: Attribute,
273 },
274 CompletionItem {
275 label: "Default",
276 source_range: 30..30,
277 delete: 30..30,
278 insert: "Default",
279 kind: Attribute,
280 },
281 CompletionItem {
282 label: "Eq, PartialEq",
283 source_range: 30..30,
284 delete: 30..30,
285 insert: "Eq, PartialEq",
286 kind: Attribute,
287 },
288 CompletionItem {
289 label: "Hash",
290 source_range: 30..30,
291 delete: 30..30,
292 insert: "Hash",
293 kind: Attribute,
294 },
295 CompletionItem {
296 label: "Ord, PartialOrd, Eq, PartialEq",
297 source_range: 30..30,
298 delete: 30..30,
299 insert: "Ord, PartialOrd, Eq, PartialEq",
300 kind: Attribute,
301 },
302 CompletionItem {
303 label: "PartialEq",
304 source_range: 30..30,
305 delete: 30..30,
306 insert: "PartialEq",
307 kind: Attribute,
308 },
309 CompletionItem {
310 label: "PartialOrd, PartialEq",
311 source_range: 30..30,
312 delete: 30..30,
313 insert: "PartialOrd, PartialEq",
314 kind: Attribute,
315 },
316 ]
317 "###
318 ); 256 );
319 } 257 }
320 258
321 #[test] 259 #[test]
322 fn no_completion_for_incorrect_derive() { 260 fn no_completion_for_incorrect_derive() {
323 assert_debug_snapshot!( 261 check(
324 do_attr_completion( 262 r#"
325 r" 263#[derive{<|>)]
326 #[derive{<|>)] 264struct Test {}
327 struct Test {} 265"#,
328 ", 266 expect![[r#""#]],
329 ), 267 )
330 @"[]"
331 );
332 } 268 }
333 269
334 #[test] 270 #[test]
335 fn derive_with_input_completion() { 271 fn derive_with_input_completion() {
336 assert_debug_snapshot!( 272 check(
337 do_attr_completion( 273 r#"
338 r" 274#[derive(serde::Serialize, PartialEq, <|>)]
339 #[derive(serde::Serialize, PartialEq, <|>)] 275struct Test {}
340 struct Test {} 276"#,
341 ", 277 expect![[r#"
342 ), 278 at Clone
343 @r###" 279 at Copy, Clone
344 [ 280 at Debug
345 CompletionItem { 281 at Default
346 label: "Clone", 282 at Eq
347 source_range: 59..59, 283 at Hash
348 delete: 59..59, 284 at Ord, PartialOrd, Eq
349 insert: "Clone", 285 at PartialOrd
350 kind: Attribute, 286 "#]],
351 }, 287 )
352 CompletionItem {
353 label: "Copy, Clone",
354 source_range: 59..59,
355 delete: 59..59,
356 insert: "Copy, Clone",
357 kind: Attribute,
358 },
359 CompletionItem {
360 label: "Debug",
361 source_range: 59..59,
362 delete: 59..59,
363 insert: "Debug",
364 kind: Attribute,
365 },
366 CompletionItem {
367 label: "Default",
368 source_range: 59..59,
369 delete: 59..59,
370 insert: "Default",
371 kind: Attribute,
372 },
373 CompletionItem {
374 label: "Eq",
375 source_range: 59..59,
376 delete: 59..59,
377 insert: "Eq",
378 kind: Attribute,
379 },
380 CompletionItem {
381 label: "Hash",
382 source_range: 59..59,
383 delete: 59..59,
384 insert: "Hash",
385 kind: Attribute,
386 },
387 CompletionItem {
388 label: "Ord, PartialOrd, Eq",
389 source_range: 59..59,
390 delete: 59..59,
391 insert: "Ord, PartialOrd, Eq",
392 kind: Attribute,
393 },
394 CompletionItem {
395 label: "PartialOrd",
396 source_range: 59..59,
397 delete: 59..59,
398 insert: "PartialOrd",
399 kind: Attribute,
400 },
401 ]
402 "###
403 );
404 } 288 }
405 289
406 #[test] 290 #[test]
407 fn test_attribute_completion() { 291 fn test_attribute_completion() {
408 assert_debug_snapshot!( 292 check(
409 do_attr_completion( 293 r#"#[<|>]"#,
410 r" 294 expect![[r#"
411 #[<|>] 295 at allow(…)
412 ", 296 at cfg(…)
413 ), 297 at cfg_attr(…)
414 @r###" 298 at deny(…)
415 [ 299 at deprecated = "…"
416 CompletionItem { 300 at derive(…)
417 label: "allow", 301 at doc = "…"
418 source_range: 19..19, 302 at forbid(…)
419 delete: 19..19, 303 at ignore = "…"
420 insert: "allow(${0:lint})", 304 at inline(…)
421 kind: Attribute, 305 at link
422 }, 306 at link_name = "…"
423 CompletionItem { 307 at macro_export
424 label: "cfg", 308 at macro_use
425 source_range: 19..19, 309 at must_use = "…"
426 delete: 19..19, 310 at no_mangle
427 insert: "cfg(${0:predicate})", 311 at non_exhaustive
428 kind: Attribute, 312 at path = "…"
429 }, 313 at proc_macro
430 CompletionItem { 314 at proc_macro_attribute
431 label: "cfg_attr", 315 at proc_macro_derive(…)
432 source_range: 19..19, 316 at repr(…)
433 delete: 19..19, 317 at should_panic(…)
434 insert: "cfg_attr(${1:predicate}, ${0:attr})", 318 at target_feature = "…"
435 kind: Attribute, 319 at test
436 }, 320 at used
437 CompletionItem { 321 at warn(…)
438 label: "deny", 322 "#]],
439 source_range: 19..19, 323 )
440 delete: 19..19, 324 }
441 insert: "deny(${0:lint})", 325
442 kind: Attribute, 326 #[test]
443 }, 327 fn test_attribute_completion_inside_nested_attr() {
444 CompletionItem { 328 check(r#"#[allow(<|>)]"#, expect![[]])
445 label: "deprecated",
446 source_range: 19..19,
447 delete: 19..19,
448 insert: "deprecated = \"${0:reason}\"",
449 kind: Attribute,
450 },
451 CompletionItem {
452 label: "derive",
453 source_range: 19..19,
454 delete: 19..19,
455 insert: "derive(${0:Debug})",
456 kind: Attribute,
457 },
458 CompletionItem {
459 label: "doc",
460 source_range: 19..19,
461 delete: 19..19,
462 insert: "doc = \"${0:docs}\"",
463 kind: Attribute,
464 },
465 CompletionItem {
466 label: "forbid",
467 source_range: 19..19,
468 delete: 19..19,
469 insert: "forbid(${0:lint})",
470 kind: Attribute,
471 },
472 CompletionItem {
473 label: "ignore",
474 source_range: 19..19,
475 delete: 19..19,
476 insert: "ignore(${0:lint})",
477 kind: Attribute,
478 },
479 CompletionItem {
480 label: "inline",
481 source_range: 19..19,
482 delete: 19..19,
483 insert: "inline(${0:lint})",
484 kind: Attribute,
485 },
486 CompletionItem {
487 label: "link",
488 source_range: 19..19,
489 delete: 19..19,
490 insert: "link",
491 kind: Attribute,
492 },
493 CompletionItem {
494 label: "link_name",
495 source_range: 19..19,
496 delete: 19..19,
497 insert: "link_name = \"${0:symbol_name}\"",
498 kind: Attribute,
499 },
500 CompletionItem {
501 label: "macro_export",
502 source_range: 19..19,
503 delete: 19..19,
504 insert: "macro_export",
505 kind: Attribute,
506 },
507 CompletionItem {
508 label: "macro_use",
509 source_range: 19..19,
510 delete: 19..19,
511 insert: "macro_use",
512 kind: Attribute,
513 },
514 CompletionItem {
515 label: "must_use",
516 source_range: 19..19,
517 delete: 19..19,
518 insert: "must_use = \"${0:reason}\"",
519 kind: Attribute,
520 },
521 CompletionItem {
522 label: "no_mangle",
523 source_range: 19..19,
524 delete: 19..19,
525 insert: "no_mangle",
526 kind: Attribute,
527 },
528 CompletionItem {
529 label: "non_exhaustive",
530 source_range: 19..19,
531 delete: 19..19,
532 insert: "non_exhaustive",
533 kind: Attribute,
534 },
535 CompletionItem {
536 label: "path",
537 source_range: 19..19,
538 delete: 19..19,
539 insert: "path =\"${0:path}\"",
540 kind: Attribute,
541 },
542 CompletionItem {
543 label: "proc_macro",
544 source_range: 19..19,
545 delete: 19..19,
546 insert: "proc_macro",
547 kind: Attribute,
548 },
549 CompletionItem {
550 label: "proc_macro_attribute",
551 source_range: 19..19,
552 delete: 19..19,
553 insert: "proc_macro_attribute",
554 kind: Attribute,
555 },
556 CompletionItem {
557 label: "proc_macro_derive",
558 source_range: 19..19,
559 delete: 19..19,
560 insert: "proc_macro_derive(${0:Trait})",
561 kind: Attribute,
562 },
563 CompletionItem {
564 label: "repr",
565 source_range: 19..19,
566 delete: 19..19,
567 insert: "repr(${0:C})",
568 kind: Attribute,
569 },
570 CompletionItem {
571 label: "should_panic",
572 source_range: 19..19,
573 delete: 19..19,
574 insert: "should_panic(expected = \"${0:reason}\")",
575 kind: Attribute,
576 },
577 CompletionItem {
578 label: "target_feature",
579 source_range: 19..19,
580 delete: 19..19,
581 insert: "target_feature = \"${0:feature}\"",
582 kind: Attribute,
583 },
584 CompletionItem {
585 label: "test",
586 source_range: 19..19,
587 delete: 19..19,
588 insert: "test",
589 kind: Attribute,
590 },
591 CompletionItem {
592 label: "used",
593 source_range: 19..19,
594 delete: 19..19,
595 insert: "used",
596 kind: Attribute,
597 },
598 CompletionItem {
599 label: "warn",
600 source_range: 19..19,
601 delete: 19..19,
602 insert: "warn(${0:lint})",
603 kind: Attribute,
604 },
605 ]
606 "###
607 );
608 } 329 }
609 330
610 #[test] 331 #[test]
611 fn test_inner_attribute_completion() { 332 fn test_inner_attribute_completion() {
612 assert_debug_snapshot!( 333 check(
613 do_attr_completion( 334 r"#![<|>]",
614 r" 335 expect![[r#"
615 #![<|>] 336 at allow(…)
616 ", 337 at cfg(…)
617 ), 338 at cfg_attr(…)
618 @r###" 339 at deny(…)
619 [ 340 at deprecated = "…"
620 CompletionItem { 341 at derive(…)
621 label: "allow", 342 at doc = "…"
622 source_range: 20..20, 343 at feature(…)
623 delete: 20..20, 344 at forbid(…)
624 insert: "allow(${0:lint})", 345 at global_allocator
625 kind: Attribute, 346 at ignore = "…"
626 }, 347 at inline(…)
627 CompletionItem { 348 at link
628 label: "cfg", 349 at link_name = "…"
629 source_range: 20..20, 350 at macro_export
630 delete: 20..20, 351 at macro_use
631 insert: "cfg(${0:predicate})", 352 at must_use = "…"
632 kind: Attribute, 353 at no_mangle
633 }, 354 at no_std
634 CompletionItem { 355 at non_exhaustive
635 label: "cfg_attr", 356 at panic_handler
636 source_range: 20..20, 357 at path = "…"
637 delete: 20..20, 358 at proc_macro
638 insert: "cfg_attr(${1:predicate}, ${0:attr})", 359 at proc_macro_attribute
639 kind: Attribute, 360 at proc_macro_derive(…)
640 }, 361 at recursion_limit = …
641 CompletionItem { 362 at repr(…)
642 label: "deny", 363 at should_panic(…)
643 source_range: 20..20, 364 at target_feature = "…"
644 delete: 20..20, 365 at test
645 insert: "deny(${0:lint})", 366 at used
646 kind: Attribute, 367 at warn(…)
647 }, 368 at windows_subsystem = "…"
648 CompletionItem { 369 "#]],
649 label: "deprecated",
650 source_range: 20..20,
651 delete: 20..20,
652 insert: "deprecated = \"${0:reason}\"",
653 kind: Attribute,
654 },
655 CompletionItem {
656 label: "derive",
657 source_range: 20..20,
658 delete: 20..20,
659 insert: "derive(${0:Debug})",
660 kind: Attribute,
661 },
662 CompletionItem {
663 label: "doc",
664 source_range: 20..20,
665 delete: 20..20,
666 insert: "doc = \"${0:docs}\"",
667 kind: Attribute,
668 },
669 CompletionItem {
670 label: "feature",
671 source_range: 20..20,
672 delete: 20..20,
673 insert: "feature(${0:flag})",
674 kind: Attribute,
675 },
676 CompletionItem {
677 label: "forbid",
678 source_range: 20..20,
679 delete: 20..20,
680 insert: "forbid(${0:lint})",
681 kind: Attribute,
682 },
683 CompletionItem {
684 label: "global_allocator",
685 source_range: 20..20,
686 delete: 20..20,
687 insert: "global_allocator",
688 kind: Attribute,
689 },
690 CompletionItem {
691 label: "ignore",
692 source_range: 20..20,
693 delete: 20..20,
694 insert: "ignore(${0:lint})",
695 kind: Attribute,
696 },
697 CompletionItem {
698 label: "inline",
699 source_range: 20..20,
700 delete: 20..20,
701 insert: "inline(${0:lint})",
702 kind: Attribute,
703 },
704 CompletionItem {
705 label: "link",
706 source_range: 20..20,
707 delete: 20..20,
708 insert: "link",
709 kind: Attribute,
710 },
711 CompletionItem {
712 label: "link_name",
713 source_range: 20..20,
714 delete: 20..20,
715 insert: "link_name = \"${0:symbol_name}\"",
716 kind: Attribute,
717 },
718 CompletionItem {
719 label: "macro_export",
720 source_range: 20..20,
721 delete: 20..20,
722 insert: "macro_export",
723 kind: Attribute,
724 },
725 CompletionItem {
726 label: "macro_use",
727 source_range: 20..20,
728 delete: 20..20,
729 insert: "macro_use",
730 kind: Attribute,
731 },
732 CompletionItem {
733 label: "must_use",
734 source_range: 20..20,
735 delete: 20..20,
736 insert: "must_use = \"${0:reason}\"",
737 kind: Attribute,
738 },
739 CompletionItem {
740 label: "no_mangle",
741 source_range: 20..20,
742 delete: 20..20,
743 insert: "no_mangle",
744 kind: Attribute,
745 },
746 CompletionItem {
747 label: "no_std",
748 source_range: 20..20,
749 delete: 20..20,
750 insert: "no_std",
751 kind: Attribute,
752 },
753 CompletionItem {
754 label: "non_exhaustive",
755 source_range: 20..20,
756 delete: 20..20,
757 insert: "non_exhaustive",
758 kind: Attribute,
759 },
760 CompletionItem {
761 label: "panic_handler",
762 source_range: 20..20,
763 delete: 20..20,
764 insert: "panic_handler",
765 kind: Attribute,
766 },
767 CompletionItem {
768 label: "path",
769 source_range: 20..20,
770 delete: 20..20,
771 insert: "path =\"${0:path}\"",
772 kind: Attribute,
773 },
774 CompletionItem {
775 label: "proc_macro",
776 source_range: 20..20,
777 delete: 20..20,
778 insert: "proc_macro",
779 kind: Attribute,
780 },
781 CompletionItem {
782 label: "proc_macro_attribute",
783 source_range: 20..20,
784 delete: 20..20,
785 insert: "proc_macro_attribute",
786 kind: Attribute,
787 },
788 CompletionItem {
789 label: "proc_macro_derive",
790 source_range: 20..20,
791 delete: 20..20,
792 insert: "proc_macro_derive(${0:Trait})",
793 kind: Attribute,
794 },
795 CompletionItem {
796 label: "recursion_limit",
797 source_range: 20..20,
798 delete: 20..20,
799 insert: "recursion_limit = ${0:128}",
800 kind: Attribute,
801 },
802 CompletionItem {
803 label: "repr",
804 source_range: 20..20,
805 delete: 20..20,
806 insert: "repr(${0:C})",
807 kind: Attribute,
808 },
809 CompletionItem {
810 label: "should_panic",
811 source_range: 20..20,
812 delete: 20..20,
813 insert: "should_panic(expected = \"${0:reason}\")",
814 kind: Attribute,
815 },
816 CompletionItem {
817 label: "target_feature",
818 source_range: 20..20,
819 delete: 20..20,
820 insert: "target_feature = \"${0:feature}\"",
821 kind: Attribute,
822 },
823 CompletionItem {
824 label: "test",
825 source_range: 20..20,
826 delete: 20..20,
827 insert: "test",
828 kind: Attribute,
829 },
830 CompletionItem {
831 label: "used",
832 source_range: 20..20,
833 delete: 20..20,
834 insert: "used",
835 kind: Attribute,
836 },
837 CompletionItem {
838 label: "warn",
839 source_range: 20..20,
840 delete: 20..20,
841 insert: "warn(${0:lint})",
842 kind: Attribute,
843 },
844 CompletionItem {
845 label: "windows_subsystem",
846 source_range: 20..20,
847 delete: 20..20,
848 insert: "windows_subsystem = \"${0:subsystem}\"",
849 kind: Attribute,
850 },
851 ]
852 "###
853 ); 370 );
854 } 371 }
855} 372}
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 05f825c6f..532665285 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -1,17 +1,12 @@
1//! FIXME: write short doc here 1//! Completes references after dot (fields and method calls).
2 2
3use hir::{HasVisibility, Type}; 3use hir::{HasVisibility, Type};
4
5use crate::{
6 completion::{
7 completion_context::CompletionContext,
8 completion_item::{CompletionKind, Completions},
9 },
10 CompletionItem,
11};
12use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use test_utils::mark;
13 6
14/// Complete dot accesses, i.e. fields or methods (and .await syntax). 7use crate::completion::{completion_context::CompletionContext, completion_item::Completions};
8
9/// Complete dot accesses, i.e. fields or methods.
15pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
16 let dot_receiver = match &ctx.dot_receiver { 11 let dot_receiver = match &ctx.dot_receiver {
17 Some(expr) => expr, 12 Some(expr) => expr,
@@ -23,24 +18,18 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
23 _ => return, 18 _ => return,
24 }; 19 };
25 20
26 if !ctx.is_call { 21 if ctx.is_call {
22 mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else {
27 complete_fields(acc, ctx, &receiver_ty); 24 complete_fields(acc, ctx, &receiver_ty);
28 } 25 }
29 complete_methods(acc, ctx, &receiver_ty); 26 complete_methods(acc, ctx, &receiver_ty);
30
31 // Suggest .await syntax for types that implement Future trait
32 if receiver_ty.impls_future(ctx.db) {
33 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
34 .detail("expr.await")
35 .insert_text("await")
36 .add_to(acc);
37 }
38} 27}
39 28
40fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 29fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
41 for receiver in receiver.autoderef(ctx.db) { 30 for receiver in receiver.autoderef(ctx.db) {
42 for (field, ty) in receiver.fields(ctx.db) { 31 for (field, ty) in receiver.fields(ctx.db) {
43 if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { 32 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
44 // Skip private field. FIXME: If the definition location of the 33 // Skip private field. FIXME: If the definition location of the
45 // field is editable, we should show the completion 34 // field is editable, we should show the completion
46 continue; 35 continue;
@@ -57,10 +46,10 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
57fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 46fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
58 if let Some(krate) = ctx.krate { 47 if let Some(krate) = ctx.krate {
59 let mut seen_methods = FxHashSet::default(); 48 let mut seen_methods = FxHashSet::default();
60 let traits_in_scope = ctx.scope().traits_in_scope(); 49 let traits_in_scope = ctx.scope.traits_in_scope();
61 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { 50 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
62 if func.has_self_param(ctx.db) 51 if func.has_self_param(ctx.db)
63 && ctx.scope().module().map_or(true, |m| func.is_visible_from(ctx.db, m)) 52 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
64 && seen_methods.insert(func.name(ctx.db)) 53 && seen_methods.insert(func.name(ctx.db))
65 { 54 {
66 acc.add_function(ctx, func, None); 55 acc.add_function(ctx, func, None);
@@ -72,801 +61,356 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
72 61
73#[cfg(test)] 62#[cfg(test)]
74mod tests { 63mod tests {
75 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 64 use expect::{expect, Expect};
76 use insta::assert_debug_snapshot; 65 use test_utils::mark;
66
67 use crate::completion::{test_utils::completion_list, CompletionKind};
77 68
78 fn do_ref_completion(code: &str) -> Vec<CompletionItem> { 69 fn check(ra_fixture: &str, expect: Expect) {
79 do_completion(code, CompletionKind::Reference) 70 let actual = completion_list(ra_fixture, CompletionKind::Reference);
71 expect.assert_eq(&actual);
80 } 72 }
81 73
82 #[test] 74 #[test]
83 fn test_struct_field_completion() { 75 fn test_struct_field_and_method_completion() {
84 assert_debug_snapshot!( 76 check(
85 do_ref_completion( 77 r#"
86 r" 78struct S { foo: u32 }
87 struct A { the_field: u32 } 79impl S {
88 fn foo(a: A) { 80 fn bar(&self) {}
89 a.<|> 81}
90 } 82fn foo(s: S) { s.<|> }
91 ", 83"#,
92 ), 84 expect![[r#"
93 @r###" 85 me bar() fn bar(&self)
94 [ 86 fd foo u32
95 CompletionItem { 87 "#]],
96 label: "the_field",
97 source_range: 94..94,
98 delete: 94..94,
99 insert: "the_field",
100 kind: Field,
101 detail: "u32",
102 },
103 ]
104 "###
105 ); 88 );
106 } 89 }
107 90
108 #[test] 91 #[test]
109 fn test_struct_field_completion_self() { 92 fn test_struct_field_completion_self() {
110 assert_debug_snapshot!( 93 check(
111 do_ref_completion( 94 r#"
112 r" 95struct S { the_field: (u32,) }
113 struct A { 96impl S {
114 /// This is the_field 97 fn foo(self) { self.<|> }
115 the_field: (u32,) 98}
116 } 99"#,
117 impl A { 100 expect![[r#"
118 fn foo(self) { 101 me foo() fn foo(self)
119 self.<|> 102 fd the_field (u32,)
120 } 103 "#]],
121 } 104 )
122 ",
123 ),
124 @r###"
125 [
126 CompletionItem {
127 label: "foo()",
128 source_range: 187..187,
129 delete: 187..187,
130 insert: "foo()$0",
131 kind: Method,
132 lookup: "foo",
133 detail: "fn foo(self)",
134 },
135 CompletionItem {
136 label: "the_field",
137 source_range: 187..187,
138 delete: 187..187,
139 insert: "the_field",
140 kind: Field,
141 detail: "(u32,)",
142 documentation: Documentation(
143 "This is the_field",
144 ),
145 },
146 ]
147 "###
148 );
149 } 105 }
150 106
151 #[test] 107 #[test]
152 fn test_struct_field_completion_autoderef() { 108 fn test_struct_field_completion_autoderef() {
153 assert_debug_snapshot!( 109 check(
154 do_ref_completion( 110 r#"
155 r" 111struct A { the_field: (u32, i32) }
156 struct A { the_field: (u32, i32) } 112impl A {
157 impl A { 113 fn foo(&self) { self.<|> }
158 fn foo(&self) { 114}
159 self.<|> 115"#,
160 } 116 expect![[r#"
161 } 117 me foo() fn foo(&self)
162 ", 118 fd the_field (u32, i32)
163 ), 119 "#]],
164 @r###" 120 )
165 [
166 CompletionItem {
167 label: "foo()",
168 source_range: 126..126,
169 delete: 126..126,
170 insert: "foo()$0",
171 kind: Method,
172 lookup: "foo",
173 detail: "fn foo(&self)",
174 },
175 CompletionItem {
176 label: "the_field",
177 source_range: 126..126,
178 delete: 126..126,
179 insert: "the_field",
180 kind: Field,
181 detail: "(u32, i32)",
182 },
183 ]
184 "###
185 );
186 } 121 }
187 122
188 #[test] 123 #[test]
189 fn test_no_struct_field_completion_for_method_call() { 124 fn test_no_struct_field_completion_for_method_call() {
190 assert_debug_snapshot!( 125 mark::check!(test_no_struct_field_completion_for_method_call);
191 do_ref_completion( 126 check(
192 r" 127 r#"
193 struct A { the_field: u32 } 128struct A { the_field: u32 }
194 fn foo(a: A) { 129fn foo(a: A) { a.<|>() }
195 a.<|>() 130"#,
196 } 131 expect![[""]],
197 ",
198 ),
199 @"[]"
200 ); 132 );
201 } 133 }
202 134
203 #[test] 135 #[test]
204 fn test_struct_field_visibility_private() { 136 fn test_visibility_filtering() {
205 assert_debug_snapshot!( 137 check(
206 do_ref_completion( 138 r#"
207 r" 139mod inner {
208 mod inner { 140 pub struct A {
209 struct A { 141 private_field: u32,
210 private_field: u32, 142 pub pub_field: u32,
211 pub pub_field: u32, 143 pub(crate) crate_field: u32,
212 pub(crate) crate_field: u32, 144 pub(super) super_field: u32,
213 pub(super) super_field: u32,
214 }
215 }
216 fn foo(a: inner::A) {
217 a.<|>
218 }
219 ",
220 ),
221 @r###"
222 [
223 CompletionItem {
224 label: "crate_field",
225 source_range: 313..313,
226 delete: 313..313,
227 insert: "crate_field",
228 kind: Field,
229 detail: "u32",
230 },
231 CompletionItem {
232 label: "pub_field",
233 source_range: 313..313,
234 delete: 313..313,
235 insert: "pub_field",
236 kind: Field,
237 detail: "u32",
238 },
239 CompletionItem {
240 label: "super_field",
241 source_range: 313..313,
242 delete: 313..313,
243 insert: "super_field",
244 kind: Field,
245 detail: "u32",
246 },
247 ]
248 "###
249 );
250 } 145 }
251 146}
252 #[test] 147fn foo(a: inner::A) { a.<|> }
253 fn test_union_field_completion() { 148"#,
254 assert_debug_snapshot!( 149 expect![[r#"
255 do_ref_completion( 150 fd crate_field u32
256 r" 151 fd pub_field u32
257 union Un { 152 fd super_field u32
258 field: u8, 153 "#]],
259 other: u16,
260 }
261
262 fn foo(u: Un) {
263 u.<|>
264 }
265 ",
266 ),
267 @r###"
268 [
269 CompletionItem {
270 label: "field",
271 source_range: 140..140,
272 delete: 140..140,
273 insert: "field",
274 kind: Field,
275 detail: "u8",
276 },
277 CompletionItem {
278 label: "other",
279 source_range: 140..140,
280 delete: 140..140,
281 insert: "other",
282 kind: Field,
283 detail: "u16",
284 },
285 ]
286 "###
287 ); 154 );
288 }
289 155
290 #[test] 156 check(
291 fn test_method_completion() { 157 r#"
292 assert_debug_snapshot!( 158struct A {}
293 do_ref_completion( 159mod m {
294 r" 160 impl super::A {
295 struct A {} 161 fn private_method(&self) {}
296 impl A { 162 pub(super) fn the_method(&self) {}
297 fn the_method(&self) {} 163 }
298 } 164}
299 fn foo(a: A) { 165fn foo(a: A) { a.<|> }
300 a.<|> 166"#,
301 } 167 expect![[r#"
302 ", 168 me the_method() pub(super) fn the_method(&self)
303 ), 169 "#]],
304 @r###"
305 [
306 CompletionItem {
307 label: "the_method()",
308 source_range: 144..144,
309 delete: 144..144,
310 insert: "the_method()$0",
311 kind: Method,
312 lookup: "the_method",
313 detail: "fn the_method(&self)",
314 },
315 ]
316 "###
317 ); 170 );
318 } 171 }
319 172
320 #[test] 173 #[test]
321 fn test_method_completion_only_fitting_impls() { 174 fn test_union_field_completion() {
322 assert_debug_snapshot!( 175 check(
323 do_ref_completion( 176 r#"
324 r" 177union U { field: u8, other: u16 }
325 struct A<T> {} 178fn foo(u: U) { u.<|> }
326 impl A<u32> { 179"#,
327 fn the_method(&self) {} 180 expect![[r#"
328 } 181 fd field u8
329 impl A<i32> { 182 fd other u16
330 fn the_other_method(&self) {} 183 "#]],
331 }
332 fn foo(a: A<u32>) {
333 a.<|>
334 }
335 ",
336 ),
337 @r###"
338 [
339 CompletionItem {
340 label: "the_method()",
341 source_range: 243..243,
342 delete: 243..243,
343 insert: "the_method()$0",
344 kind: Method,
345 lookup: "the_method",
346 detail: "fn the_method(&self)",
347 },
348 ]
349 "###
350 ); 184 );
351 } 185 }
352 186
353 #[test] 187 #[test]
354 fn test_method_completion_private() { 188 fn test_method_completion_only_fitting_impls() {
355 assert_debug_snapshot!( 189 check(
356 do_ref_completion( 190 r#"
357 r" 191struct A<T> {}
358 struct A {} 192impl A<u32> {
359 mod m { 193 fn the_method(&self) {}
360 impl super::A { 194}
361 fn private_method(&self) {} 195impl A<i32> {
362 pub(super) fn the_method(&self) {} 196 fn the_other_method(&self) {}
363 } 197}
364 } 198fn foo(a: A<u32>) { a.<|> }
365 fn foo(a: A) { 199"#,
366 a.<|> 200 expect![[r#"
367 } 201 me the_method() fn the_method(&self)
368 ", 202 "#]],
369 ), 203 )
370 @r###"
371 [
372 CompletionItem {
373 label: "the_method()",
374 source_range: 256..256,
375 delete: 256..256,
376 insert: "the_method()$0",
377 kind: Method,
378 lookup: "the_method",
379 detail: "pub(super) fn the_method(&self)",
380 },
381 ]
382 "###
383 );
384 } 204 }
385 205
386 #[test] 206 #[test]
387 fn test_trait_method_completion() { 207 fn test_trait_method_completion() {
388 assert_debug_snapshot!( 208 check(
389 do_ref_completion( 209 r#"
390 r" 210struct A {}
391 struct A {} 211trait Trait { fn the_method(&self); }
392 trait Trait { fn the_method(&self); } 212impl Trait for A {}
393 impl Trait for A {} 213fn foo(a: A) { a.<|> }
394 fn foo(a: A) { 214"#,
395 a.<|> 215 expect![[r#"
396 } 216 me the_method() fn the_method(&self)
397 ", 217 "#]],
398 ),
399 @r###"
400 [
401 CompletionItem {
402 label: "the_method()",
403 source_range: 151..151,
404 delete: 151..151,
405 insert: "the_method()$0",
406 kind: Method,
407 lookup: "the_method",
408 detail: "fn the_method(&self)",
409 },
410 ]
411 "###
412 ); 218 );
413 } 219 }
414 220
415 #[test] 221 #[test]
416 fn test_trait_method_completion_deduplicated() { 222 fn test_trait_method_completion_deduplicated() {
417 assert_debug_snapshot!( 223 check(
418 do_ref_completion( 224 r"
419 r" 225struct A {}
420 struct A {} 226trait Trait { fn the_method(&self); }
421 trait Trait { fn the_method(&self); } 227impl<T> Trait for T {}
422 impl<T> Trait for T {} 228fn foo(a: &A) { a.<|> }
423 fn foo(a: &A) { 229",
424 a.<|> 230 expect![[r#"
425 } 231 me the_method() fn the_method(&self)
426 ", 232 "#]],
427 ),
428 @r###"
429 [
430 CompletionItem {
431 label: "the_method()",
432 source_range: 155..155,
433 delete: 155..155,
434 insert: "the_method()$0",
435 kind: Method,
436 lookup: "the_method",
437 detail: "fn the_method(&self)",
438 },
439 ]
440 "###
441 ); 233 );
442 } 234 }
443 235
444 #[test] 236 #[test]
445 fn completes_trait_method_from_other_module() { 237 fn completes_trait_method_from_other_module() {
446 assert_debug_snapshot!( 238 check(
447 do_ref_completion(
448 r"
449 struct A {}
450 mod m {
451 pub trait Trait { fn the_method(&self); }
452 }
453 use m::Trait;
454 impl Trait for A {}
455 fn foo(a: A) {
456 a.<|>
457 }
458 ",
459 ),
460 @r###"
461 [
462 CompletionItem {
463 label: "the_method()",
464 source_range: 219..219,
465 delete: 219..219,
466 insert: "the_method()$0",
467 kind: Method,
468 lookup: "the_method",
469 detail: "fn the_method(&self)",
470 },
471 ]
472 "###
473 );
474 }
475
476 #[test]
477 fn test_no_non_self_method() {
478 assert_debug_snapshot!(
479 do_ref_completion(
480 r" 239 r"
481 struct A {} 240struct A {}
482 impl A { 241mod m {
483 fn the_method() {} 242 pub trait Trait { fn the_method(&self); }
484 } 243}
485 fn foo(a: A) { 244use m::Trait;
486 a.<|> 245impl Trait for A {}
487 } 246fn foo(a: A) { a.<|> }
488 ", 247",
489 ), 248 expect![[r#"
490 @"[]" 249 me the_method() fn the_method(&self)
250 "#]],
491 ); 251 );
492 } 252 }
493 253
494 #[test] 254 #[test]
495 fn test_method_attr_filtering() { 255 fn test_no_non_self_method() {
496 assert_debug_snapshot!( 256 check(
497 do_ref_completion( 257 r#"
498 r" 258struct A {}
499 struct A {} 259impl A {
500 impl A { 260 fn the_method() {}
501 #[inline] 261}
502 fn the_method(&self) { 262fn foo(a: A) {
503 let x = 1; 263 a.<|>
504 let y = 2; 264}
505 } 265"#,
506 } 266 expect![[""]],
507 fn foo(a: A) {
508 a.<|>
509 }
510 ",
511 ),
512 @r###"
513 [
514 CompletionItem {
515 label: "the_method()",
516 source_range: 249..249,
517 delete: 249..249,
518 insert: "the_method()$0",
519 kind: Method,
520 lookup: "the_method",
521 detail: "fn the_method(&self)",
522 },
523 ]
524 "###
525 ); 267 );
526 } 268 }
527 269
528 #[test] 270 #[test]
529 fn test_tuple_field_completion() { 271 fn test_tuple_field_completion() {
530 assert_debug_snapshot!( 272 check(
531 do_ref_completion( 273 r#"
532 r" 274fn foo() {
533 fn foo() { 275 let b = (0, 3.14);
534 let b = (0, 3.14); 276 b.<|>
535 b.<|> 277}
536 } 278"#,
537 ", 279 expect![[r#"
538 ), 280 fd 0 i32
539 @r###" 281 fd 1 f64
540 [ 282 "#]],
541 CompletionItem { 283 )
542 label: "0",
543 source_range: 75..75,
544 delete: 75..75,
545 insert: "0",
546 kind: Field,
547 detail: "i32",
548 },
549 CompletionItem {
550 label: "1",
551 source_range: 75..75,
552 delete: 75..75,
553 insert: "1",
554 kind: Field,
555 detail: "f64",
556 },
557 ]
558 "###
559 );
560 } 284 }
561 285
562 #[test] 286 #[test]
563 fn test_tuple_field_inference() { 287 fn test_tuple_field_inference() {
564 assert_debug_snapshot!( 288 check(
565 do_ref_completion( 289 r#"
566 r" 290pub struct S;
567 pub struct S; 291impl S { pub fn blah(&self) {} }
568 impl S {
569 pub fn blah(&self) {}
570 }
571 292
572 struct T(S); 293struct T(S);
573 294
574 impl T { 295impl T {
575 fn foo(&self) { 296 fn foo(&self) {
576 // 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
577 self.0.a<|> 298 self.0.a<|>
578 }
579 }
580 ",
581 ),
582 @r###"
583 [
584 CompletionItem {
585 label: "blah()",
586 source_range: 299..300,
587 delete: 299..300,
588 insert: "blah()$0",
589 kind: Method,
590 lookup: "blah",
591 detail: "pub fn blah(&self)",
592 },
593 ]
594 "###
595 );
596 } 299 }
597 300}
598 #[test] 301"#,
599 fn test_completion_works_in_consts() { 302 expect![[r#"
600 assert_debug_snapshot!( 303 me blah() pub fn blah(&self)
601 do_ref_completion( 304 "#]],
602 r"
603 struct A { the_field: u32 }
604 const X: u32 = {
605 A { the_field: 92 }.<|>
606 };
607 ",
608 ),
609 @r###"
610 [
611 CompletionItem {
612 label: "the_field",
613 source_range: 106..106,
614 delete: 106..106,
615 insert: "the_field",
616 kind: Field,
617 detail: "u32",
618 },
619 ]
620 "###
621 ); 305 );
622 } 306 }
623 307
624 #[test] 308 #[test]
625 fn test_completion_await_impls_future() { 309 fn test_completion_works_in_consts() {
626 assert_debug_snapshot!( 310 check(
627 do_completion( 311 r#"
628 r###" 312struct A { the_field: u32 }
629 //- /main.rs 313const X: u32 = {
630 use std::future::*; 314 A { the_field: 92 }.<|>
631 struct A {} 315};
632 impl Future for A {} 316"#,
633 fn foo(a: A) { 317 expect![[r#"
634 a.<|> 318 fd the_field u32
635 } 319 "#]],
636
637 //- /std/lib.rs
638 pub mod future {
639 #[lang = "future_trait"]
640 pub trait Future {}
641 }
642 "###, CompletionKind::Keyword),
643 @r###"
644 [
645 CompletionItem {
646 label: "await",
647 source_range: 74..74,
648 delete: 74..74,
649 insert: "await",
650 detail: "expr.await",
651 },
652 ]
653 "###
654 )
655 }
656
657 #[test]
658 fn test_super_super_completion() {
659 assert_debug_snapshot!(
660 do_ref_completion(
661 r"
662 mod a {
663 const A: usize = 0;
664
665 mod b {
666 const B: usize = 0;
667
668 mod c {
669 use super::super::<|>
670 }
671 }
672 }
673 ",
674 ),
675 @r###"
676 [
677 CompletionItem {
678 label: "A",
679 source_range: 217..217,
680 delete: 217..217,
681 insert: "A",
682 kind: Const,
683 },
684 CompletionItem {
685 label: "b",
686 source_range: 217..217,
687 delete: 217..217,
688 insert: "b",
689 kind: Module,
690 },
691 ]
692 "###
693 ); 320 );
694 } 321 }
695 322
696 #[test] 323 #[test]
697 fn works_in_simple_macro_1() { 324 fn works_in_simple_macro_1() {
698 assert_debug_snapshot!( 325 check(
699 do_ref_completion( 326 r#"
700 r" 327macro_rules! m { ($e:expr) => { $e } }
701 macro_rules! m { ($e:expr) => { $e } } 328struct A { the_field: u32 }
702 struct A { the_field: u32 } 329fn foo(a: A) {
703 fn foo(a: A) { 330 m!(a.x<|>)
704 m!(a.x<|>) 331}
705 } 332"#,
706 ", 333 expect![[r#"
707 ), 334 fd the_field u32
708 @r###" 335 "#]],
709 [
710 CompletionItem {
711 label: "the_field",
712 source_range: 156..157,
713 delete: 156..157,
714 insert: "the_field",
715 kind: Field,
716 detail: "u32",
717 },
718 ]
719 "###
720 );
721 }
722
723 #[test]
724 fn works_in_simple_macro_recursive() {
725 assert_debug_snapshot!(
726 do_ref_completion(
727 r"
728 macro_rules! m { ($e:expr) => { $e } }
729 struct A { the_field: u32 }
730 fn foo(a: A) {
731 m!(a.x<|>)
732 }
733 ",
734 ),
735 @r###"
736 [
737 CompletionItem {
738 label: "the_field",
739 source_range: 156..157,
740 delete: 156..157,
741 insert: "the_field",
742 kind: Field,
743 detail: "u32",
744 },
745 ]
746 "###
747 ); 336 );
748 } 337 }
749 338
750 #[test] 339 #[test]
751 fn works_in_simple_macro_2() { 340 fn works_in_simple_macro_2() {
752 // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery 341 // this doesn't work yet because the macro doesn't expand without the token -- maybe it can be fixed with better recovery
753 assert_debug_snapshot!( 342 check(
754 do_ref_completion( 343 r#"
755 r" 344macro_rules! m { ($e:expr) => { $e } }
756 macro_rules! m { ($e:expr) => { $e } } 345struct A { the_field: u32 }
757 struct A { the_field: u32 } 346fn foo(a: A) {
758 fn foo(a: A) { 347 m!(a.<|>)
759 m!(a.<|>) 348}
760 } 349"#,
761 ", 350 expect![[r#"
762 ), 351 fd the_field u32
763 @r###" 352 "#]],
764 [
765 CompletionItem {
766 label: "the_field",
767 source_range: 156..156,
768 delete: 156..156,
769 insert: "the_field",
770 kind: Field,
771 detail: "u32",
772 },
773 ]
774 "###
775 ); 353 );
776 } 354 }
777 355
778 #[test] 356 #[test]
779 fn works_in_simple_macro_recursive_1() { 357 fn works_in_simple_macro_recursive_1() {
780 assert_debug_snapshot!( 358 check(
781 do_ref_completion( 359 r#"
782 r" 360macro_rules! m { ($e:expr) => { $e } }
783 macro_rules! m { ($e:expr) => { $e } } 361struct A { the_field: u32 }
784 struct A { the_field: u32 } 362fn foo(a: A) {
785 fn foo(a: A) { 363 m!(m!(m!(a.x<|>)))
786 m!(m!(m!(a.x<|>))) 364}
787 } 365"#,
788 ", 366 expect![[r#"
789 ), 367 fd the_field u32
790 @r###" 368 "#]],
791 [
792 CompletionItem {
793 label: "the_field",
794 source_range: 162..163,
795 delete: 162..163,
796 insert: "the_field",
797 kind: Field,
798 detail: "u32",
799 },
800 ]
801 "###
802 ); 369 );
803 } 370 }
804 371
805 #[test] 372 #[test]
806 fn macro_expansion_resilient() { 373 fn macro_expansion_resilient() {
807 assert_debug_snapshot!( 374 check(
808 do_ref_completion( 375 r#"
809 r" 376macro_rules! dbg {
810 macro_rules! dbg { 377 () => {};
811 () => {}; 378 ($val:expr) => {
812 ($val:expr) => { 379 match $val { tmp => { tmp } }
813 match $val { tmp => { tmp } } 380 };
814 }; 381 // Trailing comma with single argument is ignored
815 // Trailing comma with single argument is ignored 382 ($val:expr,) => { $crate::dbg!($val) };
816 ($val:expr,) => { $crate::dbg!($val) }; 383 ($($val:expr),+ $(,)?) => {
817 ($($val:expr),+ $(,)?) => { 384 ($($crate::dbg!($val)),+,)
818 ($($crate::dbg!($val)),+,) 385 };
819 }; 386}
820 } 387struct A { the_field: u32 }
821 struct A { the_field: u32 } 388fn foo(a: A) {
822 fn foo(a: A) { 389 dbg!(a.<|>)
823 dbg!(a.<|>) 390}
824 } 391"#,
825 ", 392 expect![[r#"
826 ), 393 fd the_field u32
827 @r###" 394 "#]],
828 [
829 CompletionItem {
830 label: "the_field",
831 source_range: 552..552,
832 delete: 552..552,
833 insert: "the_field",
834 kind: Field,
835 detail: "u32",
836 },
837 ]
838 "###
839 ); 395 );
840 } 396 }
841 397
842 #[test] 398 #[test]
843 fn test_method_completion_3547() { 399 fn test_method_completion_issue_3547() {
844 assert_debug_snapshot!( 400 check(
845 do_ref_completion( 401 r#"
846 r" 402struct HashSet<T> {}
847 struct HashSet<T> {} 403impl<T> HashSet<T> {
848 impl<T> HashSet<T> { 404 pub fn the_method(&self) {}
849 pub fn the_method(&self) {} 405}
850 } 406fn foo() {
851 fn foo() { 407 let s: HashSet<_>;
852 let s: HashSet<_>; 408 s.<|>
853 s.<|> 409}
854 } 410"#,
855 ", 411 expect![[r#"
856 ), 412 me the_method() pub fn the_method(&self)
857 @r###" 413 "#]],
858 [
859 CompletionItem {
860 label: "the_method()",
861 source_range: 201..201,
862 delete: 201..201,
863 insert: "the_method()$0",
864 kind: Method,
865 lookup: "the_method",
866 detail: "pub fn the_method(&self)",
867 },
868 ]
869 "###
870 ); 414 );
871 } 415 }
872} 416}
diff --git a/crates/ra_ide/src/completion/complete_fn_param.rs b/crates/ra_ide/src/completion/complete_fn_param.rs
index a661932a3..db2abb4f1 100644
--- a/crates/ra_ide/src/completion/complete_fn_param.rs
+++ b/crates/ra_ide/src/completion/complete_fn_param.rs
@@ -1,4 +1,4 @@
1//! FIXME: write short doc here 1//! See `complete_fn_param`.
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 ast::{self, ModuleItemOwner}, 4 ast::{self, ModuleItemOwner},
@@ -18,6 +18,7 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
18 } 18 }
19 19
20 let mut params = FxHashMap::default(); 20 let mut params = FxHashMap::default();
21 let me = ctx.token.ancestors().find_map(ast::FnDef::cast);
21 for node in ctx.token.parent().ancestors() { 22 for node in ctx.token.parent().ancestors() {
22 let items = match_ast! { 23 let items = match_ast! {
23 match node { 24 match node {
@@ -28,25 +29,26 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
28 }; 29 };
29 for item in items { 30 for item in items {
30 if let ast::ModuleItem::FnDef(func) = item { 31 if let ast::ModuleItem::FnDef(func) = item {
32 if Some(&func) == me.as_ref() {
33 continue;
34 }
31 func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| { 35 func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
32 let text = param.syntax().text().to_string(); 36 let text = param.syntax().text().to_string();
33 params.entry(text).or_insert((0, param)).0 += 1; 37 params.entry(text).or_insert(param);
34 }) 38 })
35 } 39 }
36 } 40 }
37 } 41 }
42
38 params 43 params
39 .into_iter() 44 .into_iter()
40 .filter_map(|(label, (count, param))| { 45 .filter_map(|(label, param)| {
41 let lookup = param.pat()?.syntax().text().to_string(); 46 let lookup = param.pat()?.syntax().text().to_string();
42 if count < 2 { 47 Some((label, lookup))
43 None
44 } else {
45 Some((label, lookup))
46 }
47 }) 48 })
48 .for_each(|(label, lookup)| { 49 .for_each(|(label, lookup)| {
49 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 50 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
51 .kind(crate::CompletionItemKind::Binding)
50 .lookup_by(lookup) 52 .lookup_by(lookup)
51 .add_to(acc) 53 .add_to(acc)
52 }); 54 });
@@ -54,85 +56,70 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
54 56
55#[cfg(test)] 57#[cfg(test)]
56mod tests { 58mod tests {
57 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 59 use expect::{expect, Expect};
58 use insta::assert_debug_snapshot;
59 60
60 fn do_magic_completion(code: &str) -> Vec<CompletionItem> { 61 use crate::completion::{test_utils::completion_list, CompletionKind};
61 do_completion(code, CompletionKind::Magic) 62
63 fn check(ra_fixture: &str, expect: Expect) {
64 let actual = completion_list(ra_fixture, CompletionKind::Magic);
65 expect.assert_eq(&actual);
62 } 66 }
63 67
64 #[test] 68 #[test]
65 fn test_param_completion_last_param() { 69 fn test_param_completion_last_param() {
66 assert_debug_snapshot!( 70 check(
67 do_magic_completion( 71 r#"
68 r" 72fn foo(file_id: FileId) {}
69 fn foo(file_id: FileId) {} 73fn bar(file_id: FileId) {}
70 fn bar(file_id: FileId) {} 74fn baz(file<|>) {}
71 fn baz(file<|>) {} 75"#,
72 ", 76 expect![[r#"
73 ), 77 bn file_id: FileId
74 @r###" 78 "#]],
75 [
76 CompletionItem {
77 label: "file_id: FileId",
78 source_range: 110..114,
79 delete: 110..114,
80 insert: "file_id: FileId",
81 lookup: "file_id",
82 },
83 ]
84 "###
85 ); 79 );
86 } 80 }
87 81
88 #[test] 82 #[test]
89 fn test_param_completion_nth_param() { 83 fn test_param_completion_nth_param() {
90 assert_debug_snapshot!( 84 check(
91 do_magic_completion( 85 r#"
92 r" 86fn foo(file_id: FileId) {}
93 fn foo(file_id: FileId) {} 87fn baz(file<|>, x: i32) {}
94 fn bar(file_id: FileId) {} 88"#,
95 fn baz(file<|>, x: i32) {} 89 expect![[r#"
96 ", 90 bn file_id: FileId
97 ), 91 "#]],
98 @r###"
99 [
100 CompletionItem {
101 label: "file_id: FileId",
102 source_range: 110..114,
103 delete: 110..114,
104 insert: "file_id: FileId",
105 lookup: "file_id",
106 },
107 ]
108 "###
109 ); 92 );
110 } 93 }
111 94
112 #[test] 95 #[test]
113 fn test_param_completion_trait_param() { 96 fn test_param_completion_trait_param() {
114 assert_debug_snapshot!( 97 check(
115 do_magic_completion( 98 r#"
116 r" 99pub(crate) trait SourceRoot {
117 pub(crate) trait SourceRoot { 100 pub fn contains(&self, file_id: FileId) -> bool;
118 pub fn contains(&self, file_id: FileId) -> bool; 101 pub fn module_map(&self) -> &ModuleMap;
119 pub fn module_map(&self) -> &ModuleMap; 102 pub fn lines(&self, file_id: FileId) -> &LineIndex;
120 pub fn lines(&self, file_id: FileId) -> &LineIndex; 103 pub fn syntax(&self, file<|>)
121 pub fn syntax(&self, file<|>) 104}
122 } 105"#,
123 ", 106 expect![[r#"
124 ), 107 bn file_id: FileId
125 @r###" 108 "#]],
126 [
127 CompletionItem {
128 label: "file_id: FileId",
129 source_range: 289..293,
130 delete: 289..293,
131 insert: "file_id: FileId",
132 lookup: "file_id",
133 },
134 ]
135 "###
136 ); 109 );
137 } 110 }
111
112 #[test]
113 fn completes_param_in_inner_function() {
114 check(
115 r#"
116fn outer(text: String) {
117 fn inner(<|>)
118}
119"#,
120 expect![[r#"
121 bn text: String
122 "#]],
123 )
124 }
138} 125}
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index fd95bc410..fcdaeef49 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -1,11 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_syntax::{ 3use ra_syntax::{ast, SyntaxKind};
4 ast::{self, LoopBodyOwner}, 4use test_utils::mark;
5 match_ast, AstNode,
6 SyntaxKind::*,
7 SyntaxToken,
8};
9 5
10use crate::completion::{ 6use crate::completion::{
11 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 7 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
@@ -16,14 +12,14 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
16 let source_range = ctx.source_range(); 12 let source_range = ctx.source_range();
17 match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) { 13 match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) {
18 (Some(_), None) => { 14 (Some(_), None) => {
19 CompletionItem::new(CompletionKind::Keyword, source_range, "crate") 15 CompletionItem::new(CompletionKind::Keyword, source_range, "crate::")
20 .kind(CompletionItemKind::Keyword) 16 .kind(CompletionItemKind::Keyword)
21 .insert_text("crate::") 17 .insert_text("crate::")
22 .add_to(acc); 18 .add_to(acc);
23 CompletionItem::new(CompletionKind::Keyword, source_range, "self") 19 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
24 .kind(CompletionItemKind::Keyword) 20 .kind(CompletionItemKind::Keyword)
25 .add_to(acc); 21 .add_to(acc);
26 CompletionItem::new(CompletionKind::Keyword, source_range, "super") 22 CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
27 .kind(CompletionItemKind::Keyword) 23 .kind(CompletionItemKind::Keyword)
28 .insert_text("super::") 24 .insert_text("super::")
29 .add_to(acc); 25 .add_to(acc);
@@ -32,77 +28,147 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
32 CompletionItem::new(CompletionKind::Keyword, source_range, "self") 28 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
33 .kind(CompletionItemKind::Keyword) 29 .kind(CompletionItemKind::Keyword)
34 .add_to(acc); 30 .add_to(acc);
35 CompletionItem::new(CompletionKind::Keyword, source_range, "super") 31 CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
36 .kind(CompletionItemKind::Keyword) 32 .kind(CompletionItemKind::Keyword)
37 .insert_text("super::") 33 .insert_text("super::")
38 .add_to(acc); 34 .add_to(acc);
39 } 35 }
40 _ => {} 36 _ => {}
41 } 37 }
42}
43 38
44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { 39 // Suggest .await syntax for types that implement Future trait
45 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 40 if let Some(receiver) = &ctx.dot_receiver {
46 .kind(CompletionItemKind::Keyword); 41 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
47 42 if ty.impls_future(ctx.db) {
48 match ctx.config.snippet_cap { 43 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
49 Some(cap) => res.insert_snippet(cap, snippet), 44 .kind(CompletionItemKind::Keyword)
50 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }), 45 .detail("expr.await")
46 .insert_text("await")
47 .add_to(acc);
48 }
49 };
51 } 50 }
52 .build()
53} 51}
54 52
55pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { 53pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
56 if !ctx.is_trivial_path { 54 if ctx.token.kind() == SyntaxKind::COMMENT {
55 mark::hit!(no_keyword_completion_in_comments);
57 return; 56 return;
58 } 57 }
59 58
60 let fn_def = match &ctx.function_syntax { 59 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
61 Some(it) => it, 60 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
62 None => return, 61 add_keyword(ctx, acc, "where", "where ");
63 }; 62 return;
64 acc.add(keyword(ctx, "if", "if $0 {}")); 63 }
65 acc.add(keyword(ctx, "match", "match $0 {}")); 64 if ctx.unsafe_is_prev {
66 acc.add(keyword(ctx, "while", "while $0 {}")); 65 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
67 acc.add(keyword(ctx, "loop", "loop {$0}")); 66 add_keyword(ctx, acc, "fn", "fn $0() {}")
67 }
68
69 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
70 || ctx.block_expr_parent
71 {
72 add_keyword(ctx, acc, "trait", "trait $0 {}");
73 add_keyword(ctx, acc, "impl", "impl $0 {}");
74 }
75
76 return;
77 }
78 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
79 add_keyword(ctx, acc, "fn", "fn $0() {}");
80 }
81 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
82 || ctx.block_expr_parent
83 {
84 add_keyword(ctx, acc, "use", "use ");
85 add_keyword(ctx, acc, "impl", "impl $0 {}");
86 add_keyword(ctx, acc, "trait", "trait $0 {}");
87 }
88
89 if ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent {
90 add_keyword(ctx, acc, "enum", "enum $0 {}");
91 add_keyword(ctx, acc, "struct", "struct $0");
92 add_keyword(ctx, acc, "union", "union $0 {}");
93 }
94
95 if ctx.is_expr {
96 add_keyword(ctx, acc, "match", "match $0 {}");
97 add_keyword(ctx, acc, "while", "while $0 {}");
98 add_keyword(ctx, acc, "loop", "loop {$0}");
99 add_keyword(ctx, acc, "if", "if ");
100 add_keyword(ctx, acc, "if let", "if let ");
101 }
102
103 if ctx.if_is_prev || ctx.block_expr_parent {
104 add_keyword(ctx, acc, "let", "let ");
105 }
68 106
69 if ctx.after_if { 107 if ctx.after_if {
70 acc.add(keyword(ctx, "else", "else {$0}")); 108 add_keyword(ctx, acc, "else", "else {$0}");
71 acc.add(keyword(ctx, "else if", "else if $0 {}")); 109 add_keyword(ctx, acc, "else if", "else if $0 {}");
110 }
111 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
112 || ctx.block_expr_parent
113 {
114 add_keyword(ctx, acc, "mod", "mod $0 {}");
115 }
116 if ctx.bind_pat_parent || ctx.ref_pat_parent {
117 add_keyword(ctx, acc, "mut", "mut ");
118 }
119 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
120 add_keyword(ctx, acc, "const", "const ");
121 add_keyword(ctx, acc, "type", "type ");
122 }
123 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
124 || ctx.block_expr_parent
125 {
126 add_keyword(ctx, acc, "static", "static ");
127 };
128 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
129 || ctx.block_expr_parent
130 {
131 add_keyword(ctx, acc, "extern", "extern ");
132 }
133 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent || ctx.is_match_arm {
134 add_keyword(ctx, acc, "unsafe", "unsafe ");
72 } 135 }
73 if is_in_loop_body(&ctx.token) { 136 if ctx.in_loop_body {
74 if ctx.can_be_stmt { 137 if ctx.can_be_stmt {
75 acc.add(keyword(ctx, "continue", "continue;")); 138 add_keyword(ctx, acc, "continue", "continue;");
76 acc.add(keyword(ctx, "break", "break;")); 139 add_keyword(ctx, acc, "break", "break;");
77 } else { 140 } else {
78 acc.add(keyword(ctx, "continue", "continue")); 141 add_keyword(ctx, acc, "continue", "continue");
79 acc.add(keyword(ctx, "break", "break")); 142 add_keyword(ctx, acc, "break", "break");
80 } 143 }
81 } 144 }
145 if ctx.has_item_list_or_source_file_parent && !ctx.has_trait_parent {
146 add_keyword(ctx, acc, "pub", "pub ")
147 }
148
149 if !ctx.is_trivial_path {
150 return;
151 }
152 let fn_def = match &ctx.function_syntax {
153 Some(it) => it,
154 None => return,
155 };
82 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt)); 156 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
83} 157}
84 158
85fn is_in_loop_body(leaf: &SyntaxToken) -> bool { 159fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
86 // FIXME move this to CompletionContext and make it handle macros 160 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
87 for node in leaf.parent().ancestors() { 161 .kind(CompletionItemKind::Keyword);
88 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { 162
89 break; 163 match ctx.config.snippet_cap {
90 } 164 Some(cap) => res.insert_snippet(cap, snippet),
91 let loop_body = match_ast! { 165 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
92 match node {
93 ast::ForExpr(it) => it.loop_body(),
94 ast::WhileExpr(it) => it.loop_body(),
95 ast::LoopExpr(it) => it.loop_body(),
96 _ => None,
97 }
98 };
99 if let Some(body) = loop_body {
100 if body.syntax().text_range().contains_range(leaf.text_range()) {
101 return true;
102 }
103 }
104 } 166 }
105 false 167 .build()
168}
169
170fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
171 acc.add(keyword(ctx, kw, snippet));
106} 172}
107 173
108fn complete_return( 174fn complete_return(
@@ -121,666 +187,354 @@ fn complete_return(
121 187
122#[cfg(test)] 188#[cfg(test)]
123mod tests { 189mod tests {
124 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 190 use expect::{expect, Expect};
125 use insta::assert_debug_snapshot; 191
192 use crate::completion::{
193 test_utils::{check_edit, completion_list},
194 CompletionKind,
195 };
196 use test_utils::mark;
126 197
127 fn do_keyword_completion(code: &str) -> Vec<CompletionItem> { 198 fn check(ra_fixture: &str, expect: Expect) {
128 do_completion(code, CompletionKind::Keyword) 199 let actual = completion_list(ra_fixture, CompletionKind::Keyword);
200 expect.assert_eq(&actual)
129 } 201 }
130 202
131 #[test] 203 #[test]
132 fn completes_keywords_in_use_stmt() { 204 fn test_keywords_in_use_stmt() {
133 assert_debug_snapshot!( 205 check(
134 do_keyword_completion( 206 r"use <|>",
135 r" 207 expect![[r#"
136 use <|> 208 kw crate::
137 ", 209 kw self
138 ), 210 kw super::
139 @r###" 211 "#]],
140 [
141 CompletionItem {
142 label: "crate",
143 source_range: 21..21,
144 delete: 21..21,
145 insert: "crate::",
146 kind: Keyword,
147 },
148 CompletionItem {
149 label: "self",
150 source_range: 21..21,
151 delete: 21..21,
152 insert: "self",
153 kind: Keyword,
154 },
155 CompletionItem {
156 label: "super",
157 source_range: 21..21,
158 delete: 21..21,
159 insert: "super::",
160 kind: Keyword,
161 },
162 ]
163 "###
164 ); 212 );
165 213
166 assert_debug_snapshot!( 214 check(
167 do_keyword_completion( 215 r"use a::<|>",
168 r" 216 expect![[r#"
169 use a::<|> 217 kw self
170 ", 218 kw super::
171 ), 219 "#]],
172 @r###"
173 [
174 CompletionItem {
175 label: "self",
176 source_range: 24..24,
177 delete: 24..24,
178 insert: "self",
179 kind: Keyword,
180 },
181 CompletionItem {
182 label: "super",
183 source_range: 24..24,
184 delete: 24..24,
185 insert: "super::",
186 kind: Keyword,
187 },
188 ]
189 "###
190 ); 220 );
191 221
192 assert_debug_snapshot!( 222 check(
193 do_keyword_completion( 223 r"use a::{b, <|>}",
194 r" 224 expect![[r#"
195 use a::{b, <|>} 225 kw self
196 ", 226 kw super::
197 ), 227 "#]],
198 @r###"
199 [
200 CompletionItem {
201 label: "self",
202 source_range: 28..28,
203 delete: 28..28,
204 insert: "self",
205 kind: Keyword,
206 },
207 CompletionItem {
208 label: "super",
209 source_range: 28..28,
210 delete: 28..28,
211 insert: "super::",
212 kind: Keyword,
213 },
214 ]
215 "###
216 ); 228 );
217 } 229 }
218 230
219 #[test] 231 #[test]
220 fn completes_various_keywords_in_function() { 232 fn test_keywords_at_source_file_level() {
221 assert_debug_snapshot!( 233 check(
222 do_keyword_completion( 234 r"m<|>",
223 r" 235 expect![[r#"
224 fn quux() { 236 kw const
225 <|> 237 kw enum
226 } 238 kw extern
227 ", 239 kw fn
228 ), 240 kw impl
229 @r###" 241 kw mod
230 [ 242 kw pub
231 CompletionItem { 243 kw static
232 label: "if", 244 kw struct
233 source_range: 49..49, 245 kw trait
234 delete: 49..49, 246 kw type
235 insert: "if $0 {}", 247 kw union
236 kind: Keyword, 248 kw unsafe
237 }, 249 kw use
238 CompletionItem { 250 "#]],
239 label: "loop",
240 source_range: 49..49,
241 delete: 49..49,
242 insert: "loop {$0}",
243 kind: Keyword,
244 },
245 CompletionItem {
246 label: "match",
247 source_range: 49..49,
248 delete: 49..49,
249 insert: "match $0 {}",
250 kind: Keyword,
251 },
252 CompletionItem {
253 label: "return",
254 source_range: 49..49,
255 delete: 49..49,
256 insert: "return;",
257 kind: Keyword,
258 },
259 CompletionItem {
260 label: "while",
261 source_range: 49..49,
262 delete: 49..49,
263 insert: "while $0 {}",
264 kind: Keyword,
265 },
266 ]
267 "###
268 ); 251 );
269 } 252 }
270 253
271 #[test] 254 #[test]
272 fn completes_else_after_if() { 255 fn test_keywords_in_function() {
273 assert_debug_snapshot!( 256 check(
274 do_keyword_completion( 257 r"fn quux() { <|> }",
275 r" 258 expect![[r#"
276 fn quux() { 259 kw const
277 if true { 260 kw extern
278 () 261 kw fn
279 } <|> 262 kw if
280 } 263 kw if let
281 ", 264 kw impl
282 ), 265 kw let
283 @r###" 266 kw loop
284 [ 267 kw match
285 CompletionItem { 268 kw mod
286 label: "else", 269 kw return
287 source_range: 108..108, 270 kw static
288 delete: 108..108, 271 kw trait
289 insert: "else {$0}", 272 kw type
290 kind: Keyword, 273 kw unsafe
291 }, 274 kw use
292 CompletionItem { 275 kw while
293 label: "else if", 276 "#]],
294 source_range: 108..108,
295 delete: 108..108,
296 insert: "else if $0 {}",
297 kind: Keyword,
298 },
299 CompletionItem {
300 label: "if",
301 source_range: 108..108,
302 delete: 108..108,
303 insert: "if $0 {}",
304 kind: Keyword,
305 },
306 CompletionItem {
307 label: "loop",
308 source_range: 108..108,
309 delete: 108..108,
310 insert: "loop {$0}",
311 kind: Keyword,
312 },
313 CompletionItem {
314 label: "match",
315 source_range: 108..108,
316 delete: 108..108,
317 insert: "match $0 {}",
318 kind: Keyword,
319 },
320 CompletionItem {
321 label: "return",
322 source_range: 108..108,
323 delete: 108..108,
324 insert: "return;",
325 kind: Keyword,
326 },
327 CompletionItem {
328 label: "while",
329 source_range: 108..108,
330 delete: 108..108,
331 insert: "while $0 {}",
332 kind: Keyword,
333 },
334 ]
335 "###
336 ); 277 );
337 } 278 }
338 279
339 #[test] 280 #[test]
340 fn test_completion_return_value() { 281 fn test_keywords_inside_block() {
341 assert_debug_snapshot!( 282 check(
342 do_keyword_completion( 283 r"fn quux() { if true { <|> } }",
343 r" 284 expect![[r#"
344 fn quux() -> i32 { 285 kw const
345 <|> 286 kw extern
346 92 287 kw fn
347 } 288 kw if
348 ", 289 kw if let
349 ), 290 kw impl
350 @r###" 291 kw let
351 [ 292 kw loop
352 CompletionItem { 293 kw match
353 label: "if", 294 kw mod
354 source_range: 56..56, 295 kw return
355 delete: 56..56, 296 kw static
356 insert: "if $0 {}", 297 kw trait
357 kind: Keyword, 298 kw type
358 }, 299 kw unsafe
359 CompletionItem { 300 kw use
360 label: "loop", 301 kw while
361 source_range: 56..56, 302 "#]],
362 delete: 56..56,
363 insert: "loop {$0}",
364 kind: Keyword,
365 },
366 CompletionItem {
367 label: "match",
368 source_range: 56..56,
369 delete: 56..56,
370 insert: "match $0 {}",
371 kind: Keyword,
372 },
373 CompletionItem {
374 label: "return",
375 source_range: 56..56,
376 delete: 56..56,
377 insert: "return $0;",
378 kind: Keyword,
379 },
380 CompletionItem {
381 label: "while",
382 source_range: 56..56,
383 delete: 56..56,
384 insert: "while $0 {}",
385 kind: Keyword,
386 },
387 ]
388 "###
389 ); 303 );
390 assert_debug_snapshot!( 304 }
391 do_keyword_completion( 305
392 r" 306 #[test]
393 fn quux() { 307 fn test_keywords_after_if() {
394 <|> 308 check(
395 92 309 r#"fn quux() { if true { () } <|> }"#,
396 } 310 expect![[r#"
397 ", 311 kw const
398 ), 312 kw else
399 @r###" 313 kw else if
400 [ 314 kw extern
401 CompletionItem { 315 kw fn
402 label: "if", 316 kw if
403 source_range: 49..49, 317 kw if let
404 delete: 49..49, 318 kw impl
405 insert: "if $0 {}", 319 kw let
406 kind: Keyword, 320 kw loop
407 }, 321 kw match
408 CompletionItem { 322 kw mod
409 label: "loop", 323 kw return
410 source_range: 49..49, 324 kw static
411 delete: 49..49, 325 kw trait
412 insert: "loop {$0}", 326 kw type
413 kind: Keyword, 327 kw unsafe
414 }, 328 kw use
415 CompletionItem { 329 kw while
416 label: "match", 330 "#]],
417 source_range: 49..49, 331 );
418 delete: 49..49, 332 check_edit(
419 insert: "match $0 {}", 333 "else",
420 kind: Keyword, 334 r#"fn quux() { if true { () } <|> }"#,
421 }, 335 r#"fn quux() { if true { () } else {$0} }"#,
422 CompletionItem {
423 label: "return",
424 source_range: 49..49,
425 delete: 49..49,
426 insert: "return;",
427 kind: Keyword,
428 },
429 CompletionItem {
430 label: "while",
431 source_range: 49..49,
432 delete: 49..49,
433 insert: "while $0 {}",
434 kind: Keyword,
435 },
436 ]
437 "###
438 ); 336 );
439 } 337 }
440 338
441 #[test] 339 #[test]
442 fn dont_add_semi_after_return_if_not_a_statement() { 340 fn test_keywords_in_match_arm() {
443 assert_debug_snapshot!( 341 check(
444 do_keyword_completion( 342 r#"
445 r" 343fn quux() -> i32 {
446 fn quux() -> i32 { 344 match () { () => <|> }
447 match () { 345}
448 () => <|> 346"#,
449 } 347 expect![[r#"
450 } 348 kw if
451 ", 349 kw if let
452 ), 350 kw loop
453 @r###" 351 kw match
454 [ 352 kw return
455 CompletionItem { 353 kw unsafe
456 label: "if", 354 kw while
457 source_range: 97..97, 355 "#]],
458 delete: 97..97,
459 insert: "if $0 {}",
460 kind: Keyword,
461 },
462 CompletionItem {
463 label: "loop",
464 source_range: 97..97,
465 delete: 97..97,
466 insert: "loop {$0}",
467 kind: Keyword,
468 },
469 CompletionItem {
470 label: "match",
471 source_range: 97..97,
472 delete: 97..97,
473 insert: "match $0 {}",
474 kind: Keyword,
475 },
476 CompletionItem {
477 label: "return",
478 source_range: 97..97,
479 delete: 97..97,
480 insert: "return $0",
481 kind: Keyword,
482 },
483 CompletionItem {
484 label: "while",
485 source_range: 97..97,
486 delete: 97..97,
487 insert: "while $0 {}",
488 kind: Keyword,
489 },
490 ]
491 "###
492 ); 356 );
493 } 357 }
494 358
495 #[test] 359 #[test]
496 fn last_return_in_block_has_semi() { 360 fn test_keywords_in_trait_def() {
497 assert_debug_snapshot!( 361 check(
498 do_keyword_completion( 362 r"trait My { <|> }",
499 r" 363 expect![[r#"
500 fn quux() -> i32 { 364 kw const
501 if condition { 365 kw fn
502 <|> 366 kw type
503 } 367 kw unsafe
504 } 368 "#]],
505 ",
506 ),
507 @r###"
508 [
509 CompletionItem {
510 label: "if",
511 source_range: 95..95,
512 delete: 95..95,
513 insert: "if $0 {}",
514 kind: Keyword,
515 },
516 CompletionItem {
517 label: "loop",
518 source_range: 95..95,
519 delete: 95..95,
520 insert: "loop {$0}",
521 kind: Keyword,
522 },
523 CompletionItem {
524 label: "match",
525 source_range: 95..95,
526 delete: 95..95,
527 insert: "match $0 {}",
528 kind: Keyword,
529 },
530 CompletionItem {
531 label: "return",
532 source_range: 95..95,
533 delete: 95..95,
534 insert: "return $0;",
535 kind: Keyword,
536 },
537 CompletionItem {
538 label: "while",
539 source_range: 95..95,
540 delete: 95..95,
541 insert: "while $0 {}",
542 kind: Keyword,
543 },
544 ]
545 "###
546 ); 369 );
547 assert_debug_snapshot!( 370 }
548 do_keyword_completion( 371
549 r" 372 #[test]
550 fn quux() -> i32 { 373 fn test_keywords_in_impl_def() {
551 if condition { 374 check(
552 <|> 375 r"impl My { <|> }",
553 } 376 expect![[r#"
554 let x = 92; 377 kw const
555 x 378 kw fn
556 } 379 kw pub
557 ", 380 kw type
558 ), 381 kw unsafe
559 @r###" 382 "#]],
560 [
561 CompletionItem {
562 label: "if",
563 source_range: 95..95,
564 delete: 95..95,
565 insert: "if $0 {}",
566 kind: Keyword,
567 },
568 CompletionItem {
569 label: "loop",
570 source_range: 95..95,
571 delete: 95..95,
572 insert: "loop {$0}",
573 kind: Keyword,
574 },
575 CompletionItem {
576 label: "match",
577 source_range: 95..95,
578 delete: 95..95,
579 insert: "match $0 {}",
580 kind: Keyword,
581 },
582 CompletionItem {
583 label: "return",
584 source_range: 95..95,
585 delete: 95..95,
586 insert: "return $0;",
587 kind: Keyword,
588 },
589 CompletionItem {
590 label: "while",
591 source_range: 95..95,
592 delete: 95..95,
593 insert: "while $0 {}",
594 kind: Keyword,
595 },
596 ]
597 "###
598 ); 383 );
599 } 384 }
600 385
601 #[test] 386 #[test]
602 fn completes_break_and_continue_in_loops() { 387 fn test_keywords_in_loop() {
603 assert_debug_snapshot!( 388 check(
604 do_keyword_completion( 389 r"fn my() { loop { <|> } }",
605 r" 390 expect![[r#"
606 fn quux() -> i32 { 391 kw break
607 loop { <|> } 392 kw const
608 } 393 kw continue
609 ", 394 kw extern
610 ), 395 kw fn
611 @r###" 396 kw if
612 [ 397 kw if let
613 CompletionItem { 398 kw impl
614 label: "break", 399 kw let
615 source_range: 63..63, 400 kw loop
616 delete: 63..63, 401 kw match
617 insert: "break;", 402 kw mod
618 kind: Keyword, 403 kw return
619 }, 404 kw static
620 CompletionItem { 405 kw trait
621 label: "continue", 406 kw type
622 source_range: 63..63, 407 kw unsafe
623 delete: 63..63, 408 kw use
624 insert: "continue;", 409 kw while
625 kind: Keyword, 410 "#]],
626 },
627 CompletionItem {
628 label: "if",
629 source_range: 63..63,
630 delete: 63..63,
631 insert: "if $0 {}",
632 kind: Keyword,
633 },
634 CompletionItem {
635 label: "loop",
636 source_range: 63..63,
637 delete: 63..63,
638 insert: "loop {$0}",
639 kind: Keyword,
640 },
641 CompletionItem {
642 label: "match",
643 source_range: 63..63,
644 delete: 63..63,
645 insert: "match $0 {}",
646 kind: Keyword,
647 },
648 CompletionItem {
649 label: "return",
650 source_range: 63..63,
651 delete: 63..63,
652 insert: "return $0;",
653 kind: Keyword,
654 },
655 CompletionItem {
656 label: "while",
657 source_range: 63..63,
658 delete: 63..63,
659 insert: "while $0 {}",
660 kind: Keyword,
661 },
662 ]
663 "###
664 ); 411 );
412 }
665 413
666 // No completion: lambda isolates control flow 414 #[test]
667 assert_debug_snapshot!( 415 fn test_keywords_after_unsafe_in_item_list() {
668 do_keyword_completion( 416 check(
669 r" 417 r"unsafe <|>",
670 fn quux() -> i32 { 418 expect![[r#"
671 loop { || { <|> } } 419 kw fn
672 } 420 kw impl
673 ", 421 kw trait
674 ), 422 "#]],
675 @r###"
676 [
677 CompletionItem {
678 label: "if",
679 source_range: 68..68,
680 delete: 68..68,
681 insert: "if $0 {}",
682 kind: Keyword,
683 },
684 CompletionItem {
685 label: "loop",
686 source_range: 68..68,
687 delete: 68..68,
688 insert: "loop {$0}",
689 kind: Keyword,
690 },
691 CompletionItem {
692 label: "match",
693 source_range: 68..68,
694 delete: 68..68,
695 insert: "match $0 {}",
696 kind: Keyword,
697 },
698 CompletionItem {
699 label: "return",
700 source_range: 68..68,
701 delete: 68..68,
702 insert: "return $0;",
703 kind: Keyword,
704 },
705 CompletionItem {
706 label: "while",
707 source_range: 68..68,
708 delete: 68..68,
709 insert: "while $0 {}",
710 kind: Keyword,
711 },
712 ]
713 "###
714 ); 423 );
715 } 424 }
716 425
717 #[test] 426 #[test]
718 fn no_semi_after_break_continue_in_expr() { 427 fn test_keywords_after_unsafe_in_block_expr() {
719 assert_debug_snapshot!( 428 check(
720 do_keyword_completion( 429 r"fn my_fn() { unsafe <|> }",
721 r" 430 expect![[r#"
722 fn f() { 431 kw fn
723 loop { 432 kw impl
724 match () { 433 kw trait
725 () => br<|> 434 "#]],
726 } 435 );
727 } 436 }
728 } 437
729 ", 438 #[test]
730 ), 439 fn test_mut_in_ref_and_in_fn_parameters_list() {
731 @r###" 440 check(
732 [ 441 r"fn my_fn(&<|>) {}",
733 CompletionItem { 442 expect![[r#"
734 label: "break", 443 kw mut
735 source_range: 122..124, 444 "#]],
736 delete: 122..124, 445 );
737 insert: "break", 446 check(
738 kind: Keyword, 447 r"fn my_fn(<|>) {}",
739 }, 448 expect![[r#"
740 CompletionItem { 449 kw mut
741 label: "continue", 450 "#]],
742 source_range: 122..124, 451 );
743 delete: 122..124, 452 check(
744 insert: "continue", 453 r"fn my_fn() { let &<|> }",
745 kind: Keyword, 454 expect![[r#"
746 }, 455 kw mut
747 CompletionItem { 456 "#]],
748 label: "if", 457 );
749 source_range: 122..124, 458 }
750 delete: 122..124, 459
751 insert: "if $0 {}", 460 #[test]
752 kind: Keyword, 461 fn test_where_keyword() {
753 }, 462 check(
754 CompletionItem { 463 r"trait A <|>",
755 label: "loop", 464 expect![[r#"
756 source_range: 122..124, 465 kw where
757 delete: 122..124, 466 "#]],
758 insert: "loop {$0}", 467 );
759 kind: Keyword, 468 check(
760 }, 469 r"impl A <|>",
761 CompletionItem { 470 expect![[r#"
762 label: "match", 471 kw where
763 source_range: 122..124, 472 "#]],
764 delete: 122..124, 473 );
765 insert: "match $0 {}", 474 }
766 kind: Keyword, 475
767 }, 476 #[test]
768 CompletionItem { 477 fn no_keyword_completion_in_comments() {
769 label: "return", 478 mark::check!(no_keyword_completion_in_comments);
770 source_range: 122..124, 479 check(
771 delete: 122..124, 480 r#"
772 insert: "return", 481fn test() {
773 kind: Keyword, 482 let x = 2; // A comment<|>
774 }, 483}
775 CompletionItem { 484"#,
776 label: "while", 485 expect![[""]],
777 source_range: 122..124, 486 );
778 delete: 122..124, 487 check(
779 insert: "while $0 {}", 488 r#"
780 kind: Keyword, 489/*
781 }, 490Some multi-line comment<|>
782 ] 491*/
783 "### 492"#,
493 expect![[""]],
494 );
495 check(
496 r#"
497/// Some doc comment
498/// let test<|> = 1
499"#,
500 expect![[""]],
501 );
502 }
503
504 #[test]
505 fn test_completion_await_impls_future() {
506 check(
507 r#"
508//- /main.rs
509use std::future::*;
510struct A {}
511impl Future for A {}
512fn foo(a: A) { a.<|> }
513
514//- /std/lib.rs
515pub mod future {
516 #[lang = "future_trait"]
517 pub trait Future {}
518}
519"#,
520 expect![[r#"
521 kw await expr.await
522 "#]],
523 )
524 }
525
526 #[test]
527 fn after_let() {
528 check(
529 r#"fn main() { let _ = <|> }"#,
530 expect![[r#"
531 kw if
532 kw if let
533 kw loop
534 kw match
535 kw return
536 kw while
537 "#]],
784 ) 538 )
785 } 539 }
786} 540}
diff --git a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
index d9bb5fd25..0447f0511 100644
--- a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
+++ b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
@@ -5,7 +5,7 @@ use crate::completion::{CompletionContext, Completions};
5pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { 5pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
6 // Show only macros in top level. 6 // Show only macros in top level.
7 if ctx.is_new_item { 7 if ctx.is_new_item {
8 ctx.scope().process_all_names(&mut |name, res| { 8 ctx.scope.process_all_names(&mut |name, res| {
9 if let hir::ScopeDef::MacroDef(mac) = res { 9 if let hir::ScopeDef::MacroDef(mac) = res {
10 acc.add_macro(ctx, Some(name.to_string()), mac); 10 acc.add_macro(ctx, Some(name.to_string()), mac);
11 } 11 }
@@ -15,130 +15,27 @@ pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &Compl
15 15
16#[cfg(test)] 16#[cfg(test)]
17mod tests { 17mod tests {
18 use insta::assert_debug_snapshot; 18 use expect::{expect, Expect};
19 19
20 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 20 use crate::completion::{test_utils::completion_list, CompletionKind};
21 21
22 fn do_reference_completion(code: &str) -> Vec<CompletionItem> { 22 fn check(ra_fixture: &str, expect: Expect) {
23 do_completion(code, CompletionKind::Reference) 23 let actual = completion_list(ra_fixture, CompletionKind::Reference);
24 expect.assert_eq(&actual)
24 } 25 }
25 26
26 #[test] 27 #[test]
27 fn completes_macros_as_item() { 28 fn completes_macros_as_item() {
28 assert_debug_snapshot!( 29 check(
29 do_reference_completion( 30 r#"
30 " 31macro_rules! foo { () => {} }
31 //- /main.rs 32fn foo() {}
32 macro_rules! foo { 33
33 () => {} 34<|>
34 } 35"#,
35 36 expect![[r#"
36 fn foo() {} 37 ma foo!(…) macro_rules! foo
37 38 "#]],
38 <|> 39 )
39 "
40 ),
41 @r###"
42 [
43 CompletionItem {
44 label: "foo!(…)",
45 source_range: 46..46,
46 delete: 46..46,
47 insert: "foo!($0)",
48 kind: Macro,
49 detail: "macro_rules! foo",
50 },
51 ]
52 "###
53 );
54 }
55
56 #[test]
57 fn completes_vec_macros_with_square_brackets() {
58 assert_debug_snapshot!(
59 do_reference_completion(
60 "
61 //- /main.rs
62 /// Creates a [`Vec`] containing the arguments.
63 ///
64 /// - Create a [`Vec`] containing a given list of elements:
65 ///
66 /// ```
67 /// let v = vec![1, 2, 3];
68 /// assert_eq!(v[0], 1);
69 /// assert_eq!(v[1], 2);
70 /// assert_eq!(v[2], 3);
71 /// ```
72 macro_rules! vec {
73 () => {}
74 }
75
76 fn foo() {}
77
78 <|>
79 "
80 ),
81 @r###"
82 [
83 CompletionItem {
84 label: "vec![…]",
85 source_range: 280..280,
86 delete: 280..280,
87 insert: "vec![$0]",
88 kind: Macro,
89 detail: "macro_rules! vec",
90 documentation: Documentation(
91 "Creates a [`Vec`] containing the arguments.\n\n- Create a [`Vec`] containing a given list of elements:\n\n```\nlet v = vec![1, 2, 3];\nassert_eq!(v[0], 1);\nassert_eq!(v[1], 2);\nassert_eq!(v[2], 3);\n```",
92 ),
93 },
94 ]
95 "###
96 );
97 }
98
99 #[test]
100 fn completes_macros_braces_guessing() {
101 assert_debug_snapshot!(
102 do_reference_completion(
103 "
104 //- /main.rs
105 /// Foo
106 ///
107 /// Not call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.
108 /// Call as `let _=foo! { hello world };`
109 macro_rules! foo {
110 () => {}
111 }
112
113 fn main() {
114 <|>
115 }
116 "
117 ),
118 @r###"
119 [
120 CompletionItem {
121 label: "foo! {…}",
122 source_range: 163..163,
123 delete: 163..163,
124 insert: "foo! {$0}",
125 kind: Macro,
126 detail: "macro_rules! foo",
127 documentation: Documentation(
128 "Foo\n\nNot call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.\nCall as `let _=foo! { hello world };`",
129 ),
130 },
131 CompletionItem {
132 label: "main()",
133 source_range: 163..163,
134 delete: 163..163,
135 insert: "main()$0",
136 kind: Function,
137 lookup: "main",
138 detail: "fn main()",
139 },
140 ]
141 "###
142 );
143 } 40 }
144} 41}
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
index fdd9e928b..aceb77cb5 100644
--- a/crates/ra_ide/src/completion/complete_pattern.rs
+++ b/crates/ra_ide/src/completion/complete_pattern.rs
@@ -13,7 +13,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
13 13
14 // FIXME: ideally, we should look at the type we are matching against and 14 // FIXME: ideally, we should look at the type we are matching against and
15 // suggest variants + auto-imports 15 // suggest variants + auto-imports
16 ctx.scope().process_all_names(&mut |name, res| { 16 ctx.scope.process_all_names(&mut |name, res| {
17 match &res { 17 match &res {
18 hir::ScopeDef::ModuleDef(def) => match def { 18 hir::ScopeDef::ModuleDef(def) => match def {
19 hir::ModuleDef::Adt(hir::Adt::Enum(..)) 19 hir::ModuleDef::Adt(hir::Adt::Enum(..))
@@ -33,106 +33,56 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
33 33
34#[cfg(test)] 34#[cfg(test)]
35mod tests { 35mod tests {
36 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 36 use expect::{expect, Expect};
37 use insta::assert_debug_snapshot;
38 37
39 fn complete(code: &str) -> Vec<CompletionItem> { 38 use crate::completion::{test_utils::completion_list, CompletionKind};
40 do_completion(code, CompletionKind::Reference) 39
40 fn check(ra_fixture: &str, expect: Expect) {
41 let actual = completion_list(ra_fixture, CompletionKind::Reference);
42 expect.assert_eq(&actual)
41 } 43 }
42 44
43 #[test] 45 #[test]
44 fn completes_enum_variants_and_modules() { 46 fn completes_enum_variants_and_modules() {
45 let completions = complete( 47 check(
46 r" 48 r#"
47 enum E { X } 49enum E { X }
48 use self::E::X; 50use self::E::X;
49 const Z: E = E::X; 51const Z: E = E::X;
50 mod m {} 52mod m {}
51 53
52 static FOO: E = E::X; 54static FOO: E = E::X;
53 struct Bar { f: u32 } 55struct Bar { f: u32 }
54 56
55 fn foo() { 57fn foo() {
56 match E::X { 58 match E::X { <|> }
57 <|> 59}
58 } 60"#,
59 } 61 expect![[r#"
60 ", 62 st Bar
63 en E
64 ev X ()
65 ct Z
66 md m
67 "#]],
61 ); 68 );
62 assert_debug_snapshot!(completions, @r###"
63 [
64 CompletionItem {
65 label: "Bar",
66 source_range: 246..246,
67 delete: 246..246,
68 insert: "Bar",
69 kind: Struct,
70 },
71 CompletionItem {
72 label: "E",
73 source_range: 246..246,
74 delete: 246..246,
75 insert: "E",
76 kind: Enum,
77 },
78 CompletionItem {
79 label: "X",
80 source_range: 246..246,
81 delete: 246..246,
82 insert: "X",
83 kind: EnumVariant,
84 detail: "()",
85 },
86 CompletionItem {
87 label: "Z",
88 source_range: 246..246,
89 delete: 246..246,
90 insert: "Z",
91 kind: Const,
92 },
93 CompletionItem {
94 label: "m",
95 source_range: 246..246,
96 delete: 246..246,
97 insert: "m",
98 kind: Module,
99 },
100 ]
101 "###);
102 } 69 }
103 70
104 #[test] 71 #[test]
105 fn completes_in_simple_macro_call() { 72 fn completes_in_simple_macro_call() {
106 let completions = complete( 73 check(
107 r" 74 r#"
108 macro_rules! m { ($e:expr) => { $e } } 75macro_rules! m { ($e:expr) => { $e } }
109 enum E { X } 76enum E { X }
110 77
111 fn foo() { 78fn foo() {
112 m!(match E::X { 79 m!(match E::X { <|> })
113 <|> 80}
114 }) 81"#,
115 } 82 expect![[r#"
116 ", 83 en E
84 ma m!(…) macro_rules! m
85 "#]],
117 ); 86 );
118 assert_debug_snapshot!(completions, @r###"
119 [
120 CompletionItem {
121 label: "E",
122 source_range: 151..151,
123 delete: 151..151,
124 insert: "E",
125 kind: Enum,
126 },
127 CompletionItem {
128 label: "m!(…)",
129 source_range: 151..151,
130 delete: 151..151,
131 insert: "m!($0)",
132 kind: Macro,
133 detail: "macro_rules! m",
134 },
135 ]
136 "###);
137 } 87 }
138} 88}
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 59b58bf98..8735b9010 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -8,14 +8,13 @@ use ra_text_edit::TextEdit;
8 8
9use crate::{ 9use crate::{
10 completion::{ 10 completion::{
11 completion_config::SnippetCap,
11 completion_context::CompletionContext, 12 completion_context::CompletionContext,
12 completion_item::{Builder, CompletionKind, Completions}, 13 completion_item::{Builder, CompletionKind, Completions},
13 }, 14 },
14 CompletionItem, 15 CompletionItem, CompletionItemKind,
15}; 16};
16 17
17use super::completion_config::SnippetCap;
18
19pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 18pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
20 if !ctx.config.enable_postfix_completions { 19 if !ctx.config.enable_postfix_completions {
21 return; 20 return;
@@ -91,7 +90,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
91 &dot_receiver, 90 &dot_receiver,
92 "if", 91 "if",
93 "if expr {}", 92 "if expr {}",
94 &format!("if {} {{$0}}", receiver_text), 93 &format!("if {} {{\n $0\n}}", receiver_text),
95 ) 94 )
96 .add_to(acc); 95 .add_to(acc);
97 postfix_snippet( 96 postfix_snippet(
@@ -100,13 +99,12 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
100 &dot_receiver, 99 &dot_receiver,
101 "while", 100 "while",
102 "while expr {}", 101 "while expr {}",
103 &format!("while {} {{\n$0\n}}", receiver_text), 102 &format!("while {} {{\n $0\n}}", receiver_text),
104 ) 103 )
105 .add_to(acc); 104 .add_to(acc);
105 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
106 .add_to(acc);
106 } 107 }
107 // !&&&42 is a compiler error, ergo process it before considering the references
108 postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text))
109 .add_to(acc);
110 108
111 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)) 109 postfix_snippet(ctx, cap, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text))
112 .add_to(acc); 110 .add_to(acc);
@@ -125,33 +123,35 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
125 let dot_receiver = include_references(dot_receiver); 123 let dot_receiver = include_references(dot_receiver);
126 let receiver_text = 124 let receiver_text =
127 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal); 125 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
126
128 match try_enum { 127 match try_enum {
129 Some(try_enum) => { 128 Some(try_enum) => match try_enum {
130 match try_enum { 129 TryEnum::Result => {
131 TryEnum::Result => { 130 postfix_snippet(
132 postfix_snippet(
133 ctx, 131 ctx,
134 cap, 132 cap,
135 &dot_receiver, 133 &dot_receiver,
136 "match", 134 "match",
137 "match expr {}", 135 "match expr {}",
138 &format!("match {} {{\n Ok(${{1:_}}) => {{$2\\}},\n Err(${{3:_}}) => {{$0\\}},\n}}", receiver_text), 136 &format!("match {} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}", receiver_text),
139 ) 137 )
140 .add_to(acc); 138 .add_to(acc);
141 } 139 }
142 TryEnum::Option => { 140 TryEnum::Option => {
143 postfix_snippet( 141 postfix_snippet(
144 ctx, 142 ctx,
145 cap, 143 cap,
146 &dot_receiver, 144 &dot_receiver,
147 "match", 145 "match",
148 "match expr {}", 146 "match expr {}",
149 &format!("match {} {{\n Some(${{1:_}}) => {{$2\\}},\n None => {{$0\\}},\n}}", receiver_text), 147 &format!(
148 "match {} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}",
149 receiver_text
150 ),
150 ) 151 )
151 .add_to(acc); 152 .add_to(acc);
152 }
153 } 153 }
154 } 154 },
155 None => { 155 None => {
156 postfix_snippet( 156 postfix_snippet(
157 ctx, 157 ctx,
@@ -159,7 +159,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
159 &dot_receiver, 159 &dot_receiver,
160 "match", 160 "match",
161 "match expr {}", 161 "match expr {}",
162 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), 162 &format!("match {} {{\n ${{1:_}} => {{$0}},\n}}", receiver_text),
163 ) 163 )
164 .add_to(acc); 164 .add_to(acc);
165 } 165 }
@@ -232,536 +232,147 @@ fn postfix_snippet(
232 }; 232 };
233 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) 233 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
234 .detail(detail) 234 .detail(detail)
235 .kind(CompletionItemKind::Snippet)
235 .snippet_edit(cap, edit) 236 .snippet_edit(cap, edit)
236} 237}
237 238
238#[cfg(test)] 239#[cfg(test)]
239mod tests { 240mod tests {
240 use insta::assert_debug_snapshot; 241 use expect::{expect, Expect};
241 242
242 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 243 use crate::completion::{
244 test_utils::{check_edit, completion_list},
245 CompletionKind,
246 };
243 247
244 fn do_postfix_completion(code: &str) -> Vec<CompletionItem> { 248 fn check(ra_fixture: &str, expect: Expect) {
245 do_completion(code, CompletionKind::Postfix) 249 let actual = completion_list(ra_fixture, CompletionKind::Postfix);
250 expect.assert_eq(&actual)
246 } 251 }
247 252
248 #[test] 253 #[test]
249 fn postfix_completion_works_for_trivial_path_expression() { 254 fn postfix_completion_works_for_trivial_path_expression() {
250 assert_debug_snapshot!( 255 check(
251 do_postfix_completion( 256 r#"
252 r#" 257fn main() {
253 fn main() { 258 let bar = true;
254 let bar = true; 259 bar.<|>
255 bar.<|> 260}
256 } 261"#,
257 "#, 262 expect![[r#"
258 ), 263 sn box Box::new(expr)
259 @r###" 264 sn call function(expr)
260 [ 265 sn dbg dbg!(expr)
261 CompletionItem { 266 sn if if expr {}
262 label: "box", 267 sn match match expr {}
263 source_range: 89..89, 268 sn not !expr
264 delete: 85..89, 269 sn ref &expr
265 insert: "Box::new(bar)", 270 sn refm &mut expr
266 detail: "Box::new(expr)", 271 sn while while expr {}
267 }, 272 "#]],
268 CompletionItem {
269 label: "call",
270 source_range: 89..89,
271 delete: 85..89,
272 insert: "${1}(bar)",
273 detail: "function(expr)",
274 },
275 CompletionItem {
276 label: "dbg",
277 source_range: 89..89,
278 delete: 85..89,
279 insert: "dbg!(bar)",
280 detail: "dbg!(expr)",
281 },
282 CompletionItem {
283 label: "if",
284 source_range: 89..89,
285 delete: 85..89,
286 insert: "if bar {$0}",
287 detail: "if expr {}",
288 },
289 CompletionItem {
290 label: "match",
291 source_range: 89..89,
292 delete: 85..89,
293 insert: "match bar {\n ${1:_} => {$0\\},\n}",
294 detail: "match expr {}",
295 },
296 CompletionItem {
297 label: "not",
298 source_range: 89..89,
299 delete: 85..89,
300 insert: "!bar",
301 detail: "!expr",
302 },
303 CompletionItem {
304 label: "ref",
305 source_range: 89..89,
306 delete: 85..89,
307 insert: "&bar",
308 detail: "&expr",
309 },
310 CompletionItem {
311 label: "refm",
312 source_range: 89..89,
313 delete: 85..89,
314 insert: "&mut bar",
315 detail: "&mut expr",
316 },
317 CompletionItem {
318 label: "while",
319 source_range: 89..89,
320 delete: 85..89,
321 insert: "while bar {\n$0\n}",
322 detail: "while expr {}",
323 },
324 ]
325 "###
326 ); 273 );
327 } 274 }
328 275
329 #[test] 276 #[test]
330 fn postfix_completion_works_for_option() { 277 fn postfix_type_filtering() {
331 assert_debug_snapshot!( 278 check(
332 do_postfix_completion( 279 r#"
333 r#" 280fn main() {
334 enum Option<T> { 281 let bar: u8 = 12;
335 Some(T), 282 bar.<|>
336 None, 283}
337 } 284"#,
338 285 expect![[r#"
339 fn main() { 286 sn box Box::new(expr)
340 let bar = Option::Some(true); 287 sn call function(expr)
341 bar.<|> 288 sn dbg dbg!(expr)
342 } 289 sn match match expr {}
343 "#, 290 sn ref &expr
344 ), 291 sn refm &mut expr
345 @r###" 292 "#]],
346 [ 293 )
347 CompletionItem {
348 label: "box",
349 source_range: 210..210,
350 delete: 206..210,
351 insert: "Box::new(bar)",
352 detail: "Box::new(expr)",
353 },
354 CompletionItem {
355 label: "call",
356 source_range: 210..210,
357 delete: 206..210,
358 insert: "${1}(bar)",
359 detail: "function(expr)",
360 },
361 CompletionItem {
362 label: "dbg",
363 source_range: 210..210,
364 delete: 206..210,
365 insert: "dbg!(bar)",
366 detail: "dbg!(expr)",
367 },
368 CompletionItem {
369 label: "ifl",
370 source_range: 210..210,
371 delete: 206..210,
372 insert: "if let Some($1) = bar {\n $0\n}",
373 detail: "if let Some {}",
374 },
375 CompletionItem {
376 label: "match",
377 source_range: 210..210,
378 delete: 206..210,
379 insert: "match bar {\n Some(${1:_}) => {$2\\},\n None => {$0\\},\n}",
380 detail: "match expr {}",
381 },
382 CompletionItem {
383 label: "not",
384 source_range: 210..210,
385 delete: 206..210,
386 insert: "!bar",
387 detail: "!expr",
388 },
389 CompletionItem {
390 label: "ref",
391 source_range: 210..210,
392 delete: 206..210,
393 insert: "&bar",
394 detail: "&expr",
395 },
396 CompletionItem {
397 label: "refm",
398 source_range: 210..210,
399 delete: 206..210,
400 insert: "&mut bar",
401 detail: "&mut expr",
402 },
403 CompletionItem {
404 label: "while",
405 source_range: 210..210,
406 delete: 206..210,
407 insert: "while let Some($1) = bar {\n $0\n}",
408 detail: "while let Some {}",
409 },
410 ]
411 "###
412 );
413 } 294 }
414 295
415 #[test] 296 #[test]
416 fn postfix_completion_works_for_result() { 297 fn option_iflet() {
417 assert_debug_snapshot!( 298 check_edit(
418 do_postfix_completion( 299 "ifl",
419 r#" 300 r#"
420 enum Result<T, E> { 301enum Option<T> { Some(T), None }
421 Ok(T), 302
422 Err(E), 303fn main() {
423 } 304 let bar = Option::Some(true);
305 bar.<|>
306}
307"#,
308 r#"
309enum Option<T> { Some(T), None }
424 310
425 fn main() { 311fn main() {
426 let bar = Result::Ok(true); 312 let bar = Option::Some(true);
427 bar.<|> 313 if let Some($1) = bar {
428 } 314 $0
429 "#, 315}
430 ), 316}
431 @r###" 317"#,
432 [
433 CompletionItem {
434 label: "box",
435 source_range: 211..211,
436 delete: 207..211,
437 insert: "Box::new(bar)",
438 detail: "Box::new(expr)",
439 },
440 CompletionItem {
441 label: "call",
442 source_range: 211..211,
443 delete: 207..211,
444 insert: "${1}(bar)",
445 detail: "function(expr)",
446 },
447 CompletionItem {
448 label: "dbg",
449 source_range: 211..211,
450 delete: 207..211,
451 insert: "dbg!(bar)",
452 detail: "dbg!(expr)",
453 },
454 CompletionItem {
455 label: "ifl",
456 source_range: 211..211,
457 delete: 207..211,
458 insert: "if let Ok($1) = bar {\n $0\n}",
459 detail: "if let Ok {}",
460 },
461 CompletionItem {
462 label: "match",
463 source_range: 211..211,
464 delete: 207..211,
465 insert: "match bar {\n Ok(${1:_}) => {$2\\},\n Err(${3:_}) => {$0\\},\n}",
466 detail: "match expr {}",
467 },
468 CompletionItem {
469 label: "not",
470 source_range: 211..211,
471 delete: 207..211,
472 insert: "!bar",
473 detail: "!expr",
474 },
475 CompletionItem {
476 label: "ref",
477 source_range: 211..211,
478 delete: 207..211,
479 insert: "&bar",
480 detail: "&expr",
481 },
482 CompletionItem {
483 label: "refm",
484 source_range: 211..211,
485 delete: 207..211,
486 insert: "&mut bar",
487 detail: "&mut expr",
488 },
489 CompletionItem {
490 label: "while",
491 source_range: 211..211,
492 delete: 207..211,
493 insert: "while let Ok($1) = bar {\n $0\n}",
494 detail: "while let Ok {}",
495 },
496 ]
497 "###
498 ); 318 );
499 } 319 }
500 320
501 #[test] 321 #[test]
502 fn some_postfix_completions_ignored() { 322 fn result_match() {
503 assert_debug_snapshot!( 323 check_edit(
504 do_postfix_completion( 324 "match",
505 r#" 325 r#"
506 fn main() { 326enum Result<T, E> { Ok(T), Err(E) }
507 let bar: u8 = 12; 327
508 bar.<|> 328fn main() {
509 } 329 let bar = Result::Ok(true);
510 "#, 330 bar.<|>
511 ), 331}
512 @r###" 332"#,
513 [ 333 r#"
514 CompletionItem { 334enum Result<T, E> { Ok(T), Err(E) }
515 label: "box", 335
516 source_range: 91..91, 336fn main() {
517 delete: 87..91, 337 let bar = Result::Ok(true);
518 insert: "Box::new(bar)", 338 match bar {
519 detail: "Box::new(expr)", 339 Ok(${1:_}) => {$2},
520 }, 340 Err(${3:_}) => {$0},
521 CompletionItem { 341}
522 label: "call", 342}
523 source_range: 91..91, 343"#,
524 delete: 87..91,
525 insert: "${1}(bar)",
526 detail: "function(expr)",
527 },
528 CompletionItem {
529 label: "dbg",
530 source_range: 91..91,
531 delete: 87..91,
532 insert: "dbg!(bar)",
533 detail: "dbg!(expr)",
534 },
535 CompletionItem {
536 label: "match",
537 source_range: 91..91,
538 delete: 87..91,
539 insert: "match bar {\n ${1:_} => {$0\\},\n}",
540 detail: "match expr {}",
541 },
542 CompletionItem {
543 label: "not",
544 source_range: 91..91,
545 delete: 87..91,
546 insert: "!bar",
547 detail: "!expr",
548 },
549 CompletionItem {
550 label: "ref",
551 source_range: 91..91,
552 delete: 87..91,
553 insert: "&bar",
554 detail: "&expr",
555 },
556 CompletionItem {
557 label: "refm",
558 source_range: 91..91,
559 delete: 87..91,
560 insert: "&mut bar",
561 detail: "&mut expr",
562 },
563 ]
564 "###
565 ); 344 );
566 } 345 }
567 346
568 #[test] 347 #[test]
569 fn postfix_completion_works_for_ambiguous_float_literal() { 348 fn postfix_completion_works_for_ambiguous_float_literal() {
570 assert_debug_snapshot!( 349 check_edit("refm", r#"fn main() { 42.<|> }"#, r#"fn main() { &mut 42 }"#)
571 do_postfix_completion(
572 r#"
573 fn main() {
574 42.<|>
575 }
576 "#,
577 ),
578 @r###"
579 [
580 CompletionItem {
581 label: "box",
582 source_range: 52..52,
583 delete: 49..52,
584 insert: "Box::new(42)",
585 detail: "Box::new(expr)",
586 },
587 CompletionItem {
588 label: "call",
589 source_range: 52..52,
590 delete: 49..52,
591 insert: "${1}(42)",
592 detail: "function(expr)",
593 },
594 CompletionItem {
595 label: "dbg",
596 source_range: 52..52,
597 delete: 49..52,
598 insert: "dbg!(42)",
599 detail: "dbg!(expr)",
600 },
601 CompletionItem {
602 label: "match",
603 source_range: 52..52,
604 delete: 49..52,
605 insert: "match 42 {\n ${1:_} => {$0\\},\n}",
606 detail: "match expr {}",
607 },
608 CompletionItem {
609 label: "not",
610 source_range: 52..52,
611 delete: 49..52,
612 insert: "!42",
613 detail: "!expr",
614 },
615 CompletionItem {
616 label: "ref",
617 source_range: 52..52,
618 delete: 49..52,
619 insert: "&42",
620 detail: "&expr",
621 },
622 CompletionItem {
623 label: "refm",
624 source_range: 52..52,
625 delete: 49..52,
626 insert: "&mut 42",
627 detail: "&mut expr",
628 },
629 ]
630 "###
631 );
632 } 350 }
633 351
634 #[test] 352 #[test]
635 fn works_in_simple_macro() { 353 fn works_in_simple_macro() {
636 assert_debug_snapshot!( 354 check_edit(
637 do_postfix_completion( 355 "dbg",
638 r#" 356 r#"
639 macro_rules! m { ($e:expr) => { $e } } 357macro_rules! m { ($e:expr) => { $e } }
640 fn main() { 358fn main() {
641 let bar: u8 = 12; 359 let bar: u8 = 12;
642 m!(bar.b<|>) 360 m!(bar.d<|>)
643 } 361}
644 "#, 362"#,
645 ), 363 r#"
646 @r###" 364macro_rules! m { ($e:expr) => { $e } }
647 [ 365fn main() {
648 CompletionItem { 366 let bar: u8 = 12;
649 label: "box", 367 m!(dbg!(bar))
650 source_range: 149..150, 368}
651 delete: 145..150, 369"#,
652 insert: "Box::new(bar)",
653 detail: "Box::new(expr)",
654 },
655 CompletionItem {
656 label: "call",
657 source_range: 149..150,
658 delete: 145..150,
659 insert: "${1}(bar)",
660 detail: "function(expr)",
661 },
662 CompletionItem {
663 label: "dbg",
664 source_range: 149..150,
665 delete: 145..150,
666 insert: "dbg!(bar)",
667 detail: "dbg!(expr)",
668 },
669 CompletionItem {
670 label: "match",
671 source_range: 149..150,
672 delete: 145..150,
673 insert: "match bar {\n ${1:_} => {$0\\},\n}",
674 detail: "match expr {}",
675 },
676 CompletionItem {
677 label: "not",
678 source_range: 149..150,
679 delete: 145..150,
680 insert: "!bar",
681 detail: "!expr",
682 },
683 CompletionItem {
684 label: "ref",
685 source_range: 149..150,
686 delete: 145..150,
687 insert: "&bar",
688 detail: "&expr",
689 },
690 CompletionItem {
691 label: "refm",
692 source_range: 149..150,
693 delete: 145..150,
694 insert: "&mut bar",
695 detail: "&mut expr",
696 },
697 ]
698 "###
699 ); 370 );
700 } 371 }
701 372
702 #[test] 373 #[test]
703 fn postfix_completion_for_references() { 374 fn postfix_completion_for_references() {
704 assert_debug_snapshot!( 375 check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#);
705 do_postfix_completion( 376 check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#);
706 r#"
707 fn main() {
708 &&&&42.<|>
709 }
710 "#,
711 ),
712 @r###"
713 [
714 CompletionItem {
715 label: "box",
716 source_range: 56..56,
717 delete: 49..56,
718 insert: "Box::new(&&&&42)",
719 detail: "Box::new(expr)",
720 },
721 CompletionItem {
722 label: "call",
723 source_range: 56..56,
724 delete: 49..56,
725 insert: "${1}(&&&&42)",
726 detail: "function(expr)",
727 },
728 CompletionItem {
729 label: "dbg",
730 source_range: 56..56,
731 delete: 49..56,
732 insert: "dbg!(&&&&42)",
733 detail: "dbg!(expr)",
734 },
735 CompletionItem {
736 label: "match",
737 source_range: 56..56,
738 delete: 49..56,
739 insert: "match &&&&42 {\n ${1:_} => {$0\\},\n}",
740 detail: "match expr {}",
741 },
742 CompletionItem {
743 label: "not",
744 source_range: 56..56,
745 delete: 53..56,
746 insert: "!42",
747 detail: "!expr",
748 },
749 CompletionItem {
750 label: "ref",
751 source_range: 56..56,
752 delete: 53..56,
753 insert: "&42",
754 detail: "&expr",
755 },
756 CompletionItem {
757 label: "refm",
758 source_range: 56..56,
759 delete: 53..56,
760 insert: "&mut 42",
761 detail: "&mut expr",
762 },
763 ]
764 "###
765 );
766 } 377 }
767} 378}
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index 02ac0166b..b08f5b9b4 100644
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -17,21 +17,20 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
17 return; 17 return;
18 } 18 }
19 19
20 let scope = ctx.scope(); 20 let context_module = ctx.scope.module();
21 let context_module = scope.module();
22 21
23 let res = match scope.resolve_hir_path_qualifier(&path) { 22 let resolution = match ctx.scope.resolve_hir_path_qualifier(&path) {
24 Some(res) => res, 23 Some(res) => res,
25 None => return, 24 None => return,
26 }; 25 };
27 26
28 // Add associated types on type parameters and `Self`. 27 // Add associated types on type parameters and `Self`.
29 res.assoc_type_shorthand_candidates(ctx.db, |alias| { 28 resolution.assoc_type_shorthand_candidates(ctx.db, |alias| {
30 acc.add_type_alias(ctx, alias); 29 acc.add_type_alias(ctx, alias);
31 None::<()> 30 None::<()>
32 }); 31 });
33 32
34 match res { 33 match resolution {
35 PathResolution::Def(hir::ModuleDef::Module(module)) => { 34 PathResolution::Def(hir::ModuleDef::Module(module)) => {
36 let module_scope = module.scope(ctx.db, context_module); 35 let module_scope = module.scope(ctx.db, context_module);
37 for (name, def) in module_scope { 36 for (name, def) in module_scope {
@@ -68,7 +67,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
68 67
69 let krate = ctx.krate; 68 let krate = ctx.krate;
70 if let Some(krate) = krate { 69 if let Some(krate) = krate {
71 let traits_in_scope = ctx.scope().traits_in_scope(); 70 let traits_in_scope = ctx.scope.traits_in_scope();
72 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { 71 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
73 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 72 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
74 return None; 73 return None;
@@ -113,13 +112,13 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
113 } 112 }
114 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => { 113 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
115 if let Some(krate) = ctx.krate { 114 if let Some(krate) = ctx.krate {
116 let ty = match res { 115 let ty = match resolution {
117 PathResolution::TypeParam(param) => param.ty(ctx.db), 116 PathResolution::TypeParam(param) => param.ty(ctx.db),
118 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db), 117 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
119 _ => return, 118 _ => return,
120 }; 119 };
121 120
122 let traits_in_scope = ctx.scope().traits_in_scope(); 121 let traits_in_scope = ctx.scope.traits_in_scope();
123 let mut seen = FxHashSet::default(); 122 let mut seen = FxHashSet::default();
124 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { 123 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
125 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 124 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
@@ -147,1229 +146,588 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
147 146
148#[cfg(test)] 147#[cfg(test)]
149mod tests { 148mod tests {
149 use expect::{expect, Expect};
150 use test_utils::mark; 150 use test_utils::mark;
151 151
152 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 152 use crate::completion::{
153 use insta::assert_debug_snapshot; 153 test_utils::{check_edit, completion_list},
154 CompletionKind,
155 };
156
157 fn check(ra_fixture: &str, expect: Expect) {
158 let actual = completion_list(ra_fixture, CompletionKind::Reference);
159 expect.assert_eq(&actual);
160 }
154 161
155 fn do_reference_completion(code: &str) -> Vec<CompletionItem> { 162 fn check_builtin(ra_fixture: &str, expect: Expect) {
156 do_completion(code, CompletionKind::Reference) 163 let actual = completion_list(ra_fixture, CompletionKind::BuiltinType);
164 expect.assert_eq(&actual);
157 } 165 }
158 166
159 #[test] 167 #[test]
160 fn dont_complete_current_use() { 168 fn dont_complete_current_use() {
161 mark::check!(dont_complete_current_use); 169 mark::check!(dont_complete_current_use);
162 let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference); 170 check(r#"use self::foo<|>;"#, expect![[""]]);
163 assert!(completions.is_empty());
164 } 171 }
165 172
166 #[test] 173 #[test]
167 fn dont_complete_current_use_in_braces_with_glob() { 174 fn dont_complete_current_use_in_braces_with_glob() {
168 let completions = do_completion( 175 check(
169 r" 176 r#"
170 mod foo { pub struct S; } 177mod foo { pub struct S; }
171 use self::{foo::*, bar<|>}; 178use self::{foo::*, bar<|>};
172 ", 179"#,
173 CompletionKind::Reference, 180 expect![[r#"
181 st S
182 md foo
183 "#]],
174 ); 184 );
175 assert_eq!(completions.len(), 2);
176 } 185 }
177 186
178 #[test] 187 #[test]
179 fn dont_complete_primitive_in_use() { 188 fn dont_complete_primitive_in_use() {
180 let completions = do_completion(r"use self::<|>;", CompletionKind::BuiltinType); 189 check_builtin(r#"use self::<|>;"#, expect![[""]]);
181 assert!(completions.is_empty());
182 } 190 }
183 191
184 #[test] 192 #[test]
185 fn dont_complete_primitive_in_module_scope() { 193 fn dont_complete_primitive_in_module_scope() {
186 let completions = do_completion(r"fn foo() { self::<|> }", CompletionKind::BuiltinType); 194 check_builtin(r#"fn foo() { self::<|> }"#, expect![[""]]);
187 assert!(completions.is_empty());
188 } 195 }
189 196
190 #[test] 197 #[test]
191 fn completes_primitives() { 198 fn completes_primitives() {
192 let completions = 199 check_builtin(
193 do_completion(r"fn main() { let _: <|> = 92; }", CompletionKind::BuiltinType); 200 r#"fn main() { let _: <|> = 92; }"#,
194 assert_eq!(completions.len(), 17); 201 expect![[r#"
195 } 202 bt bool
196 203 bt char
197 #[test] 204 bt f32
198 fn completes_mod_with_docs() { 205 bt f64
199 assert_debug_snapshot!( 206 bt i128
200 do_reference_completion( 207 bt i16
201 r" 208 bt i32
202 use self::my<|>; 209 bt i64
203 210 bt i8
204 /// Some simple 211 bt isize
205 /// docs describing `mod my`. 212 bt str
206 mod my { 213 bt u128
207 struct Bar; 214 bt u16
208 } 215 bt u32
209 " 216 bt u64
210 ), 217 bt u8
211 @r###" 218 bt usize
212 [ 219 "#]],
213 CompletionItem {
214 label: "my",
215 source_range: 27..29,
216 delete: 27..29,
217 insert: "my",
218 kind: Module,
219 documentation: Documentation(
220 "Some simple\ndocs describing `mod my`.",
221 ),
222 },
223 ]
224 "###
225 ); 220 );
226 } 221 }
227 222
228 #[test] 223 #[test]
229 fn completes_mod_with_same_name_as_function() { 224 fn completes_mod_with_same_name_as_function() {
230 assert_debug_snapshot!( 225 check(
231 do_reference_completion( 226 r#"
232 r" 227use self::my::<|>;
233 use self::my::<|>; 228
234 229mod my { pub struct Bar; }
235 mod my { 230fn my() {}
236 pub struct Bar; 231"#,
237 } 232 expect![[r#"
238 233 st Bar
239 fn my() {} 234 "#]],
240 "
241 ),
242 @r###"
243 [
244 CompletionItem {
245 label: "Bar",
246 source_range: 31..31,
247 delete: 31..31,
248 insert: "Bar",
249 kind: Struct,
250 },
251 ]
252 "###
253 ); 235 );
254 } 236 }
255 237
256 #[test] 238 #[test]
257 fn path_visibility() { 239 fn filters_visibility() {
258 assert_debug_snapshot!( 240 check(
259 do_reference_completion( 241 r#"
260 r" 242use self::my::<|>;
261 use self::my::<|>; 243
262 244mod my {
263 mod my { 245 struct Bar;
264 struct Bar; 246 pub struct Foo;
265 pub struct Foo; 247 pub use Bar as PublicBar;
266 pub use Bar as PublicBar; 248}
267 } 249"#,
268 " 250 expect![[r#"
269 ), 251 st Foo
270 @r###" 252 st PublicBar
271 [ 253 "#]],
272 CompletionItem {
273 label: "Foo",
274 source_range: 31..31,
275 delete: 31..31,
276 insert: "Foo",
277 kind: Struct,
278 },
279 CompletionItem {
280 label: "PublicBar",
281 source_range: 31..31,
282 delete: 31..31,
283 insert: "PublicBar",
284 kind: Struct,
285 },
286 ]
287 "###
288 ); 254 );
289 } 255 }
290 256
291 #[test] 257 #[test]
292 fn completes_use_item_starting_with_self() { 258 fn completes_use_item_starting_with_self() {
293 assert_debug_snapshot!( 259 check(
294 do_reference_completion( 260 r#"
295 r" 261use self::m::<|>;
296 use self::m::<|>;
297 262
298 mod m { 263mod m { pub struct Bar; }
299 pub struct Bar; 264"#,
300 } 265 expect![[r#"
301 " 266 st Bar
302 ), 267 "#]],
303 @r###"
304 [
305 CompletionItem {
306 label: "Bar",
307 source_range: 30..30,
308 delete: 30..30,
309 insert: "Bar",
310 kind: Struct,
311 },
312 ]
313 "###
314 ); 268 );
315 } 269 }
316 270
317 #[test] 271 #[test]
318 fn completes_use_item_starting_with_crate() { 272 fn completes_use_item_starting_with_crate() {
319 assert_debug_snapshot!( 273 check(
320 do_reference_completion( 274 r#"
321 " 275//- /lib.rs
322 //- /lib.rs 276mod foo;
323 mod foo; 277struct Spam;
324 struct Spam; 278//- /foo.rs
325 //- /foo.rs 279use crate::Sp<|>
326 use crate::Sp<|> 280"#,
327 " 281 expect![[r#"
328 ), 282 st Spam
329 @r###" 283 md foo
330 [ 284 "#]],
331 CompletionItem {
332 label: "Spam",
333 source_range: 11..13,
334 delete: 11..13,
335 insert: "Spam",
336 kind: Struct,
337 },
338 CompletionItem {
339 label: "foo",
340 source_range: 11..13,
341 delete: 11..13,
342 insert: "foo",
343 kind: Module,
344 },
345 ]
346 "###
347 ); 285 );
348 } 286 }
349 287
350 #[test] 288 #[test]
351 fn completes_nested_use_tree() { 289 fn completes_nested_use_tree() {
352 assert_debug_snapshot!( 290 check(
353 do_reference_completion( 291 r#"
354 " 292//- /lib.rs
355 //- /lib.rs 293mod foo;
356 mod foo; 294struct Spam;
357 struct Spam; 295//- /foo.rs
358 //- /foo.rs 296use crate::{Sp<|>};
359 use crate::{Sp<|>}; 297"#,
360 " 298 expect![[r#"
361 ), 299 st Spam
362 @r###" 300 md foo
363 [ 301 "#]],
364 CompletionItem {
365 label: "Spam",
366 source_range: 12..14,
367 delete: 12..14,
368 insert: "Spam",
369 kind: Struct,
370 },
371 CompletionItem {
372 label: "foo",
373 source_range: 12..14,
374 delete: 12..14,
375 insert: "foo",
376 kind: Module,
377 },
378 ]
379 "###
380 ); 302 );
381 } 303 }
382 304
383 #[test] 305 #[test]
384 fn completes_deeply_nested_use_tree() { 306 fn completes_deeply_nested_use_tree() {
385 assert_debug_snapshot!( 307 check(
386 do_reference_completion( 308 r#"
387 " 309//- /lib.rs
388 //- /lib.rs 310mod foo;
389 mod foo; 311pub mod bar {
390 pub mod bar { 312 pub mod baz {
391 pub mod baz { 313 pub struct Spam;
392 pub struct Spam;
393 }
394 }
395 //- /foo.rs
396 use crate::{bar::{baz::Sp<|>}};
397 "
398 ),
399 @r###"
400 [
401 CompletionItem {
402 label: "Spam",
403 source_range: 23..25,
404 delete: 23..25,
405 insert: "Spam",
406 kind: Struct,
407 },
408 ]
409 "###
410 );
411 }
412
413 #[test]
414 fn completes_enum_variant() {
415 assert_debug_snapshot!(
416 do_reference_completion(
417 "
418 //- /lib.rs
419 /// An enum
420 enum E {
421 /// Foo Variant
422 Foo,
423 /// Bar Variant with i32
424 Bar(i32)
425 }
426 fn foo() { let _ = E::<|> }
427 "
428 ),
429 @r###"
430 [
431 CompletionItem {
432 label: "Bar(…)",
433 source_range: 116..116,
434 delete: 116..116,
435 insert: "Bar($0)",
436 kind: EnumVariant,
437 lookup: "Bar",
438 detail: "(i32)",
439 documentation: Documentation(
440 "Bar Variant with i32",
441 ),
442 trigger_call_info: true,
443 },
444 CompletionItem {
445 label: "Foo",
446 source_range: 116..116,
447 delete: 116..116,
448 insert: "Foo",
449 kind: EnumVariant,
450 detail: "()",
451 documentation: Documentation(
452 "Foo Variant",
453 ),
454 },
455 ]
456 "###
457 );
458 }
459
460 #[test]
461 fn completes_enum_variant_with_details() {
462 assert_debug_snapshot!(
463 do_reference_completion(
464 "
465 //- /lib.rs
466 struct S { field: u32 }
467 /// An enum
468 enum E {
469 /// Foo Variant (empty)
470 Foo,
471 /// Bar Variant with i32 and u32
472 Bar(i32, u32),
473 ///
474 S(S),
475 }
476 fn foo() { let _ = E::<|> }
477 "
478 ),
479 @r###"
480 [
481 CompletionItem {
482 label: "Bar(…)",
483 source_range: 180..180,
484 delete: 180..180,
485 insert: "Bar($0)",
486 kind: EnumVariant,
487 lookup: "Bar",
488 detail: "(i32, u32)",
489 documentation: Documentation(
490 "Bar Variant with i32 and u32",
491 ),
492 trigger_call_info: true,
493 },
494 CompletionItem {
495 label: "Foo",
496 source_range: 180..180,
497 delete: 180..180,
498 insert: "Foo",
499 kind: EnumVariant,
500 detail: "()",
501 documentation: Documentation(
502 "Foo Variant (empty)",
503 ),
504 },
505 CompletionItem {
506 label: "S(…)",
507 source_range: 180..180,
508 delete: 180..180,
509 insert: "S($0)",
510 kind: EnumVariant,
511 lookup: "S",
512 detail: "(S)",
513 documentation: Documentation(
514 "",
515 ),
516 trigger_call_info: true,
517 },
518 ]
519 "###
520 );
521 } 314 }
522 315}
523 #[test] 316//- /foo.rs
524 fn completes_struct_associated_method() { 317use crate::{bar::{baz::Sp<|>}};
525 assert_debug_snapshot!( 318"#,
526 do_reference_completion( 319 expect![[r#"
527 " 320 st Spam
528 //- /lib.rs 321 "#]],
529 /// A Struct
530 struct S;
531
532 impl S {
533 /// An associated method
534 fn m() { }
535 }
536
537 fn foo() { let _ = S::<|> }
538 "
539 ),
540 @r###"
541 [
542 CompletionItem {
543 label: "m()",
544 source_range: 100..100,
545 delete: 100..100,
546 insert: "m()$0",
547 kind: Function,
548 lookup: "m",
549 detail: "fn m()",
550 documentation: Documentation(
551 "An associated method",
552 ),
553 },
554 ]
555 "###
556 ); 322 );
557 } 323 }
558 324
559 #[test] 325 #[test]
560 fn completes_struct_associated_method_with_self() { 326 fn completes_enum_variant() {
561 assert_debug_snapshot!( 327 check(
562 do_reference_completion( 328 r#"
563 " 329enum E { Foo, Bar(i32) }
564 //- /lib.rs 330fn foo() { let _ = E::<|> }
565 /// A Struct 331"#,
566 struct S; 332 expect![[r#"
567 333 ev Bar(…) (i32)
568 impl S { 334 ev Foo ()
569 /// An associated method 335 "#]],
570 fn m(&self) { }
571 }
572
573 fn foo() { let _ = S::<|> }
574 "
575 ),
576 @r###"
577 [
578 CompletionItem {
579 label: "m()",
580 source_range: 105..105,
581 delete: 105..105,
582 insert: "m()$0",
583 kind: Method,
584 lookup: "m",
585 detail: "fn m(&self)",
586 documentation: Documentation(
587 "An associated method",
588 ),
589 },
590 ]
591 "###
592 ); 336 );
593 } 337 }
594 338
595 #[test] 339 #[test]
596 fn completes_struct_associated_const() { 340 fn completes_struct_associated_items() {
597 assert_debug_snapshot!( 341 check(
598 do_reference_completion( 342 r#"
599 " 343//- /lib.rs
600 //- /lib.rs 344struct S;
601 /// A Struct 345
602 struct S; 346impl S {
603 347 fn a() {}
604 impl S { 348 fn b(&self) {}
605 /// An associated const 349 const C: i32 = 42;
606 const C: i32 = 42; 350 type T = i32;
607 } 351}
608 352
609 fn foo() { let _ = S::<|> } 353fn foo() { let _ = S::<|> }
610 " 354"#,
611 ), 355 expect![[r#"
612 @r###" 356 ct C const C: i32 = 42;
613 [ 357 ta T type T = i32;
614 CompletionItem { 358 fn a() fn a()
615 label: "C", 359 me b() fn b(&self)
616 source_range: 107..107, 360 "#]],
617 delete: 107..107,
618 insert: "C",
619 kind: Const,
620 detail: "const C: i32 = 42;",
621 documentation: Documentation(
622 "An associated const",
623 ),
624 },
625 ]
626 "###
627 ); 361 );
628 } 362 }
629 363
630 #[test] 364 #[test]
631 fn completes_struct_associated_type() { 365 fn associated_item_visibility() {
632 assert_debug_snapshot!( 366 check(
633 do_reference_completion( 367 r#"
634 " 368struct S;
635 //- /lib.rs
636 /// A Struct
637 struct S;
638
639 impl S {
640 /// An associated type
641 type T = i32;
642 }
643 369
644 fn foo() { let _ = S::<|> } 370mod m {
645 " 371 impl super::S {
646 ), 372 pub(super) fn public_method() { }
647 @r###" 373 fn private_method() { }
648 [ 374 pub(super) type PublicType = u32;
649 CompletionItem { 375 type PrivateType = u32;
650 label: "T", 376 pub(super) const PUBLIC_CONST: u32 = 1;
651 source_range: 101..101, 377 const PRIVATE_CONST: u32 = 1;
652 delete: 101..101,
653 insert: "T",
654 kind: TypeAlias,
655 detail: "type T = i32;",
656 documentation: Documentation(
657 "An associated type",
658 ),
659 },
660 ]
661 "###
662 );
663 } 378 }
379}
664 380
665 #[test] 381fn foo() { let _ = S::<|> }
666 fn associated_item_visibility() { 382"#,
667 assert_debug_snapshot!( 383 expect![[r#"
668 do_reference_completion( 384 ct PUBLIC_CONST pub(super) const PUBLIC_CONST: u32 = 1;
669 " 385 ta PublicType pub(super) type PublicType = u32;
670 //- /lib.rs 386 fn public_method() pub(super) fn public_method()
671 struct S; 387 "#]],
672
673 mod m {
674 impl super::S {
675 pub(super) fn public_method() { }
676 fn private_method() { }
677 pub(super) type PublicType = u32;
678 type PrivateType = u32;
679 pub(super) const PUBLIC_CONST: u32 = 1;
680 const PRIVATE_CONST: u32 = 1;
681 }
682 }
683
684 fn foo() { let _ = S::<|> }
685 "
686 ),
687 @r###"
688 [
689 CompletionItem {
690 label: "PUBLIC_CONST",
691 source_range: 302..302,
692 delete: 302..302,
693 insert: "PUBLIC_CONST",
694 kind: Const,
695 detail: "pub(super) const PUBLIC_CONST: u32 = 1;",
696 },
697 CompletionItem {
698 label: "PublicType",
699 source_range: 302..302,
700 delete: 302..302,
701 insert: "PublicType",
702 kind: TypeAlias,
703 detail: "pub(super) type PublicType = u32;",
704 },
705 CompletionItem {
706 label: "public_method()",
707 source_range: 302..302,
708 delete: 302..302,
709 insert: "public_method()$0",
710 kind: Function,
711 lookup: "public_method",
712 detail: "pub(super) fn public_method()",
713 },
714 ]
715 "###
716 ); 388 );
717 } 389 }
718 390
719 #[test] 391 #[test]
720 fn completes_enum_associated_method() { 392 fn completes_enum_associated_method() {
721 assert_debug_snapshot!( 393 check(
722 do_reference_completion( 394 r#"
723 " 395enum E {};
724 //- /lib.rs 396impl E { fn m() { } }
725 /// An enum 397
726 enum S {}; 398fn foo() { let _ = E::<|> }
727 399 "#,
728 impl S { 400 expect![[r#"
729 /// An associated method 401 fn m() fn m()
730 fn m() { } 402 "#]],
731 }
732
733 fn foo() { let _ = S::<|> }
734 "
735 ),
736 @r###"
737 [
738 CompletionItem {
739 label: "m()",
740 source_range: 100..100,
741 delete: 100..100,
742 insert: "m()$0",
743 kind: Function,
744 lookup: "m",
745 detail: "fn m()",
746 documentation: Documentation(
747 "An associated method",
748 ),
749 },
750 ]
751 "###
752 ); 403 );
753 } 404 }
754 405
755 #[test] 406 #[test]
756 fn completes_union_associated_method() { 407 fn completes_union_associated_method() {
757 assert_debug_snapshot!( 408 check(
758 do_reference_completion( 409 r#"
759 " 410union U {};
760 //- /lib.rs 411impl U { fn m() { } }
761 /// A union 412
762 union U {}; 413fn foo() { let _ = U::<|> }
763 414"#,
764 impl U { 415 expect![[r#"
765 /// An associated method 416 fn m() fn m()
766 fn m() { } 417 "#]],
767 }
768
769 fn foo() { let _ = U::<|> }
770 "
771 ),
772 @r###"
773 [
774 CompletionItem {
775 label: "m()",
776 source_range: 101..101,
777 delete: 101..101,
778 insert: "m()$0",
779 kind: Function,
780 lookup: "m",
781 detail: "fn m()",
782 documentation: Documentation(
783 "An associated method",
784 ),
785 },
786 ]
787 "###
788 ); 418 );
789 } 419 }
790 420
791 #[test] 421 #[test]
792 fn completes_use_paths_across_crates() { 422 fn completes_use_paths_across_crates() {
793 assert_debug_snapshot!( 423 check(
794 do_reference_completion( 424 r#"
795 " 425//- /main.rs
796 //- /main.rs 426use foo::<|>;
797 use foo::<|>; 427
798 428//- /foo/lib.rs
799 //- /foo/lib.rs 429pub mod bar { pub struct S; }
800 pub mod bar { 430"#,
801 pub struct S; 431 expect![[r#"
802 } 432 md bar
803 " 433 "#]],
804 ),
805 @r###"
806 [
807 CompletionItem {
808 label: "bar",
809 source_range: 9..9,
810 delete: 9..9,
811 insert: "bar",
812 kind: Module,
813 },
814 ]
815 "###
816 ); 434 );
817 } 435 }
818 436
819 #[test] 437 #[test]
820 fn completes_trait_associated_method_1() { 438 fn completes_trait_associated_method_1() {
821 assert_debug_snapshot!( 439 check(
822 do_reference_completion( 440 r#"
823 " 441trait Trait { fn m(); }
824 //- /lib.rs
825 trait Trait {
826 /// A trait method
827 fn m();
828 }
829 442
830 fn foo() { let _ = Trait::<|> } 443fn foo() { let _ = Trait::<|> }
831 " 444"#,
832 ), 445 expect![[r#"
833 @r###" 446 fn m() fn m()
834 [ 447 "#]],
835 CompletionItem {
836 label: "m()",
837 source_range: 73..73,
838 delete: 73..73,
839 insert: "m()$0",
840 kind: Function,
841 lookup: "m",
842 detail: "fn m()",
843 documentation: Documentation(
844 "A trait method",
845 ),
846 },
847 ]
848 "###
849 ); 448 );
850 } 449 }
851 450
852 #[test] 451 #[test]
853 fn completes_trait_associated_method_2() { 452 fn completes_trait_associated_method_2() {
854 assert_debug_snapshot!( 453 check(
855 do_reference_completion( 454 r#"
856 " 455trait Trait { fn m(); }
857 //- /lib.rs 456
858 trait Trait { 457struct S;
859 /// A trait method 458impl Trait for S {}
860 fn m();
861 }
862 459
863 struct S; 460fn foo() { let _ = S::<|> }
864 impl Trait for S {} 461"#,
865 462 expect![[r#"
866 fn foo() { let _ = S::<|> } 463 fn m() fn m()
867 " 464 "#]],
868 ),
869 @r###"
870 [
871 CompletionItem {
872 label: "m()",
873 source_range: 99..99,
874 delete: 99..99,
875 insert: "m()$0",
876 kind: Function,
877 lookup: "m",
878 detail: "fn m()",
879 documentation: Documentation(
880 "A trait method",
881 ),
882 },
883 ]
884 "###
885 ); 465 );
886 } 466 }
887 467
888 #[test] 468 #[test]
889 fn completes_trait_associated_method_3() { 469 fn completes_trait_associated_method_3() {
890 assert_debug_snapshot!( 470 check(
891 do_reference_completion( 471 r#"
892 " 472trait Trait { fn m(); }
893 //- /lib.rs
894 trait Trait {
895 /// A trait method
896 fn m();
897 }
898 473
899 struct S; 474struct S;
900 impl Trait for S {} 475impl Trait for S {}
901 476
902 fn foo() { let _ = <S as Trait>::<|> } 477fn foo() { let _ = <S as Trait>::<|> }
903 " 478"#,
904 ), 479 expect![[r#"
905 @r###" 480 fn m() fn m()
906 [ 481 "#]],
907 CompletionItem {
908 label: "m()",
909 source_range: 110..110,
910 delete: 110..110,
911 insert: "m()$0",
912 kind: Function,
913 lookup: "m",
914 detail: "fn m()",
915 documentation: Documentation(
916 "A trait method",
917 ),
918 },
919 ]
920 "###
921 ); 482 );
922 } 483 }
923 484
924 #[test] 485 #[test]
925 fn completes_ty_param_assoc_ty() { 486 fn completes_ty_param_assoc_ty() {
926 assert_debug_snapshot!( 487 check(
927 do_reference_completion( 488 r#"
928 " 489trait Super {
929 //- /lib.rs 490 type Ty;
930 trait Super { 491 const CONST: u8;
931 type Ty; 492 fn func() {}
932 const CONST: u8; 493 fn method(&self) {}
933 fn func() {} 494}
934 fn method(&self) {}
935 }
936 495
937 trait Sub: Super { 496trait Sub: Super {
938 type SubTy; 497 type SubTy;
939 const C2: (); 498 const C2: ();
940 fn subfunc() {} 499 fn subfunc() {}
941 fn submethod(&self) {} 500 fn submethod(&self) {}
942 } 501}
943 502
944 fn foo<T: Sub>() { 503fn foo<T: Sub>() { T::<|> }
945 T::<|> 504"#,
946 } 505 expect![[r#"
947 " 506 ct C2 const C2: ();
948 ), 507 ct CONST const CONST: u8;
949 @r###" 508 ta SubTy type SubTy;
950 [ 509 ta Ty type Ty;
951 CompletionItem { 510 fn func() fn func()
952 label: "C2", 511 me method() fn method(&self)
953 source_range: 219..219, 512 fn subfunc() fn subfunc()
954 delete: 219..219, 513 me submethod() fn submethod(&self)
955 insert: "C2", 514 "#]],
956 kind: Const,
957 detail: "const C2: ();",
958 },
959 CompletionItem {
960 label: "CONST",
961 source_range: 219..219,
962 delete: 219..219,
963 insert: "CONST",
964 kind: Const,
965 detail: "const CONST: u8;",
966 },
967 CompletionItem {
968 label: "SubTy",
969 source_range: 219..219,
970 delete: 219..219,
971 insert: "SubTy",
972 kind: TypeAlias,
973 detail: "type SubTy;",
974 },
975 CompletionItem {
976 label: "Ty",
977 source_range: 219..219,
978 delete: 219..219,
979 insert: "Ty",
980 kind: TypeAlias,
981 detail: "type Ty;",
982 },
983 CompletionItem {
984 label: "func()",
985 source_range: 219..219,
986 delete: 219..219,
987 insert: "func()$0",
988 kind: Function,
989 lookup: "func",
990 detail: "fn func()",
991 },
992 CompletionItem {
993 label: "method()",
994 source_range: 219..219,
995 delete: 219..219,
996 insert: "method()$0",
997 kind: Method,
998 lookup: "method",
999 detail: "fn method(&self)",
1000 },
1001 CompletionItem {
1002 label: "subfunc()",
1003 source_range: 219..219,
1004 delete: 219..219,
1005 insert: "subfunc()$0",
1006 kind: Function,
1007 lookup: "subfunc",
1008 detail: "fn subfunc()",
1009 },
1010 CompletionItem {
1011 label: "submethod()",
1012 source_range: 219..219,
1013 delete: 219..219,
1014 insert: "submethod()$0",
1015 kind: Method,
1016 lookup: "submethod",
1017 detail: "fn submethod(&self)",
1018 },
1019 ]
1020 "###
1021 ); 515 );
1022 } 516 }
1023 517
1024 #[test] 518 #[test]
1025 fn completes_self_param_assoc_ty() { 519 fn completes_self_param_assoc_ty() {
1026 assert_debug_snapshot!( 520 check(
1027 do_reference_completion( 521 r#"
1028 " 522trait Super {
1029 //- /lib.rs 523 type Ty;
1030 trait Super { 524 const CONST: u8 = 0;
1031 type Ty; 525 fn func() {}
1032 const CONST: u8 = 0; 526 fn method(&self) {}
1033 fn func() {} 527}
1034 fn method(&self) {}
1035 }
1036 528
1037 trait Sub: Super { 529trait Sub: Super {
1038 type SubTy; 530 type SubTy;
1039 const C2: () = (); 531 const C2: () = ();
1040 fn subfunc() {} 532 fn subfunc() {}
1041 fn submethod(&self) {} 533 fn submethod(&self) {}
1042 } 534}
1043 535
1044 struct Wrap<T>(T); 536struct Wrap<T>(T);
1045 impl<T> Super for Wrap<T> {} 537impl<T> Super for Wrap<T> {}
1046 impl<T> Sub for Wrap<T> { 538impl<T> Sub for Wrap<T> {
1047 fn subfunc() { 539 fn subfunc() {
1048 // Should be able to assume `Self: Sub + Super` 540 // Should be able to assume `Self: Sub + Super`
1049 Self::<|> 541 Self::<|>
1050 } 542 }
1051 } 543}
1052 " 544"#,
1053 ), 545 expect![[r#"
1054 @r###" 546 ct C2 const C2: () = ();
1055 [ 547 ct CONST const CONST: u8 = 0;
1056 CompletionItem { 548 ta SubTy type SubTy;
1057 label: "C2", 549 ta Ty type Ty;
1058 source_range: 365..365, 550 fn func() fn func()
1059 delete: 365..365, 551 me method() fn method(&self)
1060 insert: "C2", 552 fn subfunc() fn subfunc()
1061 kind: Const, 553 me submethod() fn submethod(&self)
1062 detail: "const C2: () = ();", 554 "#]],
1063 },
1064 CompletionItem {
1065 label: "CONST",
1066 source_range: 365..365,
1067 delete: 365..365,
1068 insert: "CONST",
1069 kind: Const,
1070 detail: "const CONST: u8 = 0;",
1071 },
1072 CompletionItem {
1073 label: "SubTy",
1074 source_range: 365..365,
1075 delete: 365..365,
1076 insert: "SubTy",
1077 kind: TypeAlias,
1078 detail: "type SubTy;",
1079 },
1080 CompletionItem {
1081 label: "Ty",
1082 source_range: 365..365,
1083 delete: 365..365,
1084 insert: "Ty",
1085 kind: TypeAlias,
1086 detail: "type Ty;",
1087 },
1088 CompletionItem {
1089 label: "func()",
1090 source_range: 365..365,
1091 delete: 365..365,
1092 insert: "func()$0",
1093 kind: Function,
1094 lookup: "func",
1095 detail: "fn func()",
1096 },
1097 CompletionItem {
1098 label: "method()",
1099 source_range: 365..365,
1100 delete: 365..365,
1101 insert: "method()$0",
1102 kind: Method,
1103 lookup: "method",
1104 detail: "fn method(&self)",
1105 },
1106 CompletionItem {
1107 label: "subfunc()",
1108 source_range: 365..365,
1109 delete: 365..365,
1110 insert: "subfunc()$0",
1111 kind: Function,
1112 lookup: "subfunc",
1113 detail: "fn subfunc()",
1114 },
1115 CompletionItem {
1116 label: "submethod()",
1117 source_range: 365..365,
1118 delete: 365..365,
1119 insert: "submethod()$0",
1120 kind: Method,
1121 lookup: "submethod",
1122 detail: "fn submethod(&self)",
1123 },
1124 ]
1125 "###
1126 ); 555 );
1127 } 556 }
1128 557
1129 #[test] 558 #[test]
1130 fn completes_type_alias() { 559 fn completes_type_alias() {
1131 assert_debug_snapshot!( 560 check(
1132 do_reference_completion( 561 r#"
1133 " 562struct S;
1134 struct S; 563impl S { fn foo() {} }
1135 impl S { fn foo() {} } 564type T = S;
1136 type T = S; 565impl T { fn bar() {} }
1137 impl T { fn bar() {} } 566
1138 567fn main() { T::<|>; }
1139 fn main() { 568"#,
1140 T::<|>; 569 expect![[r#"
1141 } 570 fn bar() fn bar()
1142 " 571 fn foo() fn foo()
1143 ), 572 "#]],
1144 @r###"
1145 [
1146 CompletionItem {
1147 label: "bar()",
1148 source_range: 185..185,
1149 delete: 185..185,
1150 insert: "bar()$0",
1151 kind: Function,
1152 lookup: "bar",
1153 detail: "fn bar()",
1154 },
1155 CompletionItem {
1156 label: "foo()",
1157 source_range: 185..185,
1158 delete: 185..185,
1159 insert: "foo()$0",
1160 kind: Function,
1161 lookup: "foo",
1162 detail: "fn foo()",
1163 },
1164 ]
1165 "###
1166 ); 573 );
1167 } 574 }
1168 575
1169 #[test] 576 #[test]
1170 fn completes_qualified_macros() { 577 fn completes_qualified_macros() {
1171 assert_debug_snapshot!( 578 check(
1172 do_reference_completion( 579 r#"
1173 " 580#[macro_export]
1174 #[macro_export] 581macro_rules! foo { () => {} }
1175 macro_rules! foo { 582
1176 () => {} 583fn main() { let _ = crate::<|> }
1177 } 584 "#,
585 expect![[r##"
586 ma foo!(…) #[macro_export]
587 macro_rules! foo
588 fn main() fn main()
589 "##]],
590 );
591 }
1178 592
1179 fn main() { 593 #[test]
1180 let _ = crate::<|> 594 fn test_super_super_completion() {
1181 } 595 check(
1182 " 596 r#"
1183 ), 597mod a {
1184 @r###" 598 const A: usize = 0;
1185 [ 599 mod b {
1186 CompletionItem { 600 const B: usize = 0;
1187 label: "foo!(…)", 601 mod c { use super::super::<|> }
1188 source_range: 179..179, 602 }
1189 delete: 179..179, 603}
1190 insert: "foo!($0)", 604"#,
1191 kind: Macro, 605 expect![[r#"
1192 detail: "#[macro_export]\nmacro_rules! foo", 606 ct A
1193 }, 607 md b
1194 CompletionItem { 608 "#]],
1195 label: "main()",
1196 source_range: 179..179,
1197 delete: 179..179,
1198 insert: "main()$0",
1199 kind: Function,
1200 lookup: "main",
1201 detail: "fn main()",
1202 },
1203 ]
1204 "###
1205 ); 609 );
1206 } 610 }
1207 611
1208 #[test] 612 #[test]
1209 fn completes_reexported_items_under_correct_name() { 613 fn completes_reexported_items_under_correct_name() {
1210 assert_debug_snapshot!( 614 check(
1211 do_reference_completion( 615 r#"
1212 r" 616fn foo() { self::m::<|> }
1213 fn foo() {
1214 self::m::<|>
1215 }
1216 617
1217 mod m { 618mod m {
1218 pub use super::p::wrong_fn as right_fn; 619 pub use super::p::wrong_fn as right_fn;
1219 pub use super::p::WRONG_CONST as RIGHT_CONST; 620 pub use super::p::WRONG_CONST as RIGHT_CONST;
1220 pub use super::p::WrongType as RightType; 621 pub use super::p::WrongType as RightType;
1221 } 622}
1222 mod p { 623mod p {
1223 fn wrong_fn() {} 624 fn wrong_fn() {}
1224 const WRONG_CONST: u32 = 1; 625 const WRONG_CONST: u32 = 1;
1225 struct WrongType {}; 626 struct WrongType {};
1226 } 627}
1227 " 628"#,
1228 ), 629 expect![[r#"
1229 @r###" 630 ct RIGHT_CONST
1230 [ 631 st RightType
1231 CompletionItem { 632 fn right_fn() fn wrong_fn()
1232 label: "RIGHT_CONST", 633 "#]],
1233 source_range: 57..57, 634 );
1234 delete: 57..57, 635
1235 insert: "RIGHT_CONST", 636 check_edit(
1236 kind: Const, 637 "RightType",
1237 }, 638 r#"
1238 CompletionItem { 639fn foo() { self::m::<|> }
1239 label: "RightType", 640
1240 source_range: 57..57, 641mod m {
1241 delete: 57..57, 642 pub use super::p::wrong_fn as right_fn;
1242 insert: "RightType", 643 pub use super::p::WRONG_CONST as RIGHT_CONST;
1243 kind: Struct, 644 pub use super::p::WrongType as RightType;
1244 }, 645}
1245 CompletionItem { 646mod p {
1246 label: "right_fn()", 647 fn wrong_fn() {}
1247 source_range: 57..57, 648 const WRONG_CONST: u32 = 1;
1248 delete: 57..57, 649 struct WrongType {};
1249 insert: "right_fn()$0", 650}
1250 kind: Function, 651"#,
1251 lookup: "right_fn", 652 r#"
1252 detail: "fn wrong_fn()", 653fn foo() { self::m::RightType }
1253 }, 654
1254 ] 655mod m {
1255 "### 656 pub use super::p::wrong_fn as right_fn;
657 pub use super::p::WRONG_CONST as RIGHT_CONST;
658 pub use super::p::WrongType as RightType;
659}
660mod p {
661 fn wrong_fn() {}
662 const WRONG_CONST: u32 = 1;
663 struct WrongType {};
664}
665"#,
1256 ); 666 );
1257 } 667 }
1258 668
1259 #[test] 669 #[test]
1260 fn completes_in_simple_macro_call() { 670 fn completes_in_simple_macro_call() {
1261 let completions = do_reference_completion( 671 check(
1262 r#" 672 r#"
1263 macro_rules! m { ($e:expr) => { $e } } 673macro_rules! m { ($e:expr) => { $e } }
1264 fn main() { m!(self::f<|>); } 674fn main() { m!(self::f<|>); }
1265 fn foo() {} 675fn foo() {}
1266 "#, 676"#,
677 expect![[r#"
678 fn foo() fn foo()
679 fn main() fn main()
680 "#]],
1267 ); 681 );
1268 assert_debug_snapshot!(completions, @r###"
1269 [
1270 CompletionItem {
1271 label: "foo()",
1272 source_range: 93..94,
1273 delete: 93..94,
1274 insert: "foo()$0",
1275 kind: Function,
1276 lookup: "foo",
1277 detail: "fn foo()",
1278 },
1279 CompletionItem {
1280 label: "main()",
1281 source_range: 93..94,
1282 delete: 93..94,
1283 insert: "main()$0",
1284 kind: Function,
1285 lookup: "main",
1286 detail: "fn main()",
1287 },
1288 ]
1289 "###);
1290 } 682 }
1291 683
1292 #[test] 684 #[test]
1293 fn function_mod_share_name() { 685 fn function_mod_share_name() {
1294 assert_debug_snapshot!( 686 check(
1295 do_reference_completion( 687 r#"
1296 r" 688fn foo() { self::m::<|> }
1297 fn foo() {
1298 self::m::<|>
1299 }
1300 689
1301 mod m { 690mod m {
1302 pub mod z {} 691 pub mod z {}
1303 pub fn z() {} 692 pub fn z() {}
1304 } 693}
1305 ", 694"#,
1306 ), 695 expect![[r#"
1307 @r###" 696 md z
1308 [ 697 fn z() pub fn z()
1309 CompletionItem { 698 "#]],
1310 label: "z",
1311 source_range: 57..57,
1312 delete: 57..57,
1313 insert: "z",
1314 kind: Module,
1315 },
1316 CompletionItem {
1317 label: "z()",
1318 source_range: 57..57,
1319 delete: 57..57,
1320 insert: "z()$0",
1321 kind: Function,
1322 lookup: "z",
1323 detail: "pub fn z()",
1324 },
1325 ]
1326 "###
1327 ); 699 );
1328 } 700 }
1329 701
1330 #[test] 702 #[test]
1331 fn completes_hashmap_new() { 703 fn completes_hashmap_new() {
1332 assert_debug_snapshot!( 704 check(
1333 do_reference_completion( 705 r#"
1334 r" 706struct RandomState;
1335 struct RandomState; 707struct HashMap<K, V, S = RandomState> {}
1336 struct HashMap<K, V, S = RandomState> {} 708
1337 709impl<K, V> HashMap<K, V, RandomState> {
1338 impl<K, V> HashMap<K, V, RandomState> { 710 pub fn new() -> HashMap<K, V, RandomState> { }
1339 pub fn new() -> HashMap<K, V, RandomState> { } 711}
1340 } 712fn foo() {
1341 fn foo() { 713 HashMap::<|>
1342 HashMap::<|> 714}
1343 } 715"#,
1344 " 716 expect![[r#"
1345 ), 717 fn new() pub fn new() -> HashMap<K, V, RandomState>
1346 @r###" 718 "#]],
1347 [
1348 CompletionItem {
1349 label: "new()",
1350 source_range: 292..292,
1351 delete: 292..292,
1352 insert: "new()$0",
1353 kind: Function,
1354 lookup: "new",
1355 detail: "pub fn new() -> HashMap<K, V, RandomState>",
1356 },
1357 ]
1358 "###
1359 ); 719 );
1360 } 720 }
1361 721
1362 #[test] 722 #[test]
1363 fn dont_complete_attr() { 723 fn dont_complete_attr() {
1364 assert_debug_snapshot!( 724 check(
1365 do_reference_completion( 725 r#"
1366 r" 726mod foo { pub struct Foo; }
1367 mod foo { pub struct Foo; } 727#[foo::<|>]
1368 #[foo::<|>] 728fn f() {}
1369 fn f() {} 729"#,
1370 " 730 expect![[""]],
1371 ), 731 );
1372 @r###"[]"###
1373 )
1374 } 732 }
1375} 733}
diff --git a/crates/ra_ide/src/completion/complete_record.rs b/crates/ra_ide/src/completion/complete_record.rs
index b7ab654c5..74b94594d 100644
--- a/crates/ra_ide/src/completion/complete_record.rs
+++ b/crates/ra_ide/src/completion/complete_record.rs
@@ -18,389 +18,209 @@ pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
18 18
19#[cfg(test)] 19#[cfg(test)]
20mod tests { 20mod tests {
21 mod record_pat_tests { 21 use expect::{expect, Expect};
22 use insta::assert_debug_snapshot;
23 22
24 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 23 use crate::completion::{test_utils::completion_list, CompletionKind};
25 24
26 fn complete(code: &str) -> Vec<CompletionItem> { 25 fn check(ra_fixture: &str, expect: Expect) {
27 do_completion(code, CompletionKind::Reference) 26 let actual = completion_list(ra_fixture, CompletionKind::Reference);
28 } 27 expect.assert_eq(&actual);
29 28 }
30 #[test]
31 fn test_record_pattern_field() {
32 let completions = complete(
33 r"
34 struct S { foo: u32 }
35
36 fn process(f: S) {
37 match f {
38 S { f<|>: 92 } => (),
39 }
40 }
41 ",
42 );
43 assert_debug_snapshot!(completions, @r###"
44 [
45 CompletionItem {
46 label: "foo",
47 source_range: 117..118,
48 delete: 117..118,
49 insert: "foo",
50 kind: Field,
51 detail: "u32",
52 },
53 ]
54 "###);
55 }
56
57 #[test]
58 fn test_record_pattern_enum_variant() {
59 let completions = complete(
60 r"
61 enum E {
62 S { foo: u32, bar: () }
63 }
64
65 fn process(e: E) {
66 match e {
67 E::S { <|> } => (),
68 }
69 }
70 ",
71 );
72 assert_debug_snapshot!(completions, @r###"
73 [
74 CompletionItem {
75 label: "bar",
76 source_range: 161..161,
77 delete: 161..161,
78 insert: "bar",
79 kind: Field,
80 detail: "()",
81 },
82 CompletionItem {
83 label: "foo",
84 source_range: 161..161,
85 delete: 161..161,
86 insert: "foo",
87 kind: Field,
88 detail: "u32",
89 },
90 ]
91 "###);
92 }
93 29
94 #[test] 30 #[test]
95 fn test_record_pattern_field_in_simple_macro() { 31 fn test_record_pattern_field() {
96 let completions = complete( 32 check(
97 r" 33 r#"
98 macro_rules! m { ($e:expr) => { $e } } 34struct S { foo: u32 }
99 struct S { foo: u32 }
100 35
101 fn process(f: S) { 36fn process(f: S) {
102 m!(match f { 37 match f {
103 S { f<|>: 92 } => (), 38 S { f<|>: 92 } => (),
104 }) 39 }
105 } 40}
106 ", 41"#,
107 ); 42 expect![[r#"
108 assert_debug_snapshot!(completions, @r###" 43 fd foo u32
109 [ 44 "#]],
110 CompletionItem { 45 );
111 label: "foo", 46 }
112 source_range: 171..172,
113 delete: 171..172,
114 insert: "foo",
115 kind: Field,
116 detail: "u32",
117 },
118 ]
119 "###);
120 }
121 47
122 #[test] 48 #[test]
123 fn only_missing_fields_are_completed_in_destruct_pats() { 49 fn test_record_pattern_enum_variant() {
124 let completions = complete( 50 check(
125 r" 51 r#"
126 struct S { 52enum E { S { foo: u32, bar: () } }
127 foo1: u32,
128 foo2: u32,
129 bar: u32,
130 baz: u32,
131 }
132 53
133 fn main() { 54fn process(e: E) {
134 let s = S { 55 match e {
135 foo1: 1, 56 E::S { <|> } => (),
136 foo2: 2, 57 }
137 bar: 3, 58}
138 baz: 4, 59"#,
139 }; 60 expect![[r#"
140 if let S { foo1, foo2: a, <|> } = s {} 61 fd bar ()
141 } 62 fd foo u32
142 ", 63 "#]],
143 ); 64 );
144 assert_debug_snapshot!(completions, @r###"
145 [
146 CompletionItem {
147 label: "bar",
148 source_range: 372..372,
149 delete: 372..372,
150 insert: "bar",
151 kind: Field,
152 detail: "u32",
153 },
154 CompletionItem {
155 label: "baz",
156 source_range: 372..372,
157 delete: 372..372,
158 insert: "baz",
159 kind: Field,
160 detail: "u32",
161 },
162 ]
163 "###);
164 }
165 } 65 }
166 66
167 mod record_lit_tests { 67 #[test]
168 use insta::assert_debug_snapshot; 68 fn test_record_pattern_field_in_simple_macro() {
169 69 check(
170 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 70 r"
71macro_rules! m { ($e:expr) => { $e } }
72struct S { foo: u32 }
73
74fn process(f: S) {
75 m!(match f {
76 S { f<|>: 92 } => (),
77 })
78}
79",
80 expect![[r#"
81 fd foo u32
82 "#]],
83 );
84 }
171 85
172 fn complete(code: &str) -> Vec<CompletionItem> { 86 #[test]
173 do_completion(code, CompletionKind::Reference) 87 fn only_missing_fields_are_completed_in_destruct_pats() {
174 } 88 check(
89 r#"
90struct S {
91 foo1: u32, foo2: u32,
92 bar: u32, baz: u32,
93}
175 94
176 #[test] 95fn main() {
177 fn test_record_literal_deprecated_field() { 96 let s = S {
178 let completions = complete( 97 foo1: 1, foo2: 2,
179 r" 98 bar: 3, baz: 4,
180 struct A { 99 };
181 #[deprecated] 100 if let S { foo1, foo2: a, <|> } = s {}
182 the_field: u32, 101}
183 } 102"#,
184 fn foo() { 103 expect![[r#"
185 A { the<|> } 104 fd bar u32
186 } 105 fd baz u32
187 ", 106 "#]],
188 ); 107 );
189 assert_debug_snapshot!(completions, @r###" 108 }
190 [
191 CompletionItem {
192 label: "the_field",
193 source_range: 142..145,
194 delete: 142..145,
195 insert: "the_field",
196 kind: Field,
197 detail: "u32",
198 deprecated: true,
199 },
200 ]
201 "###);
202 }
203 109
204 #[test] 110 #[test]
205 fn test_record_literal_field() { 111 fn test_record_literal_field() {
206 let completions = complete( 112 check(
207 r" 113 r#"
208 struct A { the_field: u32 } 114struct A { the_field: u32 }
209 fn foo() { 115fn foo() {
210 A { the<|> } 116 A { the<|> }
211 } 117}
212 ", 118"#,
213 ); 119 expect![[r#"
214 assert_debug_snapshot!(completions, @r###" 120 fd the_field u32
215 [ 121 "#]],
216 CompletionItem { 122 );
217 label: "the_field", 123 }
218 source_range: 83..86,
219 delete: 83..86,
220 insert: "the_field",
221 kind: Field,
222 detail: "u32",
223 },
224 ]
225 "###);
226 }
227 124
228 #[test] 125 #[test]
229 fn test_record_literal_enum_variant() { 126 fn test_record_literal_enum_variant() {
230 let completions = complete( 127 check(
231 r" 128 r#"
232 enum E { 129enum E { A { a: u32 } }
233 A { a: u32 } 130fn foo() {
234 } 131 let _ = E::A { <|> }
235 fn foo() { 132}
236 let _ = E::A { <|> } 133"#,
237 } 134 expect![[r#"
238 ", 135 fd a u32
239 ); 136 "#]],
240 assert_debug_snapshot!(completions, @r###" 137 );
241 [ 138 }
242 CompletionItem {
243 label: "a",
244 source_range: 119..119,
245 delete: 119..119,
246 insert: "a",
247 kind: Field,
248 detail: "u32",
249 },
250 ]
251 "###);
252 }
253 139
254 #[test] 140 #[test]
255 fn test_record_literal_two_structs() { 141 fn test_record_literal_two_structs() {
256 let completions = complete( 142 check(
257 r" 143 r#"
258 struct A { a: u32 } 144struct A { a: u32 }
259 struct B { b: u32 } 145struct B { b: u32 }
260 146
261 fn foo() { 147fn foo() {
262 let _: A = B { <|> } 148 let _: A = B { <|> }
263 } 149}
264 ", 150"#,
265 ); 151 expect![[r#"
266 assert_debug_snapshot!(completions, @r###" 152 fd b u32
267 [ 153 "#]],
268 CompletionItem { 154 );
269 label: "b", 155 }
270 source_range: 119..119,
271 delete: 119..119,
272 insert: "b",
273 kind: Field,
274 detail: "u32",
275 },
276 ]
277 "###);
278 }
279 156
280 #[test] 157 #[test]
281 fn test_record_literal_generic_struct() { 158 fn test_record_literal_generic_struct() {
282 let completions = complete( 159 check(
283 r" 160 r#"
284 struct A<T> { a: T } 161struct A<T> { a: T }
285 162
286 fn foo() { 163fn foo() {
287 let _: A<u32> = A { <|> } 164 let _: A<u32> = A { <|> }
288 } 165}
289 ", 166"#,
290 ); 167 expect![[r#"
291 assert_debug_snapshot!(completions, @r###" 168 fd a u32
292 [ 169 "#]],
293 CompletionItem { 170 );
294 label: "a", 171 }
295 source_range: 93..93,
296 delete: 93..93,
297 insert: "a",
298 kind: Field,
299 detail: "u32",
300 },
301 ]
302 "###);
303 }
304 172
305 #[test] 173 #[test]
306 fn test_record_literal_field_in_simple_macro() { 174 fn test_record_literal_field_in_simple_macro() {
307 let completions = complete( 175 check(
308 r" 176 r#"
309 macro_rules! m { ($e:expr) => { $e } } 177macro_rules! m { ($e:expr) => { $e } }
310 struct A { the_field: u32 } 178struct A { the_field: u32 }
311 fn foo() { 179fn foo() {
312 m!(A { the<|> }) 180 m!(A { the<|> })
313 } 181}
314 ", 182"#,
315 ); 183 expect![[r#"
316 assert_debug_snapshot!(completions, @r###" 184 fd the_field u32
317 [ 185 "#]],
318 CompletionItem { 186 );
319 label: "the_field", 187 }
320 source_range: 137..140,
321 delete: 137..140,
322 insert: "the_field",
323 kind: Field,
324 detail: "u32",
325 },
326 ]
327 "###);
328 }
329 188
330 #[test] 189 #[test]
331 fn only_missing_fields_are_completed() { 190 fn only_missing_fields_are_completed() {
332 let completions = complete( 191 check(
333 r" 192 r#"
334 struct S { 193struct S {
335 foo1: u32, 194 foo1: u32, foo2: u32,
336 foo2: u32, 195 bar: u32, baz: u32,
337 bar: u32, 196}
338 baz: u32,
339 }
340 197
341 fn main() { 198fn main() {
342 let foo1 = 1; 199 let foo1 = 1;
343 let s = S { 200 let s = S { foo1, foo2: 5, <|> }
344 foo1, 201}
345 foo2: 5, 202"#,
346 <|> 203 expect![[r#"
347 } 204 fd bar u32
348 } 205 fd baz u32
349 ", 206 "#]],
350 ); 207 );
351 assert_debug_snapshot!(completions, @r###" 208 }
352 [
353 CompletionItem {
354 label: "bar",
355 source_range: 302..302,
356 delete: 302..302,
357 insert: "bar",
358 kind: Field,
359 detail: "u32",
360 },
361 CompletionItem {
362 label: "baz",
363 source_range: 302..302,
364 delete: 302..302,
365 insert: "baz",
366 kind: Field,
367 detail: "u32",
368 },
369 ]
370 "###);
371 }
372 209
373 #[test] 210 #[test]
374 fn completes_functional_update() { 211 fn completes_functional_update() {
375 let completions = complete( 212 check(
376 r" 213 r#"
377 struct S { 214struct S { foo1: u32, foo2: u32 }
378 foo1: u32,
379 foo2: u32,
380 }
381 215
382 fn main() { 216fn main() {
383 let foo1 = 1; 217 let foo1 = 1;
384 let s = S { 218 let s = S { foo1, <|> .. loop {} }
385 foo1, 219}
386 <|> 220"#,
387 .. loop {} 221 expect![[r#"
388 } 222 fd foo2 u32
389 } 223 "#]],
390 ", 224 );
391 );
392 assert_debug_snapshot!(completions, @r###"
393 [
394 CompletionItem {
395 label: "foo2",
396 source_range: 221..221,
397 delete: 221..221,
398 insert: "foo2",
399 kind: Field,
400 detail: "u32",
401 },
402 ]
403 "###);
404 }
405 } 225 }
406} 226}
diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs
index 0568d9ccf..28d8f7876 100644
--- a/crates/ra_ide/src/completion/complete_snippet.rs
+++ b/crates/ra_ide/src/completion/complete_snippet.rs
@@ -70,95 +70,47 @@ fn ${1:feature}() {
70 70
71#[cfg(test)] 71#[cfg(test)]
72mod tests { 72mod tests {
73 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 73 use expect::{expect, Expect};
74 use insta::assert_debug_snapshot;
75 74
76 fn do_snippet_completion(code: &str) -> Vec<CompletionItem> { 75 use crate::completion::{test_utils::completion_list, CompletionKind};
77 do_completion(code, CompletionKind::Snippet) 76
77 fn check(ra_fixture: &str, expect: Expect) {
78 let actual = completion_list(ra_fixture, CompletionKind::Snippet);
79 expect.assert_eq(&actual)
78 } 80 }
79 81
80 #[test] 82 #[test]
81 fn completes_snippets_in_expressions() { 83 fn completes_snippets_in_expressions() {
82 assert_debug_snapshot!( 84 check(
83 do_snippet_completion(r"fn foo(x: i32) { <|> }"), 85 r#"fn foo(x: i32) { <|> }"#,
84 @r###" 86 expect![[r#"
85 [ 87 sn pd
86 CompletionItem { 88 sn ppd
87 label: "pd", 89 "#]],
88 source_range: 17..17, 90 );
89 delete: 17..17,
90 insert: "eprintln!(\"$0 = {:?}\", $0);",
91 kind: Snippet,
92 },
93 CompletionItem {
94 label: "ppd",
95 source_range: 17..17,
96 delete: 17..17,
97 insert: "eprintln!(\"$0 = {:#?}\", $0);",
98 kind: Snippet,
99 },
100 ]
101 "###
102 );
103 } 91 }
104 92
105 #[test] 93 #[test]
106 fn should_not_complete_snippets_in_path() { 94 fn should_not_complete_snippets_in_path() {
107 assert_debug_snapshot!( 95 check(r#"fn foo(x: i32) { ::foo<|> }"#, expect![[""]]);
108 do_snippet_completion(r"fn foo(x: i32) { ::foo<|> }"), 96 check(r#"fn foo(x: i32) { ::<|> }"#, expect![[""]]);
109 @"[]"
110 );
111 assert_debug_snapshot!(
112 do_snippet_completion(r"fn foo(x: i32) { ::<|> }"),
113 @"[]"
114 );
115 } 97 }
116 98
117 #[test] 99 #[test]
118 fn completes_snippets_in_items() { 100 fn completes_snippets_in_items() {
119 assert_debug_snapshot!( 101 check(
120 do_snippet_completion( 102 r#"
121 r" 103#[cfg(test)]
122 #[cfg(test)] 104mod tests {
123 mod tests { 105 <|>
124 <|> 106}
125 } 107"#,
126 " 108 expect![[r#"
127 ), 109 sn Test function
128 @r###" 110 sn Test module
129 [ 111 sn macro_rules
130 CompletionItem { 112 sn pub(crate)
131 label: "Test function", 113 "#]],
132 source_range: 78..78, 114 )
133 delete: 78..78,
134 insert: "#[test]\nfn ${1:feature}() {\n $0\n}",
135 kind: Snippet,
136 lookup: "tfn",
137 },
138 CompletionItem {
139 label: "Test module",
140 source_range: 78..78,
141 delete: 78..78,
142 insert: "#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn ${1:test_name}() {\n $0\n }\n}",
143 kind: Snippet,
144 lookup: "tmod",
145 },
146 CompletionItem {
147 label: "macro_rules",
148 source_range: 78..78,
149 delete: 78..78,
150 insert: "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}",
151 kind: Snippet,
152 },
153 CompletionItem {
154 label: "pub(crate)",
155 source_range: 78..78,
156 delete: 78..78,
157 insert: "pub(crate) $0",
158 kind: Snippet,
159 },
160 ]
161 "###
162 );
163 } 115 }
164} 116}
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 21c9316e6..a610fd6d1 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -227,330 +227,264 @@ fn make_const_compl_syntax(const_: &ast::ConstDef) -> String {
227 227
228#[cfg(test)] 228#[cfg(test)]
229mod tests { 229mod tests {
230 use insta::assert_debug_snapshot; 230 use expect::{expect, Expect};
231 231
232 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 232 use crate::completion::{
233 test_utils::{check_edit, completion_list},
234 CompletionKind,
235 };
233 236
234 fn complete(code: &str) -> Vec<CompletionItem> { 237 fn check(ra_fixture: &str, expect: Expect) {
235 do_completion(code, CompletionKind::Magic) 238 let actual = completion_list(ra_fixture, CompletionKind::Magic);
239 expect.assert_eq(&actual)
236 } 240 }
237 241
238 #[test] 242 #[test]
239 fn name_ref_function_type_const() { 243 fn name_ref_function_type_const() {
240 let completions = complete( 244 check(
241 r" 245 r#"
242 trait Test { 246trait Test {
243 type TestType; 247 type TestType;
244 const TEST_CONST: u16; 248 const TEST_CONST: u16;
245 fn test(); 249 fn test();
246 } 250}
247 251struct T;
248 struct T1;
249 252
250 impl Test for T1 { 253impl Test for T {
251 t<|> 254 t<|>
252 } 255}
253 ", 256"#,
257 expect![["
258ct const TEST_CONST: u16 = \n\
259fn fn test()
260ta type TestType = \n\
261 "]],
254 ); 262 );
255 assert_debug_snapshot!(completions, @r###"
256 [
257 CompletionItem {
258 label: "const TEST_CONST: u16 = ",
259 source_range: 209..210,
260 delete: 209..210,
261 insert: "const TEST_CONST: u16 = ",
262 kind: Const,
263 lookup: "TEST_CONST",
264 },
265 CompletionItem {
266 label: "fn test()",
267 source_range: 209..210,
268 delete: 209..210,
269 insert: "fn test() {\n $0\n}",
270 kind: Function,
271 lookup: "test",
272 },
273 CompletionItem {
274 label: "type TestType = ",
275 source_range: 209..210,
276 delete: 209..210,
277 insert: "type TestType = ",
278 kind: TypeAlias,
279 lookup: "TestType",
280 },
281 ]
282 "###);
283 } 263 }
284 264
285 #[test] 265 #[test]
286 fn no_nested_fn_completions() { 266 fn no_nested_fn_completions() {
287 let completions = complete( 267 check(
288 r" 268 r"
289 trait Test { 269trait Test {
290 fn test(); 270 fn test();
291 fn test2(); 271 fn test2();
292 } 272}
293 273struct T;
294 struct T1;
295 274
296 impl Test for T1 { 275impl Test for T {
297 fn test() { 276 fn test() {
298 t<|> 277 t<|>
299 } 278 }
300 } 279}
301 ", 280",
281 expect![[""]],
302 ); 282 );
303 assert_debug_snapshot!(completions, @r###"[]"###);
304 } 283 }
305 284
306 #[test] 285 #[test]
307 fn name_ref_single_function() { 286 fn name_ref_single_function() {
308 let completions = complete( 287 check_edit(
309 r" 288 "test",
310 trait Test { 289 r#"
311 fn test(); 290trait Test {
312 } 291 fn test();
292}
293struct T;
313 294
314 struct T1; 295impl Test for T {
296 t<|>
297}
298"#,
299 r#"
300trait Test {
301 fn test();
302}
303struct T;
315 304
316 impl Test for T1 { 305impl Test for T {
317 t<|> 306 fn test() {
318 } 307 $0
319 ", 308}
309}
310"#,
320 ); 311 );
321 assert_debug_snapshot!(completions, @r###"
322 [
323 CompletionItem {
324 label: "fn test()",
325 source_range: 139..140,
326 delete: 139..140,
327 insert: "fn test() {\n $0\n}",
328 kind: Function,
329 lookup: "test",
330 },
331 ]
332 "###);
333 } 312 }
334 313
335 #[test] 314 #[test]
336 fn single_function() { 315 fn single_function() {
337 let completions = complete( 316 check_edit(
338 r" 317 "test",
339 trait Test { 318 r#"
340 fn foo(); 319trait Test {
341 } 320 fn test();
321}
322struct T;
342 323
343 struct T1; 324impl Test for T {
325 fn t<|>
326}
327"#,
328 r#"
329trait Test {
330 fn test();
331}
332struct T;
344 333
345 impl Test for T1 { 334impl Test for T {
346 fn f<|> 335 fn test() {
347 } 336 $0
348 ", 337}
338}
339"#,
349 ); 340 );
350 assert_debug_snapshot!(completions, @r###"
351 [
352 CompletionItem {
353 label: "fn foo()",
354 source_range: 141..142,
355 delete: 138..142,
356 insert: "fn foo() {\n $0\n}",
357 kind: Function,
358 lookup: "foo",
359 },
360 ]
361 "###);
362 } 341 }
363 342
364 #[test] 343 #[test]
365 fn hide_implemented_fn() { 344 fn hide_implemented_fn() {
366 let completions = complete( 345 check(
367 r" 346 r#"
368 trait Test { 347trait Test {
369 fn foo(); 348 fn foo();
370 fn foo_bar(); 349 fn foo_bar();
371 } 350}
372 351struct T;
373 struct T1;
374
375 impl Test for T1 {
376 fn foo() {}
377
378 fn f<|>
379 }
380 ",
381 );
382 assert_debug_snapshot!(completions, @r###"
383 [
384 CompletionItem {
385 label: "fn foo_bar()",
386 source_range: 200..201,
387 delete: 197..201,
388 insert: "fn foo_bar() {\n $0\n}",
389 kind: Function,
390 lookup: "foo_bar",
391 },
392 ]
393 "###);
394 }
395
396 #[test]
397 fn completes_only_on_top_level() {
398 let completions = complete(
399 r"
400 trait Test {
401 fn foo();
402
403 fn foo_bar();
404 }
405
406 struct T1;
407 352
408 impl Test for T1 { 353impl Test for T {
409 fn foo() { 354 fn foo() {}
410 <|> 355 fn f<|>
411 } 356}
412 } 357"#,
413 ", 358 expect![[r#"
359 fn fn foo_bar()
360 "#]],
414 ); 361 );
415 assert_debug_snapshot!(completions, @r###"[]"###);
416 } 362 }
417 363
418 #[test] 364 #[test]
419 fn generic_fn() { 365 fn generic_fn() {
420 let completions = complete( 366 check_edit(
421 r" 367 "foo",
422 trait Test { 368 r#"
423 fn foo<T>(); 369trait Test {
424 } 370 fn foo<T>();
371}
372struct T;
425 373
426 struct T1; 374impl Test for T {
375 fn f<|>
376}
377"#,
378 r#"
379trait Test {
380 fn foo<T>();
381}
382struct T;
427 383
428 impl Test for T1 { 384impl Test for T {
429 fn f<|> 385 fn foo<T>() {
430 } 386 $0
431 ", 387}
388}
389"#,
432 ); 390 );
433 assert_debug_snapshot!(completions, @r###" 391 check_edit(
434 [ 392 "foo",
435 CompletionItem { 393 r#"
436 label: "fn foo()", 394trait Test {
437 source_range: 144..145, 395 fn foo<T>() where T: Into<String>;
438 delete: 141..145, 396}
439 insert: "fn foo<T>() {\n $0\n}", 397struct T;
440 kind: Function,
441 lookup: "foo",
442 },
443 ]
444 "###);
445 }
446
447 #[test]
448 fn generic_constrait_fn() {
449 let completions = complete(
450 r"
451 trait Test {
452 fn foo<T>() where T: Into<String>;
453 }
454 398
455 struct T1; 399impl Test for T {
400 fn f<|>
401}
402"#,
403 r#"
404trait Test {
405 fn foo<T>() where T: Into<String>;
406}
407struct T;
456 408
457 impl Test for T1 { 409impl Test for T {
458 fn f<|> 410 fn foo<T>()
459 } 411where T: Into<String> {
460 ", 412 $0
413}
414}
415"#,
461 ); 416 );
462 assert_debug_snapshot!(completions, @r###"
463 [
464 CompletionItem {
465 label: "fn foo()",
466 source_range: 166..167,
467 delete: 163..167,
468 insert: "fn foo<T>()\nwhere T: Into<String> {\n $0\n}",
469 kind: Function,
470 lookup: "foo",
471 },
472 ]
473 "###);
474 } 417 }
475 418
476 #[test] 419 #[test]
477 fn associated_type() { 420 fn associated_type() {
478 let completions = complete( 421 check_edit(
479 r" 422 "SomeType",
480 trait Test { 423 r#"
481 type SomeType; 424trait Test {
482 } 425 type SomeType;
426}
483 427
484 impl Test for () { 428impl Test for () {
485 type S<|> 429 type S<|>
486 } 430}
487 ", 431"#,
432 "
433trait Test {
434 type SomeType;
435}
436
437impl Test for () {
438 type SomeType = \n\
439}
440",
488 ); 441 );
489 assert_debug_snapshot!(completions, @r###"
490 [
491 CompletionItem {
492 label: "type SomeType = ",
493 source_range: 124..125,
494 delete: 119..125,
495 insert: "type SomeType = ",
496 kind: TypeAlias,
497 lookup: "SomeType",
498 },
499 ]
500 "###);
501 } 442 }
502 443
503 #[test] 444 #[test]
504 fn associated_const() { 445 fn associated_const() {
505 let completions = complete( 446 check_edit(
506 r" 447 "SOME_CONST",
507 trait Test { 448 r#"
508 const SOME_CONST: u16; 449trait Test {
509 } 450 const SOME_CONST: u16;
451}
510 452
511 impl Test for () { 453impl Test for () {
512 const S<|> 454 const S<|>
513 } 455}
514 ", 456"#,
457 "
458trait Test {
459 const SOME_CONST: u16;
460}
461
462impl Test for () {
463 const SOME_CONST: u16 = \n\
464}
465",
515 ); 466 );
516 assert_debug_snapshot!(completions, @r###"
517 [
518 CompletionItem {
519 label: "const SOME_CONST: u16 = ",
520 source_range: 133..134,
521 delete: 127..134,
522 insert: "const SOME_CONST: u16 = ",
523 kind: Const,
524 lookup: "SOME_CONST",
525 },
526 ]
527 "###);
528 }
529 467
530 #[test] 468 check_edit(
531 fn associated_const_with_default() { 469 "SOME_CONST",
532 let completions = complete( 470 r#"
533 r" 471trait Test {
534 trait Test { 472 const SOME_CONST: u16 = 92;
535 const SOME_CONST: u16 = 42; 473}
536 }
537 474
538 impl Test for () { 475impl Test for () {
539 const S<|> 476 const S<|>
540 } 477}
541 ", 478"#,
479 "
480trait Test {
481 const SOME_CONST: u16 = 92;
482}
483
484impl Test for () {
485 const SOME_CONST: u16 = \n\
486}
487",
542 ); 488 );
543 assert_debug_snapshot!(completions, @r###"
544 [
545 CompletionItem {
546 label: "const SOME_CONST: u16 = ",
547 source_range: 138..139,
548 delete: 132..139,
549 insert: "const SOME_CONST: u16 = ",
550 kind: Const,
551 lookup: "SOME_CONST",
552 },
553 ]
554 "###);
555 } 489 }
556} 490}
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index 68032c37e..bd9551f35 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -1,11 +1,10 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use hir::ScopeDef; 3use hir::{Adt, ModuleDef, ScopeDef, Type};
4use ra_syntax::AstNode;
4use test_utils::mark; 5use test_utils::mark;
5 6
6use crate::completion::{CompletionContext, Completions}; 7use crate::completion::{CompletionContext, Completions};
7use hir::{Adt, ModuleDef, Type};
8use ra_syntax::AstNode;
9 8
10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 9pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
11 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { 10 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
@@ -26,7 +25,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
26 return; 25 return;
27 } 26 }
28 27
29 ctx.scope().process_all_names(&mut |name, res| { 28 ctx.scope.process_all_names(&mut |name, res| {
30 if ctx.use_item_syntax.is_some() { 29 if ctx.use_item_syntax.is_some() {
31 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 30 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
32 if name_ref.syntax().text() == name.to_string().as_str() { 31 if name_ref.syntax().text() == name.to_string().as_str() {
@@ -43,7 +42,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
43 if let Some(Adt::Enum(enum_data)) = ty.as_adt() { 42 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
44 let variants = enum_data.variants(ctx.db); 43 let variants = enum_data.variants(ctx.db);
45 44
46 let module = if let Some(module) = ctx.scope().module() { 45 let module = if let Some(module) = ctx.scope.module() {
47 // Compute path from the completion site if available. 46 // Compute path from the completion site if available.
48 module 47 module
49 } else { 48 } else {
@@ -65,1361 +64,595 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
65 64
66#[cfg(test)] 65#[cfg(test)]
67mod tests { 66mod tests {
68 use insta::assert_debug_snapshot; 67 use expect::{expect, Expect};
69 use test_utils::mark; 68 use test_utils::mark;
70 69
71 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 70 use crate::completion::{
71 test_utils::{check_edit, completion_list},
72 CompletionKind,
73 };
72 74
73 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> { 75 fn check(ra_fixture: &str, expect: Expect) {
74 do_completion(ra_fixture, CompletionKind::Reference) 76 let actual = completion_list(ra_fixture, CompletionKind::Reference);
77 expect.assert_eq(&actual)
75 } 78 }
76 79
77 #[test] 80 #[test]
78 fn self_fulfilling_completion() { 81 fn self_fulfilling_completion() {
79 mark::check!(self_fulfilling_completion); 82 mark::check!(self_fulfilling_completion);
80 assert_debug_snapshot!( 83 check(
81 do_reference_completion( 84 r#"
82 r#" 85use foo<|>
83 use foo<|> 86use std::collections;
84 use std::collections; 87"#,
85 "#, 88 expect![[r#"
86 ), 89 ?? collections
87 @r###" 90 "#]],
88 [
89 CompletionItem {
90 label: "collections",
91 source_range: 21..24,
92 delete: 21..24,
93 insert: "collections",
94 },
95 ]
96 "###
97 ); 91 );
98 } 92 }
99 93
100 #[test] 94 #[test]
101 fn bind_pat_and_path_ignore_at() { 95 fn bind_pat_and_path_ignore_at() {
102 assert_debug_snapshot!( 96 check(
103 do_reference_completion( 97 r#"
104 r" 98enum Enum { A, B }
105 enum Enum { 99fn quux(x: Option<Enum>) {
106 A, 100 match x {
107 B, 101 None => (),
108 } 102 Some(en<|> @ Enum::A) => (),
109 fn quux(x: Option<Enum>) { 103 }
110 match x { 104}
111 None => (), 105"#,
112 Some(en<|> @ Enum::A) => (), 106 expect![[""]],
113 }
114 }
115 "
116 ),
117 @"[]"
118 ); 107 );
119 } 108 }
120 109
121 #[test] 110 #[test]
122 fn bind_pat_and_path_ignore_ref() { 111 fn bind_pat_and_path_ignore_ref() {
123 assert_debug_snapshot!( 112 check(
124 do_reference_completion( 113 r#"
125 r" 114enum Enum { A, B }
126 enum Enum { 115fn quux(x: Option<Enum>) {
127 A, 116 match x {
128 B, 117 None => (),
129 } 118 Some(ref en<|>) => (),
130 fn quux(x: Option<Enum>) { 119 }
131 match x { 120}
132 None => (), 121"#,
133 Some(ref en<|>) => (), 122 expect![[""]],
134 }
135 }
136 "
137 ),
138 @r###"[]"###
139 ); 123 );
140 } 124 }
141 125
142 #[test] 126 #[test]
143 fn bind_pat_and_path() { 127 fn bind_pat_and_path() {
144 assert_debug_snapshot!( 128 check(
145 do_reference_completion( 129 r#"
146 r" 130enum Enum { A, B }
147 enum Enum { 131fn quux(x: Option<Enum>) {
148 A, 132 match x {
149 B, 133 None => (),
150 } 134 Some(En<|>) => (),
151 fn quux(x: Option<Enum>) { 135 }
152 match x { 136}
153 None => (), 137"#,
154 Some(En<|>) => (), 138 expect![[r#"
155 } 139 en Enum
156 } 140 "#]],
157 "
158 ),
159 @r###"
160 [
161 CompletionItem {
162 label: "Enum",
163 source_range: 231..233,
164 delete: 231..233,
165 insert: "Enum",
166 kind: Enum,
167 },
168 ]
169 "###
170 ); 141 );
171 } 142 }
172 143
173 #[test] 144 #[test]
174 fn completes_bindings_from_let() { 145 fn completes_bindings_from_let() {
175 assert_debug_snapshot!( 146 check(
176 do_reference_completion( 147 r#"
177 r" 148fn quux(x: i32) {
178 fn quux(x: i32) { 149 let y = 92;
179 let y = 92; 150 1 + <|>;
180 1 + <|>; 151 let z = ();
181 let z = (); 152}
182 } 153"#,
183 " 154 expect![[r#"
184 ), 155 fn quux(…) fn quux(x: i32)
185 @r###" 156 bn x i32
186 [ 157 bn y i32
187 CompletionItem { 158 "#]],
188 label: "quux(…)",
189 source_range: 91..91,
190 delete: 91..91,
191 insert: "quux(${1:x})$0",
192 kind: Function,
193 lookup: "quux",
194 detail: "fn quux(x: i32)",
195 trigger_call_info: true,
196 },
197 CompletionItem {
198 label: "x",
199 source_range: 91..91,
200 delete: 91..91,
201 insert: "x",
202 kind: Binding,
203 detail: "i32",
204 },
205 CompletionItem {
206 label: "y",
207 source_range: 91..91,
208 delete: 91..91,
209 insert: "y",
210 kind: Binding,
211 detail: "i32",
212 },
213 ]
214 "###
215 ); 159 );
216 } 160 }
217 161
218 #[test] 162 #[test]
219 fn completes_bindings_from_if_let() { 163 fn completes_bindings_from_if_let() {
220 assert_debug_snapshot!( 164 check(
221 do_reference_completion( 165 r#"
222 r" 166fn quux() {
223 fn quux() { 167 if let Some(x) = foo() {
224 if let Some(x) = foo() { 168 let y = 92;
225 let y = 92; 169 };
226 }; 170 if let Some(a) = bar() {
227 if let Some(a) = bar() { 171 let b = 62;
228 let b = 62; 172 1 + <|>
229 1 + <|> 173 }
230 } 174}
231 } 175"#,
232 " 176 expect![[r#"
233 ), 177 bn a
234 @r###" 178 bn b i32
235 [ 179 fn quux() fn quux()
236 CompletionItem { 180 "#]],
237 label: "a",
238 source_range: 242..242,
239 delete: 242..242,
240 insert: "a",
241 kind: Binding,
242 },
243 CompletionItem {
244 label: "b",
245 source_range: 242..242,
246 delete: 242..242,
247 insert: "b",
248 kind: Binding,
249 detail: "i32",
250 },
251 CompletionItem {
252 label: "quux()",
253 source_range: 242..242,
254 delete: 242..242,
255 insert: "quux()$0",
256 kind: Function,
257 lookup: "quux",
258 detail: "fn quux()",
259 },
260 ]
261 "###
262 ); 181 );
263 } 182 }
264 183
265 #[test] 184 #[test]
266 fn completes_bindings_from_for() { 185 fn completes_bindings_from_for() {
267 assert_debug_snapshot!( 186 check(
268 do_reference_completion( 187 r#"
269 r" 188fn quux() {
270 fn quux() { 189 for x in &[1, 2, 3] { <|> }
271 for x in &[1, 2, 3] { 190}
272 <|> 191"#,
273 } 192 expect![[r#"
274 } 193 fn quux() fn quux()
275 " 194 bn x
276 ), 195 "#]],
277 @r###"
278 [
279 CompletionItem {
280 label: "quux()",
281 source_range: 95..95,
282 delete: 95..95,
283 insert: "quux()$0",
284 kind: Function,
285 lookup: "quux",
286 detail: "fn quux()",
287 },
288 CompletionItem {
289 label: "x",
290 source_range: 95..95,
291 delete: 95..95,
292 insert: "x",
293 kind: Binding,
294 },
295 ]
296 "###
297 ); 196 );
298 } 197 }
299 198
300 #[test] 199 #[test]
301 fn completes_bindings_from_for_with_in_prefix() { 200 fn completes_if_prefix_is_keyword() {
302 mark::check!(completes_bindings_from_for_with_in_prefix); 201 mark::check!(completes_if_prefix_is_keyword);
303 assert_debug_snapshot!( 202 check_edit(
304 do_reference_completion( 203 "wherewolf",
305 r" 204 r#"
306 fn test() { 205fn main() {
307 for index in &[1, 2, 3] { 206 let wherewolf = 92;
308 let t = in<|> 207 drop(where<|>)
309 } 208}
310 } 209"#,
311 " 210 r#"
312 ), 211fn main() {
313 @r###" 212 let wherewolf = 92;
314 [ 213 drop(wherewolf)
315 CompletionItem { 214}
316 label: "index", 215"#,
317 source_range: 107..107, 216 )
318 delete: 107..107,
319 insert: "index",
320 kind: Binding,
321 },
322 CompletionItem {
323 label: "test()",
324 source_range: 107..107,
325 delete: 107..107,
326 insert: "test()$0",
327 kind: Function,
328 lookup: "test",
329 detail: "fn test()",
330 },
331 ]
332 "###
333 );
334 } 217 }
335 218
336 #[test] 219 #[test]
337 fn completes_generic_params() { 220 fn completes_generic_params() {
338 assert_debug_snapshot!( 221 check(
339 do_reference_completion( 222 r#"fn quux<T>() { <|> }"#,
340 r" 223 expect![[r#"
341 fn quux<T>() { 224 tp T
342 <|> 225 fn quux() fn quux<T>()
343 } 226 "#]],
344 "
345 ),
346 @r###"
347 [
348 CompletionItem {
349 label: "T",
350 source_range: 52..52,
351 delete: 52..52,
352 insert: "T",
353 kind: TypeParam,
354 },
355 CompletionItem {
356 label: "quux()",
357 source_range: 52..52,
358 delete: 52..52,
359 insert: "quux()$0",
360 kind: Function,
361 lookup: "quux",
362 detail: "fn quux<T>()",
363 },
364 ]
365 "###
366 ); 227 );
367 } 228 }
368 229
369 #[test] 230 #[test]
370 fn completes_generic_params_in_struct() { 231 fn completes_generic_params_in_struct() {
371 assert_debug_snapshot!( 232 check(
372 do_reference_completion( 233 r#"struct S<T> { x: <|>}"#,
373 r" 234 expect![[r#"
374 struct X<T> { 235 st S<…>
375 x: <|> 236 tp Self
376 } 237 tp T
377 " 238 "#]],
378 ),
379 @r###"
380 [
381 CompletionItem {
382 label: "Self",
383 source_range: 54..54,
384 delete: 54..54,
385 insert: "Self",
386 kind: TypeParam,
387 },
388 CompletionItem {
389 label: "T",
390 source_range: 54..54,
391 delete: 54..54,
392 insert: "T",
393 kind: TypeParam,
394 },
395 CompletionItem {
396 label: "X<…>",
397 source_range: 54..54,
398 delete: 54..54,
399 insert: "X<$0>",
400 kind: Struct,
401 lookup: "X",
402 },
403 ]
404 "###
405 ); 239 );
406 } 240 }
407 241
408 #[test] 242 #[test]
409 fn completes_self_in_enum() { 243 fn completes_self_in_enum() {
410 assert_debug_snapshot!( 244 check(
411 do_reference_completion( 245 r#"enum X { Y(<|>) }"#,
412 r" 246 expect![[r#"
413 enum X { 247 tp Self
414 Y(<|>) 248 en X
415 } 249 "#]],
416 "
417 ),
418 @r###"
419 [
420 CompletionItem {
421 label: "Self",
422 source_range: 48..48,
423 delete: 48..48,
424 insert: "Self",
425 kind: TypeParam,
426 },
427 CompletionItem {
428 label: "X",
429 source_range: 48..48,
430 delete: 48..48,
431 insert: "X",
432 kind: Enum,
433 },
434 ]
435 "###
436 ); 250 );
437 } 251 }
438 252
439 #[test] 253 #[test]
440 fn completes_module_items() { 254 fn completes_module_items() {
441 assert_debug_snapshot!( 255 check(
442 do_reference_completion( 256 r#"
443 r" 257struct S;
444 struct Foo; 258enum E {}
445 enum Baz {} 259fn quux() { <|> }
446 fn quux() { 260"#,
447 <|> 261 expect![[r#"
448 } 262 en E
449 " 263 st S
450 ), 264 fn quux() fn quux()
451 @r###" 265 "#]],
452 [ 266 );
453 CompletionItem {
454 label: "Baz",
455 source_range: 105..105,
456 delete: 105..105,
457 insert: "Baz",
458 kind: Enum,
459 },
460 CompletionItem {
461 label: "Foo",
462 source_range: 105..105,
463 delete: 105..105,
464 insert: "Foo",
465 kind: Struct,
466 },
467 CompletionItem {
468 label: "quux()",
469 source_range: 105..105,
470 delete: 105..105,
471 insert: "quux()$0",
472 kind: Function,
473 lookup: "quux",
474 detail: "fn quux()",
475 },
476 ]
477 "###
478 );
479 } 267 }
480 268
481 #[test] 269 #[test]
482 fn completes_extern_prelude() { 270 fn completes_extern_prelude() {
483 assert_debug_snapshot!( 271 check(
484 do_reference_completion( 272 r#"
485 r" 273//- /lib.rs
486 //- /lib.rs 274use <|>;
487 use <|>; 275
488 276//- /other_crate/lib.rs
489 //- /other_crate/lib.rs 277// nothing here
490 // nothing here 278"#,
491 " 279 expect![[r#"
492 ), 280 md other_crate
493 @r###" 281 "#]],
494 [
495 CompletionItem {
496 label: "other_crate",
497 source_range: 4..4,
498 delete: 4..4,
499 insert: "other_crate",
500 kind: Module,
501 },
502 ]
503 "###
504 ); 282 );
505 } 283 }
506 284
507 #[test] 285 #[test]
508 fn completes_module_items_in_nested_modules() { 286 fn completes_module_items_in_nested_modules() {
509 assert_debug_snapshot!( 287 check(
510 do_reference_completion( 288 r#"
511 r" 289struct Foo;
512 struct Foo; 290mod m {
513 mod m { 291 struct Bar;
514 struct Bar; 292 fn quux() { <|> }
515 fn quux() { <|> } 293}
516 } 294"#,
517 " 295 expect![[r#"
518 ), 296 st Bar
519 @r###" 297 fn quux() fn quux()
520 [ 298 "#]],
521 CompletionItem {
522 label: "Bar",
523 source_range: 117..117,
524 delete: 117..117,
525 insert: "Bar",
526 kind: Struct,
527 },
528 CompletionItem {
529 label: "quux()",
530 source_range: 117..117,
531 delete: 117..117,
532 insert: "quux()$0",
533 kind: Function,
534 lookup: "quux",
535 detail: "fn quux()",
536 },
537 ]
538 "###
539 ); 299 );
540 } 300 }
541 301
542 #[test] 302 #[test]
543 fn completes_return_type() { 303 fn completes_return_type() {
544 assert_debug_snapshot!( 304 check(
545 do_reference_completion( 305 r#"
546 r" 306struct Foo;
547 struct Foo; 307fn x() -> <|>
548 fn x() -> <|> 308"#,
549 " 309 expect![[r#"
550 ), 310 st Foo
551 @r###" 311 fn x() fn x()
552 [ 312 "#]],
553 CompletionItem {
554 label: "Foo",
555 source_range: 55..55,
556 delete: 55..55,
557 insert: "Foo",
558 kind: Struct,
559 },
560 CompletionItem {
561 label: "x()",
562 source_range: 55..55,
563 delete: 55..55,
564 insert: "x()$0",
565 kind: Function,
566 lookup: "x",
567 detail: "fn x()",
568 },
569 ]
570 "###
571 ); 313 );
572 } 314 }
573 315
574 #[test] 316 #[test]
575 fn dont_show_both_completions_for_shadowing() { 317 fn dont_show_both_completions_for_shadowing() {
576 assert_debug_snapshot!( 318 check(
577 do_reference_completion( 319 r#"
578 r" 320fn foo() {
579 fn foo() { 321 let bar = 92;
580 let bar = 92; 322 {
581 { 323 let bar = 62;
582 let bar = 62; 324 drop(<|>)
583 <|> 325 }
584 } 326}
585 } 327"#,
586 " 328 // FIXME: should be only one bar here
587 ), 329 expect![[r#"
588 @r###" 330 bn bar i32
589 [ 331 bn bar i32
590 CompletionItem { 332 fn foo() fn foo()
591 label: "bar", 333 "#]],
592 source_range: 146..146,
593 delete: 146..146,
594 insert: "bar",
595 kind: Binding,
596 detail: "i32",
597 },
598 CompletionItem {
599 label: "foo()",
600 source_range: 146..146,
601 delete: 146..146,
602 insert: "foo()$0",
603 kind: Function,
604 lookup: "foo",
605 detail: "fn foo()",
606 },
607 ]
608 "###
609 ); 334 );
610 } 335 }
611 336
612 #[test] 337 #[test]
613 fn completes_self_in_methods() { 338 fn completes_self_in_methods() {
614 assert_debug_snapshot!( 339 check(
615 do_reference_completion(r"impl S { fn foo(&self) { <|> } }"), 340 r#"impl S { fn foo(&self) { <|> } }"#,
616 @r###" 341 expect![[r#"
617 [ 342 tp Self
618 CompletionItem { 343 bn self &{unknown}
619 label: "Self", 344 "#]],
620 source_range: 25..25,
621 delete: 25..25,
622 insert: "Self",
623 kind: TypeParam,
624 },
625 CompletionItem {
626 label: "self",
627 source_range: 25..25,
628 delete: 25..25,
629 insert: "self",
630 kind: Binding,
631 detail: "&{unknown}",
632 },
633 ]
634 "###
635 ); 345 );
636 } 346 }
637 347
638 #[test] 348 #[test]
639 fn completes_prelude() { 349 fn completes_prelude() {
640 assert_debug_snapshot!( 350 check(
641 do_reference_completion( 351 r#"
642 " 352//- /main.rs
643 //- /main.rs 353fn foo() { let x: <|> }
644 fn foo() { let x: <|> } 354
645 355//- /std/lib.rs
646 //- /std/lib.rs 356#[prelude_import]
647 #[prelude_import] 357use prelude::*;
648 use prelude::*; 358
649 359mod prelude { struct Option; }
650 mod prelude { 360"#,
651 struct Option; 361 expect![[r#"
652 } 362 st Option
653 " 363 fn foo() fn foo()
654 ), 364 md std
655 @r###" 365 "#]],
656 [
657 CompletionItem {
658 label: "Option",
659 source_range: 18..18,
660 delete: 18..18,
661 insert: "Option",
662 kind: Struct,
663 },
664 CompletionItem {
665 label: "foo()",
666 source_range: 18..18,
667 delete: 18..18,
668 insert: "foo()$0",
669 kind: Function,
670 lookup: "foo",
671 detail: "fn foo()",
672 },
673 CompletionItem {
674 label: "std",
675 source_range: 18..18,
676 delete: 18..18,
677 insert: "std",
678 kind: Module,
679 },
680 ]
681 "###
682 ); 366 );
683 } 367 }
684 368
685 #[test] 369 #[test]
686 fn completes_std_prelude_if_core_is_defined() { 370 fn completes_std_prelude_if_core_is_defined() {
687 assert_debug_snapshot!( 371 check(
688 do_reference_completion( 372 r#"
689 " 373//- /main.rs
690 //- /main.rs 374fn foo() { let x: <|> }
691 fn foo() { let x: <|> } 375
692 376//- /core/lib.rs
693 //- /core/lib.rs 377#[prelude_import]
694 #[prelude_import] 378use prelude::*;
695 use prelude::*; 379
696 380mod prelude { struct Option; }
697 mod prelude { 381
698 struct Option; 382//- /std/lib.rs
699 } 383#[prelude_import]
700 384use prelude::*;
701 //- /std/lib.rs 385
702 #[prelude_import] 386mod prelude { struct String; }
703 use prelude::*; 387"#,
704 388 expect![[r#"
705 mod prelude { 389 st String
706 struct String; 390 md core
707 } 391 fn foo() fn foo()
708 " 392 md std
709 ), 393 "#]],
710 @r###"
711 [
712 CompletionItem {
713 label: "String",
714 source_range: 18..18,
715 delete: 18..18,
716 insert: "String",
717 kind: Struct,
718 },
719 CompletionItem {
720 label: "core",
721 source_range: 18..18,
722 delete: 18..18,
723 insert: "core",
724 kind: Module,
725 },
726 CompletionItem {
727 label: "foo()",
728 source_range: 18..18,
729 delete: 18..18,
730 insert: "foo()$0",
731 kind: Function,
732 lookup: "foo",
733 detail: "fn foo()",
734 },
735 CompletionItem {
736 label: "std",
737 source_range: 18..18,
738 delete: 18..18,
739 insert: "std",
740 kind: Module,
741 },
742 ]
743 "###
744 ); 394 );
745 } 395 }
746 396
747 #[test] 397 #[test]
748 fn completes_macros_as_value() { 398 fn completes_macros_as_value() {
749 assert_debug_snapshot!( 399 check(
750 do_reference_completion( 400 r#"
751 " 401macro_rules! foo { () => {} }
752 //- /main.rs
753 macro_rules! foo {
754 () => {}
755 }
756 402
757 #[macro_use] 403#[macro_use]
758 mod m1 { 404mod m1 {
759 macro_rules! bar { 405 macro_rules! bar { () => {} }
760 () => {} 406}
761 }
762 }
763 407
764 mod m2 { 408mod m2 {
765 macro_rules! nope { 409 macro_rules! nope { () => {} }
766 () => {}
767 }
768 410
769 #[macro_export] 411 #[macro_export]
770 macro_rules! baz { 412 macro_rules! baz { () => {} }
771 () => {} 413}
772 }
773 }
774 414
775 fn main() { 415fn main() { let v = <|> }
776 let v = <|> 416"#,
777 } 417 expect![[r##"
778 " 418 ma bar!(…) macro_rules! bar
779 ), 419 ma baz!(…) #[macro_export]
780 @r###" 420 macro_rules! baz
781 [ 421 ma foo!(…) macro_rules! foo
782 CompletionItem { 422 md m1
783 label: "bar!(…)", 423 md m2
784 source_range: 252..252, 424 fn main() fn main()
785 delete: 252..252, 425 "##]],
786 insert: "bar!($0)",
787 kind: Macro,
788 detail: "macro_rules! bar",
789 },
790 CompletionItem {
791 label: "baz!(…)",
792 source_range: 252..252,
793 delete: 252..252,
794 insert: "baz!($0)",
795 kind: Macro,
796 detail: "#[macro_export]\nmacro_rules! baz",
797 },
798 CompletionItem {
799 label: "foo!(…)",
800 source_range: 252..252,
801 delete: 252..252,
802 insert: "foo!($0)",
803 kind: Macro,
804 detail: "macro_rules! foo",
805 },
806 CompletionItem {
807 label: "m1",
808 source_range: 252..252,
809 delete: 252..252,
810 insert: "m1",
811 kind: Module,
812 },
813 CompletionItem {
814 label: "m2",
815 source_range: 252..252,
816 delete: 252..252,
817 insert: "m2",
818 kind: Module,
819 },
820 CompletionItem {
821 label: "main()",
822 source_range: 252..252,
823 delete: 252..252,
824 insert: "main()$0",
825 kind: Function,
826 lookup: "main",
827 detail: "fn main()",
828 },
829 ]
830 "###
831 ); 426 );
832 } 427 }
833 428
834 #[test] 429 #[test]
835 fn completes_both_macro_and_value() { 430 fn completes_both_macro_and_value() {
836 assert_debug_snapshot!( 431 check(
837 do_reference_completion( 432 r#"
838 " 433macro_rules! foo { () => {} }
839 //- /main.rs 434fn foo() { <|> }
840 macro_rules! foo { 435"#,
841 () => {} 436 expect![[r#"
842 } 437 ma foo!(…) macro_rules! foo
843 438 fn foo() fn foo()
844 fn foo() { 439 "#]],
845 <|>
846 }
847 "
848 ),
849 @r###"
850 [
851 CompletionItem {
852 label: "foo!(…)",
853 source_range: 49..49,
854 delete: 49..49,
855 insert: "foo!($0)",
856 kind: Macro,
857 detail: "macro_rules! foo",
858 },
859 CompletionItem {
860 label: "foo()",
861 source_range: 49..49,
862 delete: 49..49,
863 insert: "foo()$0",
864 kind: Function,
865 lookup: "foo",
866 detail: "fn foo()",
867 },
868 ]
869 "###
870 ); 440 );
871 } 441 }
872 442
873 #[test] 443 #[test]
874 fn completes_macros_as_type() { 444 fn completes_macros_as_type() {
875 assert_debug_snapshot!( 445 check(
876 do_reference_completion( 446 r#"
877 " 447macro_rules! foo { () => {} }
878 //- /main.rs 448fn main() { let x: <|> }
879 macro_rules! foo { 449"#,
880 () => {} 450 expect![[r#"
881 } 451 ma foo!(…) macro_rules! foo
882 452 fn main() fn main()
883 fn main() { 453 "#]],
884 let x: <|>
885 }
886 "
887 ),
888 @r###"
889 [
890 CompletionItem {
891 label: "foo!(…)",
892 source_range: 57..57,
893 delete: 57..57,
894 insert: "foo!($0)",
895 kind: Macro,
896 detail: "macro_rules! foo",
897 },
898 CompletionItem {
899 label: "main()",
900 source_range: 57..57,
901 delete: 57..57,
902 insert: "main()$0",
903 kind: Function,
904 lookup: "main",
905 detail: "fn main()",
906 },
907 ]
908 "###
909 ); 454 );
910 } 455 }
911 456
912 #[test] 457 #[test]
913 fn completes_macros_as_stmt() { 458 fn completes_macros_as_stmt() {
914 assert_debug_snapshot!( 459 check(
915 do_reference_completion( 460 r#"
916 " 461macro_rules! foo { () => {} }
917 //- /main.rs 462fn main() { <|> }
918 macro_rules! foo { 463"#,
919 () => {} 464 expect![[r#"
920 } 465 ma foo!(…) macro_rules! foo
921 466 fn main() fn main()
922 fn main() { 467 "#]],
923 <|>
924 }
925 "
926 ),
927 @r###"
928 [
929 CompletionItem {
930 label: "foo!(…)",
931 source_range: 50..50,
932 delete: 50..50,
933 insert: "foo!($0)",
934 kind: Macro,
935 detail: "macro_rules! foo",
936 },
937 CompletionItem {
938 label: "main()",
939 source_range: 50..50,
940 delete: 50..50,
941 insert: "main()$0",
942 kind: Function,
943 lookup: "main",
944 detail: "fn main()",
945 },
946 ]
947 "###
948 ); 468 );
949 } 469 }
950 470
951 #[test] 471 #[test]
952 fn completes_local_item() { 472 fn completes_local_item() {
953 assert_debug_snapshot!( 473 check(
954 do_reference_completion( 474 r#"
955 " 475fn main() {
956 //- /main.rs 476 return f<|>;
957 fn main() { 477 fn frobnicate() {}
958 return f<|>; 478}
959 fn frobnicate() {} 479"#,
960 } 480 expect![[r#"
961 " 481 fn frobnicate() fn frobnicate()
962 ), 482 fn main() fn main()
963 @r###" 483 "#]],
964 [ 484 );
965 CompletionItem {
966 label: "frobnicate()",
967 source_range: 23..24,
968 delete: 23..24,
969 insert: "frobnicate()$0",
970 kind: Function,
971 lookup: "frobnicate",
972 detail: "fn frobnicate()",
973 },
974 CompletionItem {
975 label: "main()",
976 source_range: 23..24,
977 delete: 23..24,
978 insert: "main()$0",
979 kind: Function,
980 lookup: "main",
981 detail: "fn main()",
982 },
983 ]
984 "###
985 )
986 } 485 }
987 486
988 #[test] 487 #[test]
989 fn completes_in_simple_macro_1() { 488 fn completes_in_simple_macro_1() {
990 assert_debug_snapshot!( 489 check(
991 do_reference_completion( 490 r#"
992 r" 491macro_rules! m { ($e:expr) => { $e } }
993 macro_rules! m { ($e:expr) => { $e } } 492fn quux(x: i32) {
994 fn quux(x: i32) { 493 let y = 92;
995 let y = 92; 494 m!(<|>);
996 m!(<|>); 495}
997 } 496"#,
998 " 497 expect![[r#"
999 ), 498 ma m!(…) macro_rules! m
1000 @r###" 499 fn quux(…) fn quux(x: i32)
1001 [ 500 bn x i32
1002 CompletionItem { 501 bn y i32
1003 label: "m!(…)", 502 "#]],
1004 source_range: 145..145,
1005 delete: 145..145,
1006 insert: "m!($0)",
1007 kind: Macro,
1008 detail: "macro_rules! m",
1009 },
1010 CompletionItem {
1011 label: "quux(…)",
1012 source_range: 145..145,
1013 delete: 145..145,
1014 insert: "quux(${1:x})$0",
1015 kind: Function,
1016 lookup: "quux",
1017 detail: "fn quux(x: i32)",
1018 trigger_call_info: true,
1019 },
1020 CompletionItem {
1021 label: "x",
1022 source_range: 145..145,
1023 delete: 145..145,
1024 insert: "x",
1025 kind: Binding,
1026 detail: "i32",
1027 },
1028 CompletionItem {
1029 label: "y",
1030 source_range: 145..145,
1031 delete: 145..145,
1032 insert: "y",
1033 kind: Binding,
1034 detail: "i32",
1035 },
1036 ]
1037 "###
1038 ); 503 );
1039 } 504 }
1040 505
1041 #[test] 506 #[test]
1042 fn completes_in_simple_macro_2() { 507 fn completes_in_simple_macro_2() {
1043 assert_debug_snapshot!( 508 check(
1044 do_reference_completion( 509 r"
1045 r" 510macro_rules! m { ($e:expr) => { $e } }
1046 macro_rules! m { ($e:expr) => { $e } } 511fn quux(x: i32) {
1047 fn quux(x: i32) { 512 let y = 92;
1048 let y = 92; 513 m!(x<|>);
1049 m!(x<|>); 514}
1050 } 515",
1051 " 516 expect![[r#"
1052 ), 517 ma m!(…) macro_rules! m
1053 @r###" 518 fn quux(…) fn quux(x: i32)
1054 [ 519 bn x i32
1055 CompletionItem { 520 bn y i32
1056 label: "m!(…)", 521 "#]],
1057 source_range: 145..146,
1058 delete: 145..146,
1059 insert: "m!($0)",
1060 kind: Macro,
1061 detail: "macro_rules! m",
1062 },
1063 CompletionItem {
1064 label: "quux(…)",
1065 source_range: 145..146,
1066 delete: 145..146,
1067 insert: "quux(${1:x})$0",
1068 kind: Function,
1069 lookup: "quux",
1070 detail: "fn quux(x: i32)",
1071 trigger_call_info: true,
1072 },
1073 CompletionItem {
1074 label: "x",
1075 source_range: 145..146,
1076 delete: 145..146,
1077 insert: "x",
1078 kind: Binding,
1079 detail: "i32",
1080 },
1081 CompletionItem {
1082 label: "y",
1083 source_range: 145..146,
1084 delete: 145..146,
1085 insert: "y",
1086 kind: Binding,
1087 detail: "i32",
1088 },
1089 ]
1090 "###
1091 ); 522 );
1092 } 523 }
1093 524
1094 #[test] 525 #[test]
1095 fn completes_in_simple_macro_without_closing_parens() { 526 fn completes_in_simple_macro_without_closing_parens() {
1096 assert_debug_snapshot!( 527 check(
1097 do_reference_completion( 528 r#"
1098 r" 529macro_rules! m { ($e:expr) => { $e } }
1099 macro_rules! m { ($e:expr) => { $e } } 530fn quux(x: i32) {
1100 fn quux(x: i32) { 531 let y = 92;
1101 let y = 92; 532 m!(x<|>
1102 m!(x<|> 533}
1103 } 534"#,
1104 " 535 expect![[r#"
1105 ), 536 ma m!(…) macro_rules! m
1106 @r###" 537 fn quux(…) fn quux(x: i32)
1107 [ 538 bn x i32
1108 CompletionItem { 539 bn y i32
1109 label: "m!(…)", 540 "#]],
1110 source_range: 145..146,
1111 delete: 145..146,
1112 insert: "m!($0)",
1113 kind: Macro,
1114 detail: "macro_rules! m",
1115 },
1116 CompletionItem {
1117 label: "quux(…)",
1118 source_range: 145..146,
1119 delete: 145..146,
1120 insert: "quux(${1:x})$0",
1121 kind: Function,
1122 lookup: "quux",
1123 detail: "fn quux(x: i32)",
1124 trigger_call_info: true,
1125 },
1126 CompletionItem {
1127 label: "x",
1128 source_range: 145..146,
1129 delete: 145..146,
1130 insert: "x",
1131 kind: Binding,
1132 detail: "i32",
1133 },
1134 CompletionItem {
1135 label: "y",
1136 source_range: 145..146,
1137 delete: 145..146,
1138 insert: "y",
1139 kind: Binding,
1140 detail: "i32",
1141 },
1142 ]
1143 "###
1144 ); 541 );
1145 } 542 }
1146 543
1147 #[test] 544 #[test]
1148 fn completes_unresolved_uses() { 545 fn completes_unresolved_uses() {
1149 assert_debug_snapshot!( 546 check(
1150 do_reference_completion( 547 r#"
1151 r" 548use spam::Quux;
1152 use spam::Quux; 549
1153 550fn main() { <|> }
1154 fn main() { 551"#,
1155 <|> 552 expect![[r#"
1156 } 553 ?? Quux
1157 " 554 fn main() fn main()
1158 ), 555 "#]],
1159 @r###"
1160 [
1161 CompletionItem {
1162 label: "Quux",
1163 source_range: 82..82,
1164 delete: 82..82,
1165 insert: "Quux",
1166 },
1167 CompletionItem {
1168 label: "main()",
1169 source_range: 82..82,
1170 delete: 82..82,
1171 insert: "main()$0",
1172 kind: Function,
1173 lookup: "main",
1174 detail: "fn main()",
1175 },
1176 ]
1177 "###
1178 ); 556 );
1179 } 557 }
1180 #[test] 558 #[test]
1181 fn completes_enum_variant_matcharm() { 559 fn completes_enum_variant_matcharm() {
1182 assert_debug_snapshot!( 560 check(
1183 do_reference_completion( 561 r#"
1184 r" 562enum Foo { Bar, Baz, Quux }
1185 enum Foo {
1186 Bar,
1187 Baz,
1188 Quux
1189 }
1190
1191 fn main() {
1192 let foo = Foo::Quux;
1193 563
1194 match foo { 564fn main() {
1195 Qu<|> 565 let foo = Foo::Quux;
1196 } 566 match foo { Qu<|> }
1197 } 567}
1198 " 568"#,
1199 ), 569 expect![[r#"
1200 @r###" 570 en Foo
1201 [ 571 ev Foo::Bar ()
1202 CompletionItem { 572 ev Foo::Baz ()
1203 label: "Foo", 573 ev Foo::Quux ()
1204 source_range: 248..250, 574 "#]],
1205 delete: 248..250,
1206 insert: "Foo",
1207 kind: Enum,
1208 },
1209 CompletionItem {
1210 label: "Foo::Bar",
1211 source_range: 248..250,
1212 delete: 248..250,
1213 insert: "Foo::Bar",
1214 kind: EnumVariant,
1215 lookup: "Bar",
1216 detail: "()",
1217 },
1218 CompletionItem {
1219 label: "Foo::Baz",
1220 source_range: 248..250,
1221 delete: 248..250,
1222 insert: "Foo::Baz",
1223 kind: EnumVariant,
1224 lookup: "Baz",
1225 detail: "()",
1226 },
1227 CompletionItem {
1228 label: "Foo::Quux",
1229 source_range: 248..250,
1230 delete: 248..250,
1231 insert: "Foo::Quux",
1232 kind: EnumVariant,
1233 lookup: "Quux",
1234 detail: "()",
1235 },
1236 ]
1237 "###
1238 ) 575 )
1239 } 576 }
1240 577
1241 #[test] 578 #[test]
1242 fn completes_enum_variant_iflet() { 579 fn completes_enum_variant_iflet() {
1243 assert_debug_snapshot!( 580 check(
1244 do_reference_completion( 581 r#"
1245 r" 582enum Foo { Bar, Baz, Quux }
1246 enum Foo {
1247 Bar,
1248 Baz,
1249 Quux
1250 }
1251 583
1252 fn main() { 584fn main() {
1253 let foo = Foo::Quux; 585 let foo = Foo::Quux;
1254 586 if let Qu<|> = foo { }
1255 if let Qu<|> = foo { 587}
1256 588"#,
1257 } 589 expect![[r#"
1258 } 590 en Foo
1259 " 591 ev Foo::Bar ()
1260 ), 592 ev Foo::Baz ()
1261 @r###" 593 ev Foo::Quux ()
1262 [ 594 "#]],
1263 CompletionItem {
1264 label: "Foo",
1265 source_range: 219..221,
1266 delete: 219..221,
1267 insert: "Foo",
1268 kind: Enum,
1269 },
1270 CompletionItem {
1271 label: "Foo::Bar",
1272 source_range: 219..221,
1273 delete: 219..221,
1274 insert: "Foo::Bar",
1275 kind: EnumVariant,
1276 lookup: "Bar",
1277 detail: "()",
1278 },
1279 CompletionItem {
1280 label: "Foo::Baz",
1281 source_range: 219..221,
1282 delete: 219..221,
1283 insert: "Foo::Baz",
1284 kind: EnumVariant,
1285 lookup: "Baz",
1286 detail: "()",
1287 },
1288 CompletionItem {
1289 label: "Foo::Quux",
1290 source_range: 219..221,
1291 delete: 219..221,
1292 insert: "Foo::Quux",
1293 kind: EnumVariant,
1294 lookup: "Quux",
1295 detail: "()",
1296 },
1297 ]
1298 "###
1299 ) 595 )
1300 } 596 }
1301 597
1302 #[test] 598 #[test]
1303 fn completes_enum_variant_basic_expr() { 599 fn completes_enum_variant_basic_expr() {
1304 assert_debug_snapshot!( 600 check(
1305 do_reference_completion( 601 r#"
1306 r" 602enum Foo { Bar, Baz, Quux }
1307 enum Foo { 603fn main() { let foo: Foo = Q<|> }
1308 Bar, 604"#,
1309 Baz, 605 expect![[r#"
1310 Quux 606 en Foo
1311 } 607 ev Foo::Bar ()
1312 608 ev Foo::Baz ()
1313 fn main() { 609 ev Foo::Quux ()
1314 let foo: Foo = Q<|> 610 fn main() fn main()
1315 } 611 "#]],
1316 "
1317 ),
1318 @r###"
1319 [
1320 CompletionItem {
1321 label: "Foo",
1322 source_range: 185..186,
1323 delete: 185..186,
1324 insert: "Foo",
1325 kind: Enum,
1326 },
1327 CompletionItem {
1328 label: "Foo::Bar",
1329 source_range: 185..186,
1330 delete: 185..186,
1331 insert: "Foo::Bar",
1332 kind: EnumVariant,
1333 lookup: "Bar",
1334 detail: "()",
1335 },
1336 CompletionItem {
1337 label: "Foo::Baz",
1338 source_range: 185..186,
1339 delete: 185..186,
1340 insert: "Foo::Baz",
1341 kind: EnumVariant,
1342 lookup: "Baz",
1343 detail: "()",
1344 },
1345 CompletionItem {
1346 label: "Foo::Quux",
1347 source_range: 185..186,
1348 delete: 185..186,
1349 insert: "Foo::Quux",
1350 kind: EnumVariant,
1351 lookup: "Quux",
1352 detail: "()",
1353 },
1354 CompletionItem {
1355 label: "main()",
1356 source_range: 185..186,
1357 delete: 185..186,
1358 insert: "main()$0",
1359 kind: Function,
1360 lookup: "main",
1361 detail: "fn main()",
1362 },
1363 ]
1364 "###
1365 ) 612 )
1366 } 613 }
1367 614
1368 #[test] 615 #[test]
1369 fn completes_enum_variant_from_module() { 616 fn completes_enum_variant_from_module() {
1370 assert_debug_snapshot!( 617 check(
1371 do_reference_completion( 618 r#"
1372 r" 619mod m { pub enum E { V } }
1373 mod m { pub enum E { V } } 620fn f() -> m::E { V<|> }
1374 621"#,
1375 fn f() -> m::E { 622 expect![[r#"
1376 V<|> 623 fn f() fn f() -> m::E
1377 } 624 md m
1378 " 625 ev m::E::V ()
1379 ), 626 "#]],
1380 @r###"
1381 [
1382 CompletionItem {
1383 label: "f()",
1384 source_range: 98..99,
1385 delete: 98..99,
1386 insert: "f()$0",
1387 kind: Function,
1388 lookup: "f",
1389 detail: "fn f() -> m::E",
1390 },
1391 CompletionItem {
1392 label: "m",
1393 source_range: 98..99,
1394 delete: 98..99,
1395 insert: "m",
1396 kind: Module,
1397 },
1398 CompletionItem {
1399 label: "m::E::V",
1400 source_range: 98..99,
1401 delete: 98..99,
1402 insert: "m::E::V",
1403 kind: EnumVariant,
1404 lookup: "V",
1405 detail: "()",
1406 },
1407 ]
1408 "###
1409 ) 627 )
1410 } 628 }
1411 629
1412 #[test] 630 #[test]
1413 fn dont_complete_attr() { 631 fn dont_complete_attr() {
1414 assert_debug_snapshot!( 632 check(
1415 do_reference_completion( 633 r#"
1416 r" 634struct Foo;
1417 struct Foo; 635#[<|>]
1418 #[<|>] 636fn f() {}
1419 fn f() {} 637"#,
1420 " 638 expect![[""]],
1421 ), 639 )
1422 @r###"[]"### 640 }
641
642 #[test]
643 fn completes_type_or_trait_in_impl_block() {
644 check(
645 r#"
646trait MyTrait {}
647struct MyStruct {}
648
649impl My<|>
650"#,
651 expect![[r#"
652 st MyStruct
653 tt MyTrait
654 tp Self
655 "#]],
1423 ) 656 )
1424 } 657 }
1425} 658}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index c4646b727..9e82d6854 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -5,12 +5,17 @@ use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
8 ast, match_ast, AstNode, 8 ast, match_ast, AstNode, NodeOrToken,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, 10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11}; 11};
12use ra_text_edit::Indel; 12use ra_text_edit::Indel;
13 13
14use super::patterns::{
15 has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_impl_parent,
16 has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling,
17 has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, unsafe_is_prev,
18};
14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; 19use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
15use test_utils::mark; 20use test_utils::mark;
16 21
@@ -19,6 +24,7 @@ use test_utils::mark;
19#[derive(Debug)] 24#[derive(Debug)]
20pub(crate) struct CompletionContext<'a> { 25pub(crate) struct CompletionContext<'a> {
21 pub(super) sema: Semantics<'a, RootDatabase>, 26 pub(super) sema: Semantics<'a, RootDatabase>,
27 pub(super) scope: SemanticsScope<'a>,
22 pub(super) db: &'a RootDatabase, 28 pub(super) db: &'a RootDatabase,
23 pub(super) config: &'a CompletionConfig, 29 pub(super) config: &'a CompletionConfig,
24 pub(super) offset: TextSize, 30 pub(super) offset: TextSize,
@@ -48,6 +54,8 @@ pub(crate) struct CompletionContext<'a> {
48 pub(super) after_if: bool, 54 pub(super) after_if: bool,
49 /// `true` if we are a statement or a last expr in the block. 55 /// `true` if we are a statement or a last expr in the block.
50 pub(super) can_be_stmt: bool, 56 pub(super) can_be_stmt: bool,
57 /// `true` if we expect an expression at the cursor position.
58 pub(super) is_expr: bool,
51 /// Something is typed at the "top" level, in module or impl/trait. 59 /// Something is typed at the "top" level, in module or impl/trait.
52 pub(super) is_new_item: bool, 60 pub(super) is_new_item: bool,
53 /// The receiver if this is a field or method access, i.e. writing something.<|> 61 /// The receiver if this is a field or method access, i.e. writing something.<|>
@@ -55,11 +63,25 @@ pub(crate) struct CompletionContext<'a> {
55 pub(super) dot_receiver_is_ambiguous_float_literal: bool, 63 pub(super) dot_receiver_is_ambiguous_float_literal: bool,
56 /// If this is a call (method or function) in particular, i.e. the () are already there. 64 /// If this is a call (method or function) in particular, i.e. the () are already there.
57 pub(super) is_call: bool, 65 pub(super) is_call: bool,
66 /// Like `is_call`, but for tuple patterns.
67 pub(super) is_pattern_call: bool,
58 /// If this is a macro call, i.e. the () are already there. 68 /// If this is a macro call, i.e. the () are already there.
59 pub(super) is_macro_call: bool, 69 pub(super) is_macro_call: bool,
60 pub(super) is_path_type: bool, 70 pub(super) is_path_type: bool,
61 pub(super) has_type_args: bool, 71 pub(super) has_type_args: bool,
62 pub(super) attribute_under_caret: Option<ast::Attr>, 72 pub(super) attribute_under_caret: Option<ast::Attr>,
73 pub(super) unsafe_is_prev: bool,
74 pub(super) if_is_prev: bool,
75 pub(super) block_expr_parent: bool,
76 pub(super) bind_pat_parent: bool,
77 pub(super) ref_pat_parent: bool,
78 pub(super) in_loop_body: bool,
79 pub(super) has_trait_parent: bool,
80 pub(super) has_impl_parent: bool,
81 pub(super) trait_as_prev_sibling: bool,
82 pub(super) impl_as_prev_sibling: bool,
83 pub(super) is_match_arm: bool,
84 pub(super) has_item_list_or_source_file_parent: bool,
63} 85}
64 86
65impl<'a> CompletionContext<'a> { 87impl<'a> CompletionContext<'a> {
@@ -87,8 +109,10 @@ impl<'a> CompletionContext<'a> {
87 let original_token = 109 let original_token =
88 original_file.syntax().token_at_offset(position.offset).left_biased()?; 110 original_file.syntax().token_at_offset(position.offset).left_biased()?;
89 let token = sema.descend_into_macros(original_token.clone()); 111 let token = sema.descend_into_macros(original_token.clone());
112 let scope = sema.scope_at_offset(&token.parent(), position.offset);
90 let mut ctx = CompletionContext { 113 let mut ctx = CompletionContext {
91 sema, 114 sema,
115 scope,
92 db, 116 db,
93 config, 117 config,
94 original_token, 118 original_token,
@@ -110,14 +134,28 @@ impl<'a> CompletionContext<'a> {
110 path_prefix: None, 134 path_prefix: None,
111 after_if: false, 135 after_if: false,
112 can_be_stmt: false, 136 can_be_stmt: false,
137 is_expr: false,
113 is_new_item: false, 138 is_new_item: false,
114 dot_receiver: None, 139 dot_receiver: None,
115 is_call: false, 140 is_call: false,
141 is_pattern_call: false,
116 is_macro_call: false, 142 is_macro_call: false,
117 is_path_type: false, 143 is_path_type: false,
118 has_type_args: false, 144 has_type_args: false,
119 dot_receiver_is_ambiguous_float_literal: false, 145 dot_receiver_is_ambiguous_float_literal: false,
120 attribute_under_caret: None, 146 attribute_under_caret: None,
147 unsafe_is_prev: false,
148 in_loop_body: false,
149 ref_pat_parent: false,
150 bind_pat_parent: false,
151 block_expr_parent: false,
152 has_trait_parent: false,
153 has_impl_parent: false,
154 trait_as_prev_sibling: false,
155 impl_as_prev_sibling: false,
156 if_is_prev: false,
157 is_match_arm: false,
158 has_item_list_or_source_file_parent: false,
121 }; 159 };
122 160
123 let mut original_file = original_file.syntax().clone(); 161 let mut original_file = original_file.syntax().clone();
@@ -159,7 +197,7 @@ impl<'a> CompletionContext<'a> {
159 break; 197 break;
160 } 198 }
161 } 199 }
162 200 ctx.fill_keyword_patterns(&hypothetical_file, offset);
163 ctx.fill(&original_file, hypothetical_file, offset); 201 ctx.fill(&original_file, hypothetical_file, offset);
164 Some(ctx) 202 Some(ctx)
165 } 203 }
@@ -167,25 +205,30 @@ impl<'a> CompletionContext<'a> {
167 // The range of the identifier that is being completed. 205 // The range of the identifier that is being completed.
168 pub(crate) fn source_range(&self) -> TextRange { 206 pub(crate) fn source_range(&self) -> TextRange {
169 // check kind of macro-expanded token, but use range of original token 207 // check kind of macro-expanded token, but use range of original token
170 match self.token.kind() { 208 if self.token.kind() == IDENT || self.token.kind().is_keyword() {
171 // workaroud when completion is triggered by trigger characters. 209 mark::hit!(completes_if_prefix_is_keyword);
172 IDENT => self.original_token.text_range(), 210 self.original_token.text_range()
173 _ => { 211 } else {
174 // If we haven't characters between keyword and our cursor we take the keyword start range to edit 212 TextRange::empty(self.offset)
175 if self.token.kind().is_keyword()
176 && self.offset == self.original_token.text_range().end()
177 {
178 mark::hit!(completes_bindings_from_for_with_in_prefix);
179 TextRange::empty(self.original_token.text_range().start())
180 } else {
181 TextRange::empty(self.offset)
182 }
183 }
184 } 213 }
185 } 214 }
186 215
187 pub(crate) fn scope(&self) -> SemanticsScope<'_, RootDatabase> { 216 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
188 self.sema.scope_at_offset(&self.token.parent(), self.offset) 217 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
218 let syntax_element = NodeOrToken::Token(fake_ident_token.clone());
219 self.block_expr_parent = has_block_expr_parent(syntax_element.clone());
220 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone());
221 self.if_is_prev = if_is_prev(syntax_element.clone());
222 self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone());
223 self.ref_pat_parent = has_ref_parent(syntax_element.clone());
224 self.in_loop_body = is_in_loop_body(syntax_element.clone());
225 self.has_trait_parent = has_trait_parent(syntax_element.clone());
226 self.has_impl_parent = has_impl_parent(syntax_element.clone());
227 self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone());
228 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
229 self.is_match_arm = is_match_arm(syntax_element.clone());
230 self.has_item_list_or_source_file_parent =
231 has_item_list_or_source_file_parent(syntax_element.clone());
189 } 232 }
190 233
191 fn fill( 234 fn fill(
@@ -330,10 +373,13 @@ impl<'a> CompletionContext<'a> {
330 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) 373 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
331 .is_some(); 374 .is_some();
332 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some(); 375 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
376 self.is_pattern_call =
377 path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some();
333 378
334 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 379 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
335 self.has_type_args = segment.type_arg_list().is_some(); 380 self.has_type_args = segment.type_arg_list().is_some();
336 381
382 #[allow(deprecated)]
337 if let Some(path) = hir::Path::from_ast(path.clone()) { 383 if let Some(path) = hir::Path::from_ast(path.clone()) {
338 if let Some(path_prefix) = path.qualifier() { 384 if let Some(path_prefix) = path.qualifier() {
339 self.path_prefix = Some(path_prefix); 385 self.path_prefix = Some(path_prefix);
@@ -364,6 +410,7 @@ impl<'a> CompletionContext<'a> {
364 None 410 None
365 }) 411 })
366 .unwrap_or(false); 412 .unwrap_or(false);
413 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
367 414
368 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) { 415 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
369 if let Some(if_expr) = 416 if let Some(if_expr) =
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index cfb7c1e38..7bdda316c 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -58,7 +58,7 @@ pub struct CompletionItem {
58 score: Option<CompletionScore>, 58 score: Option<CompletionScore>,
59} 59}
60 60
61// We use custom debug for CompletionItem to make `insta`'s diffs more readable. 61// We use custom debug for CompletionItem to make snapshot tests more readable.
62impl fmt::Debug for CompletionItem { 62impl fmt::Debug for CompletionItem {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 let mut s = f.debug_struct("CompletionItem"); 64 let mut s = f.debug_struct("CompletionItem");
@@ -95,7 +95,7 @@ impl fmt::Debug for CompletionItem {
95 } 95 }
96} 96}
97 97
98#[derive(Debug, Clone, Copy)] 98#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)]
99pub enum CompletionScore { 99pub enum CompletionScore {
100 /// If only type match 100 /// If only type match
101 TypeMatch, 101 TypeMatch,
@@ -123,6 +123,34 @@ pub enum CompletionItemKind {
123 TypeParam, 123 TypeParam,
124 Macro, 124 Macro,
125 Attribute, 125 Attribute,
126 UnresolvedReference,
127}
128
129impl CompletionItemKind {
130 #[cfg(test)]
131 pub(crate) fn tag(&self) -> &'static str {
132 match self {
133 CompletionItemKind::Attribute => "at",
134 CompletionItemKind::Binding => "bn",
135 CompletionItemKind::BuiltinType => "bt",
136 CompletionItemKind::Const => "ct",
137 CompletionItemKind::Enum => "en",
138 CompletionItemKind::EnumVariant => "ev",
139 CompletionItemKind::Field => "fd",
140 CompletionItemKind::Function => "fn",
141 CompletionItemKind::Keyword => "kw",
142 CompletionItemKind::Macro => "ma",
143 CompletionItemKind::Method => "me",
144 CompletionItemKind::Module => "md",
145 CompletionItemKind::Snippet => "sn",
146 CompletionItemKind::Static => "sc",
147 CompletionItemKind::Struct => "st",
148 CompletionItemKind::Trait => "tt",
149 CompletionItemKind::TypeAlias => "ta",
150 CompletionItemKind::TypeParam => "tp",
151 CompletionItemKind::UnresolvedReference => "??",
152 }
153 }
126} 154}
127 155
128#[derive(Debug, PartialEq, Eq, Copy, Clone)] 156#[derive(Debug, PartialEq, Eq, Copy, Clone)]
diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs
new file mode 100644
index 000000000..b2fe13280
--- /dev/null
+++ b/crates/ra_ide/src/completion/patterns.rs
@@ -0,0 +1,194 @@
1//! Patterns telling us certain facts about current syntax element, they are used in completion context
2
3use ra_syntax::{
4 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken,
9};
10
11#[cfg(test)]
12use crate::completion::test_utils::check_pattern_is_applicable;
13
14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool {
15 not_same_range_ancestor(element)
16 .filter(|it| it.kind() == ITEM_LIST)
17 .and_then(|it| it.parent())
18 .filter(|it| it.kind() == TRAIT_DEF)
19 .is_some()
20}
21#[test]
22fn test_has_trait_parent() {
23 check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent);
24}
25
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool {
27 not_same_range_ancestor(element)
28 .filter(|it| it.kind() == ITEM_LIST)
29 .and_then(|it| it.parent())
30 .filter(|it| it.kind() == IMPL_DEF)
31 .is_some()
32}
33#[test]
34fn test_has_impl_parent() {
35 check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent);
36}
37
38pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
39 not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some()
40}
41#[test]
42fn test_has_block_expr_parent() {
43 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f<|> }", has_block_expr_parent);
44}
45
46pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
47 element.ancestors().find(|it| it.kind() == BIND_PAT).is_some()
48}
49#[test]
50fn test_has_bind_pat_parent() {
51 check_pattern_is_applicable(r"fn my_fn(m<|>) {}", has_bind_pat_parent);
52 check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent);
53}
54
55pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
56 not_same_range_ancestor(element)
57 .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
58 .is_some()
59}
60#[test]
61fn test_has_ref_parent() {
62 check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent);
63 check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent);
64}
65
66pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
67 let ancestor = not_same_range_ancestor(element);
68 if !ancestor.is_some() {
69 return true;
70 }
71 ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some()
72}
73#[test]
74fn test_has_item_list_or_source_file_parent() {
75 check_pattern_is_applicable(r"i<|>", has_item_list_or_source_file_parent);
76 check_pattern_is_applicable(r"impl { f<|> }", has_item_list_or_source_file_parent);
77}
78
79pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
80 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
81 && previous_sibling_or_ancestor_sibling(element)
82 .and_then(|it| it.into_token())
83 .filter(|it| it.kind() == FAT_ARROW)
84 .is_some()
85}
86#[test]
87fn test_is_match_arm() {
88 check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm);
89}
90
91pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
92 element
93 .into_token()
94 .and_then(|it| previous_non_trivia_token(it))
95 .filter(|it| it.kind() == UNSAFE_KW)
96 .is_some()
97}
98#[test]
99fn test_unsafe_is_prev() {
100 check_pattern_is_applicable(r"unsafe i<|>", unsafe_is_prev);
101}
102
103pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
104 element
105 .into_token()
106 .and_then(|it| previous_non_trivia_token(it))
107 .filter(|it| it.kind() == IF_KW)
108 .is_some()
109}
110#[test]
111fn test_if_is_prev() {
112 check_pattern_is_applicable(r"if l<|>", if_is_prev);
113}
114
115pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
116 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT_DEF).is_some()
117}
118#[test]
119fn test_has_trait_as_prev_sibling() {
120 check_pattern_is_applicable(r"trait A w<|> {}", has_trait_as_prev_sibling);
121}
122
123pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
124 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL_DEF).is_some()
125}
126#[test]
127fn test_has_impl_as_prev_sibling() {
128 check_pattern_is_applicable(r"impl A w<|> {}", has_impl_as_prev_sibling);
129}
130
131pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
132 let leaf = match element {
133 NodeOrToken::Node(node) => node,
134 NodeOrToken::Token(token) => token.parent(),
135 };
136 for node in leaf.ancestors() {
137 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
138 break;
139 }
140 let loop_body = match_ast! {
141 match node {
142 ast::ForExpr(it) => it.loop_body(),
143 ast::WhileExpr(it) => it.loop_body(),
144 ast::LoopExpr(it) => it.loop_body(),
145 _ => None,
146 }
147 };
148 if let Some(body) = loop_body {
149 if body.syntax().text_range().contains_range(leaf.text_range()) {
150 return true;
151 }
152 }
153 }
154 false
155}
156
157fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
158 element
159 .ancestors()
160 .take_while(|it| it.text_range() == element.text_range())
161 .last()
162 .and_then(|it| it.parent())
163}
164
165fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
166 let mut token = token.prev_token();
167 while let Some(inner) = token.clone() {
168 if !inner.kind().is_trivia() {
169 return Some(inner);
170 } else {
171 token = inner.prev_token();
172 }
173 }
174 None
175}
176
177fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> {
178 let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev);
179 if let Some(sibling) = token_sibling {
180 Some(sibling)
181 } else {
182 // if not trying to find first ancestor which has such a sibling
183 let node = match element {
184 NodeOrToken::Node(node) => node,
185 NodeOrToken::Token(token) => token.parent(),
186 };
187 let range = node.text_range();
188 let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?;
189 let prev_sibling_node = top_node.ancestors().find(|it| {
190 non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some()
191 })?;
192 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)
193 }
194}
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 61565c84f..64349dcb8 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -1,4 +1,5 @@
1//! This modules takes care of rendering various definitions as completion items. 1//! This modules takes care of rendering various definitions as completion items.
2//! It also handles scoring (sorting) completions.
2 3
3use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; 4use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
4use ra_syntax::ast::NameOwner; 5use ra_syntax::ast::NameOwner;
@@ -78,11 +79,10 @@ impl Completions {
78 return self.add_macro(ctx, Some(local_name), *mac); 79 return self.add_macro(ctx, Some(local_name), *mac);
79 } 80 }
80 ScopeDef::Unknown => { 81 ScopeDef::Unknown => {
81 return self.add(CompletionItem::new( 82 return self.add(
82 CompletionKind::Reference, 83 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), local_name)
83 ctx.source_range(), 84 .kind(CompletionItemKind::UnresolvedReference),
84 local_name, 85 );
85 ));
86 } 86 }
87 }; 87 };
88 88
@@ -173,6 +173,7 @@ impl Completions {
173 builder 173 builder
174 .insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket)) 174 .insert_snippet(cap, format!("{}!{}$0{}", name, bra, ket))
175 .label(format!("{}!{}…{}", name, bra, ket)) 175 .label(format!("{}!{}…{}", name, bra, ket))
176 .lookup_by(format!("{}!", name))
176 } 177 }
177 None if needs_bang => builder.insert_text(format!("{}!", name)), 178 None if needs_bang => builder.insert_text(format!("{}!", name)),
178 _ => { 179 _ => {
@@ -314,6 +315,7 @@ impl Completions {
314 } 315 }
315 316
316 if variant_kind == StructKind::Tuple { 317 if variant_kind == StructKind::Tuple {
318 mark::hit!(inserts_parens_for_tuple_enums);
317 let params = Params::Anonymous(variant.fields(ctx.db).len()); 319 let params = Params::Anonymous(variant.fields(ctx.db).len());
318 res = res.add_call_parens(ctx, qualified_name, params) 320 res = res.add_call_parens(ctx, qualified_name, params)
319 } 321 }
@@ -330,14 +332,14 @@ pub(crate) fn compute_score(
330 // FIXME: this should not fall back to string equality. 332 // FIXME: this should not fall back to string equality.
331 let ty = &ty.display(ctx.db).to_string(); 333 let ty = &ty.display(ctx.db).to_string();
332 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { 334 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
333 mark::hit!(test_struct_field_completion_in_record_lit); 335 mark::hit!(record_field_type_match);
334 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; 336 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
335 ( 337 (
336 struct_field.name(ctx.db).to_string(), 338 struct_field.name(ctx.db).to_string(),
337 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(), 339 struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
338 ) 340 )
339 } else if let Some(active_parameter) = &ctx.active_parameter { 341 } else if let Some(active_parameter) = &ctx.active_parameter {
340 mark::hit!(test_struct_field_completion_in_func_call); 342 mark::hit!(active_param_type_match);
341 (active_parameter.name.clone(), active_parameter.ty.clone()) 343 (active_parameter.name.clone(), active_parameter.ty.clone())
342 } else { 344 } else {
343 return None; 345 return None;
@@ -382,13 +384,22 @@ impl Builder {
382 if !ctx.config.add_call_parenthesis { 384 if !ctx.config.add_call_parenthesis {
383 return self; 385 return self;
384 } 386 }
385 if ctx.use_item_syntax.is_some() || ctx.is_call { 387 if ctx.use_item_syntax.is_some() {
388 mark::hit!(no_parens_in_use_item);
389 return self;
390 }
391 if ctx.is_pattern_call {
392 mark::hit!(dont_duplicate_pattern_parens);
393 return self;
394 }
395 if ctx.is_call {
386 return self; 396 return self;
387 } 397 }
388 398
389 // Don't add parentheses if the expected type is some function reference. 399 // Don't add parentheses if the expected type is some function reference.
390 if let Some(ty) = &ctx.expected_type { 400 if let Some(ty) = &ctx.expected_type {
391 if ty.is_fn() { 401 if ty.is_fn() {
402 mark::hit!(no_call_parens_if_fn_ptr_needed);
392 return self; 403 return self;
393 } 404 }
394 } 405 }
@@ -413,7 +424,10 @@ impl Builder {
413 .sep_by(", "); 424 .sep_by(", ");
414 format!("{}({})$0", name, function_params_snippet) 425 format!("{}({})$0", name, function_params_snippet)
415 } 426 }
416 _ => format!("{}($0)", name), 427 _ => {
428 mark::hit!(suppress_arg_snippets);
429 format!("{}($0)", name)
430 }
417 }; 431 };
418 432
419 (snippet, format!("{}(…)", name)) 433 (snippet, format!("{}(…)", name))
@@ -456,1061 +470,750 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s
456 470
457#[cfg(test)] 471#[cfg(test)]
458mod tests { 472mod tests {
459 use insta::assert_debug_snapshot; 473 use std::cmp::Reverse;
474
475 use expect::{expect, Expect};
460 use test_utils::mark; 476 use test_utils::mark;
461 477
462 use crate::completion::{ 478 use crate::{
463 test_utils::{do_completion, do_completion_with_options}, 479 completion::{
464 CompletionConfig, CompletionItem, CompletionKind, 480 test_utils::{
481 check_edit, check_edit_with_config, do_completion, get_all_completion_items,
482 },
483 CompletionConfig, CompletionKind,
484 },
485 CompletionScore,
465 }; 486 };
466 487
467 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> { 488 fn check(ra_fixture: &str, expect: Expect) {
468 do_completion(ra_fixture, CompletionKind::Reference) 489 let actual = do_completion(ra_fixture, CompletionKind::Reference);
490 expect.assert_debug_eq(&actual);
469 } 491 }
470 492
471 fn do_reference_completion_with_options( 493 fn check_scores(ra_fixture: &str, expect: Expect) {
472 ra_fixture: &str, 494 fn display_score(score: Option<CompletionScore>) -> &'static str {
473 options: CompletionConfig, 495 match score {
474 ) -> Vec<CompletionItem> { 496 Some(CompletionScore::TypeMatch) => "[type]",
475 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options) 497 Some(CompletionScore::TypeAndNameMatch) => "[type+name]",
498 None => "[]".into(),
499 }
500 }
501
502 let mut completions = get_all_completion_items(CompletionConfig::default(), ra_fixture);
503 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
504 let actual = completions
505 .into_iter()
506 .filter(|it| it.completion_kind == CompletionKind::Reference)
507 .map(|it| {
508 let tag = it.kind().unwrap().tag();
509 let score = display_score(it.score());
510 format!("{} {} {}\n", tag, it.label(), score)
511 })
512 .collect::<String>();
513 expect.assert_eq(&actual);
476 } 514 }
477 515
478 #[test] 516 #[test]
479 fn enum_detail_includes_names_for_record() { 517 fn enum_detail_includes_record_fields() {
480 assert_debug_snapshot!( 518 check(
481 do_reference_completion(
482 r#" 519 r#"
483 enum Foo { 520enum Foo { Foo { x: i32, y: i32 } }
484 Foo {x: i32, y: i32} 521
485 } 522fn main() { Foo::Fo<|> }
486 523"#,
487 fn main() { Foo::Fo<|> } 524 expect![[r#"
488 "#, 525 [
489 ), 526 CompletionItem {
490 @r###" 527 label: "Foo",
491 [ 528 source_range: 54..56,
492 CompletionItem { 529 delete: 54..56,
493 label: "Foo", 530 insert: "Foo",
494 source_range: 121..123, 531 kind: EnumVariant,
495 delete: 121..123, 532 detail: "{ x: i32, y: i32 }",
496 insert: "Foo", 533 },
497 kind: EnumVariant, 534 ]
498 detail: "{ x: i32, y: i32 }", 535 "#]],
499 },
500 ]"###
501 ); 536 );
502 } 537 }
503 538
504 #[test] 539 #[test]
505 fn enum_detail_doesnt_include_names_for_tuple() { 540 fn enum_detail_doesnt_include_tuple_fields() {
506 assert_debug_snapshot!( 541 check(
507 do_reference_completion(
508 r#" 542 r#"
509 enum Foo { 543enum Foo { Foo (i32, i32) }
510 Foo (i32, i32) 544
511 } 545fn main() { Foo::Fo<|> }
512 546"#,
513 fn main() { Foo::Fo<|> } 547 expect![[r#"
514 "#, 548 [
515 ), 549 CompletionItem {
516 @r###" 550 label: "Foo(…)",
517 [ 551 source_range: 46..48,
518 CompletionItem { 552 delete: 46..48,
519 label: "Foo(…)", 553 insert: "Foo($0)",
520 source_range: 115..117, 554 kind: EnumVariant,
521 delete: 115..117, 555 lookup: "Foo",
522 insert: "Foo($0)", 556 detail: "(i32, i32)",
523 kind: EnumVariant, 557 trigger_call_info: true,
524 lookup: "Foo", 558 },
525 detail: "(i32, i32)", 559 ]
526 trigger_call_info: true, 560 "#]],
527 },
528 ]"###
529 ); 561 );
530 } 562 }
531 563
532 #[test] 564 #[test]
533 fn enum_detail_just_parentheses_for_unit() { 565 fn enum_detail_just_parentheses_for_unit() {
534 assert_debug_snapshot!( 566 check(
535 do_reference_completion(
536 r#" 567 r#"
537 enum Foo { 568enum Foo { Foo }
538 Foo 569
539 } 570fn main() { Foo::Fo<|> }
540 571"#,
541 fn main() { Foo::Fo<|> } 572 expect![[r#"
542 "#, 573 [
543 ), 574 CompletionItem {
544 @r###" 575 label: "Foo",
545 [ 576 source_range: 35..37,
546 CompletionItem { 577 delete: 35..37,
547 label: "Foo", 578 insert: "Foo",
548 source_range: 104..106, 579 kind: EnumVariant,
549 delete: 104..106, 580 detail: "()",
550 insert: "Foo", 581 },
551 kind: EnumVariant, 582 ]
552 detail: "()", 583 "#]],
553 },
554 ]"###
555 ); 584 );
556 } 585 }
557 586
558 #[test] 587 #[test]
559 fn sets_deprecated_flag_in_completion_items() { 588 fn sets_deprecated_flag_in_completion_items() {
560 assert_debug_snapshot!( 589 check(
561 do_reference_completion( 590 r#"
562 r#" 591#[deprecated]
563 #[deprecated] 592fn something_deprecated() {}
564 fn something_deprecated() {} 593#[deprecated(since = "1.0.0")]
565 594fn something_else_deprecated() {}
566 #[deprecated(since = "1.0.0")] 595
567 fn something_else_deprecated() {} 596fn main() { som<|> }
568 597"#,
569 fn main() { som<|> } 598 expect![[r#"
570 "#, 599 [
571 ), 600 CompletionItem {
572 @r###" 601 label: "main()",
573 [ 602 source_range: 121..124,
574 CompletionItem { 603 delete: 121..124,
575 label: "main()", 604 insert: "main()$0",
576 source_range: 203..206, 605 kind: Function,
577 delete: 203..206, 606 lookup: "main",
578 insert: "main()$0", 607 detail: "fn main()",
579 kind: Function, 608 },
580 lookup: "main", 609 CompletionItem {
581 detail: "fn main()", 610 label: "something_deprecated()",
582 }, 611 source_range: 121..124,
583 CompletionItem { 612 delete: 121..124,
584 label: "something_deprecated()", 613 insert: "something_deprecated()$0",
585 source_range: 203..206, 614 kind: Function,
586 delete: 203..206, 615 lookup: "something_deprecated",
587 insert: "something_deprecated()$0", 616 detail: "fn something_deprecated()",
588 kind: Function, 617 deprecated: true,
589 lookup: "something_deprecated", 618 },
590 detail: "fn something_deprecated()", 619 CompletionItem {
591 deprecated: true, 620 label: "something_else_deprecated()",
592 }, 621 source_range: 121..124,
593 CompletionItem { 622 delete: 121..124,
594 label: "something_else_deprecated()", 623 insert: "something_else_deprecated()$0",
595 source_range: 203..206, 624 kind: Function,
596 delete: 203..206, 625 lookup: "something_else_deprecated",
597 insert: "something_else_deprecated()$0", 626 detail: "fn something_else_deprecated()",
598 kind: Function, 627 deprecated: true,
599 lookup: "something_else_deprecated", 628 },
600 detail: "fn something_else_deprecated()", 629 ]
601 deprecated: true, 630 "#]],
602 }, 631 );
603 ] 632
604 "### 633 check(
634 r#"
635struct A { #[deprecated] the_field: u32 }
636fn foo() { A { the<|> } }
637"#,
638 expect![[r#"
639 [
640 CompletionItem {
641 label: "the_field",
642 source_range: 57..60,
643 delete: 57..60,
644 insert: "the_field",
645 kind: Field,
646 detail: "u32",
647 deprecated: true,
648 },
649 ]
650 "#]],
605 ); 651 );
606 } 652 }
607 653
608 #[test] 654 #[test]
655 fn renders_docs() {
656 check(
657 r#"
658struct S {
659 /// Field docs
660 foo:
661}
662impl S {
663 /// Method docs
664 fn bar(self) { self.<|> }
665}"#,
666 expect![[r#"
667 [
668 CompletionItem {
669 label: "bar()",
670 source_range: 94..94,
671 delete: 94..94,
672 insert: "bar()$0",
673 kind: Method,
674 lookup: "bar",
675 detail: "fn bar(self)",
676 documentation: Documentation(
677 "Method docs",
678 ),
679 },
680 CompletionItem {
681 label: "foo",
682 source_range: 94..94,
683 delete: 94..94,
684 insert: "foo",
685 kind: Field,
686 detail: "{unknown}",
687 documentation: Documentation(
688 "Field docs",
689 ),
690 },
691 ]
692 "#]],
693 );
694
695 check(
696 r#"
697use self::my<|>;
698
699/// mod docs
700mod my { }
701
702/// enum docs
703enum E {
704 /// variant docs
705 V
706}
707use self::E::*;
708"#,
709 expect![[r#"
710 [
711 CompletionItem {
712 label: "E",
713 source_range: 10..12,
714 delete: 10..12,
715 insert: "E",
716 kind: Enum,
717 documentation: Documentation(
718 "enum docs",
719 ),
720 },
721 CompletionItem {
722 label: "V",
723 source_range: 10..12,
724 delete: 10..12,
725 insert: "V",
726 kind: EnumVariant,
727 detail: "()",
728 documentation: Documentation(
729 "variant docs",
730 ),
731 },
732 CompletionItem {
733 label: "my",
734 source_range: 10..12,
735 delete: 10..12,
736 insert: "my",
737 kind: Module,
738 documentation: Documentation(
739 "mod docs",
740 ),
741 },
742 ]
743 "#]],
744 )
745 }
746
747 #[test]
748 fn dont_render_attrs() {
749 check(
750 r#"
751struct S;
752impl S {
753 #[inline]
754 fn the_method(&self) { }
755}
756fn foo(s: S) { s.<|> }
757"#,
758 expect![[r#"
759 [
760 CompletionItem {
761 label: "the_method()",
762 source_range: 81..81,
763 delete: 81..81,
764 insert: "the_method()$0",
765 kind: Method,
766 lookup: "the_method",
767 detail: "fn the_method(&self)",
768 },
769 ]
770 "#]],
771 )
772 }
773
774 #[test]
609 fn inserts_parens_for_function_calls() { 775 fn inserts_parens_for_function_calls() {
610 mark::check!(inserts_parens_for_function_calls); 776 mark::check!(inserts_parens_for_function_calls);
611 assert_debug_snapshot!( 777 check_edit(
612 do_reference_completion( 778 "no_args",
613 r" 779 r#"
614 fn no_args() {} 780fn no_args() {}
615 fn main() { no_<|> } 781fn main() { no_<|> }
616 " 782"#,
617 ), 783 r#"
618 @r###" 784fn no_args() {}
619 [ 785fn main() { no_args()$0 }
620 CompletionItem { 786"#,
621 label: "main()",
622 source_range: 61..64,
623 delete: 61..64,
624 insert: "main()$0",
625 kind: Function,
626 lookup: "main",
627 detail: "fn main()",
628 },
629 CompletionItem {
630 label: "no_args()",
631 source_range: 61..64,
632 delete: 61..64,
633 insert: "no_args()$0",
634 kind: Function,
635 lookup: "no_args",
636 detail: "fn no_args()",
637 },
638 ]
639 "###
640 );
641 assert_debug_snapshot!(
642 do_reference_completion(
643 r"
644 fn with_args(x: i32, y: String) {}
645 fn main() { with_<|> }
646 "
647 ),
648 @r###"
649 [
650 CompletionItem {
651 label: "main()",
652 source_range: 80..85,
653 delete: 80..85,
654 insert: "main()$0",
655 kind: Function,
656 lookup: "main",
657 detail: "fn main()",
658 },
659 CompletionItem {
660 label: "with_args(…)",
661 source_range: 80..85,
662 delete: 80..85,
663 insert: "with_args(${1:x}, ${2:y})$0",
664 kind: Function,
665 lookup: "with_args",
666 detail: "fn with_args(x: i32, y: String)",
667 trigger_call_info: true,
668 },
669 ]
670 "###
671 ); 787 );
672 assert_debug_snapshot!( 788
673 do_reference_completion( 789 check_edit(
674 r" 790 "with_args",
675 fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String) {} 791 r#"
676 fn main() { with_<|> } 792fn with_args(x: i32, y: String) {}
677 " 793fn main() { with_<|> }
678 ), 794"#,
679 @r###" 795 r#"
680 [ 796fn with_args(x: i32, y: String) {}
681 CompletionItem { 797fn main() { with_args(${1:x}, ${2:y})$0 }
682 label: "main()", 798"#,
683 source_range: 110..115,
684 delete: 110..115,
685 insert: "main()$0",
686 kind: Function,
687 lookup: "main",
688 detail: "fn main()",
689 },
690 CompletionItem {
691 label: "with_ignored_args(…)",
692 source_range: 110..115,
693 delete: 110..115,
694 insert: "with_ignored_args(${1:foo}, ${2:bar}, ${3:ho_ge_})$0",
695 kind: Function,
696 lookup: "with_ignored_args",
697 detail: "fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String)",
698 trigger_call_info: true,
699 },
700 ]
701 "###
702 ); 799 );
703 assert_debug_snapshot!( 800
704 do_reference_completion( 801 check_edit(
705 r" 802 "foo",
706 struct S {} 803 r#"
707 impl S { 804struct S;
708 fn foo(&self) {} 805impl S {
709 } 806 fn foo(&self) {}
710 fn bar(s: &S) { 807}
711 s.f<|> 808fn bar(s: &S) { s.f<|> }
712 } 809"#,
713 " 810 r#"
714 ), 811struct S;
715 @r###" 812impl S {
716 [ 813 fn foo(&self) {}
717 CompletionItem { 814}
718 label: "foo()", 815fn bar(s: &S) { s.foo()$0 }
719 source_range: 163..164, 816"#,
720 delete: 163..164,
721 insert: "foo()$0",
722 kind: Method,
723 lookup: "foo",
724 detail: "fn foo(&self)",
725 },
726 ]
727 "###
728 ); 817 );
729 assert_debug_snapshot!( 818
730 do_reference_completion( 819 check_edit(
731 r" 820 "foo",
732 struct S {} 821 r#"
733 impl S { 822struct S {}
734 fn foo_ignored_args(&self, _a: bool, b: i32) {} 823impl S {
735 } 824 fn foo(&self, x: i32) {}
736 fn bar(s: &S) { 825}
737 s.f<|> 826fn bar(s: &S) {
738 } 827 s.f<|>
739 " 828}
740 ), 829"#,
741 @r###" 830 r#"
742 [ 831struct S {}
743 CompletionItem { 832impl S {
744 label: "foo_ignored_args(…)", 833 fn foo(&self, x: i32) {}
745 source_range: 194..195, 834}
746 delete: 194..195, 835fn bar(s: &S) {
747 insert: "foo_ignored_args(${1:a}, ${2:b})$0", 836 s.foo(${1:x})$0
748 kind: Method, 837}
749 lookup: "foo_ignored_args", 838"#,
750 detail: "fn foo_ignored_args(&self, _a: bool, b: i32)",
751 trigger_call_info: true,
752 },
753 ]
754 "###
755 ); 839 );
756 } 840 }
757 841
758 #[test] 842 #[test]
759 fn inserts_parens_for_tuple_enums() { 843 fn suppress_arg_snippets() {
760 assert_debug_snapshot!( 844 mark::check!(suppress_arg_snippets);
761 do_reference_completion( 845 check_edit_with_config(
762 r" 846 CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
763 enum Option<T> { Some(T), None } 847 "with_args",
764 use Option::*; 848 r#"
765 fn main() -> Option<i32> { 849fn with_args(x: i32, y: String) {}
766 Som<|> 850fn main() { with_<|> }
767 } 851"#,
768 " 852 r#"
769 ), 853fn with_args(x: i32, y: String) {}
770 @r###" 854fn main() { with_args($0) }
771 [ 855"#,
772 CompletionItem {
773 label: "None",
774 source_range: 144..147,
775 delete: 144..147,
776 insert: "None",
777 kind: EnumVariant,
778 detail: "()",
779 },
780 CompletionItem {
781 label: "Option",
782 source_range: 144..147,
783 delete: 144..147,
784 insert: "Option",
785 kind: Enum,
786 },
787 CompletionItem {
788 label: "Some(…)",
789 source_range: 144..147,
790 delete: 144..147,
791 insert: "Some($0)",
792 kind: EnumVariant,
793 lookup: "Some",
794 detail: "(T)",
795 trigger_call_info: true,
796 },
797 CompletionItem {
798 label: "main()",
799 source_range: 144..147,
800 delete: 144..147,
801 insert: "main()$0",
802 kind: Function,
803 lookup: "main",
804 detail: "fn main() -> Option<i32>",
805 },
806 ]
807 "###
808 );
809 assert_debug_snapshot!(
810 do_reference_completion(
811 r"
812 enum Option<T> { Some(T), None }
813 use Option::*;
814 fn main(value: Option<i32>) {
815 match value {
816 Som<|>
817 }
818 }
819 "
820 ),
821 @r###"
822 [
823 CompletionItem {
824 label: "None",
825 source_range: 185..188,
826 delete: 185..188,
827 insert: "None",
828 kind: EnumVariant,
829 detail: "()",
830 },
831 CompletionItem {
832 label: "Option",
833 source_range: 185..188,
834 delete: 185..188,
835 insert: "Option",
836 kind: Enum,
837 },
838 CompletionItem {
839 label: "Some(…)",
840 source_range: 185..188,
841 delete: 185..188,
842 insert: "Some($0)",
843 kind: EnumVariant,
844 lookup: "Some",
845 detail: "(T)",
846 trigger_call_info: true,
847 },
848 ]
849 "###
850 ); 856 );
851 } 857 }
852 858
853 #[test] 859 #[test]
854 fn no_call_parens_if_fn_ptr_needed() { 860 fn strips_underscores_from_args() {
855 assert_debug_snapshot!( 861 check_edit(
856 do_reference_completion( 862 "foo",
857 r" 863 r#"
858 fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8) {} 864fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
859 865fn main() { f<|> }
860 struct ManualVtable { 866"#,
861 method: fn(u8, u8, u8, u8, u8), 867 r#"
862 } 868fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {}
869fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 }
870"#,
871 );
872 }
863 873
864 fn main() -> ManualVtable { 874 #[test]
865 ManualVtable { 875 fn inserts_parens_for_tuple_enums() {
866 method: some<|> 876 mark::check!(inserts_parens_for_tuple_enums);
867 } 877 check_edit(
868 } 878 "Some",
869 " 879 r#"
870 ), 880enum Option<T> { Some(T), None }
871 @r###" 881use Option::*;
872 [ 882fn main() -> Option<i32> {
873 CompletionItem { 883 Som<|>
874 label: "ManualVtable", 884}
875 source_range: 295..299, 885"#,
876 delete: 295..299, 886 r#"
877 insert: "ManualVtable", 887enum Option<T> { Some(T), None }
878 kind: Struct, 888use Option::*;
879 }, 889fn main() -> Option<i32> {
880 CompletionItem { 890 Some($0)
881 label: "main", 891}
882 source_range: 295..299, 892"#,
883 delete: 295..299, 893 );
884 insert: "main", 894 check_edit(
885 kind: Function, 895 "Some",
886 detail: "fn main() -> ManualVtable", 896 r#"
887 }, 897enum Option<T> { Some(T), None }
888 CompletionItem { 898use Option::*;
889 label: "somefn", 899fn main(value: Option<i32>) {
890 source_range: 295..299, 900 match value {
891 delete: 295..299, 901 Som<|>
892 insert: "somefn", 902 }
893 kind: Function, 903}
894 detail: "fn somefn(with: u8, a: u8, lot: u8, of: u8, args: u8)", 904"#,
895 }, 905 r#"
896 ] 906enum Option<T> { Some(T), None }
897 "### 907use Option::*;
908fn main(value: Option<i32>) {
909 match value {
910 Some($0)
911 }
912}
913"#,
898 ); 914 );
899 } 915 }
900 916
901 #[test] 917 #[test]
902 fn arg_snippets_for_method_call() { 918 fn dont_duplicate_pattern_parens() {
903 assert_debug_snapshot!( 919 mark::check!(dont_duplicate_pattern_parens);
904 do_reference_completion( 920 check_edit(
905 r" 921 "Var",
906 struct S {} 922 r#"
907 impl S { 923enum E { Var(i32) }
908 fn foo(&self, x: i32) {} 924fn main() {
909 } 925 match E::Var(92) {
910 fn bar(s: &S) { 926 E::<|>(92) => (),
911 s.f<|> 927 }
912 } 928}
913 " 929"#,
914 ), 930 r#"
915 @r###" 931enum E { Var(i32) }
916 [ 932fn main() {
917 CompletionItem { 933 match E::Var(92) {
918 label: "foo(…)", 934 E::Var(92) => (),
919 source_range: 171..172, 935 }
920 delete: 171..172, 936}
921 insert: "foo(${1:x})$0", 937"#,
922 kind: Method, 938 );
923 lookup: "foo",
924 detail: "fn foo(&self, x: i32)",
925 trigger_call_info: true,
926 },
927 ]
928 "###
929 )
930 } 939 }
931 940
932 #[test] 941 #[test]
933 fn no_arg_snippets_for_method_call() { 942 fn no_call_parens_if_fn_ptr_needed() {
934 assert_debug_snapshot!( 943 mark::check!(no_call_parens_if_fn_ptr_needed);
935 do_reference_completion_with_options( 944 check_edit(
936 r" 945 "foo",
937 struct S {} 946 r#"
938 impl S { 947fn foo(foo: u8, bar: u8) {}
939 fn foo(&self, x: i32) {} 948struct ManualVtable { f: fn(u8, u8) }
940 } 949
941 fn bar(s: &S) { 950fn main() -> ManualVtable {
942 s.f<|> 951 ManualVtable { f: f<|> }
943 } 952}
944 ", 953"#,
945 CompletionConfig { 954 r#"
946 add_call_argument_snippets: false, 955fn foo(foo: u8, bar: u8) {}
947 .. Default::default() 956struct ManualVtable { f: fn(u8, u8) }
948 } 957
949 ), 958fn main() -> ManualVtable {
950 @r###" 959 ManualVtable { f: foo }
951 [ 960}
952 CompletionItem { 961"#,
953 label: "foo(…)", 962 );
954 source_range: 171..172,
955 delete: 171..172,
956 insert: "foo($0)",
957 kind: Method,
958 lookup: "foo",
959 detail: "fn foo(&self, x: i32)",
960 trigger_call_info: true,
961 },
962 ]
963 "###
964 )
965 } 963 }
966 964
967 #[test] 965 #[test]
968 fn dont_render_function_parens_in_use_item() { 966 fn no_parens_in_use_item() {
969 assert_debug_snapshot!( 967 mark::check!(no_parens_in_use_item);
970 do_reference_completion( 968 check_edit(
971 " 969 "foo",
972 //- /lib.rs 970 r#"
973 mod m { pub fn foo() {} } 971mod m { pub fn foo() {} }
974 use crate::m::f<|>; 972use crate::m::f<|>;
975 " 973"#,
976 ), 974 r#"
977 @r###" 975mod m { pub fn foo() {} }
978 [ 976use crate::m::foo;
979 CompletionItem { 977"#,
980 label: "foo",
981 source_range: 40..41,
982 delete: 40..41,
983 insert: "foo",
984 kind: Function,
985 detail: "pub fn foo()",
986 },
987 ]
988 "###
989 ); 978 );
990 } 979 }
991 980
992 #[test] 981 #[test]
993 fn dont_render_function_parens_if_already_call() { 982 fn no_parens_in_call() {
994 assert_debug_snapshot!( 983 check_edit(
995 do_reference_completion( 984 "foo",
996 " 985 r#"
997 //- /lib.rs 986fn foo(x: i32) {}
998 fn frobnicate() {} 987fn main() { f<|>(); }
999 fn main() { 988"#,
1000 frob<|>(); 989 r#"
1001 } 990fn foo(x: i32) {}
1002 " 991fn main() { foo(); }
1003 ), 992"#,
1004 @r###"
1005 [
1006 CompletionItem {
1007 label: "frobnicate",
1008 source_range: 35..39,
1009 delete: 35..39,
1010 insert: "frobnicate",
1011 kind: Function,
1012 detail: "fn frobnicate()",
1013 },
1014 CompletionItem {
1015 label: "main",
1016 source_range: 35..39,
1017 delete: 35..39,
1018 insert: "main",
1019 kind: Function,
1020 detail: "fn main()",
1021 },
1022 ]
1023 "###
1024 ); 993 );
1025 assert_debug_snapshot!( 994 check_edit(
1026 do_reference_completion( 995 "foo",
1027 " 996 r#"
1028 //- /lib.rs 997struct Foo;
1029 struct Foo {} 998impl Foo { fn foo(&self){} }
1030 impl Foo { fn new() -> Foo {} } 999fn f(foo: &Foo) { foo.f<|>(); }
1031 fn main() { 1000"#,
1032 Foo::ne<|>(); 1001 r#"
1033 } 1002struct Foo;
1034 " 1003impl Foo { fn foo(&self){} }
1035 ), 1004fn f(foo: &Foo) { foo.foo(); }
1036 @r###" 1005"#,
1037 [
1038 CompletionItem {
1039 label: "new",
1040 source_range: 67..69,
1041 delete: 67..69,
1042 insert: "new",
1043 kind: Function,
1044 detail: "fn new() -> Foo",
1045 },
1046 ]
1047 "###
1048 ); 1006 );
1049 } 1007 }
1050 1008
1051 #[test] 1009 #[test]
1052 fn inserts_angle_brackets_for_generics() { 1010 fn inserts_angle_brackets_for_generics() {
1053 mark::check!(inserts_angle_brackets_for_generics); 1011 mark::check!(inserts_angle_brackets_for_generics);
1054 assert_debug_snapshot!( 1012 check_edit(
1055 do_reference_completion( 1013 "Vec",
1056 r" 1014 r#"
1057 struct Vec<T> {} 1015struct Vec<T> {}
1058 fn foo(xs: Ve<|>) 1016fn foo(xs: Ve<|>)
1059 " 1017"#,
1060 ), 1018 r#"
1061 @r###" 1019struct Vec<T> {}
1062 [ 1020fn foo(xs: Vec<$0>)
1063 CompletionItem { 1021"#,
1064 label: "Vec<…>",
1065 source_range: 61..63,
1066 delete: 61..63,
1067 insert: "Vec<$0>",
1068 kind: Struct,
1069 lookup: "Vec",
1070 },
1071 CompletionItem {
1072 label: "foo(…)",
1073 source_range: 61..63,
1074 delete: 61..63,
1075 insert: "foo(${1:xs})$0",
1076 kind: Function,
1077 lookup: "foo",
1078 detail: "fn foo(xs: Ve)",
1079 trigger_call_info: true,
1080 },
1081 ]
1082 "###
1083 ); 1022 );
1084 assert_debug_snapshot!( 1023 check_edit(
1085 do_reference_completion( 1024 "Vec",
1086 r" 1025 r#"
1087 type Vec<T> = (T,); 1026type Vec<T> = (T,);
1088 fn foo(xs: Ve<|>) 1027fn foo(xs: Ve<|>)
1089 " 1028"#,
1090 ), 1029 r#"
1091 @r###" 1030type Vec<T> = (T,);
1092 [ 1031fn foo(xs: Vec<$0>)
1093 CompletionItem { 1032"#,
1094 label: "Vec<…>",
1095 source_range: 64..66,
1096 delete: 64..66,
1097 insert: "Vec<$0>",
1098 kind: TypeAlias,
1099 lookup: "Vec",
1100 },
1101 CompletionItem {
1102 label: "foo(…)",
1103 source_range: 64..66,
1104 delete: 64..66,
1105 insert: "foo(${1:xs})$0",
1106 kind: Function,
1107 lookup: "foo",
1108 detail: "fn foo(xs: Ve)",
1109 trigger_call_info: true,
1110 },
1111 ]
1112 "###
1113 ); 1033 );
1114 assert_debug_snapshot!( 1034 check_edit(
1115 do_reference_completion( 1035 "Vec",
1116 r" 1036 r#"
1117 struct Vec<T = i128> {} 1037struct Vec<T = i128> {}
1118 fn foo(xs: Ve<|>) 1038fn foo(xs: Ve<|>)
1119 " 1039"#,
1120 ), 1040 r#"
1121 @r###" 1041struct Vec<T = i128> {}
1122 [ 1042fn foo(xs: Vec)
1123 CompletionItem { 1043"#,
1124 label: "Vec",
1125 source_range: 68..70,
1126 delete: 68..70,
1127 insert: "Vec",
1128 kind: Struct,
1129 },
1130 CompletionItem {
1131 label: "foo(…)",
1132 source_range: 68..70,
1133 delete: 68..70,
1134 insert: "foo(${1:xs})$0",
1135 kind: Function,
1136 lookup: "foo",
1137 detail: "fn foo(xs: Ve)",
1138 trigger_call_info: true,
1139 },
1140 ]
1141 "###
1142 ); 1044 );
1143 assert_debug_snapshot!( 1045 check_edit(
1144 do_reference_completion( 1046 "Vec",
1145 r" 1047 r#"
1146 struct Vec<T> {} 1048struct Vec<T> {}
1147 fn foo(xs: Ve<|><i128>) 1049fn foo(xs: Ve<|><i128>)
1148 " 1050"#,
1149 ), 1051 r#"
1150 @r###" 1052struct Vec<T> {}
1151 [ 1053fn foo(xs: Vec<i128>)
1152 CompletionItem { 1054"#,
1153 label: "Vec",
1154 source_range: 61..63,
1155 delete: 61..63,
1156 insert: "Vec",
1157 kind: Struct,
1158 },
1159 CompletionItem {
1160 label: "foo(…)",
1161 source_range: 61..63,
1162 delete: 61..63,
1163 insert: "foo(${1:xs})$0",
1164 kind: Function,
1165 lookup: "foo",
1166 detail: "fn foo(xs: Ve<i128>)",
1167 trigger_call_info: true,
1168 },
1169 ]
1170 "###
1171 ); 1055 );
1172 } 1056 }
1173 1057
1174 #[test] 1058 #[test]
1175 fn dont_insert_macro_call_parens_unncessary() { 1059 fn dont_insert_macro_call_parens_unncessary() {
1176 mark::check!(dont_insert_macro_call_parens_unncessary); 1060 mark::check!(dont_insert_macro_call_parens_unncessary);
1177 assert_debug_snapshot!( 1061 check_edit(
1178 do_reference_completion( 1062 "frobnicate!",
1179 r" 1063 r#"
1180 //- /main.rs 1064//- /main.rs
1181 use foo::<|>; 1065use foo::<|>;
1182 1066//- /foo/lib.rs
1183 //- /foo/lib.rs 1067#[macro_export]
1184 #[macro_export] 1068macro_rules frobnicate { () => () }
1185 macro_rules frobnicate { 1069"#,
1186 () => () 1070 r#"
1187 } 1071use foo::frobnicate;
1188 " 1072"#,
1189 ),
1190 @r###"
1191 [
1192 CompletionItem {
1193 label: "frobnicate!",
1194 source_range: 9..9,
1195 delete: 9..9,
1196 insert: "frobnicate",
1197 kind: Macro,
1198 detail: "#[macro_export]\nmacro_rules! frobnicate",
1199 },
1200 ]
1201 "###
1202 ); 1073 );
1203 1074
1204 assert_debug_snapshot!( 1075 check_edit(
1205 do_reference_completion( 1076 "frobnicate!",
1206 r" 1077 r#"
1207 //- /main.rs 1078macro_rules frobnicate { () => () }
1208 macro_rules frobnicate { 1079fn main() { frob<|>!(); }
1209 () => () 1080"#,
1210 } 1081 r#"
1211 fn main() { 1082macro_rules frobnicate { () => () }
1212 frob<|>!(); 1083fn main() { frobnicate!(); }
1213 } 1084"#,
1214 "
1215 ),
1216 @r###"
1217 [
1218 CompletionItem {
1219 label: "frobnicate!",
1220 source_range: 56..60,
1221 delete: 56..60,
1222 insert: "frobnicate",
1223 kind: Macro,
1224 detail: "macro_rules! frobnicate",
1225 },
1226 CompletionItem {
1227 label: "main()",
1228 source_range: 56..60,
1229 delete: 56..60,
1230 insert: "main()$0",
1231 kind: Function,
1232 lookup: "main",
1233 detail: "fn main()",
1234 },
1235 ]
1236 "###
1237 ); 1085 );
1238 } 1086 }
1239 1087
1240 #[test] 1088 #[test]
1241 fn test_struct_field_completion_in_func_call() { 1089 fn active_param_score() {
1242 mark::check!(test_struct_field_completion_in_func_call); 1090 mark::check!(active_param_type_match);
1243 assert_debug_snapshot!( 1091 check_scores(
1244 do_reference_completion( 1092 r#"
1245 r" 1093struct S { foo: i64, bar: u32, baz: u32 }
1246 struct A { another_field: i64, the_field: u32, my_string: String } 1094fn test(bar: u32) { }
1247 fn test(my_param: u32) -> u32 { my_param } 1095fn foo(s: S) { test(s.<|>) }
1248 fn foo(a: A) { 1096"#,
1249 test(a.<|>) 1097 expect![[r#"
1250 } 1098 fd bar [type+name]
1251 ", 1099 fd baz [type]
1252 ), 1100 fd foo []
1253 @r###" 1101 "#]],
1254 [
1255 CompletionItem {
1256 label: "another_field",
1257 source_range: 201..201,
1258 delete: 201..201,
1259 insert: "another_field",
1260 kind: Field,
1261 detail: "i64",
1262 },
1263 CompletionItem {
1264 label: "my_string",
1265 source_range: 201..201,
1266 delete: 201..201,
1267 insert: "my_string",
1268 kind: Field,
1269 detail: "{unknown}",
1270 },
1271 CompletionItem {
1272 label: "the_field",
1273 source_range: 201..201,
1274 delete: 201..201,
1275 insert: "the_field",
1276 kind: Field,
1277 detail: "u32",
1278 score: TypeMatch,
1279 },
1280 ]
1281 "###
1282 ); 1102 );
1283 } 1103 }
1284 1104
1285 #[test] 1105 #[test]
1286 fn test_struct_field_completion_in_func_call_with_type_and_name() { 1106 fn record_field_scores() {
1287 assert_debug_snapshot!( 1107 mark::check!(record_field_type_match);
1288 do_reference_completion( 1108 check_scores(
1289 r" 1109 r#"
1290 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1110struct A { foo: i64, bar: u32, baz: u32 }
1291 fn test(the_field: u32) -> u32 { the_field } 1111struct B { x: (), y: f32, bar: u32 }
1292 fn foo(a: A) { 1112fn foo(a: A) { B { bar: a.<|> }; }
1293 test(a.<|>) 1113"#,
1294 } 1114 expect![[r#"
1295 ", 1115 fd bar [type+name]
1296 ), 1116 fd baz [type]
1297 @r###" 1117 fd foo []
1298 [ 1118 "#]],
1299 CompletionItem { 1119 )
1300 label: "another_field",
1301 source_range: 208..208,
1302 delete: 208..208,
1303 insert: "another_field",
1304 kind: Field,
1305 detail: "i64",
1306 },
1307 CompletionItem {
1308 label: "another_good_type",
1309 source_range: 208..208,
1310 delete: 208..208,
1311 insert: "another_good_type",
1312 kind: Field,
1313 detail: "u32",
1314 score: TypeMatch,
1315 },
1316 CompletionItem {
1317 label: "the_field",
1318 source_range: 208..208,
1319 delete: 208..208,
1320 insert: "the_field",
1321 kind: Field,
1322 detail: "u32",
1323 score: TypeAndNameMatch,
1324 },
1325 ]
1326 "###
1327 );
1328 } 1120 }
1329 1121
1330 #[test] 1122 #[test]
1331 fn test_struct_field_completion_in_record_lit() { 1123 fn record_field_and_call_scores() {
1332 mark::check!(test_struct_field_completion_in_record_lit); 1124 check_scores(
1333 assert_debug_snapshot!( 1125 r#"
1334 do_reference_completion( 1126struct A { foo: i64, bar: u32, baz: u32 }
1335 r" 1127struct B { x: (), y: f32, bar: u32 }
1336 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1128fn f(foo: i64) { }
1337 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 1129fn foo(a: A) { B { bar: f(a.<|>) }; }
1338 fn foo(a: A) { 1130"#,
1339 let b = B { 1131 expect![[r#"
1340 the_field: a.<|> 1132 fd foo [type+name]
1341 }; 1133 fd bar []
1342 } 1134 fd baz []
1343 ", 1135 "#]],
1344 ),
1345 @r###"
1346 [
1347 CompletionItem {
1348 label: "another_field",
1349 source_range: 270..270,
1350 delete: 270..270,
1351 insert: "another_field",
1352 kind: Field,
1353 detail: "i64",
1354 },
1355 CompletionItem {
1356 label: "another_good_type",
1357 source_range: 270..270,
1358 delete: 270..270,
1359 insert: "another_good_type",
1360 kind: Field,
1361 detail: "u32",
1362 score: TypeMatch,
1363 },
1364 CompletionItem {
1365 label: "the_field",
1366 source_range: 270..270,
1367 delete: 270..270,
1368 insert: "the_field",
1369 kind: Field,
1370 detail: "u32",
1371 score: TypeAndNameMatch,
1372 },
1373 ]
1374 "###
1375 ); 1136 );
1376 } 1137 check_scores(
1377 1138 r#"
1378 #[test] 1139struct A { foo: i64, bar: u32, baz: u32 }
1379 fn test_struct_field_completion_in_record_lit_and_fn_call() { 1140struct B { x: (), y: f32, bar: u32 }
1380 assert_debug_snapshot!( 1141fn f(foo: i64) { }
1381 do_reference_completion( 1142fn foo(a: A) { f(B { bar: a.<|> }); }
1382 r" 1143"#,
1383 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1144 expect![[r#"
1384 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 1145 fd bar [type+name]
1385 fn test(the_field: i64) -> i64 { the_field } 1146 fd baz [type]
1386 fn foo(a: A) { 1147 fd foo []
1387 let b = B { 1148 "#]],
1388 the_field: test(a.<|>)
1389 };
1390 }
1391 ",
1392 ),
1393 @r###"
1394 [
1395 CompletionItem {
1396 label: "another_field",
1397 source_range: 336..336,
1398 delete: 336..336,
1399 insert: "another_field",
1400 kind: Field,
1401 detail: "i64",
1402 score: TypeMatch,
1403 },
1404 CompletionItem {
1405 label: "another_good_type",
1406 source_range: 336..336,
1407 delete: 336..336,
1408 insert: "another_good_type",
1409 kind: Field,
1410 detail: "u32",
1411 },
1412 CompletionItem {
1413 label: "the_field",
1414 source_range: 336..336,
1415 delete: 336..336,
1416 insert: "the_field",
1417 kind: Field,
1418 detail: "u32",
1419 },
1420 ]
1421 "###
1422 ); 1149 );
1423 } 1150 }
1424 1151
1425 #[test] 1152 #[test]
1426 fn test_struct_field_completion_in_fn_call_and_record_lit() { 1153 fn prioritize_exact_ref_match() {
1427 assert_debug_snapshot!( 1154 check_scores(
1428 do_reference_completion( 1155 r#"
1429 r" 1156struct WorldSnapshot { _f: () };
1430 struct A { another_field: i64, another_good_type: u32, the_field: u32 } 1157fn go(world: &WorldSnapshot) { go(w<|>) }
1431 struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 } 1158"#,
1432 fn test(the_field: i64) -> i64 { the_field } 1159 expect![[r#"
1433 fn foo(a: A) { 1160 bn world [type+name]
1434 test(B { 1161 st WorldSnapshot []
1435 the_field: a.<|> 1162 fn go(…) []
1436 }); 1163 "#]],
1437 }
1438 ",
1439 ),
1440 @r###"
1441 [
1442 CompletionItem {
1443 label: "another_field",
1444 source_range: 328..328,
1445 delete: 328..328,
1446 insert: "another_field",
1447 kind: Field,
1448 detail: "i64",
1449 },
1450 CompletionItem {
1451 label: "another_good_type",
1452 source_range: 328..328,
1453 delete: 328..328,
1454 insert: "another_good_type",
1455 kind: Field,
1456 detail: "u32",
1457 score: TypeMatch,
1458 },
1459 CompletionItem {
1460 label: "the_field",
1461 source_range: 328..328,
1462 delete: 328..328,
1463 insert: "the_field",
1464 kind: Field,
1465 detail: "u32",
1466 score: TypeAndNameMatch,
1467 },
1468 ]
1469 "###
1470 ); 1164 );
1471 } 1165 }
1472 1166
1473 #[test] 1167 #[test]
1474 fn prioritize_exact_ref_match() { 1168 fn guesses_macro_braces() {
1475 assert_debug_snapshot!( 1169 check_edit(
1476 do_reference_completion( 1170 "vec!",
1477 r" 1171 r#"
1478 struct WorldSnapshot { _f: () }; 1172/// Creates a [`Vec`] containing the arguments.
1479 fn go(world: &WorldSnapshot) { 1173///
1480 go(w<|>) 1174/// ```
1481 } 1175/// let v = vec![1, 2, 3];
1482 ", 1176/// assert_eq!(v[0], 1);
1483 ), 1177/// assert_eq!(v[1], 2);
1484 @r###" 1178/// assert_eq!(v[2], 3);
1485 [ 1179/// ```
1486 CompletionItem { 1180macro_rules! vec { () => {} }
1487 label: "WorldSnapshot", 1181
1488 source_range: 132..133, 1182fn fn main() { v<|> }
1489 delete: 132..133, 1183"#,
1490 insert: "WorldSnapshot", 1184 r#"
1491 kind: Struct, 1185/// Creates a [`Vec`] containing the arguments.
1492 }, 1186///
1493 CompletionItem { 1187/// ```
1494 label: "go(…)", 1188/// let v = vec![1, 2, 3];
1495 source_range: 132..133, 1189/// assert_eq!(v[0], 1);
1496 delete: 132..133, 1190/// assert_eq!(v[1], 2);
1497 insert: "go(${1:world})$0", 1191/// assert_eq!(v[2], 3);
1498 kind: Function, 1192/// ```
1499 lookup: "go", 1193macro_rules! vec { () => {} }
1500 detail: "fn go(world: &WorldSnapshot)", 1194
1501 trigger_call_info: true, 1195fn fn main() { vec![$0] }
1502 }, 1196"#,
1503 CompletionItem {
1504 label: "world",
1505 source_range: 132..133,
1506 delete: 132..133,
1507 insert: "world",
1508 kind: Binding,
1509 detail: "&WorldSnapshot",
1510 score: TypeAndNameMatch,
1511 },
1512 ]
1513 "###
1514 ); 1197 );
1198
1199 check_edit(
1200 "foo!",
1201 r#"
1202/// Foo
1203///
1204/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1205/// call as `let _=foo! { hello world };`
1206macro_rules! foo { () => {} }
1207fn main() { <|> }
1208"#,
1209 r#"
1210/// Foo
1211///
1212/// Don't call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`,
1213/// call as `let _=foo! { hello world };`
1214macro_rules! foo { () => {} }
1215fn main() { foo! {$0} }
1216"#,
1217 )
1515 } 1218 }
1516} 1219}
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index bf22452a2..919177745 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -1,29 +1,114 @@
1//! Runs completion for testing purposes. 1//! Runs completion for testing purposes.
2 2
3use hir::Semantics;
4use itertools::Itertools;
5use ra_syntax::{AstNode, NodeOrToken, SyntaxElement};
6use stdx::{format_to, trim_indent};
7use test_utils::assert_eq_text;
8
3use crate::{ 9use crate::{
4 completion::{completion_item::CompletionKind, CompletionConfig}, 10 completion::{completion_item::CompletionKind, CompletionConfig},
5 mock_analysis::{analysis_and_position, single_file_with_position}, 11 mock_analysis::analysis_and_position,
6 CompletionItem, 12 CompletionItem,
7}; 13};
8 14
9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 15pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
10 do_completion_with_options(code, kind, &CompletionConfig::default()) 16 do_completion_with_config(CompletionConfig::default(), code, kind)
11} 17}
12 18
13pub(crate) fn do_completion_with_options( 19pub(crate) fn do_completion_with_config(
20 config: CompletionConfig,
14 code: &str, 21 code: &str,
15 kind: CompletionKind, 22 kind: CompletionKind,
16 options: &CompletionConfig,
17) -> Vec<CompletionItem> { 23) -> Vec<CompletionItem> {
18 let (analysis, position) = if code.contains("//-") { 24 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
19 analysis_and_position(code) 25 .into_iter()
20 } else { 26 .filter(|c| c.completion_kind == kind)
21 single_file_with_position(code) 27 .collect();
22 }; 28 kind_completions.sort_by(|l, r| l.label().cmp(r.label()));
23 let completions = analysis.completions(options, position).unwrap().unwrap(); 29 kind_completions
24 let completion_items: Vec<CompletionItem> = completions.into(); 30}
25 let mut kind_completions: Vec<CompletionItem> = 31
26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); 32pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
33 completion_list_with_config(CompletionConfig::default(), code, kind)
34}
35
36pub(crate) fn completion_list_with_config(
37 config: CompletionConfig,
38 code: &str,
39 kind: CompletionKind,
40) -> String {
41 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
42 .into_iter()
43 .filter(|c| c.completion_kind == kind)
44 .collect();
27 kind_completions.sort_by_key(|c| c.label().to_owned()); 45 kind_completions.sort_by_key(|c| c.label().to_owned());
46 let label_width = kind_completions
47 .iter()
48 .map(|it| monospace_width(it.label()))
49 .max()
50 .unwrap_or_default()
51 .min(16);
28 kind_completions 52 kind_completions
53 .into_iter()
54 .map(|it| {
55 let tag = it.kind().unwrap().tag();
56 let var_name = format!("{} {}", tag, it.label());
57 let mut buf = var_name;
58 if let Some(detail) = it.detail() {
59 let width = label_width.saturating_sub(monospace_width(it.label()));
60 format_to!(buf, "{:width$} {}", "", detail, width = width);
61 }
62 format_to!(buf, "\n");
63 buf
64 })
65 .collect()
66}
67
68fn monospace_width(s: &str) -> usize {
69 s.chars().count()
70}
71
72pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
73 check_edit_with_config(CompletionConfig::default(), what, ra_fixture_before, ra_fixture_after)
74}
75
76pub(crate) fn check_edit_with_config(
77 config: CompletionConfig,
78 what: &str,
79 ra_fixture_before: &str,
80 ra_fixture_after: &str,
81) {
82 let ra_fixture_after = trim_indent(ra_fixture_after);
83 let (analysis, position) = analysis_and_position(ra_fixture_before);
84 let completions: Vec<CompletionItem> =
85 analysis.completions(&config, position).unwrap().unwrap().into();
86 let (completion,) = completions
87 .iter()
88 .filter(|it| it.lookup() == what)
89 .collect_tuple()
90 .unwrap_or_else(|| panic!("can't find {:?} completion in {:#?}", what, completions));
91 let mut actual = analysis.file_text(position.file_id).unwrap().to_string();
92 completion.text_edit().apply(&mut actual);
93 assert_eq_text!(&ra_fixture_after, &actual)
94}
95
96pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) {
97 let (analysis, pos) = analysis_and_position(code);
98 analysis
99 .with_db(|db| {
100 let sema = Semantics::new(db);
101 let original_file = sema.parse(pos.file_id);
102 let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap();
103 assert!(check(NodeOrToken::Token(token)));
104 })
105 .unwrap();
106}
107
108pub(crate) fn get_all_completion_items(
109 config: CompletionConfig,
110 code: &str,
111) -> Vec<CompletionItem> {
112 let (analysis, position) = analysis_and_position(code);
113 analysis.completions(&config, position).unwrap().unwrap().into()
29} 114}
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 15dc50cf1..fe75f4b2c 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -8,20 +8,20 @@ use std::cell::RefCell;
8 8
9use hir::{ 9use hir::{
10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}, 10 diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink},
11 Semantics, 11 HasSource, HirDisplay, Semantics, VariantDef,
12}; 12};
13use itertools::Itertools; 13use itertools::Itertools;
14use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; 14use ra_db::SourceDatabase;
15use ra_ide_db::RootDatabase; 15use ra_ide_db::RootDatabase;
16use ra_prof::profile; 16use ra_prof::profile;
17use ra_syntax::{ 17use ra_syntax::{
18 algo, 18 algo,
19 ast::{self, make, AstNode}, 19 ast::{self, edit::IndentLevel, make, AstNode},
20 SyntaxNode, TextRange, T, 20 SyntaxNode, TextRange, T,
21}; 21};
22use ra_text_edit::{TextEdit, TextEditBuilder}; 22use ra_text_edit::{TextEdit, TextEditBuilder};
23 23
24use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceChange, SourceFileEdit}; 24use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceFileEdit};
25 25
26#[derive(Debug, Copy, Clone)] 26#[derive(Debug, Copy, Clone)]
27pub enum Severity { 27pub enum Severity {
@@ -35,7 +35,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
35 let parse = db.parse(file_id); 35 let parse = db.parse(file_id);
36 let mut res = Vec::new(); 36 let mut res = Vec::new();
37 37
38 res.extend(parse.errors().iter().map(|err| Diagnostic { 38 // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
39 res.extend(parse.errors().iter().take(128).map(|err| Diagnostic {
39 range: err.range(), 40 range: err.range(),
40 message: format!("Syntax Error: {}", err), 41 message: format!("Syntax Error: {}", err),
41 severity: Severity::Error, 42 severity: Severity::Error,
@@ -57,14 +58,10 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
57 }) 58 })
58 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 59 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
59 let original_file = d.source().file_id.original_file(db); 60 let original_file = d.source().file_id.original_file(db);
60 let source_root = db.file_source_root(original_file); 61 let fix = Fix::new(
61 let path = db 62 "Create module",
62 .file_relative_path(original_file) 63 FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }.into(),
63 .parent() 64 );
64 .unwrap_or_else(|| RelativePath::new(""))
65 .join(&d.candidate);
66 let fix =
67 Fix::new("Create module", FileSystemEdit::CreateFile { source_root, path }.into());
68 res.borrow_mut().push(Diagnostic { 65 res.borrow_mut().push(Diagnostic {
69 range: sema.diagnostics_range(d).range, 66 range: sema.diagnostics_range(d).range,
70 message: d.message(), 67 message: d.message(),
@@ -103,19 +100,11 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
103 fix, 100 fix,
104 }) 101 })
105 }) 102 })
106 .on::<hir::diagnostics::MissingMatchArms, _>(|d| {
107 res.borrow_mut().push(Diagnostic {
108 range: sema.diagnostics_range(d).range,
109 message: d.message(),
110 severity: Severity::Error,
111 fix: None,
112 })
113 })
114 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { 103 .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
115 let node = d.ast(db); 104 let node = d.ast(db);
116 let replacement = format!("Ok({})", node.syntax()); 105 let replacement = format!("Ok({})", node.syntax());
117 let edit = TextEdit::replace(node.syntax().text_range(), replacement); 106 let edit = TextEdit::replace(node.syntax().text_range(), replacement);
118 let source_change = SourceChange::source_file_edit_from(file_id, edit); 107 let source_change = SourceFileEdit { file_id, edit }.into();
119 let fix = Fix::new("Wrap with ok", source_change); 108 let fix = Fix::new("Wrap with ok", source_change);
120 res.borrow_mut().push(Diagnostic { 109 res.borrow_mut().push(Diagnostic {
121 range: sema.diagnostics_range(d).range, 110 range: sema.diagnostics_range(d).range,
@@ -123,7 +112,16 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
123 severity: Severity::Error, 112 severity: Severity::Error,
124 fix: Some(fix), 113 fix: Some(fix),
125 }) 114 })
115 })
116 .on::<hir::diagnostics::NoSuchField, _>(|d| {
117 res.borrow_mut().push(Diagnostic {
118 range: sema.diagnostics_range(d).range,
119 message: d.message(),
120 severity: Severity::Error,
121 fix: missing_struct_field_fix(&sema, file_id, d),
122 })
126 }); 123 });
124
127 if let Some(m) = sema.to_module_def(file_id) { 125 if let Some(m) = sema.to_module_def(file_id) {
128 m.diagnostics(db, &mut sink); 126 m.diagnostics(db, &mut sink);
129 }; 127 };
@@ -131,6 +129,80 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
131 res.into_inner() 129 res.into_inner()
132} 130}
133 131
132fn missing_struct_field_fix(
133 sema: &Semantics<RootDatabase>,
134 usage_file_id: FileId,
135 d: &hir::diagnostics::NoSuchField,
136) -> Option<Fix> {
137 let record_expr = sema.ast(d);
138
139 let record_lit = ast::RecordLit::cast(record_expr.syntax().parent()?.parent()?)?;
140 let def_id = sema.resolve_variant(record_lit)?;
141 let module;
142 let def_file_id;
143 let record_fields = match VariantDef::from(def_id) {
144 VariantDef::Struct(s) => {
145 module = s.module(sema.db);
146 let source = s.source(sema.db);
147 def_file_id = source.file_id;
148 let fields = source.value.field_def_list()?;
149 record_field_def_list(fields)?
150 }
151 VariantDef::Union(u) => {
152 module = u.module(sema.db);
153 let source = u.source(sema.db);
154 def_file_id = source.file_id;
155 source.value.record_field_def_list()?
156 }
157 VariantDef::EnumVariant(e) => {
158 module = e.module(sema.db);
159 let source = e.source(sema.db);
160 def_file_id = source.file_id;
161 let fields = source.value.field_def_list()?;
162 record_field_def_list(fields)?
163 }
164 };
165 let def_file_id = def_file_id.original_file(sema.db);
166
167 let new_field_type = sema.type_of_expr(&record_expr.expr()?)?;
168 if new_field_type.is_unknown() {
169 return None;
170 }
171 let new_field = make::record_field_def(
172 record_expr.field_name()?,
173 make::type_ref(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
174 );
175
176 let last_field = record_fields.fields().last()?;
177 let last_field_syntax = last_field.syntax();
178 let indent = IndentLevel::from_node(last_field_syntax);
179
180 let mut new_field = new_field.to_string();
181 if usage_file_id != def_file_id {
182 new_field = format!("pub(crate) {}", new_field);
183 }
184 new_field = format!("\n{}{}", indent, new_field);
185
186 let needs_comma = !last_field_syntax.to_string().ends_with(",");
187 if needs_comma {
188 new_field = format!(",{}", new_field);
189 }
190
191 let source_change = SourceFileEdit {
192 file_id: def_file_id,
193 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field),
194 };
195 let fix = Fix::new("Create field", source_change.into());
196 return Some(fix);
197
198 fn record_field_def_list(field_def_list: ast::FieldDefList) -> Option<ast::RecordFieldDefList> {
199 match field_def_list {
200 ast::FieldDefList::RecordFieldDefList(it) => Some(it),
201 ast::FieldDefList::TupleFieldDefList(_) => None,
202 }
203 }
204}
205
134fn check_unnecessary_braces_in_use_statement( 206fn check_unnecessary_braces_in_use_statement(
135 acc: &mut Vec<Diagnostic>, 207 acc: &mut Vec<Diagnostic>,
136 file_id: FileId, 208 file_id: FileId,
@@ -187,7 +259,8 @@ fn check_struct_shorthand_initialization(
187 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) { 259 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) {
188 let field_name = name_ref.syntax().text().to_string(); 260 let field_name = name_ref.syntax().text().to_string();
189 let field_expr = expr.syntax().text().to_string(); 261 let field_expr = expr.syntax().text().to_string();
190 if field_name == field_expr { 262 let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
263 if field_name == field_expr && !field_name_is_tup_index {
191 let mut edit_builder = TextEditBuilder::default(); 264 let mut edit_builder = TextEditBuilder::default();
192 edit_builder.delete(record_field.syntax().text_range()); 265 edit_builder.delete(record_field.syntax().text_range());
193 edit_builder.insert(record_field.syntax().text_range().start(), field_name); 266 edit_builder.insert(record_field.syntax().text_range().start(), field_name);
@@ -210,51 +283,21 @@ fn check_struct_shorthand_initialization(
210 283
211#[cfg(test)] 284#[cfg(test)]
212mod tests { 285mod tests {
213 use insta::assert_debug_snapshot; 286 use stdx::trim_indent;
214 use ra_syntax::SourceFile;
215 use stdx::SepBy;
216 use test_utils::assert_eq_text; 287 use test_utils::assert_eq_text;
217 288
218 use crate::mock_analysis::{analysis_and_position, single_file}; 289 use crate::mock_analysis::{analysis_and_position, single_file, MockAnalysis};
219 290 use expect::{expect, Expect};
220 use super::*;
221
222 type DiagnosticChecker = fn(&mut Vec<Diagnostic>, FileId, &SyntaxNode) -> Option<()>;
223
224 fn check_not_applicable(code: &str, func: DiagnosticChecker) {
225 let parse = SourceFile::parse(code);
226 let mut diagnostics = Vec::new();
227 for node in parse.tree().syntax().descendants() {
228 func(&mut diagnostics, FileId(0), &node);
229 }
230 assert!(diagnostics.is_empty());
231 }
232
233 fn check_apply(before: &str, after: &str, func: DiagnosticChecker) {
234 let parse = SourceFile::parse(before);
235 let mut diagnostics = Vec::new();
236 for node in parse.tree().syntax().descendants() {
237 func(&mut diagnostics, FileId(0), &node);
238 }
239 let diagnostic =
240 diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
241 let mut fix = diagnostic.fix.unwrap();
242 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
243 let actual = {
244 let mut actual = before.to_string();
245 edit.apply(&mut actual);
246 actual
247 };
248 assert_eq_text!(after, &actual);
249 }
250 291
251 /// Takes a multi-file input fixture with annotated cursor positions, 292 /// Takes a multi-file input fixture with annotated cursor positions,
252 /// and checks that: 293 /// and checks that:
253 /// * a diagnostic is produced 294 /// * a diagnostic is produced
254 /// * this diagnostic touches the input cursor position 295 /// * this diagnostic touches the input cursor position
255 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied 296 /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
256 fn check_apply_diagnostic_fix_from_position(fixture: &str, after: &str) { 297 fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
257 let (analysis, file_position) = analysis_and_position(fixture); 298 let after = trim_indent(ra_fixture_after);
299
300 let (analysis, file_position) = analysis_and_position(ra_fixture_before);
258 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); 301 let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap();
259 let mut fix = diagnostic.fix.unwrap(); 302 let mut fix = diagnostic.fix.unwrap();
260 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 303 let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
@@ -265,21 +308,6 @@ mod tests {
265 actual 308 actual
266 }; 309 };
267 310
268 // Strip indent and empty lines from `after`, to match the behaviour of
269 // `parse_fixture` called from `analysis_and_position`.
270 let margin = fixture
271 .lines()
272 .filter(|it| it.trim_start().starts_with("//-"))
273 .map(|it| it.len() - it.trim_start().len())
274 .next()
275 .expect("empty fixture");
276 let after = after
277 .lines()
278 .filter_map(|line| if line.len() > margin { Some(&line[margin..]) } else { None })
279 .sep_by("\n")
280 .suffix("\n")
281 .to_string();
282
283 assert_eq_text!(&after, &actual); 311 assert_eq_text!(&after, &actual);
284 assert!( 312 assert!(
285 diagnostic.range.start() <= file_position.offset 313 diagnostic.range.start() <= file_position.offset
@@ -290,508 +318,470 @@ mod tests {
290 ); 318 );
291 } 319 }
292 320
293 fn check_apply_diagnostic_fix(before: &str, after: &str) { 321 /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker
294 let (analysis, file_id) = single_file(before); 322 /// which has a fix that can apply to other files.
295 let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); 323 fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) {
324 let ra_fixture_after = &trim_indent(ra_fixture_after);
325 let (analysis, file_pos) = analysis_and_position(ra_fixture_before);
326 let current_file_id = file_pos.file_id;
327 let diagnostic = analysis.diagnostics(current_file_id).unwrap().pop().unwrap();
296 let mut fix = diagnostic.fix.unwrap(); 328 let mut fix = diagnostic.fix.unwrap();
297 let edit = fix.source_change.source_file_edits.pop().unwrap().edit; 329 let edit = fix.source_change.source_file_edits.pop().unwrap();
330 let changed_file_id = edit.file_id;
331 let before = analysis.file_text(changed_file_id).unwrap();
298 let actual = { 332 let actual = {
299 let mut actual = before.to_string(); 333 let mut actual = before.to_string();
300 edit.apply(&mut actual); 334 edit.edit.apply(&mut actual);
301 actual 335 actual
302 }; 336 };
303 assert_eq_text!(after, &actual); 337 assert_eq_text!(ra_fixture_after, &actual);
304 } 338 }
305 339
306 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 340 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
307 /// apply to the file containing the cursor. 341 /// apply to the file containing the cursor.
308 fn check_no_diagnostic_for_target_file(fixture: &str) { 342 fn check_no_diagnostics(ra_fixture: &str) {
309 let (analysis, file_position) = analysis_and_position(fixture); 343 let mock = MockAnalysis::with_files(ra_fixture);
310 let diagnostics = analysis.diagnostics(file_position.file_id).unwrap(); 344 let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>();
311 assert_eq!(diagnostics.len(), 0); 345 let analysis = mock.analysis();
346 let diagnostics = files
347 .into_iter()
348 .flat_map(|file_id| analysis.diagnostics(file_id).unwrap())
349 .collect::<Vec<_>>();
350 assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
312 } 351 }
313 352
314 fn check_no_diagnostic(content: &str) { 353 fn check_expect(ra_fixture: &str, expect: Expect) {
315 let (analysis, file_id) = single_file(content); 354 let (analysis, file_id) = single_file(ra_fixture);
316 let diagnostics = analysis.diagnostics(file_id).unwrap(); 355 let diagnostics = analysis.diagnostics(file_id).unwrap();
317 assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one"); 356 expect.assert_debug_eq(&diagnostics)
318 } 357 }
319 358
320 #[test] 359 #[test]
321 fn test_wrap_return_type() { 360 fn test_wrap_return_type() {
322 let before = r#" 361 check_fix(
323 //- /main.rs 362 r#"
324 use std::{string::String, result::Result::{self, Ok, Err}}; 363//- /main.rs
364use core::result::Result::{self, Ok, Err};
325 365
326 fn div(x: i32, y: i32) -> Result<i32, String> { 366fn div(x: i32, y: i32) -> Result<i32, ()> {
327 if y == 0 { 367 if y == 0 {
328 return Err("div by zero".into()); 368 return Err(());
329 } 369 }
330 x / y<|> 370 x / y<|>
331 } 371}
372//- /core/lib.rs
373pub mod result {
374 pub enum Result<T, E> { Ok(T), Err(E) }
375}
376"#,
377 r#"
378use core::result::Result::{self, Ok, Err};
332 379
333 //- /std/lib.rs 380fn div(x: i32, y: i32) -> Result<i32, ()> {
334 pub mod string { 381 if y == 0 {
335 pub struct String { } 382 return Err(());
336 } 383 }
337 pub mod result { 384 Ok(x / y)
338 pub enum Result<T, E> { Ok(T), Err(E) } 385}
339 } 386"#,
340 "#; 387 );
341 let after = r#"
342 use std::{string::String, result::Result::{self, Ok, Err}};
343
344 fn div(x: i32, y: i32) -> Result<i32, String> {
345 if y == 0 {
346 return Err("div by zero".into());
347 }
348 Ok(x / y)
349 }
350 "#;
351 check_apply_diagnostic_fix_from_position(before, after);
352 } 388 }
353 389
354 #[test] 390 #[test]
355 fn test_wrap_return_type_handles_generic_functions() { 391 fn test_wrap_return_type_handles_generic_functions() {
356 let before = r#" 392 check_fix(
357 //- /main.rs 393 r#"
358 use std::result::Result::{self, Ok, Err}; 394//- /main.rs
395use core::result::Result::{self, Ok, Err};
359 396
360 fn div<T>(x: T) -> Result<T, i32> { 397fn div<T>(x: T) -> Result<T, i32> {
361 if x == 0 { 398 if x == 0 {
362 return Err(7); 399 return Err(7);
363 } 400 }
364 <|>x 401 <|>x
365 } 402}
403//- /core/lib.rs
404pub mod result {
405 pub enum Result<T, E> { Ok(T), Err(E) }
406}
407"#,
408 r#"
409use core::result::Result::{self, Ok, Err};
366 410
367 //- /std/lib.rs 411fn div<T>(x: T) -> Result<T, i32> {
368 pub mod result { 412 if x == 0 {
369 pub enum Result<T, E> { Ok(T), Err(E) } 413 return Err(7);
370 } 414 }
371 "#; 415 Ok(x)
372 let after = r#" 416}
373 use std::result::Result::{self, Ok, Err}; 417"#,
374 418 );
375 fn div<T>(x: T) -> Result<T, i32> {
376 if x == 0 {
377 return Err(7);
378 }
379 Ok(x)
380 }
381 "#;
382 check_apply_diagnostic_fix_from_position(before, after);
383 } 419 }
384 420
385 #[test] 421 #[test]
386 fn test_wrap_return_type_handles_type_aliases() { 422 fn test_wrap_return_type_handles_type_aliases() {
387 let before = r#" 423 check_fix(
388 //- /main.rs 424 r#"
389 use std::{string::String, result::Result::{self, Ok, Err}}; 425//- /main.rs
426use core::result::Result::{self, Ok, Err};
390 427
391 type MyResult<T> = Result<T, String>; 428type MyResult<T> = Result<T, ()>;
392 429
393 fn div(x: i32, y: i32) -> MyResult<i32> { 430fn div(x: i32, y: i32) -> MyResult<i32> {
394 if y == 0 { 431 if y == 0 {
395 return Err("div by zero".into()); 432 return Err(());
396 } 433 }
397 x <|>/ y 434 x <|>/ y
398 } 435}
436//- /core/lib.rs
437pub mod result {
438 pub enum Result<T, E> { Ok(T), Err(E) }
439}
440"#,
441 r#"
442use core::result::Result::{self, Ok, Err};
399 443
400 //- /std/lib.rs 444type MyResult<T> = Result<T, ()>;
401 pub mod string { 445
402 pub struct String { } 446fn div(x: i32, y: i32) -> MyResult<i32> {
403 } 447 if y == 0 {
404 pub mod result { 448 return Err(());
405 pub enum Result<T, E> { Ok(T), Err(E) } 449 }
406 } 450 Ok(x / y)
407 "#; 451}
408 let after = r#" 452"#,
409 use std::{string::String, result::Result::{self, Ok, Err}}; 453 );
410
411 type MyResult<T> = Result<T, String>;
412 fn div(x: i32, y: i32) -> MyResult<i32> {
413 if y == 0 {
414 return Err("div by zero".into());
415 }
416 Ok(x / y)
417 }
418 "#;
419 check_apply_diagnostic_fix_from_position(before, after);
420 } 454 }
421 455
422 #[test] 456 #[test]
423 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 457 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
424 let content = r#" 458 check_no_diagnostics(
425 //- /main.rs 459 r#"
426 use std::{string::String, result::Result::{self, Ok, Err}}; 460//- /main.rs
461use core::result::Result::{self, Ok, Err};
427 462
428 fn foo() -> Result<String, i32> { 463fn foo() -> Result<(), i32> { 0 }
429 0<|>
430 }
431 464
432 //- /std/lib.rs 465//- /core/lib.rs
433 pub mod string { 466pub mod result {
434 pub struct String { } 467 pub enum Result<T, E> { Ok(T), Err(E) }
435 } 468}
436 pub mod result { 469"#,
437 pub enum Result<T, E> { Ok(T), Err(E) } 470 );
438 }
439 "#;
440 check_no_diagnostic_for_target_file(content);
441 } 471 }
442 472
443 #[test] 473 #[test]
444 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { 474 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
445 let content = r#" 475 check_no_diagnostics(
446 //- /main.rs 476 r#"
447 use std::{string::String, result::Result::{self, Ok, Err}}; 477//- /main.rs
478use core::result::Result::{self, Ok, Err};
448 479
449 enum SomeOtherEnum { 480enum SomeOtherEnum { Ok(i32), Err(String) }
450 Ok(i32),
451 Err(String),
452 }
453 481
454 fn foo() -> SomeOtherEnum { 482fn foo() -> SomeOtherEnum { 0 }
455 0<|>
456 }
457 483
458 //- /std/lib.rs 484//- /core/lib.rs
459 pub mod string { 485pub mod result {
460 pub struct String { } 486 pub enum Result<T, E> { Ok(T), Err(E) }
461 } 487}
462 pub mod result { 488"#,
463 pub enum Result<T, E> { Ok(T), Err(E) } 489 );
464 }
465 "#;
466 check_no_diagnostic_for_target_file(content);
467 } 490 }
468 491
469 #[test] 492 #[test]
470 fn test_fill_struct_fields_empty() { 493 fn test_fill_struct_fields_empty() {
471 let before = r" 494 check_fix(
472 struct TestStruct { 495 r#"
473 one: i32, 496struct TestStruct { one: i32, two: i64 }
474 two: i64,
475 }
476 497
477 fn test_fn() { 498fn test_fn() {
478 let s = TestStruct{}; 499 let s = TestStruct {<|>};
479 } 500}
480 "; 501"#,
481 let after = r" 502 r#"
482 struct TestStruct { 503struct TestStruct { one: i32, two: i64 }
483 one: i32,
484 two: i64,
485 }
486 504
487 fn test_fn() { 505fn test_fn() {
488 let s = TestStruct{ one: (), two: ()}; 506 let s = TestStruct { one: (), two: ()};
489 } 507}
490 "; 508"#,
491 check_apply_diagnostic_fix(before, after); 509 );
492 } 510 }
493 511
494 #[test] 512 #[test]
495 fn test_fill_struct_fields_self() { 513 fn test_fill_struct_fields_self() {
496 let before = r" 514 check_fix(
497 struct TestStruct { 515 r#"
498 one: i32, 516struct TestStruct { one: i32 }
499 }
500 517
501 impl TestStruct { 518impl TestStruct {
502 fn test_fn() { 519 fn test_fn() { let s = Self {<|>}; }
503 let s = Self {}; 520}
504 } 521"#,
505 } 522 r#"
506 "; 523struct TestStruct { one: i32 }
507 let after = r"
508 struct TestStruct {
509 one: i32,
510 }
511 524
512 impl TestStruct { 525impl TestStruct {
513 fn test_fn() { 526 fn test_fn() { let s = Self { one: ()}; }
514 let s = Self { one: ()}; 527}
515 } 528"#,
516 } 529 );
517 ";
518 check_apply_diagnostic_fix(before, after);
519 } 530 }
520 531
521 #[test] 532 #[test]
522 fn test_fill_struct_fields_enum() { 533 fn test_fill_struct_fields_enum() {
523 let before = r" 534 check_fix(
524 enum Expr { 535 r#"
525 Bin { lhs: Box<Expr>, rhs: Box<Expr> } 536enum Expr {
526 } 537 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
527 538}
528 impl Expr {
529 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
530 Expr::Bin { <|> }
531 }
532 }
533
534 ";
535 let after = r"
536 enum Expr {
537 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
538 }
539 539
540 impl Expr { 540impl Expr {
541 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr { 541 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
542 Expr::Bin { lhs: (), rhs: () <|> } 542 Expr::Bin {<|> }
543 } 543 }
544 } 544}
545"#,
546 r#"
547enum Expr {
548 Bin { lhs: Box<Expr>, rhs: Box<Expr> }
549}
545 550
546 "; 551impl Expr {
547 check_apply_diagnostic_fix(before, after); 552 fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
553 Expr::Bin { lhs: (), rhs: () }
554 }
555}
556"#,
557 );
548 } 558 }
549 559
550 #[test] 560 #[test]
551 fn test_fill_struct_fields_partial() { 561 fn test_fill_struct_fields_partial() {
552 let before = r" 562 check_fix(
553 struct TestStruct { 563 r#"
554 one: i32, 564struct TestStruct { one: i32, two: i64 }
555 two: i64,
556 }
557 565
558 fn test_fn() { 566fn test_fn() {
559 let s = TestStruct{ two: 2 }; 567 let s = TestStruct{ two: 2<|> };
560 } 568}
561 "; 569"#,
562 let after = r" 570 r"
563 struct TestStruct { 571struct TestStruct { one: i32, two: i64 }
564 one: i32,
565 two: i64,
566 }
567 572
568 fn test_fn() { 573fn test_fn() {
569 let s = TestStruct{ two: 2, one: () }; 574 let s = TestStruct{ two: 2, one: () };
570 } 575}
571 "; 576",
572 check_apply_diagnostic_fix(before, after); 577 );
573 } 578 }
574 579
575 #[test] 580 #[test]
576 fn test_fill_struct_fields_no_diagnostic() { 581 fn test_fill_struct_fields_no_diagnostic() {
577 let content = r" 582 check_no_diagnostics(
578 struct TestStruct { 583 r"
579 one: i32, 584 struct TestStruct { one: i32, two: i64 }
580 two: i64,
581 }
582 585
583 fn test_fn() { 586 fn test_fn() {
584 let one = 1; 587 let one = 1;
585 let s = TestStruct{ one, two: 2 }; 588 let s = TestStruct{ one, two: 2 };
586 } 589 }
587 "; 590 ",
588 591 );
589 check_no_diagnostic(content);
590 } 592 }
591 593
592 #[test] 594 #[test]
593 fn test_fill_struct_fields_no_diagnostic_on_spread() { 595 fn test_fill_struct_fields_no_diagnostic_on_spread() {
594 let content = r" 596 check_no_diagnostics(
595 struct TestStruct { 597 r"
596 one: i32, 598 struct TestStruct { one: i32, two: i64 }
597 two: i64,
598 }
599 599
600 fn test_fn() { 600 fn test_fn() {
601 let one = 1; 601 let one = 1;
602 let s = TestStruct{ ..a }; 602 let s = TestStruct{ ..a };
603 } 603 }
604 "; 604 ",
605 605 );
606 check_no_diagnostic(content);
607 } 606 }
608 607
609 #[test] 608 #[test]
610 fn test_unresolved_module_diagnostic() { 609 fn test_unresolved_module_diagnostic() {
611 let (analysis, file_id) = single_file("mod foo;"); 610 check_expect(
612 let diagnostics = analysis.diagnostics(file_id).unwrap(); 611 r#"mod foo;"#,
613 assert_debug_snapshot!(diagnostics, @r###" 612 expect![[r#"
614 [ 613 [
615 Diagnostic { 614 Diagnostic {
616 message: "unresolved module", 615 message: "unresolved module",
617 range: 0..8, 616 range: 0..8,
618 severity: Error, 617 severity: Error,
619 fix: Some( 618 fix: Some(
620 Fix { 619 Fix {
621 label: "Create module", 620 label: "Create module",
622 source_change: SourceChange { 621 source_change: SourceChange {
623 source_file_edits: [], 622 source_file_edits: [],
624 file_system_edits: [ 623 file_system_edits: [
625 CreateFile { 624 CreateFile {
626 source_root: SourceRootId( 625 anchor: FileId(
627 0, 626 1,
628 ), 627 ),
629 path: "foo.rs", 628 dst: "foo.rs",
629 },
630 ],
631 is_snippet: false,
630 }, 632 },
631 ], 633 },
632 is_snippet: false, 634 ),
633 },
634 }, 635 },
635 ), 636 ]
636 }, 637 "#]],
637 ] 638 );
638 "###);
639 } 639 }
640 640
641 #[test] 641 #[test]
642 fn range_mapping_out_of_macros() { 642 fn range_mapping_out_of_macros() {
643 let (analysis, file_id) = single_file( 643 // FIXME: this is very wrong, but somewhat tricky to fix.
644 r" 644 check_fix(
645 fn some() {} 645 r#"
646 fn items() {} 646fn some() {}
647 fn here() {} 647fn items() {}
648fn here() {}
648 649
649 macro_rules! id { 650macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
650 ($($tt:tt)*) => { $($tt)*};
651 }
652 651
653 fn main() { 652fn main() {
654 let _x = id![Foo { a: 42 }]; 653 let _x = id![Foo { a: <|>42 }];
655 } 654}
656 655
657 pub struct Foo { 656pub struct Foo { pub a: i32, pub b: i32 }
658 pub a: i32, 657"#,
659 pub b: i32, 658 r#"
660 } 659fn {a:42, b: ()} {}
661 ", 660fn items() {}
661fn here() {}
662
663macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
664
665fn main() {
666 let _x = id![Foo { a: 42 }];
667}
668
669pub struct Foo { pub a: i32, pub b: i32 }
670"#,
662 ); 671 );
663 let diagnostics = analysis.diagnostics(file_id).unwrap();
664 assert_debug_snapshot!(diagnostics, @r###"
665 [
666 Diagnostic {
667 message: "Missing structure fields:\n- b\n",
668 range: 224..233,
669 severity: Error,
670 fix: Some(
671 Fix {
672 label: "Fill struct fields",
673 source_change: SourceChange {
674 source_file_edits: [
675 SourceFileEdit {
676 file_id: FileId(
677 1,
678 ),
679 edit: TextEdit {
680 indels: [
681 Indel {
682 insert: "{a:42, b: ()}",
683 delete: 3..9,
684 },
685 ],
686 },
687 },
688 ],
689 file_system_edits: [],
690 is_snippet: false,
691 },
692 },
693 ),
694 },
695 ]
696 "###);
697 } 672 }
698 673
699 #[test] 674 #[test]
700 fn test_check_unnecessary_braces_in_use_statement() { 675 fn test_check_unnecessary_braces_in_use_statement() {
701 check_not_applicable( 676 check_no_diagnostics(
702 " 677 r#"
703 use a; 678use a;
704 use a::{c, d::e}; 679use a::{c, d::e};
705 ", 680"#,
706 check_unnecessary_braces_in_use_statement,
707 );
708 check_apply("use {b};", "use b;", check_unnecessary_braces_in_use_statement);
709 check_apply("use a::{c};", "use a::c;", check_unnecessary_braces_in_use_statement);
710 check_apply("use a::{self};", "use a;", check_unnecessary_braces_in_use_statement);
711 check_apply(
712 "use a::{c, d::{e}};",
713 "use a::{c, d::e};",
714 check_unnecessary_braces_in_use_statement,
715 ); 681 );
682 check_fix(r#"use {<|>b};"#, r#"use b;"#);
683 check_fix(r#"use {b<|>};"#, r#"use b;"#);
684 check_fix(r#"use a::{c<|>};"#, r#"use a::c;"#);
685 check_fix(r#"use a::{self<|>};"#, r#"use a;"#);
686 check_fix(r#"use a::{c, d::{e<|>}};"#, r#"use a::{c, d::e};"#);
716 } 687 }
717 688
718 #[test] 689 #[test]
719 fn test_check_struct_shorthand_initialization() { 690 fn test_check_struct_shorthand_initialization() {
720 check_not_applicable( 691 check_no_diagnostics(
721 r#" 692 r#"
722 struct A { 693struct A { a: &'static str }
723 a: &'static str 694fn main() { A { a: "hello" } }
724 } 695"#,
725
726 fn main() {
727 A {
728 a: "hello"
729 }
730 }
731 "#,
732 check_struct_shorthand_initialization,
733 ); 696 );
734 697 check_no_diagnostics(
735 check_apply(
736 r#" 698 r#"
737struct A { 699struct A(usize);
738 a: &'static str 700fn main() { A { 0: 0 } }
739} 701"#,
702 );
740 703
704 check_fix(
705 r#"
706struct A { a: &'static str }
741fn main() { 707fn main() {
742 let a = "haha"; 708 let a = "haha";
743 A { 709 A { a<|>: a }
744 a: a
745 }
746} 710}
747 "#, 711"#,
748 r#" 712 r#"
749struct A { 713struct A { a: &'static str }
750 a: &'static str
751}
752
753fn main() { 714fn main() {
754 let a = "haha"; 715 let a = "haha";
755 A { 716 A { a }
756 a
757 }
758} 717}
759 "#, 718"#,
760 check_struct_shorthand_initialization,
761 ); 719 );
762 720
763 check_apply( 721 check_fix(
764 r#" 722 r#"
765struct A { 723struct A { a: &'static str, b: &'static str }
766 a: &'static str,
767 b: &'static str
768}
769
770fn main() { 724fn main() {
771 let a = "haha"; 725 let a = "haha";
772 let b = "bb"; 726 let b = "bb";
773 A { 727 A { a<|>: a, b }
774 a: a,
775 b
776 }
777} 728}
778 "#, 729"#,
779 r#" 730 r#"
780struct A { 731struct A { a: &'static str, b: &'static str }
781 a: &'static str,
782 b: &'static str
783}
784
785fn main() { 732fn main() {
786 let a = "haha"; 733 let a = "haha";
787 let b = "bb"; 734 let b = "bb";
788 A { 735 A { a, b }
789 a,
790 b
791 }
792} 736}
793 "#, 737"#,
794 check_struct_shorthand_initialization,
795 ); 738 );
796 } 739 }
740
741 #[test]
742 fn test_add_field_from_usage() {
743 check_fix(
744 r"
745fn main() {
746 Foo { bar: 3, baz<|>: false};
747}
748struct Foo {
749 bar: i32
750}
751",
752 r"
753fn main() {
754 Foo { bar: 3, baz: false};
755}
756struct Foo {
757 bar: i32,
758 baz: bool
759}
760",
761 )
762 }
763
764 #[test]
765 fn test_add_field_in_other_file_from_usage() {
766 check_apply_diagnostic_fix_in_other_file(
767 r"
768 //- /main.rs
769 mod foo;
770
771 fn main() {
772 <|>foo::Foo { bar: 3, baz: false};
773 }
774 //- /foo.rs
775 struct Foo {
776 bar: i32
777 }
778 ",
779 r"
780 struct Foo {
781 bar: i32,
782 pub(crate) baz: bool
783 }
784 ",
785 )
786 }
797} 787}
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index 827c094e7..70d2a2dd1 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -6,13 +6,10 @@ mod navigation_target;
6mod structure; 6mod structure;
7mod short_label; 7mod short_label;
8 8
9use std::fmt::Display;
10
11use ra_syntax::{ 9use ra_syntax::{
12 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, 10 ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner},
13 SyntaxKind::{ATTR, COMMENT}, 11 SyntaxKind::{ATTR, COMMENT},
14}; 12};
15use stdx::format_to;
16 13
17pub use function_signature::FunctionSignature; 14pub use function_signature::FunctionSignature;
18pub use navigation_target::NavigationTarget; 15pub use navigation_target::NavigationTarget;
@@ -69,29 +66,3 @@ pub(crate) fn macro_label(node: &ast::MacroCall) -> String {
69 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" }; 66 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" };
70 format!("{}macro_rules! {}", vis, name) 67 format!("{}macro_rules! {}", vis, name)
71} 68}
72
73pub(crate) fn rust_code_markup(code: &impl Display) -> String {
74 rust_code_markup_with_doc(code, None, None)
75}
76
77pub(crate) fn rust_code_markup_with_doc(
78 code: &impl Display,
79 doc: Option<&str>,
80 mod_path: Option<&str>,
81) -> String {
82 let mut buf = String::new();
83
84 if let Some(mod_path) = mod_path {
85 if !mod_path.is_empty() {
86 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
87 }
88 }
89 format_to!(buf, "```rust\n{}\n```", code);
90
91 if let Some(doc) = doc {
92 format_to!(buf, "\n___");
93 format_to!(buf, "\n\n{}", doc);
94 }
95
96 buf
97}
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index 9572debd8..1d39544d3 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -10,7 +10,7 @@ use std::{
10use hir::{Docs, Documentation, HasSource, HirDisplay}; 10use hir::{Docs, Documentation, HasSource, HirDisplay};
11use ra_ide_db::RootDatabase; 11use ra_ide_db::RootDatabase;
12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; 12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
13use stdx::SepBy; 13use stdx::{split_delim, SepBy};
14 14
15use crate::display::{generic_parameters, where_predicates}; 15use crate::display::{generic_parameters, where_predicates};
16 16
@@ -61,15 +61,11 @@ pub struct FunctionQualifier {
61} 61}
62 62
63impl FunctionSignature { 63impl FunctionSignature {
64 pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self {
65 self.doc = doc;
66 self
67 }
68
69 pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self { 64 pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self {
70 let doc = function.docs(db);
71 let ast_node = function.source(db).value; 65 let ast_node = function.source(db).value;
72 FunctionSignature::from(&ast_node).with_doc_opt(doc) 66 let mut res = FunctionSignature::from(&ast_node);
67 res.doc = function.docs(db);
68 res
73 } 69 }
74 70
75 pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> { 71 pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
@@ -93,24 +89,21 @@ impl FunctionSignature {
93 params.push(raw_param); 89 params.push(raw_param);
94 } 90 }
95 91
96 Some( 92 Some(FunctionSignature {
97 FunctionSignature { 93 kind: CallableKind::StructConstructor,
98 kind: CallableKind::StructConstructor, 94 visibility: node.visibility().map(|n| n.syntax().text().to_string()),
99 visibility: node.visibility().map(|n| n.syntax().text().to_string()), 95 // Do we need `const`?
100 // Do we need `const`? 96 qualifier: Default::default(),
101 qualifier: Default::default(), 97 name: node.name().map(|n| n.text().to_string()),
102 name: node.name().map(|n| n.text().to_string()), 98 ret_type: node.name().map(|n| n.text().to_string()),
103 ret_type: node.name().map(|n| n.text().to_string()), 99 parameters: params,
104 parameters: params, 100 parameter_names: vec![],
105 parameter_names: vec![], 101 parameter_types,
106 parameter_types, 102 generic_parameters: generic_parameters(&node),
107 generic_parameters: generic_parameters(&node), 103 where_predicates: where_predicates(&node),
108 where_predicates: where_predicates(&node), 104 doc: st.docs(db),
109 doc: None, 105 has_self_param: false,
110 has_self_param: false, 106 })
111 }
112 .with_doc_opt(st.docs(db)),
113 )
114 } 107 }
115 108
116 pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> { 109 pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
@@ -140,24 +133,21 @@ impl FunctionSignature {
140 params.push(format!("{}: {}", name, ty.display(db))); 133 params.push(format!("{}: {}", name, ty.display(db)));
141 } 134 }
142 135
143 Some( 136 Some(FunctionSignature {
144 FunctionSignature { 137 kind: CallableKind::VariantConstructor,
145 kind: CallableKind::VariantConstructor, 138 visibility: None,
146 visibility: None, 139 // Do we need `const`?
147 // Do we need `const`? 140 qualifier: Default::default(),
148 qualifier: Default::default(), 141 name: Some(name),
149 name: Some(name), 142 ret_type: None,
150 ret_type: None, 143 parameters: params,
151 parameters: params, 144 parameter_names: vec![],
152 parameter_names: vec![], 145 parameter_types,
153 parameter_types, 146 generic_parameters: vec![],
154 generic_parameters: vec![], 147 where_predicates: vec![],
155 where_predicates: vec![], 148 doc: variant.docs(db),
156 doc: None, 149 has_self_param: false,
157 has_self_param: false, 150 })
158 }
159 .with_doc_opt(variant.docs(db)),
160 )
161 } 151 }
162 152
163 pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> { 153 pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
@@ -165,23 +155,20 @@ impl FunctionSignature {
165 155
166 let params = vec![]; 156 let params = vec![];
167 157
168 Some( 158 Some(FunctionSignature {
169 FunctionSignature { 159 kind: CallableKind::Macro,
170 kind: CallableKind::Macro, 160 visibility: None,
171 visibility: None, 161 qualifier: Default::default(),
172 qualifier: Default::default(), 162 name: node.name().map(|n| n.text().to_string()),
173 name: node.name().map(|n| n.text().to_string()), 163 ret_type: None,
174 ret_type: None, 164 parameters: params,
175 parameters: params, 165 parameter_names: vec![],
176 parameter_names: vec![], 166 parameter_types: vec![],
177 parameter_types: vec![], 167 generic_parameters: vec![],
178 generic_parameters: vec![], 168 where_predicates: vec![],
179 where_predicates: vec![], 169 doc: macro_def.docs(db),
180 doc: None, 170 has_self_param: false,
181 has_self_param: false, 171 })
182 }
183 .with_doc_opt(macro_def.docs(db)),
184 )
185 } 172 }
186} 173}
187 174
@@ -207,7 +194,16 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
207 res.push(raw_param); 194 res.push(raw_param);
208 } 195 }
209 196
210 res.extend(param_list.params().map(|param| param.syntax().text().to_string())); 197 // macro-generated functions are missing whitespace
198 fn fmt_param(param: ast::Param) -> String {
199 let text = param.syntax().text().to_string();
200 match split_delim(&text, ':') {
201 Some((left, right)) => format!("{}: {}", left.trim(), right.trim()),
202 _ => text,
203 }
204 }
205
206 res.extend(param_list.params().map(fmt_param));
211 res_types.extend(param_list.params().map(|param| { 207 res_types.extend(param_list.params().map(|param| {
212 let param_text = param.syntax().text().to_string(); 208 let param_text = param.syntax().text().to_string();
213 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { 209 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) {
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 5da28edd2..8bf2428ed 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11 TextRange, 11 TextRange,
12}; 12};
13 13
14use crate::{FileRange, FileSymbol}; 14use crate::FileSymbol;
15 15
16use super::short_label::ShortLabel; 16use super::short_label::ShortLabel;
17 17
@@ -47,6 +47,19 @@ impl NavigationTarget {
47 pub fn range(&self) -> TextRange { 47 pub fn range(&self) -> TextRange {
48 self.focus_range.unwrap_or(self.full_range) 48 self.focus_range.unwrap_or(self.full_range)
49 } 49 }
50 /// A "most interesting" range withing the `full_range`.
51 ///
52 /// Typically, `full_range` is the whole syntax node,
53 /// including doc comments, and `focus_range` is the range of the identifier.
54 pub fn focus_range(&self) -> Option<TextRange> {
55 self.focus_range
56 }
57 pub fn full_range(&self) -> TextRange {
58 self.full_range
59 }
60 pub fn file_id(&self) -> FileId {
61 self.file_id
62 }
50 63
51 pub fn name(&self) -> &SmolStr { 64 pub fn name(&self) -> &SmolStr {
52 &self.name 65 &self.name
@@ -60,18 +73,6 @@ impl NavigationTarget {
60 self.kind 73 self.kind
61 } 74 }
62 75
63 pub fn file_id(&self) -> FileId {
64 self.file_id
65 }
66
67 pub fn file_range(&self) -> FileRange {
68 FileRange { file_id: self.file_id, range: self.full_range }
69 }
70
71 pub fn full_range(&self) -> TextRange {
72 self.full_range
73 }
74
75 pub fn docs(&self) -> Option<&str> { 76 pub fn docs(&self) -> Option<&str> {
76 self.docs.as_deref() 77 self.docs.as_deref()
77 } 78 }
@@ -80,27 +81,20 @@ impl NavigationTarget {
80 self.description.as_deref() 81 self.description.as_deref()
81 } 82 }
82 83
83 /// A "most interesting" range withing the `full_range`.
84 ///
85 /// Typically, `full_range` is the whole syntax node,
86 /// including doc comments, and `focus_range` is the range of the identifier.
87 pub fn focus_range(&self) -> Option<TextRange> {
88 self.focus_range
89 }
90
91 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { 84 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
92 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 85 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
93 if let Some(src) = module.declaration_source(db) { 86 if let Some(src) = module.declaration_source(db) {
94 let frange = original_range(db, src.as_ref().map(|it| it.syntax())); 87 let frange = original_range(db, src.as_ref().map(|it| it.syntax()));
95 return NavigationTarget::from_syntax( 88 let mut res = NavigationTarget::from_syntax(
96 frange.file_id, 89 frange.file_id,
97 name, 90 name,
98 None, 91 None,
99 frange.range, 92 frange.range,
100 src.value.syntax().kind(), 93 src.value.syntax().kind(),
101 src.value.doc_comment_text(),
102 src.value.short_label(),
103 ); 94 );
95 res.docs = src.value.doc_comment_text();
96 res.description = src.value.short_label();
97 return res;
104 } 98 }
105 module.to_nav(db) 99 module.to_nav(db)
106 } 100 }
@@ -130,14 +124,12 @@ impl NavigationTarget {
130 } 124 }
131 125
132 /// Allows `NavigationTarget` to be created from a `NameOwner` 126 /// Allows `NavigationTarget` to be created from a `NameOwner`
133 fn from_named( 127 pub(crate) fn from_named(
134 db: &RootDatabase, 128 db: &RootDatabase,
135 node: InFile<&dyn ast::NameOwner>, 129 node: InFile<&dyn ast::NameOwner>,
136 docs: Option<String>,
137 description: Option<String>,
138 ) -> NavigationTarget { 130 ) -> NavigationTarget {
139 //FIXME: use `_` instead of empty string 131 let name =
140 let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default(); 132 node.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
141 let focus_range = 133 let focus_range =
142 node.value.name().map(|it| original_range(db, node.with_value(it.syntax())).range); 134 node.value.name().map(|it| original_range(db, node.with_value(it.syntax())).range);
143 let frange = original_range(db, node.map(|it| it.syntax())); 135 let frange = original_range(db, node.map(|it| it.syntax()));
@@ -148,8 +140,25 @@ impl NavigationTarget {
148 focus_range, 140 focus_range,
149 frange.range, 141 frange.range,
150 node.value.syntax().kind(), 142 node.value.syntax().kind(),
151 docs, 143 )
152 description, 144 }
145
146 /// Allows `NavigationTarget` to be created from a `DocCommentsOwner` and a `NameOwner`
147 pub(crate) fn from_doc_commented(
148 db: &RootDatabase,
149 named: InFile<&dyn ast::NameOwner>,
150 node: InFile<&dyn ast::DocCommentsOwner>,
151 ) -> NavigationTarget {
152 let name =
153 named.value.name().map(|it| it.text().clone()).unwrap_or_else(|| SmolStr::new("_"));
154 let frange = original_range(db, node.map(|it| it.syntax()));
155
156 NavigationTarget::from_syntax(
157 frange.file_id,
158 name,
159 None,
160 frange.range,
161 node.value.syntax().kind(),
153 ) 162 )
154 } 163 }
155 164
@@ -159,8 +168,6 @@ impl NavigationTarget {
159 focus_range: Option<TextRange>, 168 focus_range: Option<TextRange>,
160 full_range: TextRange, 169 full_range: TextRange,
161 kind: SyntaxKind, 170 kind: SyntaxKind,
162 docs: Option<String>,
163 description: Option<String>,
164 ) -> NavigationTarget { 171 ) -> NavigationTarget {
165 NavigationTarget { 172 NavigationTarget {
166 file_id, 173 file_id,
@@ -169,8 +176,8 @@ impl NavigationTarget {
169 full_range, 176 full_range,
170 focus_range, 177 focus_range,
171 container_name: None, 178 container_name: None,
172 description, 179 description: None,
173 docs, 180 docs: None,
174 } 181 }
175 } 182 }
176} 183}
@@ -238,12 +245,11 @@ where
238{ 245{
239 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 246 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
240 let src = self.source(db); 247 let src = self.source(db);
241 NavigationTarget::from_named( 248 let mut res =
242 db, 249 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
243 src.as_ref().map(|it| it as &dyn ast::NameOwner), 250 res.docs = src.value.doc_comment_text();
244 src.value.doc_comment_text(), 251 res.description = src.value.short_label();
245 src.value.short_label(), 252 res
246 )
247 } 253 }
248} 254}
249 255
@@ -258,35 +264,31 @@ impl ToNav for hir::Module {
258 } 264 }
259 }; 265 };
260 let frange = original_range(db, src.with_value(syntax)); 266 let frange = original_range(db, src.with_value(syntax));
261 NavigationTarget::from_syntax( 267 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind())
262 frange.file_id,
263 name,
264 focus,
265 frange.range,
266 syntax.kind(),
267 None,
268 None,
269 )
270 } 268 }
271} 269}
272 270
273impl ToNav for hir::ImplDef { 271impl ToNav for hir::ImplDef {
274 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 272 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
275 let src = self.source(db); 273 let src = self.source(db);
276 let frange = if let Some(item) = self.is_builtin_derive(db) { 274 let derive_attr = self.is_builtin_derive(db);
275 let frange = if let Some(item) = &derive_attr {
277 original_range(db, item.syntax()) 276 original_range(db, item.syntax())
278 } else { 277 } else {
279 original_range(db, src.as_ref().map(|it| it.syntax())) 278 original_range(db, src.as_ref().map(|it| it.syntax()))
280 }; 279 };
280 let focus_range = if derive_attr.is_some() {
281 None
282 } else {
283 src.value.target_type().map(|ty| original_range(db, src.with_value(ty.syntax())).range)
284 };
281 285
282 NavigationTarget::from_syntax( 286 NavigationTarget::from_syntax(
283 frange.file_id, 287 frange.file_id,
284 "impl".into(), 288 "impl".into(),
285 None, 289 focus_range,
286 frange.range, 290 frange.range,
287 src.value.syntax().kind(), 291 src.value.syntax().kind(),
288 None,
289 None,
290 ) 292 )
291 } 293 }
292} 294}
@@ -296,12 +298,12 @@ impl ToNav for hir::Field {
296 let src = self.source(db); 298 let src = self.source(db);
297 299
298 match &src.value { 300 match &src.value {
299 FieldSource::Named(it) => NavigationTarget::from_named( 301 FieldSource::Named(it) => {
300 db, 302 let mut res = NavigationTarget::from_named(db, src.with_value(it));
301 src.with_value(it), 303 res.docs = it.doc_comment_text();
302 it.doc_comment_text(), 304 res.description = it.short_label();
303 it.short_label(), 305 res
304 ), 306 }
305 FieldSource::Pos(it) => { 307 FieldSource::Pos(it) => {
306 let frange = original_range(db, src.with_value(it.syntax())); 308 let frange = original_range(db, src.with_value(it.syntax()));
307 NavigationTarget::from_syntax( 309 NavigationTarget::from_syntax(
@@ -310,8 +312,6 @@ impl ToNav for hir::Field {
310 None, 312 None,
311 frange.range, 313 frange.range,
312 it.syntax().kind(), 314 it.syntax().kind(),
313 None,
314 None,
315 ) 315 )
316 } 316 }
317 } 317 }
@@ -322,12 +322,10 @@ impl ToNav for hir::MacroDef {
322 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 322 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
323 let src = self.source(db); 323 let src = self.source(db);
324 log::debug!("nav target {:#?}", src.value.syntax()); 324 log::debug!("nav target {:#?}", src.value.syntax());
325 NavigationTarget::from_named( 325 let mut res =
326 db, 326 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
327 src.as_ref().map(|it| it as &dyn ast::NameOwner), 327 res.docs = src.value.doc_comment_text();
328 src.value.doc_comment_text(), 328 res
329 None,
330 )
331 } 329 }
332} 330}
333 331
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
index aad5a8e4d..1f6a3febf 100644
--- a/crates/ra_ide/src/display/structure.rs
+++ b/crates/ra_ide/src/display/structure.rs
@@ -127,6 +127,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
127 decl_with_detail(it, Some(detail)) 127 decl_with_detail(it, Some(detail))
128 }, 128 },
129 ast::StructDef(it) => decl(it), 129 ast::StructDef(it) => decl(it),
130 ast::UnionDef(it) => decl(it),
130 ast::EnumDef(it) => decl(it), 131 ast::EnumDef(it) => decl(it),
131 ast::EnumVariant(it) => decl(it), 132 ast::EnumVariant(it) => decl(it),
132 ast::TraitDef(it) => decl(it), 133 ast::TraitDef(it) => decl(it),
@@ -173,12 +174,19 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
173 174
174#[cfg(test)] 175#[cfg(test)]
175mod tests { 176mod tests {
177 use expect::{expect, Expect};
178
176 use super::*; 179 use super::*;
177 use insta::assert_debug_snapshot; 180
181 fn check(ra_fixture: &str, expect: Expect) {
182 let file = SourceFile::parse(ra_fixture).ok().unwrap();
183 let structure = file_structure(&file);
184 expect.assert_debug_eq(&structure)
185 }
178 186
179 #[test] 187 #[test]
180 fn test_file_structure() { 188 fn test_file_structure() {
181 let file = SourceFile::parse( 189 check(
182 r#" 190 r#"
183struct Foo { 191struct Foo {
184 x: i32 192 x: i32
@@ -223,216 +231,211 @@ fn obsolete() {}
223#[deprecated(note = "for awhile")] 231#[deprecated(note = "for awhile")]
224fn very_obsolete() {} 232fn very_obsolete() {}
225"#, 233"#,
226 ) 234 expect![[r#"
227 .ok() 235 [
228 .unwrap(); 236 StructureNode {
229 let structure = file_structure(&file); 237 parent: None,
230 assert_debug_snapshot!(structure, 238 label: "Foo",
231 @r###" 239 navigation_range: 8..11,
232 [ 240 node_range: 1..26,
233 StructureNode { 241 kind: STRUCT_DEF,
234 parent: None, 242 detail: None,
235 label: "Foo", 243 deprecated: false,
236 navigation_range: 8..11, 244 },
237 node_range: 1..26, 245 StructureNode {
238 kind: STRUCT_DEF, 246 parent: Some(
239 detail: None, 247 0,
240 deprecated: false, 248 ),
241 }, 249 label: "x",
242 StructureNode { 250 navigation_range: 18..19,
243 parent: Some( 251 node_range: 18..24,
244 0, 252 kind: RECORD_FIELD_DEF,
245 ), 253 detail: Some(
246 label: "x", 254 "i32",
247 navigation_range: 18..19, 255 ),
248 node_range: 18..24, 256 deprecated: false,
249 kind: RECORD_FIELD_DEF, 257 },
250 detail: Some( 258 StructureNode {
251 "i32", 259 parent: None,
252 ), 260 label: "m",
253 deprecated: false, 261 navigation_range: 32..33,
254 }, 262 node_range: 28..158,
255 StructureNode { 263 kind: MODULE,
256 parent: None, 264 detail: None,
257 label: "m", 265 deprecated: false,
258 navigation_range: 32..33, 266 },
259 node_range: 28..158, 267 StructureNode {
260 kind: MODULE, 268 parent: Some(
261 detail: None, 269 2,
262 deprecated: false, 270 ),
263 }, 271 label: "bar1",
264 StructureNode { 272 navigation_range: 43..47,
265 parent: Some( 273 node_range: 40..52,
266 2, 274 kind: FN_DEF,
267 ), 275 detail: Some(
268 label: "bar1", 276 "fn()",
269 navigation_range: 43..47, 277 ),
270 node_range: 40..52, 278 deprecated: false,
271 kind: FN_DEF, 279 },
272 detail: Some( 280 StructureNode {
273 "fn()", 281 parent: Some(
274 ), 282 2,
275 deprecated: false, 283 ),
276 }, 284 label: "bar2",
277 StructureNode { 285 navigation_range: 60..64,
278 parent: Some( 286 node_range: 57..81,
279 2, 287 kind: FN_DEF,
280 ), 288 detail: Some(
281 label: "bar2", 289 "fn<T>(t: T) -> T",
282 navigation_range: 60..64, 290 ),
283 node_range: 57..81, 291 deprecated: false,
284 kind: FN_DEF, 292 },
285 detail: Some( 293 StructureNode {
286 "fn<T>(t: T) -> T", 294 parent: Some(
287 ), 295 2,
288 deprecated: false, 296 ),
289 }, 297 label: "bar3",
290 StructureNode { 298 navigation_range: 89..93,
291 parent: Some( 299 node_range: 86..156,
292 2, 300 kind: FN_DEF,
293 ), 301 detail: Some(
294 label: "bar3", 302 "fn<A, B>(a: A, b: B) -> Vec< u32 >",
295 navigation_range: 89..93, 303 ),
296 node_range: 86..156, 304 deprecated: false,
297 kind: FN_DEF, 305 },
298 detail: Some( 306 StructureNode {
299 "fn<A, B>(a: A, b: B) -> Vec< u32 >", 307 parent: None,
300 ), 308 label: "E",
301 deprecated: false, 309 navigation_range: 165..166,
302 }, 310 node_range: 160..180,
303 StructureNode { 311 kind: ENUM_DEF,
304 parent: None, 312 detail: None,
305 label: "E", 313 deprecated: false,
306 navigation_range: 165..166, 314 },
307 node_range: 160..180, 315 StructureNode {
308 kind: ENUM_DEF, 316 parent: Some(
309 detail: None, 317 6,
310 deprecated: false, 318 ),
311 }, 319 label: "X",
312 StructureNode { 320 navigation_range: 169..170,
313 parent: Some( 321 node_range: 169..170,
314 6, 322 kind: ENUM_VARIANT,
315 ), 323 detail: None,
316 label: "X", 324 deprecated: false,
317 navigation_range: 169..170, 325 },
318 node_range: 169..170, 326 StructureNode {
319 kind: ENUM_VARIANT, 327 parent: Some(
320 detail: None, 328 6,
321 deprecated: false, 329 ),
322 }, 330 label: "Y",
323 StructureNode { 331 navigation_range: 172..173,
324 parent: Some( 332 node_range: 172..178,
325 6, 333 kind: ENUM_VARIANT,
326 ), 334 detail: None,
327 label: "Y", 335 deprecated: false,
328 navigation_range: 172..173, 336 },
329 node_range: 172..178, 337 StructureNode {
330 kind: ENUM_VARIANT, 338 parent: None,
331 detail: None, 339 label: "T",
332 deprecated: false, 340 navigation_range: 186..187,
333 }, 341 node_range: 181..193,
334 StructureNode { 342 kind: TYPE_ALIAS_DEF,
335 parent: None, 343 detail: Some(
336 label: "T", 344 "()",
337 navigation_range: 186..187, 345 ),
338 node_range: 181..193, 346 deprecated: false,
339 kind: TYPE_ALIAS_DEF, 347 },
340 detail: Some( 348 StructureNode {
341 "()", 349 parent: None,
342 ), 350 label: "S",
343 deprecated: false, 351 navigation_range: 201..202,
344 }, 352 node_range: 194..213,
345 StructureNode { 353 kind: STATIC_DEF,
346 parent: None, 354 detail: Some(
347 label: "S", 355 "i32",
348 navigation_range: 201..202, 356 ),
349 node_range: 194..213, 357 deprecated: false,
350 kind: STATIC_DEF, 358 },
351 detail: Some( 359 StructureNode {
352 "i32", 360 parent: None,
353 ), 361 label: "C",
354 deprecated: false, 362 navigation_range: 220..221,
355 }, 363 node_range: 214..232,
356 StructureNode { 364 kind: CONST_DEF,
357 parent: None, 365 detail: Some(
358 label: "C", 366 "i32",
359 navigation_range: 220..221, 367 ),
360 node_range: 214..232, 368 deprecated: false,
361 kind: CONST_DEF, 369 },
362 detail: Some( 370 StructureNode {
363 "i32", 371 parent: None,
364 ), 372 label: "impl E",
365 deprecated: false, 373 navigation_range: 239..240,
366 }, 374 node_range: 234..243,
367 StructureNode { 375 kind: IMPL_DEF,
368 parent: None, 376 detail: None,
369 label: "impl E", 377 deprecated: false,
370 navigation_range: 239..240, 378 },
371 node_range: 234..243, 379 StructureNode {
372 kind: IMPL_DEF, 380 parent: None,
373 detail: None, 381 label: "impl fmt::Debug for E",
374 deprecated: false, 382 navigation_range: 265..266,
375 }, 383 node_range: 245..269,
376 StructureNode { 384 kind: IMPL_DEF,
377 parent: None, 385 detail: None,
378 label: "impl fmt::Debug for E", 386 deprecated: false,
379 navigation_range: 265..266, 387 },
380 node_range: 245..269, 388 StructureNode {
381 kind: IMPL_DEF, 389 parent: None,
382 detail: None, 390 label: "mc",
383 deprecated: false, 391 navigation_range: 284..286,
384 }, 392 node_range: 271..303,
385 StructureNode { 393 kind: MACRO_CALL,
386 parent: None, 394 detail: None,
387 label: "mc", 395 deprecated: false,
388 navigation_range: 284..286, 396 },
389 node_range: 271..303, 397 StructureNode {
390 kind: MACRO_CALL, 398 parent: None,
391 detail: None, 399 label: "mcexp",
392 deprecated: false, 400 navigation_range: 334..339,
393 }, 401 node_range: 305..356,
394 StructureNode { 402 kind: MACRO_CALL,
395 parent: None, 403 detail: None,
396 label: "mcexp", 404 deprecated: false,
397 navigation_range: 334..339, 405 },
398 node_range: 305..356, 406 StructureNode {
399 kind: MACRO_CALL, 407 parent: None,
400 detail: None, 408 label: "mcexp",
401 deprecated: false, 409 navigation_range: 387..392,
402 }, 410 node_range: 358..409,
403 StructureNode { 411 kind: MACRO_CALL,
404 parent: None, 412 detail: None,
405 label: "mcexp", 413 deprecated: false,
406 navigation_range: 387..392, 414 },
407 node_range: 358..409, 415 StructureNode {
408 kind: MACRO_CALL, 416 parent: None,
409 detail: None, 417 label: "obsolete",
410 deprecated: false, 418 navigation_range: 428..436,
411 }, 419 node_range: 411..441,
412 StructureNode { 420 kind: FN_DEF,
413 parent: None, 421 detail: Some(
414 label: "obsolete", 422 "fn()",
415 navigation_range: 428..436, 423 ),
416 node_range: 411..441, 424 deprecated: true,
417 kind: FN_DEF, 425 },
418 detail: Some( 426 StructureNode {
419 "fn()", 427 parent: None,
420 ), 428 label: "very_obsolete",
421 deprecated: true, 429 navigation_range: 481..494,
422 }, 430 node_range: 443..499,
423 StructureNode { 431 kind: FN_DEF,
424 parent: None, 432 detail: Some(
425 label: "very_obsolete", 433 "fn()",
426 navigation_range: 481..494, 434 ),
427 node_range: 443..499, 435 deprecated: true,
428 kind: FN_DEF, 436 },
429 detail: Some( 437 ]
430 "fn()", 438 "#]],
431 ), 439 );
432 deprecated: true,
433 },
434 ]
435 "###
436 );
437 } 440 }
438} 441}
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs
index 54a47aac0..043515f54 100644
--- a/crates/ra_ide/src/expand_macro.rs
+++ b/crates/ra_ide/src/expand_macro.rs
@@ -2,7 +2,9 @@ use hir::Semantics;
2use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
3use ra_syntax::{ 3use ra_syntax::{
4 algo::{find_node_at_offset, SyntaxRewriter}, 4 algo::{find_node_at_offset, SyntaxRewriter},
5 ast, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T, 5 ast, AstNode, NodeOrToken, SyntaxKind,
6 SyntaxKind::*,
7 SyntaxNode, WalkEvent, T,
6}; 8};
7 9
8use crate::FilePosition; 10use crate::FilePosition;
@@ -65,8 +67,6 @@ fn expand_macro_recur(
65// FIXME: It would also be cool to share logic here and in the mbe tests, 67// FIXME: It would also be cool to share logic here and in the mbe tests,
66// which are pretty unreadable at the moment. 68// which are pretty unreadable at the moment.
67fn insert_whitespaces(syn: SyntaxNode) -> String { 69fn insert_whitespaces(syn: SyntaxNode) -> String {
68 use SyntaxKind::*;
69
70 let mut res = String::new(); 70 let mut res = String::new();
71 let mut token_iter = syn 71 let mut token_iter = syn
72 .preorder_with_tokens() 72 .preorder_with_tokens()
@@ -120,175 +120,164 @@ fn insert_whitespaces(syn: SyntaxNode) -> String {
120 120
121#[cfg(test)] 121#[cfg(test)]
122mod tests { 122mod tests {
123 use insta::assert_snapshot; 123 use expect::{expect, Expect};
124 124
125 use crate::mock_analysis::analysis_and_position; 125 use crate::mock_analysis::analysis_and_position;
126 126
127 use super::*; 127 fn check(ra_fixture: &str, expect: Expect) {
128 128 let (analysis, pos) = analysis_and_position(ra_fixture);
129 fn check_expand_macro(fixture: &str) -> ExpandedMacro { 129 let expansion = analysis.expand_macro(pos).unwrap().unwrap();
130 let (analysis, pos) = analysis_and_position(fixture); 130 let actual = format!("{}\n{}", expansion.name, expansion.expansion);
131 analysis.expand_macro(pos).unwrap().unwrap() 131 expect.assert_eq(&actual);
132 } 132 }
133 133
134 #[test] 134 #[test]
135 fn macro_expand_recursive_expansion() { 135 fn macro_expand_recursive_expansion() {
136 let res = check_expand_macro( 136 check(
137 r#" 137 r#"
138 //- /lib.rs 138macro_rules! bar {
139 macro_rules! bar { 139 () => { fn b() {} }
140 () => { fn b() {} } 140}
141 } 141macro_rules! foo {
142 macro_rules! foo { 142 () => { bar!(); }
143 () => { bar!(); } 143}
144 } 144macro_rules! baz {
145 macro_rules! baz { 145 () => { foo!(); }
146 () => { foo!(); } 146}
147 } 147f<|>oo!();
148 f<|>oo!(); 148"#,
149 "#, 149 expect![[r#"
150 foo
151 fn b(){}
152 "#]],
150 ); 153 );
151
152 assert_eq!(res.name, "foo");
153 assert_snapshot!(res.expansion, @r###"
154fn b(){}
155"###);
156 } 154 }
157 155
158 #[test] 156 #[test]
159 fn macro_expand_multiple_lines() { 157 fn macro_expand_multiple_lines() {
160 let res = check_expand_macro( 158 check(
161 r#" 159 r#"
162 //- /lib.rs 160macro_rules! foo {
163 macro_rules! foo { 161 () => {
164 () => { 162 fn some_thing() -> u32 {
165 fn some_thing() -> u32 { 163 let a = 0;
166 let a = 0; 164 a + 10
167 a + 10
168 }
169 }
170 } 165 }
171 f<|>oo!(); 166 }
167}
168f<|>oo!();
172 "#, 169 "#,
170 expect![[r#"
171 foo
172 fn some_thing() -> u32 {
173 let a = 0;
174 a+10
175 }"#]],
173 ); 176 );
174
175 assert_eq!(res.name, "foo");
176 assert_snapshot!(res.expansion, @r###"
177fn some_thing() -> u32 {
178 let a = 0;
179 a+10
180}
181"###);
182 } 177 }
183 178
184 #[test] 179 #[test]
185 fn macro_expand_match_ast() { 180 fn macro_expand_match_ast() {
186 let res = check_expand_macro( 181 check(
187 r#" 182 r#"
188 //- /lib.rs 183macro_rules! match_ast {
189 macro_rules! match_ast { 184 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
190 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; 185 (match ($node:expr) {
186 $( ast::$ast:ident($it:ident) => $res:block, )*
187 _ => $catch_all:expr $(,)?
188 }) => {{
189 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
190 { $catch_all }
191 }};
192}
191 193
192 (match ($node:expr) { 194fn main() {
193 $( ast::$ast:ident($it:ident) => $res:block, )* 195 mat<|>ch_ast! {
194 _ => $catch_all:expr $(,)? 196 match container {
195 }) => {{ 197 ast::TraitDef(it) => {},
196 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* 198 ast::ImplDef(it) => {},
197 { $catch_all } 199 _ => { continue },
198 }};
199 } 200 }
200
201 fn main() {
202 mat<|>ch_ast! {
203 match container {
204 ast::TraitDef(it) => {},
205 ast::ImplDef(it) => {},
206 _ => { continue },
207 }
208 }
209 }
210 "#,
211 );
212
213 assert_eq!(res.name, "match_ast");
214 assert_snapshot!(res.expansion, @r###"
215{
216 if let Some(it) = ast::TraitDef::cast(container.clone()){}
217 else if let Some(it) = ast::ImplDef::cast(container.clone()){}
218 else {
219 {
220 continue
221 } 201 }
222 }
223} 202}
224"###); 203"#,
204 expect![[r#"
205 match_ast
206 {
207 if let Some(it) = ast::TraitDef::cast(container.clone()){}
208 else if let Some(it) = ast::ImplDef::cast(container.clone()){}
209 else {
210 {
211 continue
212 }
213 }
214 }"#]],
215 );
225 } 216 }
226 217
227 #[test] 218 #[test]
228 fn macro_expand_match_ast_inside_let_statement() { 219 fn macro_expand_match_ast_inside_let_statement() {
229 let res = check_expand_macro( 220 check(
230 r#" 221 r#"
231 //- /lib.rs 222macro_rules! match_ast {
232 macro_rules! match_ast { 223 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
233 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) }; 224 (match ($node:expr) {}) => {{}};
234 (match ($node:expr) {}) => {{}}; 225}
235 }
236 226
237 fn main() { 227fn main() {
238 let p = f(|it| { 228 let p = f(|it| {
239 let res = mat<|>ch_ast! { match c {}}; 229 let res = mat<|>ch_ast! { match c {}};
240 Some(res) 230 Some(res)
241 })?; 231 })?;
242 } 232}
243 "#, 233"#,
234 expect![[r#"
235 match_ast
236 {}
237 "#]],
244 ); 238 );
245
246 assert_eq!(res.name, "match_ast");
247 assert_snapshot!(res.expansion, @r###"{}"###);
248 } 239 }
249 240
250 #[test] 241 #[test]
251 fn macro_expand_inner_macro_fail_to_expand() { 242 fn macro_expand_inner_macro_fail_to_expand() {
252 let res = check_expand_macro( 243 check(
253 r#" 244 r#"
254 //- /lib.rs 245macro_rules! bar {
255 macro_rules! bar { 246 (BAD) => {};
256 (BAD) => {}; 247}
257 } 248macro_rules! foo {
258 macro_rules! foo { 249 () => {bar!()};
259 () => {bar!()}; 250}
260 }
261 251
262 fn main() { 252fn main() {
263 let res = fo<|>o!(); 253 let res = fo<|>o!();
264 } 254}
265 "#, 255"#,
256 expect![[r#"
257 foo
258 "#]],
266 ); 259 );
267
268 assert_eq!(res.name, "foo");
269 assert_snapshot!(res.expansion, @r###""###);
270 } 260 }
271 261
272 #[test] 262 #[test]
273 fn macro_expand_with_dollar_crate() { 263 fn macro_expand_with_dollar_crate() {
274 let res = check_expand_macro( 264 check(
275 r#" 265 r#"
276 //- /lib.rs 266#[macro_export]
277 #[macro_export] 267macro_rules! bar {
278 macro_rules! bar { 268 () => {0};
279 () => {0}; 269}
280 } 270macro_rules! foo {
281 macro_rules! foo { 271 () => {$crate::bar!()};
282 () => {$crate::bar!()}; 272}
283 }
284 273
285 fn main() { 274fn main() {
286 let res = fo<|>o!(); 275 let res = fo<|>o!();
287 } 276}
288 "#, 277"#,
278 expect![[r#"
279 foo
280 0 "#]],
289 ); 281 );
290
291 assert_eq!(res.name, "foo");
292 assert_snapshot!(res.expansion, @r###"0"###);
293 } 282 }
294} 283}
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs
index a4bc93cdb..8a6b3ea99 100644
--- a/crates/ra_ide/src/extend_selection.rs
+++ b/crates/ra_ide/src/extend_selection.rs
@@ -315,17 +315,15 @@ fn adj_comments(comment: &ast::Comment, dir: Direction) -> ast::Comment {
315 315
316#[cfg(test)] 316#[cfg(test)]
317mod tests { 317mod tests {
318 use test_utils::extract_offset; 318 use crate::mock_analysis::analysis_and_position;
319
320 use crate::mock_analysis::single_file;
321 319
322 use super::*; 320 use super::*;
323 321
324 fn do_check(before: &str, afters: &[&str]) { 322 fn do_check(before: &str, afters: &[&str]) {
325 let (cursor, before) = extract_offset(before); 323 let (analysis, position) = analysis_and_position(&before);
326 let (analysis, file_id) = single_file(&before); 324 let before = analysis.file_text(position.file_id).unwrap();
327 let range = TextRange::empty(cursor); 325 let range = TextRange::empty(position.offset);
328 let mut frange = FileRange { file_id, range }; 326 let mut frange = FileRange { file_id: position.file_id, range };
329 327
330 for &after in afters { 328 for &after in afters {
331 frange.range = analysis.extend_selection(frange).unwrap(); 329 frange.range = analysis.extend_selection(frange).unwrap();
diff --git a/crates/ra_ide/src/folding_ranges.rs b/crates/ra_ide/src/folding_ranges.rs
index 8657377de..e7ec9953f 100644
--- a/crates/ra_ide/src/folding_ranges.rs
+++ b/crates/ra_ide/src/folding_ranges.rs
@@ -15,6 +15,7 @@ pub enum FoldKind {
15 Imports, 15 Imports,
16 Mods, 16 Mods,
17 Block, 17 Block,
18 ArgList,
18} 19}
19 20
20#[derive(Debug)] 21#[derive(Debug)]
@@ -83,8 +84,10 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
83 match kind { 84 match kind {
84 COMMENT => Some(FoldKind::Comment), 85 COMMENT => Some(FoldKind::Comment),
85 USE_ITEM => Some(FoldKind::Imports), 86 USE_ITEM => Some(FoldKind::Imports),
87 ARG_LIST => Some(FoldKind::ArgList),
86 RECORD_FIELD_DEF_LIST 88 RECORD_FIELD_DEF_LIST
87 | RECORD_FIELD_PAT_LIST 89 | RECORD_FIELD_PAT_LIST
90 | RECORD_FIELD_LIST
88 | ITEM_LIST 91 | ITEM_LIST
89 | EXTERN_ITEM_LIST 92 | EXTERN_ITEM_LIST
90 | USE_TREE_LIST 93 | USE_TREE_LIST
@@ -196,89 +199,85 @@ fn contiguous_range_for_comment(
196 199
197#[cfg(test)] 200#[cfg(test)]
198mod tests { 201mod tests {
202 use test_utils::extract_tags;
203
199 use super::*; 204 use super::*;
200 use test_utils::extract_ranges;
201 205
202 fn do_check(text: &str, fold_kinds: &[FoldKind]) { 206 fn check(ra_fixture: &str) {
203 let (ranges, text) = extract_ranges(text, "fold"); 207 let (ranges, text) = extract_tags(ra_fixture, "fold");
208
204 let parse = SourceFile::parse(&text); 209 let parse = SourceFile::parse(&text);
205 let folds = folding_ranges(&parse.tree()); 210 let folds = folding_ranges(&parse.tree());
206
207 assert_eq!( 211 assert_eq!(
208 folds.len(), 212 folds.len(),
209 ranges.len(), 213 ranges.len(),
210 "The amount of folds is different than the expected amount" 214 "The amount of folds is different than the expected amount"
211 ); 215 );
212 assert_eq!( 216
213 folds.len(), 217 for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) {
214 fold_kinds.len(),
215 "The amount of fold kinds is different than the expected amount"
216 );
217 for ((fold, range), fold_kind) in
218 folds.iter().zip(ranges.into_iter()).zip(fold_kinds.iter())
219 {
220 assert_eq!(fold.range.start(), range.start()); 218 assert_eq!(fold.range.start(), range.start());
221 assert_eq!(fold.range.end(), range.end()); 219 assert_eq!(fold.range.end(), range.end());
222 assert_eq!(&fold.kind, fold_kind); 220
221 let kind = match fold.kind {
222 FoldKind::Comment => "comment",
223 FoldKind::Imports => "imports",
224 FoldKind::Mods => "mods",
225 FoldKind::Block => "block",
226 FoldKind::ArgList => "arglist",
227 };
228 assert_eq!(kind, &attr.unwrap());
223 } 229 }
224 } 230 }
225 231
226 #[test] 232 #[test]
227 fn test_fold_comments() { 233 fn test_fold_comments() {
228 let text = r#" 234 check(
229<fold>// Hello 235 r#"
236<fold comment>// Hello
230// this is a multiline 237// this is a multiline
231// comment 238// comment
232//</fold> 239//</fold>
233 240
234// But this is not 241// But this is not
235 242
236fn main() <fold>{ 243fn main() <fold block>{
237 <fold>// We should 244 <fold comment>// We should
238 // also 245 // also
239 // fold 246 // fold
240 // this one.</fold> 247 // this one.</fold>
241 <fold>//! But this one is different 248 <fold comment>//! But this one is different
242 //! because it has another flavor</fold> 249 //! because it has another flavor</fold>
243 <fold>/* As does this 250 <fold comment>/* As does this
244 multiline comment */</fold> 251 multiline comment */</fold>
245}</fold>"#; 252}</fold>"#,
246 253 );
247 let fold_kinds = &[
248 FoldKind::Comment,
249 FoldKind::Block,
250 FoldKind::Comment,
251 FoldKind::Comment,
252 FoldKind::Comment,
253 ];
254 do_check(text, fold_kinds);
255 } 254 }
256 255
257 #[test] 256 #[test]
258 fn test_fold_imports() { 257 fn test_fold_imports() {
259 let text = r#" 258 check(
260<fold>use std::<fold>{ 259 r#"
260<fold imports>use std::<fold block>{
261 str, 261 str,
262 vec, 262 vec,
263 io as iop 263 io as iop
264}</fold>;</fold> 264}</fold>;</fold>
265 265
266fn main() <fold>{ 266fn main() <fold block>{
267}</fold>"#; 267}</fold>"#,
268 268 );
269 let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block];
270 do_check(text, folds);
271 } 269 }
272 270
273 #[test] 271 #[test]
274 fn test_fold_mods() { 272 fn test_fold_mods() {
275 let text = r#" 273 check(
274 r#"
276 275
277pub mod foo; 276pub mod foo;
278<fold>mod after_pub; 277<fold mods>mod after_pub;
279mod after_pub_next;</fold> 278mod after_pub_next;</fold>
280 279
281<fold>mod before_pub; 280<fold mods>mod before_pub;
282mod before_pub_next;</fold> 281mod before_pub_next;</fold>
283pub mod bar; 282pub mod bar;
284 283
@@ -286,90 +285,105 @@ mod not_folding_single;
286pub mod foobar; 285pub mod foobar;
287pub not_folding_single_next; 286pub not_folding_single_next;
288 287
289<fold>#[cfg(test)] 288<fold mods>#[cfg(test)]
290mod with_attribute; 289mod with_attribute;
291mod with_attribute_next;</fold> 290mod with_attribute_next;</fold>
292 291
293fn main() <fold>{ 292fn main() <fold block>{
294}</fold>"#; 293}</fold>"#,
295 294 );
296 let folds = &[FoldKind::Mods, FoldKind::Mods, FoldKind::Mods, FoldKind::Block];
297 do_check(text, folds);
298 } 295 }
299 296
300 #[test] 297 #[test]
301 fn test_fold_import_groups() { 298 fn test_fold_import_groups() {
302 let text = r#" 299 check(
303<fold>use std::str; 300 r#"
301<fold imports>use std::str;
304use std::vec; 302use std::vec;
305use std::io as iop;</fold> 303use std::io as iop;</fold>
306 304
307<fold>use std::mem; 305<fold imports>use std::mem;
308use std::f64;</fold> 306use std::f64;</fold>
309 307
310use std::collections::HashMap; 308use std::collections::HashMap;
311// Some random comment 309// Some random comment
312use std::collections::VecDeque; 310use std::collections::VecDeque;
313 311
314fn main() <fold>{ 312fn main() <fold block>{
315}</fold>"#; 313}</fold>"#,
316 314 );
317 let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block];
318 do_check(text, folds);
319 } 315 }
320 316
321 #[test] 317 #[test]
322 fn test_fold_import_and_groups() { 318 fn test_fold_import_and_groups() {
323 let text = r#" 319 check(
324<fold>use std::str; 320 r#"
321<fold imports>use std::str;
325use std::vec; 322use std::vec;
326use std::io as iop;</fold> 323use std::io as iop;</fold>
327 324
328<fold>use std::mem; 325<fold imports>use std::mem;
329use std::f64;</fold> 326use std::f64;</fold>
330 327
331<fold>use std::collections::<fold>{ 328<fold imports>use std::collections::<fold block>{
332 HashMap, 329 HashMap,
333 VecDeque, 330 VecDeque,
334}</fold>;</fold> 331}</fold>;</fold>
335// Some random comment 332// Some random comment
336 333
337fn main() <fold>{ 334fn main() <fold block>{
338}</fold>"#; 335}</fold>"#,
339 336 );
340 let folds = &[
341 FoldKind::Imports,
342 FoldKind::Imports,
343 FoldKind::Imports,
344 FoldKind::Block,
345 FoldKind::Block,
346 ];
347 do_check(text, folds);
348 } 337 }
349 338
350 #[test] 339 #[test]
351 fn test_folds_macros() { 340 fn test_folds_macros() {
352 let text = r#" 341 check(
353macro_rules! foo <fold>{ 342 r#"
343macro_rules! foo <fold block>{
354 ($($tt:tt)*) => { $($tt)* } 344 ($($tt:tt)*) => { $($tt)* }
355}</fold> 345}</fold>
356"#; 346"#,
357 347 );
358 let folds = &[FoldKind::Block];
359 do_check(text, folds);
360 } 348 }
361 349
362 #[test] 350 #[test]
363 fn test_fold_match_arms() { 351 fn test_fold_match_arms() {
364 let text = r#" 352 check(
365fn main() <fold>{ 353 r#"
366 match 0 <fold>{ 354fn main() <fold block>{
355 match 0 <fold block>{
367 0 => 0, 356 0 => 0,
368 _ => 1, 357 _ => 1,
369 }</fold> 358 }</fold>
370}</fold>"#; 359}</fold>
360"#,
361 );
362 }
363
364 #[test]
365 fn fold_big_calls() {
366 check(
367 r#"
368fn main() <fold block>{
369 frobnicate<fold arglist>(
370 1,
371 2,
372 3,
373 )</fold>
374}</fold>
375"#,
376 )
377 }
371 378
372 let folds = &[FoldKind::Block, FoldKind::Block]; 379 #[test]
373 do_check(text, folds); 380 fn fold_record_literals() {
381 check(
382 r#"
383const _: S = S <fold block>{
384
385}</fold>;
386"#,
387 )
374 } 388 }
375} 389}
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index a6c86e99c..c30b20611 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -1,13 +1,13 @@
1use hir::Semantics; 1use hir::Semantics;
2use ra_ide_db::{ 2use ra_ide_db::{
3 defs::{classify_name, classify_name_ref}, 3 defs::{classify_name, classify_name_ref, NameClass},
4 symbol_index, RootDatabase, 4 symbol_index, RootDatabase,
5}; 5};
6use ra_syntax::{ 6use ra_syntax::{
7 ast::{self}, 7 ast::{self},
8 match_ast, AstNode, 8 match_ast, AstNode,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxToken, TokenAtOffset, 10 SyntaxToken, TokenAtOffset, T,
11}; 11};
12 12
13use crate::{ 13use crate::{
@@ -32,14 +32,18 @@ pub(crate) fn goto_definition(
32 let file = sema.parse(position.file_id).syntax().clone(); 32 let file = sema.parse(position.file_id).syntax().clone();
33 let original_token = pick_best(file.token_at_offset(position.offset))?; 33 let original_token = pick_best(file.token_at_offset(position.offset))?;
34 let token = sema.descend_into_macros(original_token.clone()); 34 let token = sema.descend_into_macros(original_token.clone());
35 let parent = token.parent();
35 36
36 let nav_targets = match_ast! { 37 let nav_targets = match_ast! {
37 match (token.parent()) { 38 match parent {
38 ast::NameRef(name_ref) => { 39 ast::NameRef(name_ref) => {
39 reference_definition(&sema, &name_ref).to_vec() 40 reference_definition(&sema, &name_ref).to_vec()
40 }, 41 },
41 ast::Name(name) => { 42 ast::Name(name) => {
42 let def = classify_name(&sema, &name)?.definition(); 43 let def = match classify_name(&sema, &name)? {
44 NameClass::Definition(def) | NameClass::ConstReference(def) => def,
45 NameClass::FieldShorthand { local: _, field } => field,
46 };
43 let nav = def.try_to_nav(sema.db)?; 47 let nav = def.try_to_nav(sema.db)?;
44 vec![nav] 48 vec![nav]
45 }, 49 },
@@ -54,7 +58,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
54 return tokens.max_by_key(priority); 58 return tokens.max_by_key(priority);
55 fn priority(n: &SyntaxToken) -> usize { 59 fn priority(n: &SyntaxToken) -> usize {
56 match n.kind() { 60 match n.kind() {
57 IDENT | INT_NUMBER => 2, 61 IDENT | INT_NUMBER | T![self] => 2,
58 kind if kind.is_trivia() => 0, 62 kind if kind.is_trivia() => 0,
59 _ => 1, 63 _ => 1,
60 } 64 }
@@ -100,790 +104,784 @@ pub(crate) fn reference_definition(
100 104
101#[cfg(test)] 105#[cfg(test)]
102mod tests { 106mod tests {
103 use test_utils::assert_eq_text; 107 use ra_db::FileRange;
104 108 use ra_syntax::{TextRange, TextSize};
105 use crate::mock_analysis::analysis_and_position; 109
106 110 use crate::mock_analysis::MockAnalysis;
107 fn check_goto(ra_fixture: &str, expected: &str, expected_range: &str) { 111
108 let (analysis, pos) = analysis_and_position(ra_fixture); 112 fn check(ra_fixture: &str) {
113 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
114 let (mut expected, data) = mock.annotation();
115 let analysis = mock.analysis();
116 match data.as_str() {
117 "" => (),
118 "file" => {
119 expected.range =
120 TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap()))
121 }
122 data => panic!("bad data: {}", data),
123 }
109 124
110 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; 125 let mut navs =
126 analysis.goto_definition(position).unwrap().expect("no definition found").info;
111 if navs.len() == 0 { 127 if navs.len() == 0 {
112 panic!("unresolved reference") 128 panic!("unresolved reference")
113 } 129 }
114 assert_eq!(navs.len(), 1); 130 assert_eq!(navs.len(), 1);
115 131
116 let nav = navs.pop().unwrap(); 132 let nav = navs.pop().unwrap();
117 let file_text = analysis.file_text(nav.file_id()).unwrap(); 133 assert_eq!(expected, FileRange { file_id: nav.file_id(), range: nav.range() });
118
119 let mut actual = file_text[nav.full_range()].to_string();
120 if let Some(focus) = nav.focus_range() {
121 actual += "|";
122 actual += &file_text[focus];
123 }
124
125 if !expected_range.contains("...") {
126 test_utils::assert_eq_text!(&actual, expected_range);
127 } else {
128 let mut parts = expected_range.split("...");
129 let prefix = parts.next().unwrap();
130 let suffix = parts.next().unwrap();
131 assert!(
132 actual.starts_with(prefix) && actual.ends_with(suffix),
133 "\nExpected: {}\n Actual: {}\n",
134 expected_range,
135 actual
136 );
137 }
138
139 nav.assert_match(expected);
140 } 134 }
141 135
142 #[test] 136 #[test]
143 fn goto_def_in_items() { 137 fn goto_def_in_items() {
144 check_goto( 138 check(
145 " 139 r#"
146 //- /lib.rs 140struct Foo;
147 struct Foo; 141 //^^^
148 enum E { X(Foo<|>) } 142enum E { X(Foo<|>) }
149 ", 143"#,
150 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
151 "struct Foo;|Foo",
152 ); 144 );
153 } 145 }
154 146
155 #[test] 147 #[test]
156 fn goto_def_at_start_of_item() { 148 fn goto_def_at_start_of_item() {
157 check_goto( 149 check(
158 " 150 r#"
159 //- /lib.rs 151struct Foo;
160 struct Foo; 152 //^^^
161 enum E { X(<|>Foo) } 153enum E { X(<|>Foo) }
162 ", 154"#,
163 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
164 "struct Foo;|Foo",
165 ); 155 );
166 } 156 }
167 157
168 #[test] 158 #[test]
169 fn goto_definition_resolves_correct_name() { 159 fn goto_definition_resolves_correct_name() {
170 check_goto( 160 check(
171 " 161 r#"
172 //- /lib.rs 162//- /lib.rs
173 use a::Foo; 163use a::Foo;
174 mod a; 164mod a;
175 mod b; 165mod b;
176 enum E { X(Foo<|>) } 166enum E { X(Foo<|>) }
177
178 //- /a.rs
179 struct Foo;
180 167
181 //- /b.rs 168//- /a.rs
182 struct Foo; 169struct Foo;
183 ", 170 //^^^
184 "Foo STRUCT_DEF FileId(2) 0..11 7..10", 171//- /b.rs
185 "struct Foo;|Foo", 172struct Foo;
173"#,
186 ); 174 );
187 } 175 }
188 176
189 #[test] 177 #[test]
190 fn goto_def_for_module_declaration() { 178 fn goto_def_for_module_declaration() {
191 check_goto( 179 check(
192 " 180 r#"
193 //- /lib.rs 181//- /lib.rs
194 mod <|>foo; 182mod <|>foo;
195 183
196 //- /foo.rs 184//- /foo.rs
197 // empty 185// empty
198 ", 186//^ file
199 "foo SOURCE_FILE FileId(2) 0..10", 187"#,
200 "// empty\n\n",
201 ); 188 );
202 189
203 check_goto( 190 check(
204 " 191 r#"
205 //- /lib.rs 192//- /lib.rs
206 mod <|>foo; 193mod <|>foo;
207 194
208 //- /foo/mod.rs 195//- /foo/mod.rs
209 // empty 196// empty
210 ", 197//^ file
211 "foo SOURCE_FILE FileId(2) 0..10", 198"#,
212 "// empty\n\n",
213 ); 199 );
214 } 200 }
215 201
216 #[test] 202 #[test]
217 fn goto_def_for_macros() { 203 fn goto_def_for_macros() {
218 check_goto( 204 check(
219 " 205 r#"
220 //- /lib.rs 206macro_rules! foo { () => { () } }
221 macro_rules! foo { () => { () } } 207 //^^^
222 208fn bar() {
223 fn bar() { 209 <|>foo!();
224 <|>foo!(); 210}
225 } 211"#,
226 ",
227 "foo MACRO_CALL FileId(1) 0..33 13..16",
228 "macro_rules! foo { () => { () } }|foo",
229 ); 212 );
230 } 213 }
231 214
232 #[test] 215 #[test]
233 fn goto_def_for_macros_from_other_crates() { 216 fn goto_def_for_macros_from_other_crates() {
234 check_goto( 217 check(
235 " 218 r#"
236 //- /lib.rs 219//- /lib.rs
237 use foo::foo; 220use foo::foo;
238 fn bar() { 221fn bar() {
239 <|>foo!(); 222 <|>foo!();
240 } 223}
241 224
242 //- /foo/lib.rs 225//- /foo/lib.rs
243 #[macro_export] 226#[macro_export]
244 macro_rules! foo { () => { () } } 227macro_rules! foo { () => { () } }
245 ", 228 //^^^
246 "foo MACRO_CALL FileId(2) 0..49 29..32", 229"#,
247 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
248 ); 230 );
249 } 231 }
250 232
251 #[test] 233 #[test]
252 fn goto_def_for_use_alias() { 234 fn goto_def_for_macros_in_use_tree() {
253 check_goto( 235 check(
254 " 236 r#"
255 //- /lib.rs 237//- /lib.rs
256 use foo as bar<|>; 238use foo::foo<|>;
257
258 239
259 //- /foo/lib.rs 240//- /foo/lib.rs
260 #[macro_export] 241#[macro_export]
261 macro_rules! foo { () => { () } }", 242macro_rules! foo { () => { () } }
262 "SOURCE_FILE FileId(2) 0..50", 243 //^^^
263 "#[macro_export]\nmacro_rules! foo { () => { () } }\n", 244"#,
264 ); 245 );
265 } 246 }
266 247
267 #[test] 248 #[test]
268 fn goto_def_for_use_alias_foo_macro() { 249 fn goto_def_for_macro_defined_fn_with_arg() {
269 check_goto( 250 check(
270 " 251 r#"
271 //- /lib.rs 252//- /lib.rs
272 use foo::foo as bar<|>; 253macro_rules! define_fn {
254 ($name:ident) => (fn $name() {})
255}
256
257define_fn!(foo);
258 //^^^
273 259
274 //- /foo/lib.rs 260fn bar() {
275 #[macro_export] 261 <|>foo();
276 macro_rules! foo { () => { () } } 262}
277 ", 263"#,
278 "foo MACRO_CALL FileId(2) 0..49 29..32",
279 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
280 ); 264 );
281 } 265 }
282 266
283 #[test] 267 #[test]
284 fn goto_def_for_macros_in_use_tree() { 268 fn goto_def_for_macro_defined_fn_no_arg() {
285 check_goto( 269 check(
286 " 270 r#"
287 //- /lib.rs 271//- /lib.rs
288 use foo::foo<|>; 272macro_rules! define_fn {
273 () => (fn foo() {})
274}
275
276 define_fn!();
277//^^^^^^^^^^^^^
289 278
290 //- /foo/lib.rs 279fn bar() {
291 #[macro_export] 280 <|>foo();
292 macro_rules! foo { () => { () } } 281}
293 ", 282"#,
294 "foo MACRO_CALL FileId(2) 0..49 29..32",
295 "#[macro_export]\nmacro_rules! foo { () => { () } }|foo",
296 ); 283 );
297 } 284 }
298 285
299 #[test] 286 #[test]
300 fn goto_def_for_macro_defined_fn_with_arg() { 287 fn goto_definition_works_for_macro_inside_pattern() {
301 check_goto( 288 check(
302 " 289 r#"
303 //- /lib.rs 290//- /lib.rs
304 macro_rules! define_fn { 291macro_rules! foo {() => {0}}
305 ($name:ident) => (fn $name() {}) 292 //^^^
306 }
307
308 define_fn!(foo);
309 293
310 fn bar() { 294fn bar() {
311 <|>foo(); 295 match (0,1) {
312 } 296 (<|>foo!(), _) => {}
313 ", 297 }
314 "foo FN_DEF FileId(1) 64..80 75..78", 298}
315 "define_fn!(foo);|foo", 299"#,
316 ); 300 );
317 } 301 }
318 302
319 #[test] 303 #[test]
320 fn goto_def_for_macro_defined_fn_no_arg() { 304 fn goto_definition_works_for_macro_inside_match_arm_lhs() {
321 check_goto( 305 check(
322 " 306 r#"
323 //- /lib.rs 307//- /lib.rs
324 macro_rules! define_fn { 308macro_rules! foo {() => {0}}
325 () => (fn foo() {}) 309 //^^^
326 } 310fn bar() {
327 311 match 0 {
328 define_fn!(); 312 <|>foo!() => {}
329 313 }
330 fn bar() { 314}
331 <|>foo(); 315"#,
332 }
333 ",
334 "foo FN_DEF FileId(1) 51..64 51..64",
335 "define_fn!();|define_fn!();",
336 ); 316 );
337 } 317 }
338 318
339 #[test] 319 #[test]
340 fn goto_definition_works_for_macro_inside_pattern() { 320 fn goto_def_for_use_alias() {
341 check_goto( 321 check(
342 " 322 r#"
343 //- /lib.rs 323//- /lib.rs
344 macro_rules! foo {() => {0}} 324use foo as bar<|>;
345 325
346 fn bar() { 326//- /foo/lib.rs
347 match (0,1) { 327// empty
348 (<|>foo!(), _) => {} 328//^ file
349 } 329"#,
350 }
351 ",
352 "foo MACRO_CALL FileId(1) 0..28 13..16",
353 "macro_rules! foo {() => {0}}|foo",
354 ); 330 );
355 } 331 }
356 332
357 #[test] 333 #[test]
358 fn goto_definition_works_for_macro_inside_match_arm_lhs() { 334 fn goto_def_for_use_alias_foo_macro() {
359 check_goto( 335 check(
360 " 336 r#"
361 //- /lib.rs 337//- /lib.rs
362 macro_rules! foo {() => {0}} 338use foo::foo as bar<|>;
363 339
364 fn bar() { 340//- /foo/lib.rs
365 match 0 { 341#[macro_export]
366 <|>foo!() => {} 342macro_rules! foo { () => { () } }
367 } 343 //^^^
368 } 344"#,
369 ",
370 "foo MACRO_CALL FileId(1) 0..28 13..16",
371 "macro_rules! foo {() => {0}}|foo",
372 ); 345 );
373 } 346 }
374 347
375 #[test] 348 #[test]
376 fn goto_def_for_methods() { 349 fn goto_def_for_methods() {
377 check_goto( 350 check(
378 " 351 r#"
379 //- /lib.rs 352//- /lib.rs
380 struct Foo; 353struct Foo;
381 impl Foo { 354impl Foo {
382 fn frobnicate(&self) { } 355 fn frobnicate(&self) { }
383 } 356 //^^^^^^^^^^
357}
384 358
385 fn bar(foo: &Foo) { 359fn bar(foo: &Foo) {
386 foo.frobnicate<|>(); 360 foo.frobnicate<|>();
387 } 361}
388 ", 362"#,
389 "frobnicate FN_DEF FileId(1) 27..51 30..40",
390 "fn frobnicate(&self) { }|frobnicate",
391 ); 363 );
392 } 364 }
393 365
394 #[test] 366 #[test]
395 fn goto_def_for_fields() { 367 fn goto_def_for_fields() {
396 check_goto( 368 check(
397 r" 369 r#"
398 //- /lib.rs 370struct Foo {
399 struct Foo { 371 spam: u32,
400 spam: u32, 372} //^^^^
401 }
402 373
403 fn bar(foo: &Foo) { 374fn bar(foo: &Foo) {
404 foo.spam<|>; 375 foo.spam<|>;
405 } 376}
406 ", 377"#,
407 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
408 "spam: u32|spam",
409 ); 378 );
410 } 379 }
411 380
412 #[test] 381 #[test]
413 fn goto_def_for_record_fields() { 382 fn goto_def_for_record_fields() {
414 check_goto( 383 check(
415 r" 384 r#"
416 //- /lib.rs 385//- /lib.rs
417 struct Foo { 386struct Foo {
418 spam: u32, 387 spam: u32,
419 } 388} //^^^^
420 389
421 fn bar() -> Foo { 390fn bar() -> Foo {
422 Foo { 391 Foo {
423 spam<|>: 0, 392 spam<|>: 0,
424 } 393 }
425 } 394}
426 ", 395"#,
427 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
428 "spam: u32|spam",
429 ); 396 );
430 } 397 }
431 398
432 #[test] 399 #[test]
433 fn goto_def_for_record_pat_fields() { 400 fn goto_def_for_record_pat_fields() {
434 check_goto( 401 check(
435 r" 402 r#"
436 //- /lib.rs 403//- /lib.rs
437 struct Foo { 404struct Foo {
438 spam: u32, 405 spam: u32,
439 } 406} //^^^^
440 407
441 fn bar(foo: Foo) -> Foo { 408fn bar(foo: Foo) -> Foo {
442 let Foo { spam<|>: _, } = foo 409 let Foo { spam<|>: _, } = foo
443 } 410}
444 ", 411"#,
445 "spam RECORD_FIELD_DEF FileId(1) 17..26 17..21",
446 "spam: u32|spam",
447 ); 412 );
448 } 413 }
449 414
450 #[test] 415 #[test]
451 fn goto_def_for_record_fields_macros() { 416 fn goto_def_for_record_fields_macros() {
452 check_goto( 417 check(
453 r" 418 r"
454 //- /lib.rs 419macro_rules! m { () => { 92 };}
455 macro_rules! m { () => { 92 };} 420struct Foo { spam: u32 }
456 struct Foo { spam: u32 } 421 //^^^^
457 422
458 fn bar() -> Foo { 423fn bar() -> Foo {
459 Foo { spam<|>: m!() } 424 Foo { spam<|>: m!() }
460 } 425}
461 ", 426",
462 "spam RECORD_FIELD_DEF FileId(1) 45..54 45..49",
463 "spam: u32|spam",
464 ); 427 );
465 } 428 }
466 429
467 #[test] 430 #[test]
468 fn goto_for_tuple_fields() { 431 fn goto_for_tuple_fields() {
469 check_goto( 432 check(
470 " 433 r#"
471 //- /lib.rs 434struct Foo(u32);
472 struct Foo(u32); 435 //^^^
473 436
474 fn bar() { 437fn bar() {
475 let foo = Foo(0); 438 let foo = Foo(0);
476 foo.<|>0; 439 foo.<|>0;
477 } 440}
478 ", 441"#,
479 "TUPLE_FIELD_DEF FileId(1) 11..14",
480 "u32",
481 ); 442 );
482 } 443 }
483 444
484 #[test] 445 #[test]
485 fn goto_def_for_ufcs_inherent_methods() { 446 fn goto_def_for_ufcs_inherent_methods() {
486 check_goto( 447 check(
487 " 448 r#"
488 //- /lib.rs 449struct Foo;
489 struct Foo; 450impl Foo {
490 impl Foo { 451 fn frobnicate() { }
491 fn frobnicate() { } 452} //^^^^^^^^^^
492 }
493 453
494 fn bar(foo: &Foo) { 454fn bar(foo: &Foo) {
495 Foo::frobnicate<|>(); 455 Foo::frobnicate<|>();
496 } 456}
497 ", 457"#,
498 "frobnicate FN_DEF FileId(1) 27..46 30..40",
499 "fn frobnicate() { }|frobnicate",
500 ); 458 );
501 } 459 }
502 460
503 #[test] 461 #[test]
504 fn goto_def_for_ufcs_trait_methods_through_traits() { 462 fn goto_def_for_ufcs_trait_methods_through_traits() {
505 check_goto( 463 check(
506 " 464 r#"
507 //- /lib.rs 465trait Foo {
508 trait Foo { 466 fn frobnicate();
509 fn frobnicate(); 467} //^^^^^^^^^^
510 }
511 468
512 fn bar() { 469fn bar() {
513 Foo::frobnicate<|>(); 470 Foo::frobnicate<|>();
514 } 471}
515 ", 472"#,
516 "frobnicate FN_DEF FileId(1) 16..32 19..29",
517 "fn frobnicate();|frobnicate",
518 ); 473 );
519 } 474 }
520 475
521 #[test] 476 #[test]
522 fn goto_def_for_ufcs_trait_methods_through_self() { 477 fn goto_def_for_ufcs_trait_methods_through_self() {
523 check_goto( 478 check(
524 " 479 r#"
525 //- /lib.rs 480struct Foo;
526 struct Foo; 481trait Trait {
527 trait Trait { 482 fn frobnicate();
528 fn frobnicate(); 483} //^^^^^^^^^^
529 } 484impl Trait for Foo {}
530 impl Trait for Foo {} 485
531 486fn bar() {
532 fn bar() { 487 Foo::frobnicate<|>();
533 Foo::frobnicate<|>(); 488}
534 } 489"#,
535 ",
536 "frobnicate FN_DEF FileId(1) 30..46 33..43",
537 "fn frobnicate();|frobnicate",
538 ); 490 );
539 } 491 }
540 492
541 #[test] 493 #[test]
542 fn goto_definition_on_self() { 494 fn goto_definition_on_self() {
543 check_goto( 495 check(
544 " 496 r#"
545 //- /lib.rs 497struct Foo;
546 struct Foo; 498impl Foo {
547 impl Foo { 499 //^^^
548 pub fn new() -> Self { 500 pub fn new() -> Self {
549 Self<|> {} 501 Self<|> {}
550 } 502 }
551 } 503}
552 ", 504"#,
553 "impl IMPL_DEF FileId(1) 12..73", 505 );
554 "impl Foo {...}", 506 check(
555 ); 507 r#"
556 508struct Foo;
557 check_goto( 509impl Foo {
558 " 510 //^^^
559 //- /lib.rs 511 pub fn new() -> Self<|> {
560 struct Foo; 512 Self {}
561 impl Foo { 513 }
562 pub fn new() -> Self<|> { 514}
563 Self {} 515"#,
564 } 516 );
565 } 517
566 ", 518 check(
567 "impl IMPL_DEF FileId(1) 12..73", 519 r#"
568 "impl Foo {...}", 520enum Foo { A }
569 ); 521impl Foo {
570 522 //^^^
571 check_goto( 523 pub fn new() -> Self<|> {
572 " 524 Foo::A
573 //- /lib.rs 525 }
574 enum Foo { A } 526}
575 impl Foo { 527"#,
576 pub fn new() -> Self<|> { 528 );
577 Foo::A 529
578 } 530 check(
579 } 531 r#"
580 ", 532enum Foo { A }
581 "impl IMPL_DEF FileId(1) 15..75", 533impl Foo {
582 "impl Foo {...}", 534 //^^^
583 ); 535 pub fn thing(a: &Self<|>) {
584 536 }
585 check_goto( 537}
586 " 538"#,
587 //- /lib.rs
588 enum Foo { A }
589 impl Foo {
590 pub fn thing(a: &Self<|>) {
591 }
592 }
593 ",
594 "impl IMPL_DEF FileId(1) 15..62",
595 "impl Foo {...}",
596 ); 539 );
597 } 540 }
598 541
599 #[test] 542 #[test]
600 fn goto_definition_on_self_in_trait_impl() { 543 fn goto_definition_on_self_in_trait_impl() {
601 check_goto( 544 check(
602 " 545 r#"
603 //- /lib.rs 546struct Foo;
604 struct Foo; 547trait Make {
605 trait Make { 548 fn new() -> Self;
606 fn new() -> Self; 549}
607 } 550impl Make for Foo {
608 impl Make for Foo { 551 //^^^
609 fn new() -> Self { 552 fn new() -> Self {
610 Self<|> {} 553 Self<|> {}
611 } 554 }
612 } 555}
613 ", 556"#,
614 "impl IMPL_DEF FileId(1) 49..115",
615 "impl Make for Foo {...}",
616 ); 557 );
617 558
618 check_goto( 559 check(
619 " 560 r#"
620 //- /lib.rs 561struct Foo;
621 struct Foo; 562trait Make {
622 trait Make { 563 fn new() -> Self;
623 fn new() -> Self; 564}
624 } 565impl Make for Foo {
625 impl Make for Foo { 566 //^^^
626 fn new() -> Self<|> { 567 fn new() -> Self<|> {
627 Self {} 568 Self {}
628 } 569 }
629 } 570}
630 ", 571"#,
631 "impl IMPL_DEF FileId(1) 49..115",
632 "impl Make for Foo {...}",
633 ); 572 );
634 } 573 }
635 574
636 #[test] 575 #[test]
637 fn goto_def_when_used_on_definition_name_itself() { 576 fn goto_def_when_used_on_definition_name_itself() {
638 check_goto( 577 check(
639 " 578 r#"
640 //- /lib.rs 579struct Foo<|> { value: u32 }
641 struct Foo<|> { value: u32 } 580 //^^^
642 ", 581 "#,
643 "Foo STRUCT_DEF FileId(1) 0..25 7..10",
644 "struct Foo { value: u32 }|Foo",
645 ); 582 );
646 583
647 check_goto( 584 check(
648 r#" 585 r#"
649 //- /lib.rs 586struct Foo {
650 struct Foo { 587 field<|>: string,
651 field<|>: string, 588} //^^^^^
652 } 589"#,
653 "#,
654 "field RECORD_FIELD_DEF FileId(1) 17..30 17..22",
655 "field: string|field",
656 ); 590 );
657 591
658 check_goto( 592 check(
659 " 593 r#"
660 //- /lib.rs 594fn foo_test<|>() { }
661 fn foo_test<|>() { } 595 //^^^^^^^^
662 ", 596"#,
663 "foo_test FN_DEF FileId(1) 0..17 3..11",
664 "fn foo_test() { }|foo_test",
665 ); 597 );
666 598
667 check_goto( 599 check(
668 " 600 r#"
669 //- /lib.rs 601enum Foo<|> { Variant }
670 enum Foo<|> { 602 //^^^
671 Variant, 603"#,
672 }
673 ",
674 "Foo ENUM_DEF FileId(1) 0..25 5..8",
675 "enum Foo {...}|Foo",
676 );
677
678 check_goto(
679 "
680 //- /lib.rs
681 enum Foo {
682 Variant1,
683 Variant2<|>,
684 Variant3,
685 }
686 ",
687 "Variant2 ENUM_VARIANT FileId(1) 29..37 29..37",
688 "Variant2|Variant2",
689 ); 604 );
690 605
691 check_goto( 606 check(
692 r#" 607 r#"
693 //- /lib.rs 608enum Foo {
694 static INNER<|>: &str = ""; 609 Variant1,
695 "#, 610 Variant2<|>,
696 "INNER STATIC_DEF FileId(1) 0..24 7..12", 611 //^^^^^^^^
697 "static INNER: &str = \"\";|INNER", 612 Variant3,
613}
614"#,
698 ); 615 );
699 616
700 check_goto( 617 check(
701 r#" 618 r#"
702 //- /lib.rs 619static INNER<|>: &str = "";
703 const INNER<|>: &str = ""; 620 //^^^^^
704 "#, 621"#,
705 "INNER CONST_DEF FileId(1) 0..23 6..11",
706 "const INNER: &str = \"\";|INNER",
707 ); 622 );
708 623
709 check_goto( 624 check(
710 r#" 625 r#"
711 //- /lib.rs 626const INNER<|>: &str = "";
712 type Thing<|> = Option<()>; 627 //^^^^^
713 "#, 628"#,
714 "Thing TYPE_ALIAS_DEF FileId(1) 0..24 5..10",
715 "type Thing = Option<()>;|Thing",
716 ); 629 );
717 630
718 check_goto( 631 check(
719 r#" 632 r#"
720 //- /lib.rs 633type Thing<|> = Option<()>;
721 trait Foo<|> { } 634 //^^^^^
722 "#, 635"#,
723 "Foo TRAIT_DEF FileId(1) 0..13 6..9",
724 "trait Foo { }|Foo",
725 ); 636 );
726 637
727 check_goto( 638 check(
728 r#" 639 r#"
729 //- /lib.rs 640trait Foo<|> { }
730 mod bar<|> { } 641 //^^^
731 "#, 642"#,
732 "bar MODULE FileId(1) 0..11 4..7", 643 );
733 "mod bar { }|bar", 644
645 check(
646 r#"
647mod bar<|> { }
648 //^^^
649"#,
734 ); 650 );
735 } 651 }
736 652
737 #[test] 653 #[test]
738 fn goto_from_macro() { 654 fn goto_from_macro() {
739 check_goto( 655 check(
740 " 656 r#"
741 //- /lib.rs 657macro_rules! id {
742 macro_rules! id { 658 ($($tt:tt)*) => { $($tt)* }
743 ($($tt:tt)*) => { $($tt)* } 659}
744 } 660fn foo() {}
745 fn foo() {} 661 //^^^
746 id! { 662id! {
747 fn bar() { 663 fn bar() {
748 fo<|>o(); 664 fo<|>o();
749 } 665 }
750 } 666}
751 mod confuse_index { fn foo(); } 667mod confuse_index { fn foo(); }
752 ", 668"#,
753 "foo FN_DEF FileId(1) 52..63 55..58",
754 "fn foo() {}|foo",
755 ); 669 );
756 } 670 }
757 671
758 #[test] 672 #[test]
759 fn goto_through_format() { 673 fn goto_through_format() {
760 check_goto( 674 check(
761 " 675 r#"
762 //- /lib.rs 676#[macro_export]
763 #[macro_export] 677macro_rules! format {
764 macro_rules! format { 678 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
765 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*))) 679}
766 } 680#[rustc_builtin_macro]
767 #[rustc_builtin_macro] 681#[macro_export]
768 #[macro_export] 682macro_rules! format_args {
769 macro_rules! format_args { 683 ($fmt:expr) => ({ /* compiler built-in */ });
770 ($fmt:expr) => ({ /* compiler built-in */ }); 684 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
771 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) 685}
772 } 686pub mod __export {
773 pub mod __export { 687 pub use crate::format_args;
774 pub use crate::format_args; 688 fn foo() {} // for index confusion
775 fn foo() {} // for index confusion 689}
776 } 690fn foo() -> i8 {}
777 fn foo() -> i8 {} 691 //^^^
778 fn test() { 692fn test() {
779 format!(\"{}\", fo<|>o()) 693 format!("{}", fo<|>o())
780 } 694}
781 ", 695"#,
782 "foo FN_DEF FileId(1) 398..415 401..404",
783 "fn foo() -> i8 {}|foo",
784 ); 696 );
785 } 697 }
786 698
787 #[test] 699 #[test]
788 fn goto_for_type_param() { 700 fn goto_for_type_param() {
789 check_goto( 701 check(
790 r#" 702 r#"
791 //- /lib.rs 703struct Foo<T: Clone> { t: <|>T }
792 struct Foo<T: Clone> { 704 //^
793 t: <|>T, 705"#,
794 }
795 "#,
796 "T TYPE_PARAM FileId(1) 11..19 11..12",
797 "T: Clone|T",
798 ); 706 );
799 } 707 }
800 708
801 #[test] 709 #[test]
802 fn goto_within_macro() { 710 fn goto_within_macro() {
803 check_goto( 711 check(
804 " 712 r#"
805 //- /lib.rs 713macro_rules! id {
806 macro_rules! id { 714 ($($tt:tt)*) => ($($tt)*)
807 ($($tt:tt)*) => ($($tt)*) 715}
808 }
809 716
810 fn foo() { 717fn foo() {
811 let x = 1; 718 let x = 1;
812 id!({ 719 //^
813 let y = <|>x; 720 id!({
814 let z = y; 721 let y = <|>x;
815 }); 722 let z = y;
816 } 723 });
817 ", 724}
818 "x BIND_PAT FileId(1) 69..70", 725"#,
819 "x",
820 ); 726 );
821 727
822 check_goto( 728 check(
823 " 729 r#"
824 //- /lib.rs 730macro_rules! id {
825 macro_rules! id { 731 ($($tt:tt)*) => ($($tt)*)
826 ($($tt:tt)*) => ($($tt)*) 732}
827 }
828 733
829 fn foo() { 734fn foo() {
830 let x = 1; 735 let x = 1;
831 id!({ 736 id!({
832 let y = x; 737 let y = x;
833 let z = <|>y; 738 //^
834 }); 739 let z = <|>y;
835 } 740 });
836 ", 741}
837 "y BIND_PAT FileId(1) 98..99", 742"#,
838 "y",
839 ); 743 );
840 } 744 }
841 745
842 #[test] 746 #[test]
843 fn goto_def_in_local_fn() { 747 fn goto_def_in_local_fn() {
844 check_goto( 748 check(
845 " 749 r#"
846 //- /lib.rs 750fn main() {
847 fn main() { 751 fn foo() {
848 fn foo() { 752 let x = 92;
849 let x = 92; 753 //^
850 <|>x; 754 <|>x;
851 } 755 }
852 } 756}
853 ", 757"#,
854 "x BIND_PAT FileId(1) 39..40",
855 "x",
856 ); 758 );
857 } 759 }
858 760
859 #[test] 761 #[test]
860 fn goto_def_in_local_macro() { 762 fn goto_def_in_local_macro() {
861 check_goto( 763 check(
862 r" 764 r#"
863 //- /lib.rs 765fn bar() {
864 fn bar() { 766 macro_rules! foo { () => { () } }
865 macro_rules! foo { () => { () } } 767 //^^^
866 <|>foo!(); 768 <|>foo!();
867 } 769}
868 ", 770"#,
869 "foo MACRO_CALL FileId(1) 15..48 28..31",
870 "macro_rules! foo { () => { () } }|foo",
871 ); 771 );
872 } 772 }
873 773
874 #[test] 774 #[test]
875 fn goto_def_for_field_init_shorthand() { 775 fn goto_def_for_field_init_shorthand() {
876 check_goto( 776 check(
877 " 777 r#"
878 //- /lib.rs 778struct Foo { x: i32 }
879 struct Foo { x: i32 } 779fn main() {
880 fn main() { 780 let x = 92;
881 let x = 92; 781 //^
882 Foo { x<|> }; 782 Foo { x<|> };
883 } 783}
884 ", 784"#,
885 "x BIND_PAT FileId(1) 42..43",
886 "x",
887 ) 785 )
888 } 786 }
787
788 #[test]
789 fn goto_def_for_enum_variant_field() {
790 check(
791 r#"
792enum Foo {
793 Bar { x: i32 }
794} //^
795fn baz(foo: Foo) {
796 match foo {
797 Foo::Bar { x<|> } => x
798 };
799}
800"#,
801 );
802 }
803
804 #[test]
805 fn goto_def_for_enum_variant_self_pattern_const() {
806 check(
807 r#"
808enum Foo { Bar }
809 //^^^
810impl Foo {
811 fn baz(self) {
812 match self { Self::Bar<|> => {} }
813 }
814}
815"#,
816 );
817 }
818
819 #[test]
820 fn goto_def_for_enum_variant_self_pattern_record() {
821 check(
822 r#"
823enum Foo { Bar { val: i32 } }
824 //^^^
825impl Foo {
826 fn baz(self) -> i32 {
827 match self { Self::Bar<|> { val } => {} }
828 }
829}
830"#,
831 );
832 }
833
834 #[test]
835 fn goto_def_for_enum_variant_self_expr_const() {
836 check(
837 r#"
838enum Foo { Bar }
839 //^^^
840impl Foo {
841 fn baz(self) { Self::Bar<|>; }
842}
843"#,
844 );
845 }
846
847 #[test]
848 fn goto_def_for_enum_variant_self_expr_record() {
849 check(
850 r#"
851enum Foo { Bar { val: i32 } }
852 //^^^
853impl Foo {
854 fn baz(self) { Self::Bar<|> {val: 4}; }
855}
856"#,
857 );
858 }
859
860 #[test]
861 fn goto_def_for_type_alias_generic_parameter() {
862 check(
863 r#"
864type Alias<T> = T<|>;
865 //^
866"#,
867 )
868 }
869
870 #[test]
871 fn goto_def_for_macro_container() {
872 check(
873 r#"
874//- /lib.rs
875foo::module<|>::mac!();
876
877//- /foo/lib.rs
878pub mod module {
879 //^^^^^^
880 #[macro_export]
881 macro_rules! _mac { () => { () } }
882 pub use crate::_mac as mac;
883}
884"#,
885 );
886 }
889} 887}
diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs
index 0cec0657e..9acc960fc 100644
--- a/crates/ra_ide/src/goto_implementation.rs
+++ b/crates/ra_ide/src/goto_implementation.rs
@@ -74,135 +74,156 @@ fn impls_for_trait(
74 74
75#[cfg(test)] 75#[cfg(test)]
76mod tests { 76mod tests {
77 use crate::mock_analysis::analysis_and_position; 77 use ra_db::FileRange;
78 78
79 fn check_goto(fixture: &str, expected: &[&str]) { 79 use crate::mock_analysis::MockAnalysis;
80 let (analysis, pos) = analysis_and_position(fixture);
81 80
82 let mut navs = analysis.goto_implementation(pos).unwrap().unwrap().info; 81 fn check(ra_fixture: &str) {
83 assert_eq!(navs.len(), expected.len()); 82 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
84 navs.sort_by_key(|nav| (nav.file_id(), nav.full_range().start())); 83 let annotations = mock.annotations();
85 navs.into_iter().enumerate().for_each(|(i, nav)| nav.assert_match(expected[i])); 84 let analysis = mock.analysis();
85
86 let navs = analysis.goto_implementation(position).unwrap().unwrap().info;
87
88 let key = |frange: &FileRange| (frange.file_id, frange.range.start());
89
90 let mut expected = annotations
91 .into_iter()
92 .map(|(range, data)| {
93 assert!(data.is_empty());
94 range
95 })
96 .collect::<Vec<_>>();
97 expected.sort_by_key(key);
98
99 let mut actual = navs
100 .into_iter()
101 .map(|nav| FileRange { file_id: nav.file_id(), range: nav.range() })
102 .collect::<Vec<_>>();
103 actual.sort_by_key(key);
104
105 assert_eq!(expected, actual);
86 } 106 }
87 107
88 #[test] 108 #[test]
89 fn goto_implementation_works() { 109 fn goto_implementation_works() {
90 check_goto( 110 check(
91 " 111 r#"
92 //- /lib.rs 112struct Foo<|>;
93 struct Foo<|>; 113impl Foo {}
94 impl Foo {} 114 //^^^
95 ", 115"#,
96 &["impl IMPL_DEF FileId(1) 12..23"],
97 ); 116 );
98 } 117 }
99 118
100 #[test] 119 #[test]
101 fn goto_implementation_works_multiple_blocks() { 120 fn goto_implementation_works_multiple_blocks() {
102 check_goto( 121 check(
103 " 122 r#"
104 //- /lib.rs 123struct Foo<|>;
105 struct Foo<|>; 124impl Foo {}
106 impl Foo {} 125 //^^^
107 impl Foo {} 126impl Foo {}
108 ", 127 //^^^
109 &["impl IMPL_DEF FileId(1) 12..23", "impl IMPL_DEF FileId(1) 24..35"], 128"#,
110 ); 129 );
111 } 130 }
112 131
113 #[test] 132 #[test]
114 fn goto_implementation_works_multiple_mods() { 133 fn goto_implementation_works_multiple_mods() {
115 check_goto( 134 check(
116 " 135 r#"
117 //- /lib.rs 136struct Foo<|>;
118 struct Foo<|>; 137mod a {
119 mod a { 138 impl super::Foo {}
120 impl super::Foo {} 139 //^^^^^^^^^^
121 } 140}
122 mod b { 141mod b {
123 impl super::Foo {} 142 impl super::Foo {}
124 } 143 //^^^^^^^^^^
125 ", 144}
126 &["impl IMPL_DEF FileId(1) 24..42", "impl IMPL_DEF FileId(1) 57..75"], 145"#,
127 ); 146 );
128 } 147 }
129 148
130 #[test] 149 #[test]
131 fn goto_implementation_works_multiple_files() { 150 fn goto_implementation_works_multiple_files() {
132 check_goto( 151 check(
133 " 152 r#"
134 //- /lib.rs 153//- /lib.rs
135 struct Foo<|>; 154struct Foo<|>;
136 mod a; 155mod a;
137 mod b; 156mod b;
138 //- /a.rs 157//- /a.rs
139 impl crate::Foo {} 158impl crate::Foo {}
140 //- /b.rs 159 //^^^^^^^^^^
141 impl crate::Foo {} 160//- /b.rs
142 ", 161impl crate::Foo {}
143 &["impl IMPL_DEF FileId(2) 0..18", "impl IMPL_DEF FileId(3) 0..18"], 162 //^^^^^^^^^^
163"#,
144 ); 164 );
145 } 165 }
146 166
147 #[test] 167 #[test]
148 fn goto_implementation_for_trait() { 168 fn goto_implementation_for_trait() {
149 check_goto( 169 check(
150 " 170 r#"
151 //- /lib.rs 171trait T<|> {}
152 trait T<|> {} 172struct Foo;
153 struct Foo; 173impl T for Foo {}
154 impl T for Foo {} 174 //^^^
155 ", 175"#,
156 &["impl IMPL_DEF FileId(1) 23..40"],
157 ); 176 );
158 } 177 }
159 178
160 #[test] 179 #[test]
161 fn goto_implementation_for_trait_multiple_files() { 180 fn goto_implementation_for_trait_multiple_files() {
162 check_goto( 181 check(
163 " 182 r#"
164 //- /lib.rs 183//- /lib.rs
165 trait T<|> {}; 184trait T<|> {};
166 struct Foo; 185struct Foo;
167 mod a; 186mod a;
168 mod b; 187mod b;
169 //- /a.rs 188//- /a.rs
170 impl crate::T for crate::Foo {} 189impl crate::T for crate::Foo {}
171 //- /b.rs 190 //^^^^^^^^^^
172 impl crate::T for crate::Foo {} 191//- /b.rs
173 ", 192impl crate::T for crate::Foo {}
174 &["impl IMPL_DEF FileId(2) 0..31", "impl IMPL_DEF FileId(3) 0..31"], 193 //^^^^^^^^^^
194 "#,
175 ); 195 );
176 } 196 }
177 197
178 #[test] 198 #[test]
179 fn goto_implementation_all_impls() { 199 fn goto_implementation_all_impls() {
180 check_goto( 200 check(
181 " 201 r#"
182 //- /lib.rs 202//- /lib.rs
183 trait T {} 203trait T {}
184 struct Foo<|>; 204struct Foo<|>;
185 impl Foo {} 205impl Foo {}
186 impl T for Foo {} 206 //^^^
187 impl T for &Foo {} 207impl T for Foo {}
188 ", 208 //^^^
189 &[ 209impl T for &Foo {}
190 "impl IMPL_DEF FileId(1) 23..34", 210 //^^^^
191 "impl IMPL_DEF FileId(1) 35..52", 211"#,
192 "impl IMPL_DEF FileId(1) 53..71",
193 ],
194 ); 212 );
195 } 213 }
196 214
197 #[test] 215 #[test]
198 fn goto_implementation_to_builtin_derive() { 216 fn goto_implementation_to_builtin_derive() {
199 check_goto( 217 check(
200 " 218 r#"
201 //- /lib.rs 219 #[derive(Copy)]
202 #[derive(Copy)] 220//^^^^^^^^^^^^^^^
203 struct Foo<|>; 221struct Foo<|>;
204 ", 222
205 &["impl IMPL_DEF FileId(1) 0..15"], 223mod marker {
224 trait Copy {}
225}
226"#,
206 ); 227 );
207 } 228 }
208} 229}
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index 91a3097fb..069cb283e 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -1,5 +1,5 @@
1use ra_ide_db::RootDatabase; 1use ra_ide_db::RootDatabase;
2use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; 2use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
3 3
4use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 4use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
5 5
@@ -25,8 +25,9 @@ pub(crate) fn goto_type_definition(
25 let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| { 25 let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| {
26 let ty = match_ast! { 26 let ty = match_ast! {
27 match node { 27 match node {
28 ast::Expr(expr) => sema.type_of_expr(&expr)?, 28 ast::Expr(it) => sema.type_of_expr(&it)?,
29 ast::Pat(pat) => sema.type_of_pat(&pat)?, 29 ast::Pat(it) => sema.type_of_pat(&it)?,
30 ast::SelfParam(it) => sema.type_of_self(&it)?,
30 _ => return None, 31 _ => return None,
31 } 32 }
32 }; 33 };
@@ -34,7 +35,7 @@ pub(crate) fn goto_type_definition(
34 Some((ty, node)) 35 Some((ty, node))
35 })?; 36 })?;
36 37
37 let adt_def = ty.autoderef(db).find_map(|ty| ty.as_adt())?; 38 let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?;
38 39
39 let nav = adt_def.to_nav(db); 40 let nav = adt_def.to_nav(db);
40 Some(RangeInfo::new(node.text_range(), vec![nav])) 41 Some(RangeInfo::new(node.text_range(), vec![nav]))
@@ -44,7 +45,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
44 return tokens.max_by_key(priority); 45 return tokens.max_by_key(priority);
45 fn priority(n: &SyntaxToken) -> usize { 46 fn priority(n: &SyntaxToken) -> usize {
46 match n.kind() { 47 match n.kind() {
47 IDENT | INT_NUMBER => 2, 48 IDENT | INT_NUMBER | T![self] => 2,
48 kind if kind.is_trivia() => 0, 49 kind if kind.is_trivia() => 0,
49 _ => 1, 50 _ => 1,
50 } 51 }
@@ -53,91 +54,98 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
53 54
54#[cfg(test)] 55#[cfg(test)]
55mod tests { 56mod tests {
56 use crate::mock_analysis::analysis_and_position; 57 use ra_db::FileRange;
57 58
58 fn check_goto(fixture: &str, expected: &str) { 59 use crate::mock_analysis::MockAnalysis;
59 let (analysis, pos) = analysis_and_position(fixture);
60 60
61 let mut navs = analysis.goto_type_definition(pos).unwrap().unwrap().info; 61 fn check(ra_fixture: &str) {
62 let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture);
63 let (expected, data) = mock.annotation();
64 assert!(data.is_empty());
65 let analysis = mock.analysis();
66
67 let mut navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
62 assert_eq!(navs.len(), 1); 68 assert_eq!(navs.len(), 1);
63 let nav = navs.pop().unwrap(); 69 let nav = navs.pop().unwrap();
64 nav.assert_match(expected); 70 assert_eq!(expected, FileRange { file_id: nav.file_id(), range: nav.range() });
65 } 71 }
66 72
67 #[test] 73 #[test]
68 fn goto_type_definition_works_simple() { 74 fn goto_type_definition_works_simple() {
69 check_goto( 75 check(
70 " 76 r#"
71 //- /lib.rs 77struct Foo;
72 struct Foo; 78 //^^^
73 fn foo() { 79fn foo() {
74 let f: Foo; 80 let f: Foo; f<|>
75 f<|> 81}
76 } 82"#,
77 ",
78 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
79 ); 83 );
80 } 84 }
81 85
82 #[test] 86 #[test]
83 fn goto_type_definition_works_simple_ref() { 87 fn goto_type_definition_works_simple_ref() {
84 check_goto( 88 check(
85 " 89 r#"
86 //- /lib.rs 90struct Foo;
87 struct Foo; 91 //^^^
88 fn foo() { 92fn foo() {
89 let f: &Foo; 93 let f: &Foo; f<|>
90 f<|> 94}
91 } 95"#,
92 ",
93 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
94 ); 96 );
95 } 97 }
96 98
97 #[test] 99 #[test]
98 fn goto_type_definition_works_through_macro() { 100 fn goto_type_definition_works_through_macro() {
99 check_goto( 101 check(
100 " 102 r#"
101 //- /lib.rs 103macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
102 macro_rules! id { 104struct Foo {}
103 ($($tt:tt)*) => { $($tt)* } 105 //^^^
104 } 106id! {
105 struct Foo {} 107 fn bar() { let f<|> = Foo {}; }
106 id! { 108}
107 fn bar() { 109"#,
108 let f<|> = Foo {};
109 }
110 }
111 ",
112 "Foo STRUCT_DEF FileId(1) 52..65 59..62",
113 ); 110 );
114 } 111 }
115 112
116 #[test] 113 #[test]
117 fn goto_type_definition_for_param() { 114 fn goto_type_definition_for_param() {
118 check_goto( 115 check(
119 " 116 r#"
120 //- /lib.rs 117struct Foo;
121 struct Foo; 118 //^^^
122 fn foo(<|>f: Foo) {} 119fn foo(<|>f: Foo) {}
123 ", 120"#,
124 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
125 ); 121 );
126 } 122 }
127 123
128 #[test] 124 #[test]
129 fn goto_type_definition_for_tuple_field() { 125 fn goto_type_definition_for_tuple_field() {
130 check_goto( 126 check(
131 " 127 r#"
132 //- /lib.rs 128struct Foo;
133 struct Foo; 129 //^^^
134 struct Bar(Foo); 130struct Bar(Foo);
135 fn foo() { 131fn foo() {
136 let bar = Bar(Foo); 132 let bar = Bar(Foo);
137 bar.<|>0; 133 bar.<|>0;
138 } 134}
139 ", 135"#,
140 "Foo STRUCT_DEF FileId(1) 0..11 7..10",
141 ); 136 );
142 } 137 }
138
139 #[test]
140 fn goto_def_for_self_param() {
141 check(
142 r#"
143struct Foo;
144 //^^^
145impl Foo {
146 fn f(&self<|>) {}
147}
148"#,
149 )
150 }
143} 151}
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index d96cb5596..a4c97e7f9 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,8 +1,6 @@
1use std::iter::once;
2
3use hir::{ 1use hir::{
4 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, 2 Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay,
5 ModuleSource, Semantics, 3 Module, ModuleDef, ModuleSource, Semantics,
6}; 4};
7use itertools::Itertools; 5use itertools::Itertools;
8use ra_db::SourceDatabase; 6use ra_db::SourceDatabase;
@@ -10,54 +8,66 @@ use ra_ide_db::{
10 defs::{classify_name, classify_name_ref, Definition}, 8 defs::{classify_name, classify_name_ref, Definition},
11 RootDatabase, 9 RootDatabase,
12}; 10};
13use ra_syntax::{ 11use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
14 ast::{self, DocCommentsOwner}, 12use stdx::format_to;
15 match_ast, AstNode, 13use test_utils::mark;
16 SyntaxKind::*,
17 SyntaxToken, TokenAtOffset,
18};
19 14
20use crate::{ 15use crate::{
21 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, 16 display::{macro_label, ShortLabel, ToNav, TryToNav},
22 FilePosition, RangeInfo, 17 markup::Markup,
18 runnables::runnable,
19 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
23}; 20};
24 21
25/// Contains the results when hovering over an item 22#[derive(Clone, Debug, PartialEq, Eq)]
26#[derive(Debug, Default)] 23pub struct HoverConfig {
27pub struct HoverResult { 24 pub implementations: bool,
28 results: Vec<String>, 25 pub run: bool,
26 pub debug: bool,
27 pub goto_type_def: bool,
29} 28}
30 29
31impl HoverResult { 30impl Default for HoverConfig {
32 pub fn new() -> HoverResult { 31 fn default() -> Self {
33 Self::default() 32 Self { implementations: true, run: true, debug: true, goto_type_def: true }
34 } 33 }
34}
35 35
36 pub fn extend(&mut self, item: Option<String>) { 36impl HoverConfig {
37 self.results.extend(item); 37 pub const NO_ACTIONS: Self =
38 } 38 Self { implementations: false, run: false, debug: false, goto_type_def: false };
39 39
40 pub fn is_empty(&self) -> bool { 40 pub fn any(&self) -> bool {
41 self.results.is_empty() 41 self.implementations || self.runnable() || self.goto_type_def
42 } 42 }
43 43
44 pub fn len(&self) -> usize { 44 pub fn none(&self) -> bool {
45 self.results.len() 45 !self.any()
46 } 46 }
47 47
48 pub fn first(&self) -> Option<&str> { 48 pub fn runnable(&self) -> bool {
49 self.results.first().map(String::as_str) 49 self.run || self.debug
50 } 50 }
51}
51 52
52 pub fn results(&self) -> &[String] { 53#[derive(Debug, Clone)]
53 &self.results 54pub enum HoverAction {
54 } 55 Runnable(Runnable),
56 Implementaion(FilePosition),
57 GoToType(Vec<HoverGotoTypeData>),
58}
55 59
56 /// Returns the results converted into markup 60#[derive(Debug, Clone, Eq, PartialEq)]
57 /// for displaying in a UI 61pub struct HoverGotoTypeData {
58 pub fn to_markup(&self) -> String { 62 pub mod_path: String,
59 self.results.join("\n\n---\n") 63 pub nav: NavigationTarget,
60 } 64}
65
66/// Contains the results when hovering over an item
67#[derive(Debug, Default)]
68pub struct HoverResult {
69 pub markup: Markup,
70 pub actions: Vec<HoverAction>,
61} 71}
62 72
63// Feature: Hover 73// Feature: Hover
@@ -70,23 +80,32 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
70 let token = pick_best(file.token_at_offset(position.offset))?; 80 let token = pick_best(file.token_at_offset(position.offset))?;
71 let token = sema.descend_into_macros(token); 81 let token = sema.descend_into_macros(token);
72 82
73 let mut res = HoverResult::new(); 83 let mut res = HoverResult::default();
74 84
75 if let Some((node, name_kind)) = match_ast! { 85 let node = token.parent();
76 match (token.parent()) { 86 let definition = match_ast! {
77 ast::NameRef(name_ref) => { 87 match node {
78 classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition())) 88 ast::NameRef(name_ref) => classify_name_ref(&sema, &name_ref).map(|d| d.definition()),
79 }, 89 ast::Name(name) => classify_name(&sema, &name).map(|d| d.definition()),
80 ast::Name(name) => {
81 classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
82 },
83 _ => None, 90 _ => None,
84 } 91 }
85 } { 92 };
86 let range = sema.original_range(&node).range; 93 if let Some(definition) = definition {
87 res.extend(hover_text_from_name_kind(db, name_kind)); 94 if let Some(markup) = hover_for_definition(db, definition) {
95 res.markup = markup;
96 if let Some(action) = show_implementations_action(db, definition) {
97 res.actions.push(action);
98 }
88 99
89 if !res.is_empty() { 100 if let Some(action) = runnable_action(&sema, definition, position.file_id) {
101 res.actions.push(action);
102 }
103
104 if let Some(action) = goto_type_action(db, definition) {
105 res.actions.push(action);
106 }
107
108 let range = sema.original_range(&node).range;
90 return Some(RangeInfo::new(range, res)); 109 return Some(RangeInfo::new(range, res));
91 } 110 }
92 } 111 }
@@ -97,35 +116,134 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
97 116
98 let ty = match_ast! { 117 let ty = match_ast! {
99 match node { 118 match node {
100 ast::MacroCall(_it) => { 119 ast::Expr(it) => sema.type_of_expr(&it)?,
101 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve. 120 ast::Pat(it) => sema.type_of_pat(&it)?,
102 // (e.g expanding a builtin macro). So we give up here. 121 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
103 return None; 122 // (e.g expanding a builtin macro). So we give up here.
104 }, 123 ast::MacroCall(_it) => return None,
105 ast::Expr(it) => { 124 _ => return None,
106 sema.type_of_expr(&it)
107 },
108 ast::Pat(it) => {
109 sema.type_of_pat(&it)
110 },
111 _ => None,
112 } 125 }
113 }?; 126 };
114 127
115 res.extend(Some(rust_code_markup(&ty.display(db)))); 128 res.markup = Markup::fenced_block(&ty.display(db));
116 let range = sema.original_range(&node).range; 129 let range = sema.original_range(&node).range;
117 Some(RangeInfo::new(range, res)) 130 Some(RangeInfo::new(range, res))
118} 131}
119 132
120fn hover_text( 133fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
134 fn to_action(nav_target: NavigationTarget) -> HoverAction {
135 HoverAction::Implementaion(FilePosition {
136 file_id: nav_target.file_id(),
137 offset: nav_target.range().start(),
138 })
139 }
140
141 match def {
142 Definition::ModuleDef(it) => match it {
143 ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))),
144 ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))),
145 ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))),
146 ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))),
147 _ => None,
148 },
149 _ => None,
150 }
151}
152
153fn runnable_action(
154 sema: &Semantics<RootDatabase>,
155 def: Definition,
156 file_id: FileId,
157) -> Option<HoverAction> {
158 match def {
159 Definition::ModuleDef(it) => match it {
160 ModuleDef::Module(it) => match it.definition_source(sema.db).value {
161 ModuleSource::Module(it) => runnable(&sema, it.syntax().clone(), file_id)
162 .map(|it| HoverAction::Runnable(it)),
163 _ => None,
164 },
165 ModuleDef::Function(it) => {
166 let src = it.source(sema.db);
167 if src.file_id != file_id.into() {
168 mark::hit!(hover_macro_generated_struct_fn_doc_comment);
169 mark::hit!(hover_macro_generated_struct_fn_doc_attr);
170
171 return None;
172 }
173
174 runnable(&sema, src.value.syntax().clone(), file_id)
175 .map(|it| HoverAction::Runnable(it))
176 }
177 _ => None,
178 },
179 _ => None,
180 }
181}
182
183fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
184 match def {
185 Definition::Local(it) => {
186 let mut targets: Vec<ModuleDef> = Vec::new();
187 let mut push_new_def = |item: ModuleDef| {
188 if !targets.contains(&item) {
189 targets.push(item);
190 }
191 };
192
193 it.ty(db).walk(db, |t| {
194 if let Some(adt) = t.as_adt() {
195 push_new_def(adt.into());
196 } else if let Some(trait_) = t.as_dyn_trait() {
197 push_new_def(trait_.into());
198 } else if let Some(traits) = t.as_impl_traits(db) {
199 traits.into_iter().for_each(|it| push_new_def(it.into()));
200 } else if let Some(trait_) = t.as_associated_type_parent_trait(db) {
201 push_new_def(trait_.into());
202 }
203 });
204
205 let targets = targets
206 .into_iter()
207 .filter_map(|it| {
208 Some(HoverGotoTypeData {
209 mod_path: render_path(
210 db,
211 it.module(db)?,
212 it.name(db).map(|name| name.to_string()),
213 ),
214 nav: it.try_to_nav(db)?,
215 })
216 })
217 .collect();
218
219 Some(HoverAction::GoToType(targets))
220 }
221 _ => None,
222 }
223}
224
225fn hover_markup(
121 docs: Option<String>, 226 docs: Option<String>,
122 desc: Option<String>, 227 desc: Option<String>,
123 mod_path: Option<String>, 228 mod_path: Option<String>,
124) -> Option<String> { 229) -> Option<Markup> {
125 if let Some(desc) = desc { 230 match desc {
126 Some(rust_code_markup_with_doc(&desc, docs.as_deref(), mod_path.as_deref())) 231 Some(desc) => {
127 } else { 232 let mut buf = String::new();
128 docs 233
234 if let Some(mod_path) = mod_path {
235 if !mod_path.is_empty() {
236 format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
237 }
238 }
239 format_to!(buf, "```rust\n{}\n```", desc);
240
241 if let Some(doc) = docs {
242 format_to!(buf, "\n___\n\n{}", doc);
243 }
244 Some(buf.into())
245 }
246 None => docs.map(Markup::from),
129 } 247 }
130} 248}
131 249
@@ -147,35 +265,35 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
147 .map(|name| name.to_string()) 265 .map(|name| name.to_string())
148} 266}
149 267
150fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> { 268fn render_path(db: &RootDatabase, module: Module, item_name: Option<String>) -> String {
151 let mod_path = def.module(db).map(|module| { 269 let crate_name =
152 once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string)) 270 db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string);
153 .chain( 271 let module_path = module
154 module 272 .path_to_root(db)
155 .path_to_root(db) 273 .into_iter()
156 .into_iter() 274 .rev()
157 .rev() 275 .flat_map(|it| it.name(db).map(|name| name.to_string()));
158 .map(|it| it.name(db).map(|name| name.to_string())), 276 crate_name.into_iter().chain(module_path).chain(item_name).join("::")
159 ) 277}
160 .chain(once(definition_owner_name(db, def))) 278
161 .flatten() 279fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option<String> {
162 .join("::") 280 def.module(db).map(|module| render_path(db, module, definition_owner_name(db, def)))
163 }); 281}
164 mod_path 282
165} 283fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
166 284 let mod_path = definition_mod_path(db, &def);
167fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<String> {
168 let mod_path = determine_mod_path(db, &def);
169 return match def { 285 return match def {
170 Definition::Macro(it) => { 286 Definition::Macro(it) => {
171 let src = it.source(db); 287 let src = it.source(db);
172 hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path) 288 let docs = Documentation::from_ast(&src.value).map(Into::into);
289 hover_markup(docs, Some(macro_label(&src.value)), mod_path)
173 } 290 }
174 Definition::Field(it) => { 291 Definition::Field(it) => {
175 let src = it.source(db); 292 let src = it.source(db);
176 match src.value { 293 match src.value {
177 FieldSource::Named(it) => { 294 FieldSource::Named(it) => {
178 hover_text(it.doc_comment_text(), it.short_label(), mod_path) 295 let docs = Documentation::from_ast(&it).map(Into::into);
296 hover_markup(docs, it.short_label(), mod_path)
179 } 297 }
180 _ => None, 298 _ => None,
181 } 299 }
@@ -183,7 +301,8 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
183 Definition::ModuleDef(it) => match it { 301 Definition::ModuleDef(it) => match it {
184 ModuleDef::Module(it) => match it.definition_source(db).value { 302 ModuleDef::Module(it) => match it.definition_source(db).value {
185 ModuleSource::Module(it) => { 303 ModuleSource::Module(it) => {
186 hover_text(it.doc_comment_text(), it.short_label(), mod_path) 304 let docs = Documentation::from_ast(&it).map(Into::into);
305 hover_markup(docs, it.short_label(), mod_path)
187 } 306 }
188 _ => None, 307 _ => None,
189 }, 308 },
@@ -196,22 +315,23 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
196 ModuleDef::Static(it) => from_def_source(db, it, mod_path), 315 ModuleDef::Static(it) => from_def_source(db, it, mod_path),
197 ModuleDef::Trait(it) => from_def_source(db, it, mod_path), 316 ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
198 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), 317 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
199 ModuleDef::BuiltinType(it) => Some(it.to_string()), 318 ModuleDef::BuiltinType(it) => return Some(it.to_string().into()),
200 }, 319 },
201 Definition::Local(it) => Some(rust_code_markup(&it.ty(db).display(db))), 320 Definition::Local(it) => return Some(Markup::fenced_block(&it.ty(db).display(db))),
202 Definition::TypeParam(_) | Definition::SelfType(_) => { 321 Definition::TypeParam(_) | Definition::SelfType(_) => {
203 // FIXME: Hover for generic param 322 // FIXME: Hover for generic param
204 None 323 None
205 } 324 }
206 }; 325 };
207 326
208 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> 327 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
209 where 328 where
210 D: HasSource<Ast = A>, 329 D: HasSource<Ast = A>,
211 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, 330 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner,
212 { 331 {
213 let src = def.source(db); 332 let src = def.source(db);
214 hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path) 333 let docs = Documentation::from_ast(&src.value).map(Into::into);
334 hover_markup(docs, src.value.short_label(), mod_path)
215 } 335 }
216} 336}
217 337
@@ -220,7 +340,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
220 fn priority(n: &SyntaxToken) -> usize { 340 fn priority(n: &SyntaxToken) -> usize {
221 match n.kind() { 341 match n.kind() {
222 IDENT | INT_NUMBER => 3, 342 IDENT | INT_NUMBER => 3,
223 L_PAREN | R_PAREN => 2, 343 T!['('] | T![')'] => 2,
224 kind if kind.is_trivia() => 0, 344 kind if kind.is_trivia() => 0,
225 _ => 1, 345 _ => 1,
226 } 346 }
@@ -229,649 +349,682 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
229 349
230#[cfg(test)] 350#[cfg(test)]
231mod tests { 351mod tests {
352 use expect::{expect, Expect};
232 use ra_db::FileLoader; 353 use ra_db::FileLoader;
233 use ra_syntax::TextRange;
234 354
235 use crate::mock_analysis::{analysis_and_position, single_file_with_position}; 355 use crate::mock_analysis::analysis_and_position;
236 356
237 fn trim_markup(s: &str) -> &str { 357 use super::*;
238 s.trim_start_matches("```rust\n").trim_end_matches("\n```")
239 }
240 358
241 fn trim_markup_opt(s: Option<&str>) -> Option<&str> { 359 fn check_hover_no_result(ra_fixture: &str) {
242 s.map(trim_markup) 360 let (analysis, position) = analysis_and_position(ra_fixture);
361 assert!(analysis.hover(position).unwrap().is_none());
243 } 362 }
244 363
245 fn check_hover_result(fixture: &str, expected: &[&str]) -> String { 364 fn check(ra_fixture: &str, expect: Expect) {
246 let (analysis, position) = analysis_and_position(fixture); 365 let (analysis, position) = analysis_and_position(ra_fixture);
247 let hover = analysis.hover(position).unwrap().unwrap(); 366 let hover = analysis.hover(position).unwrap().unwrap();
248 let mut results = Vec::from(hover.info.results());
249 results.sort();
250
251 for (markup, expected) in
252 results.iter().zip(expected.iter().chain(std::iter::repeat(&"<missing>")))
253 {
254 assert_eq!(trim_markup(&markup), *expected);
255 }
256
257 assert_eq!(hover.info.len(), expected.len());
258 367
259 let content = analysis.db.file_text(position.file_id); 368 let content = analysis.db.file_text(position.file_id);
260 content[hover.range].to_string() 369 let hovered_element = &content[hover.range];
370
371 let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup);
372 expect.assert_eq(&actual)
261 } 373 }
262 374
263 fn check_hover_no_result(fixture: &str) { 375 fn check_actions(ra_fixture: &str, expect: Expect) {
264 let (analysis, position) = analysis_and_position(fixture); 376 let (analysis, position) = analysis_and_position(ra_fixture);
265 assert!(analysis.hover(position).unwrap().is_none()); 377 let hover = analysis.hover(position).unwrap().unwrap();
378 expect.assert_debug_eq(&hover.info.actions)
266 } 379 }
267 380
268 #[test] 381 #[test]
269 fn hover_shows_type_of_an_expression() { 382 fn hover_shows_type_of_an_expression() {
270 let (analysis, position) = single_file_with_position( 383 check(
271 " 384 r#"
272 pub fn foo() -> u32 { 1 } 385pub fn foo() -> u32 { 1 }
273 386
274 fn main() { 387fn main() {
275 let foo_test = foo()<|>; 388 let foo_test = foo()<|>;
276 } 389}
277 ", 390"#,
391 expect![[r#"
392 *foo()*
393 ```rust
394 u32
395 ```
396 "#]],
278 ); 397 );
279 let hover = analysis.hover(position).unwrap().unwrap();
280 assert_eq!(hover.range, TextRange::new(95.into(), 100.into()));
281 assert_eq!(trim_markup_opt(hover.info.first()), Some("u32"));
282 } 398 }
283 399
284 #[test] 400 #[test]
285 fn hover_shows_long_type_of_an_expression() { 401 fn hover_shows_long_type_of_an_expression() {
286 check_hover_result( 402 check(
287 r#" 403 r#"
288 //- /main.rs 404struct Scan<A, B, C> { a: A, b: B, c: C }
289 struct Scan<A, B, C> { 405struct Iter<I> { inner: I }
290 a: A, 406enum Option<T> { Some(T), None }
291 b: B,
292 c: C,
293 }
294 407
295 struct FakeIter<I> { 408struct OtherStruct<T> { i: T }
296 inner: I,
297 }
298
299 struct OtherStruct<T> {
300 i: T,
301 }
302 409
303 enum FakeOption<T> { 410fn scan<A, B, C>(a: A, b: B, c: C) -> Iter<Scan<OtherStruct<A>, B, C>> {
304 Some(T), 411 Iter { inner: Scan { a, b, c } }
305 None, 412}
306 }
307
308 fn scan<A, B, C>(a: A, b: B, c: C) -> FakeIter<Scan<OtherStruct<A>, B, C>> {
309 FakeIter { inner: Scan { a, b, c } }
310 }
311 413
312 fn main() { 414fn main() {
313 let num: i32 = 55; 415 let num: i32 = 55;
314 let closure = |memo: &mut u32, value: &u32, _another: &mut u32| -> FakeOption<u32> { 416 let closure = |memo: &mut u32, value: &u32, _another: &mut u32| -> Option<u32> {
315 FakeOption::Some(*memo + value) 417 Option::Some(*memo + value)
316 }; 418 };
317 let number = 5u32; 419 let number = 5u32;
318 let mut iter<|> = scan(OtherStruct { i: num }, closure, number); 420 let mut iter<|> = scan(OtherStruct { i: num }, closure, number);
319 } 421}
320 "#, 422"#,
321 &["FakeIter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> FakeOption<u32>, u32>>"], 423 expect![[r#"
424 *iter*
425 ```rust
426 Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
427 ```
428 "#]],
322 ); 429 );
323 } 430 }
324 431
325 #[test] 432 #[test]
326 fn hover_shows_fn_signature() { 433 fn hover_shows_fn_signature() {
327 // Single file with result 434 // Single file with result
328 check_hover_result( 435 check(
329 r#" 436 r#"
330 //- /main.rs 437pub fn foo() -> u32 { 1 }
331 pub fn foo() -> u32 { 1 } 438
332 439fn main() { let foo_test = fo<|>o(); }
333 fn main() { 440"#,
334 let foo_test = fo<|>o(); 441 expect![[r#"
335 } 442 *foo*
336 "#, 443 ```rust
337 &["pub fn foo() -> u32"], 444 pub fn foo() -> u32
445 ```
446 "#]],
338 ); 447 );
339 448
340 // Multiple candidates but results are ambiguous. 449 // Multiple candidates but results are ambiguous.
341 check_hover_result( 450 check(
342 r#" 451 r#"
343 //- /a.rs 452//- /a.rs
344 pub fn foo() -> u32 { 1 } 453pub fn foo() -> u32 { 1 }
345 454
346 //- /b.rs 455//- /b.rs
347 pub fn foo() -> &str { "" } 456pub fn foo() -> &str { "" }
348 457
349 //- /c.rs 458//- /c.rs
350 pub fn foo(a: u32, b: u32) {} 459pub fn foo(a: u32, b: u32) {}
351 460
352 //- /main.rs 461//- /main.rs
353 mod a; 462mod a;
354 mod b; 463mod b;
355 mod c; 464mod c;
356 465
357 fn main() { 466fn main() { let foo_test = fo<|>o(); }
358 let foo_test = fo<|>o();
359 }
360 "#, 467 "#,
361 &["{unknown}"], 468 expect![[r#"
469 *foo*
470 ```rust
471 {unknown}
472 ```
473 "#]],
362 ); 474 );
363 } 475 }
364 476
365 #[test] 477 #[test]
366 fn hover_shows_fn_signature_with_type_params() { 478 fn hover_shows_fn_signature_with_type_params() {
367 check_hover_result( 479 check(
368 r#" 480 r#"
369 //- /main.rs 481pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
370 pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str { }
371 482
372 fn main() { 483fn main() { let foo_test = fo<|>o(); }
373 let foo_test = fo<|>o();
374 }
375 "#, 484 "#,
376 &["pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str"], 485 expect![[r#"
486 *foo*
487 ```rust
488 pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str
489 ```
490 "#]],
377 ); 491 );
378 } 492 }
379 493
380 #[test] 494 #[test]
381 fn hover_shows_fn_signature_on_fn_name() { 495 fn hover_shows_fn_signature_on_fn_name() {
382 check_hover_result( 496 check(
383 r#" 497 r#"
384 //- /main.rs 498pub fn foo<|>(a: u32, b: u32) -> u32 {}
385 pub fn foo<|>(a: u32, b: u32) -> u32 {} 499
386 500fn main() { }
387 fn main() { 501"#,
388 } 502 expect![[r#"
389 "#, 503 *foo*
390 &["pub fn foo(a: u32, b: u32) -> u32"], 504 ```rust
505 pub fn foo(a: u32, b: u32) -> u32
506 ```
507 "#]],
391 ); 508 );
392 } 509 }
393 510
394 #[test] 511 #[test]
395 fn hover_shows_struct_field_info() { 512 fn hover_shows_struct_field_info() {
396 // Hovering over the field when instantiating 513 // Hovering over the field when instantiating
397 check_hover_result( 514 check(
398 r#" 515 r#"
399 //- /main.rs 516struct Foo { field_a: u32 }
400 struct Foo {
401 field_a: u32,
402 }
403 517
404 fn main() { 518fn main() {
405 let foo = Foo { 519 let foo = Foo { field_a<|>: 0, };
406 field_a<|>: 0, 520}
407 }; 521"#,
408 } 522 expect![[r#"
409 "#, 523 *field_a*
410 &["Foo\n```\n\n```rust\nfield_a: u32"], 524 ```rust
525 Foo
526 ```
527
528 ```rust
529 field_a: u32
530 ```
531 "#]],
411 ); 532 );
412 533
413 // Hovering over the field in the definition 534 // Hovering over the field in the definition
414 check_hover_result( 535 check(
415 r#" 536 r#"
416 //- /main.rs 537struct Foo { field_a<|>: u32 }
417 struct Foo {
418 field_a<|>: u32,
419 }
420 538
421 fn main() { 539fn main() {
422 let foo = Foo { 540 let foo = Foo { field_a: 0 };
423 field_a: 0, 541}
424 }; 542"#,
425 } 543 expect![[r#"
426 "#, 544 *field_a*
427 &["Foo\n```\n\n```rust\nfield_a: u32"], 545 ```rust
546 Foo
547 ```
548
549 ```rust
550 field_a: u32
551 ```
552 "#]],
428 ); 553 );
429 } 554 }
430 555
431 #[test] 556 #[test]
432 fn hover_const_static() { 557 fn hover_const_static() {
433 check_hover_result( 558 check(
434 r#" 559 r#"const foo<|>: u32 = 0;"#,
435 //- /main.rs 560 expect![[r#"
436 const foo<|>: u32 = 0; 561 *foo*
437 "#, 562 ```rust
438 &["const foo: u32"], 563 const foo: u32
564 ```
565 "#]],
439 ); 566 );
440 567 check(
441 check_hover_result( 568 r#"static foo<|>: u32 = 0;"#,
442 r#" 569 expect![[r#"
443 //- /main.rs 570 *foo*
444 static foo<|>: u32 = 0; 571 ```rust
445 "#, 572 static foo: u32
446 &["static foo: u32"], 573 ```
574 "#]],
447 ); 575 );
448 } 576 }
449 577
450 #[test] 578 #[test]
451 fn hover_default_generic_types() { 579 fn hover_default_generic_types() {
452 check_hover_result( 580 check(
453 r#" 581 r#"
454//- /main.rs 582struct Test<K, T = u8> { k: K, t: T }
455struct Test<K, T = u8> {
456 k: K,
457 t: T,
458}
459 583
460fn main() { 584fn main() {
461 let zz<|> = Test { t: 23, k: 33 }; 585 let zz<|> = Test { t: 23u8, k: 33 };
462}"#, 586}"#,
463 &["Test<i32, u8>"], 587 expect![[r#"
588 *zz*
589 ```rust
590 Test<i32, u8>
591 ```
592 "#]],
464 ); 593 );
465 } 594 }
466 595
467 #[test] 596 #[test]
468 fn hover_some() { 597 fn hover_some() {
469 let (analysis, position) = single_file_with_position( 598 check(
470 " 599 r#"
471 enum Option<T> { Some(T) } 600enum Option<T> { Some(T) }
472 use Option::Some; 601use Option::Some;
473 602
474 fn main() { 603fn main() { So<|>me(12); }
475 So<|>me(12); 604"#,
476 } 605 expect![[r#"
477 ", 606 *Some*
607 ```rust
608 Option
609 ```
610
611 ```rust
612 Some
613 ```
614 "#]],
478 ); 615 );
479 let hover = analysis.hover(position).unwrap().unwrap();
480 assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n```\n\n```rust\nSome"));
481
482 let (analysis, position) = single_file_with_position(
483 "
484 enum Option<T> { Some(T) }
485 use Option::Some;
486 616
487 fn main() { 617 check(
488 let b<|>ar = Some(12); 618 r#"
489 } 619enum Option<T> { Some(T) }
490 ", 620use Option::Some;
621
622fn main() { let b<|>ar = Some(12); }
623"#,
624 expect![[r#"
625 *bar*
626 ```rust
627 Option<i32>
628 ```
629 "#]],
491 ); 630 );
492 let hover = analysis.hover(position).unwrap().unwrap();
493 assert_eq!(trim_markup_opt(hover.info.first()), Some("Option<i32>"));
494 } 631 }
495 632
496 #[test] 633 #[test]
497 fn hover_enum_variant() { 634 fn hover_enum_variant() {
498 check_hover_result( 635 check(
499 r#" 636 r#"
500 //- /main.rs 637enum Option<T> {
501 enum Option<T> { 638 /// The None variant
502 /// The None variant 639 Non<|>e
503 Non<|>e 640}
504 } 641"#,
505 "#, 642 expect![[r#"
506 &[" 643 *None*
507Option 644 ```rust
508``` 645 Option
509 646 ```
510```rust 647
511None 648 ```rust
512``` 649 None
513___ 650 ```
514 651 ___
515The None variant 652
516 " 653 The None variant
517 .trim()], 654 "#]],
518 ); 655 );
519 656
520 check_hover_result( 657 check(
521 r#" 658 r#"
522 //- /main.rs 659enum Option<T> {
523 enum Option<T> { 660 /// The Some variant
524 /// The Some variant 661 Some(T)
525 Some(T) 662}
526 } 663fn main() {
527 fn main() { 664 let s = Option::Som<|>e(12);
528 let s = Option::Som<|>e(12); 665}
529 } 666"#,
530 "#, 667 expect![[r#"
531 &[" 668 *Some*
532Option 669 ```rust
533``` 670 Option
534 671 ```
535```rust 672
536Some 673 ```rust
537``` 674 Some
538___ 675 ```
539 676 ___
540The Some variant 677
541 " 678 The Some variant
542 .trim()], 679 "#]],
543 ); 680 );
544 } 681 }
545 682
546 #[test] 683 #[test]
547 fn hover_for_local_variable() { 684 fn hover_for_local_variable() {
548 let (analysis, position) = single_file_with_position("fn func(foo: i32) { fo<|>o; }"); 685 check(
549 let hover = analysis.hover(position).unwrap().unwrap(); 686 r#"fn func(foo: i32) { fo<|>o; }"#,
550 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 687 expect![[r#"
688 *foo*
689 ```rust
690 i32
691 ```
692 "#]],
693 )
551 } 694 }
552 695
553 #[test] 696 #[test]
554 fn hover_for_local_variable_pat() { 697 fn hover_for_local_variable_pat() {
555 let (analysis, position) = single_file_with_position("fn func(fo<|>o: i32) {}"); 698 check(
556 let hover = analysis.hover(position).unwrap().unwrap(); 699 r#"fn func(fo<|>o: i32) {}"#,
557 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 700 expect![[r#"
701 *foo*
702 ```rust
703 i32
704 ```
705 "#]],
706 )
558 } 707 }
559 708
560 #[test] 709 #[test]
561 fn hover_local_var_edge() { 710 fn hover_local_var_edge() {
562 let (analysis, position) = single_file_with_position( 711 check(
563 " 712 r#"fn func(foo: i32) { if true { <|>foo; }; }"#,
564fn func(foo: i32) { if true { <|>foo; }; } 713 expect![[r#"
565", 714 *foo*
566 ); 715 ```rust
567 let hover = analysis.hover(position).unwrap().unwrap(); 716 i32
568 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 717 ```
718 "#]],
719 )
569 } 720 }
570 721
571 #[test] 722 #[test]
572 fn hover_for_param_edge() { 723 fn hover_for_param_edge() {
573 let (analysis, position) = single_file_with_position("fn func(<|>foo: i32) {}"); 724 check(
574 let hover = analysis.hover(position).unwrap().unwrap(); 725 r#"fn func(<|>foo: i32) {}"#,
575 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 726 expect![[r#"
727 *foo*
728 ```rust
729 i32
730 ```
731 "#]],
732 )
576 } 733 }
577 734
578 #[test] 735 #[test]
579 fn test_hover_infer_associated_method_result() { 736 fn test_hover_infer_associated_method_result() {
580 let (analysis, position) = single_file_with_position( 737 check(
581 " 738 r#"
582 struct Thing { x: u32 } 739struct Thing { x: u32 }
583 740
584 impl Thing { 741impl Thing {
585 fn new() -> Thing { 742 fn new() -> Thing { Thing { x: 0 } }
586 Thing { x: 0 } 743}
587 }
588 }
589 744
590 fn main() { 745fn main() { let foo_<|>test = Thing::new(); }
591 let foo_<|>test = Thing::new(); 746 "#,
592 } 747 expect![[r#"
593 ", 748 *foo_test*
594 ); 749 ```rust
595 let hover = analysis.hover(position).unwrap().unwrap(); 750 Thing
596 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); 751 ```
752 "#]],
753 )
597 } 754 }
598 755
599 #[test] 756 #[test]
600 fn test_hover_infer_associated_method_exact() { 757 fn test_hover_infer_associated_method_exact() {
601 let (analysis, position) = single_file_with_position( 758 check(
602 " 759 r#"
603 mod wrapper { 760mod wrapper {
604 struct Thing { x: u32 } 761 struct Thing { x: u32 }
605 762
606 impl Thing { 763 impl Thing {
607 fn new() -> Thing { 764 fn new() -> Thing { Thing { x: 0 } }
608 Thing { x: 0 } 765 }
609 } 766}
610 }
611 }
612 767
613 fn main() { 768fn main() { let foo_test = wrapper::Thing::new<|>(); }
614 let foo_test = wrapper::Thing::new<|>(); 769"#,
615 } 770 expect![[r#"
616 ", 771 *new*
617 ); 772 ```rust
618 let hover = analysis.hover(position).unwrap().unwrap(); 773 wrapper::Thing
619 assert_eq!( 774 ```
620 trim_markup_opt(hover.info.first()), 775
621 Some("wrapper::Thing\n```\n\n```rust\nfn new() -> Thing") 776 ```rust
622 ); 777 fn new() -> Thing
778 ```
779 "#]],
780 )
623 } 781 }
624 782
625 #[test] 783 #[test]
626 fn test_hover_infer_associated_const_in_pattern() { 784 fn test_hover_infer_associated_const_in_pattern() {
627 let (analysis, position) = single_file_with_position( 785 check(
628 " 786 r#"
629 struct X; 787struct X;
630 impl X { 788impl X {
631 const C: u32 = 1; 789 const C: u32 = 1;
632 } 790}
633 791
634 fn main() { 792fn main() {
635 match 1 { 793 match 1 {
636 X::C<|> => {}, 794 X::C<|> => {},
637 2 => {}, 795 2 => {},
638 _ => {} 796 _ => {}
639 }; 797 };
640 } 798}
641 ", 799"#,
642 ); 800 expect![[r#"
643 let hover = analysis.hover(position).unwrap().unwrap(); 801 *C*
644 assert_eq!(trim_markup_opt(hover.info.first()), Some("const C: u32")); 802 ```rust
803 const C: u32
804 ```
805 "#]],
806 )
645 } 807 }
646 808
647 #[test] 809 #[test]
648 fn test_hover_self() { 810 fn test_hover_self() {
649 let (analysis, position) = single_file_with_position( 811 check(
650 " 812 r#"
651 struct Thing { x: u32 } 813struct Thing { x: u32 }
652 impl Thing { 814impl Thing {
653 fn new() -> Self { 815 fn new() -> Self { Self<|> { x: 0 } }
654 Self<|> { x: 0 } 816}
655 } 817"#,
656 } 818 expect![[r#"
657 ", 819 *Self { x: 0 }*
658 ); 820 ```rust
659 let hover = analysis.hover(position).unwrap().unwrap(); 821 Thing
660 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); 822 ```
661 823 "#]],
662 /* FIXME: revive these tests 824 )
663 let (analysis, position) = single_file_with_position( 825 } /* FIXME: revive these tests
664 " 826 let (analysis, position) = analysis_and_position(
665 struct Thing { x: u32 } 827 "
666 impl Thing { 828 struct Thing { x: u32 }
667 fn new() -> Self<|> { 829 impl Thing {
668 Self { x: 0 } 830 fn new() -> Self<|> {
669 } 831 Self { x: 0 }
670 } 832 }
671 ", 833 }
672 ); 834 ",
673 835 );
674 let hover = analysis.hover(position).unwrap().unwrap(); 836
675 assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing")); 837 let hover = analysis.hover(position).unwrap().unwrap();
676 838 assert_eq!(trim_markup(&hover.info.markup.as_str()), ("Thing"));
677 let (analysis, position) = single_file_with_position( 839
678 " 840 let (analysis, position) = analysis_and_position(
679 enum Thing { A } 841 "
680 impl Thing { 842 enum Thing { A }
681 pub fn new() -> Self<|> { 843 impl Thing {
682 Thing::A 844 pub fn new() -> Self<|> {
683 } 845 Thing::A
684 } 846 }
685 ", 847 }
686 ); 848 ",
687 let hover = analysis.hover(position).unwrap().unwrap(); 849 );
688 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing")); 850 let hover = analysis.hover(position).unwrap().unwrap();
689 851 assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing"));
690 let (analysis, position) = single_file_with_position( 852
691 " 853 let (analysis, position) = analysis_and_position(
692 enum Thing { A } 854 "
693 impl Thing { 855 enum Thing { A }
694 pub fn thing(a: Self<|>) { 856 impl Thing {
695 } 857 pub fn thing(a: Self<|>) {
696 } 858 }
697 ", 859 }
698 ); 860 ",
699 let hover = analysis.hover(position).unwrap().unwrap(); 861 );
700 assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing")); 862 let hover = analysis.hover(position).unwrap().unwrap();
701 */ 863 assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing"));
702 } 864 */
703 865
704 #[test] 866 #[test]
705 fn test_hover_shadowing_pat() { 867 fn test_hover_shadowing_pat() {
706 let (analysis, position) = single_file_with_position( 868 check(
707 " 869 r#"
708 fn x() {} 870fn x() {}
709 871
710 fn y() { 872fn y() {
711 let x = 0i32; 873 let x = 0i32;
712 x<|>; 874 x<|>;
713 } 875}
714 ", 876"#,
715 ); 877 expect![[r#"
716 let hover = analysis.hover(position).unwrap().unwrap(); 878 *x*
717 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 879 ```rust
880 i32
881 ```
882 "#]],
883 )
718 } 884 }
719 885
720 #[test] 886 #[test]
721 fn test_hover_macro_invocation() { 887 fn test_hover_macro_invocation() {
722 let (analysis, position) = single_file_with_position( 888 check(
723 " 889 r#"
724 macro_rules! foo { 890macro_rules! foo { () => {} }
725 () => {} 891
726 } 892fn f() { fo<|>o!(); }
727 893"#,
728 fn f() { 894 expect![[r#"
729 fo<|>o!(); 895 *foo*
730 } 896 ```rust
731 ", 897 macro_rules! foo
732 ); 898 ```
733 let hover = analysis.hover(position).unwrap().unwrap(); 899 "#]],
734 assert_eq!(trim_markup_opt(hover.info.first()), Some("macro_rules! foo")); 900 )
735 } 901 }
736 902
737 #[test] 903 #[test]
738 fn test_hover_tuple_field() { 904 fn test_hover_tuple_field() {
739 let (analysis, position) = single_file_with_position( 905 check(
740 " 906 r#"struct TS(String, i32<|>);"#,
741 struct TS(String, i32<|>); 907 expect![[r#"
742 ", 908 *i32*
743 ); 909 i32
744 let hover = analysis.hover(position).unwrap().unwrap(); 910 "#]],
745 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 911 )
746 } 912 }
747 913
748 #[test] 914 #[test]
749 fn test_hover_through_macro() { 915 fn test_hover_through_macro() {
750 let hover_on = check_hover_result( 916 check(
751 " 917 r#"
752 //- /lib.rs 918macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
753 macro_rules! id { 919fn foo() {}
754 ($($tt:tt)*) => { $($tt)* } 920id! {
755 } 921 fn bar() { fo<|>o(); }
756 fn foo() {} 922}
757 id! { 923"#,
758 fn bar() { 924 expect![[r#"
759 fo<|>o(); 925 *foo*
760 } 926 ```rust
761 } 927 fn foo()
762 ", 928 ```
763 &["fn foo()"], 929 "#]],
764 ); 930 );
765
766 assert_eq!(hover_on, "foo")
767 } 931 }
768 932
769 #[test] 933 #[test]
770 fn test_hover_through_expr_in_macro() { 934 fn test_hover_through_expr_in_macro() {
771 let hover_on = check_hover_result( 935 check(
772 " 936 r#"
773 //- /lib.rs 937macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
774 macro_rules! id { 938fn foo(bar:u32) { let a = id!(ba<|>r); }
775 ($($tt:tt)*) => { $($tt)* } 939"#,
776 } 940 expect![[r#"
777 fn foo(bar:u32) { 941 *bar*
778 let a = id!(ba<|>r); 942 ```rust
779 } 943 u32
780 ", 944 ```
781 &["u32"], 945 "#]],
782 ); 946 );
783
784 assert_eq!(hover_on, "bar")
785 } 947 }
786 948
787 #[test] 949 #[test]
788 fn test_hover_through_expr_in_macro_recursive() { 950 fn test_hover_through_expr_in_macro_recursive() {
789 let hover_on = check_hover_result( 951 check(
790 " 952 r#"
791 //- /lib.rs 953macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
792 macro_rules! id_deep { 954macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
793 ($($tt:tt)*) => { $($tt)* } 955fn foo(bar:u32) { let a = id!(ba<|>r); }
794 } 956"#,
795 macro_rules! id { 957 expect![[r#"
796 ($($tt:tt)*) => { id_deep!($($tt)*) } 958 *bar*
797 } 959 ```rust
798 fn foo(bar:u32) { 960 u32
799 let a = id!(ba<|>r); 961 ```
800 } 962 "#]],
801 ",
802 &["u32"],
803 ); 963 );
804
805 assert_eq!(hover_on, "bar")
806 } 964 }
807 965
808 #[test] 966 #[test]
809 fn test_hover_through_func_in_macro_recursive() { 967 fn test_hover_through_func_in_macro_recursive() {
810 let hover_on = check_hover_result( 968 check(
811 " 969 r#"
812 //- /lib.rs 970macro_rules! id_deep { ($($tt:tt)*) => { $($tt)* } }
813 macro_rules! id_deep { 971macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
814 ($($tt:tt)*) => { $($tt)* } 972fn bar() -> u32 { 0 }
815 } 973fn foo() { let a = id!([0u32, bar(<|>)] ); }
816 macro_rules! id { 974"#,
817 ($($tt:tt)*) => { id_deep!($($tt)*) } 975 expect![[r#"
818 } 976 *bar()*
819 fn bar() -> u32 { 977 ```rust
820 0 978 u32
821 } 979 ```
822 fn foo() { 980 "#]],
823 let a = id!([0u32, bar(<|>)] );
824 }
825 ",
826 &["u32"],
827 ); 981 );
828
829 assert_eq!(hover_on, "bar()")
830 } 982 }
831 983
832 #[test] 984 #[test]
833 fn test_hover_through_literal_string_in_macro() { 985 fn test_hover_through_literal_string_in_macro() {
834 let hover_on = check_hover_result( 986 check(
835 r#" 987 r#"
836 //- /lib.rs 988macro_rules! arr { ($($tt:tt)*) => { [$($tt)*)] } }
837 macro_rules! arr { 989fn foo() {
838 ($($tt:tt)*) => { [$($tt)*)] } 990 let mastered_for_itunes = "";
839 } 991 let _ = arr!("Tr<|>acks", &mastered_for_itunes);
840 fn foo() { 992}
841 let mastered_for_itunes = ""; 993"#,
842 let _ = arr!("Tr<|>acks", &mastered_for_itunes); 994 expect![[r#"
843 } 995 *"Tracks"*
844 "#, 996 ```rust
845 &["&str"], 997 &str
998 ```
999 "#]],
846 ); 1000 );
847
848 assert_eq!(hover_on, "\"Tracks\"");
849 } 1001 }
850 1002
851 #[test] 1003 #[test]
852 fn test_hover_through_assert_macro() { 1004 fn test_hover_through_assert_macro() {
853 let hover_on = check_hover_result( 1005 check(
854 r#" 1006 r#"
855 //- /lib.rs 1007#[rustc_builtin_macro]
856 #[rustc_builtin_macro] 1008macro_rules! assert {}
857 macro_rules! assert {}
858 1009
859 fn bar() -> bool { true } 1010fn bar() -> bool { true }
860 fn foo() { 1011fn foo() {
861 assert!(ba<|>r()); 1012 assert!(ba<|>r());
862 } 1013}
863 "#, 1014"#,
864 &["fn bar() -> bool"], 1015 expect![[r#"
1016 *bar*
1017 ```rust
1018 fn bar() -> bool
1019 ```
1020 "#]],
865 ); 1021 );
866
867 assert_eq!(hover_on, "bar");
868 } 1022 }
869 1023
870 #[test] 1024 #[test]
871 fn test_hover_through_literal_string_in_builtin_macro() { 1025 fn test_hover_through_literal_string_in_builtin_macro() {
872 check_hover_no_result( 1026 check_hover_no_result(
873 r#" 1027 r#"
874 //- /lib.rs
875 #[rustc_builtin_macro] 1028 #[rustc_builtin_macro]
876 macro_rules! format {} 1029 macro_rules! format {}
877 1030
@@ -884,71 +1037,1351 @@ fn func(foo: i32) { if true { <|>foo; }; }
884 1037
885 #[test] 1038 #[test]
886 fn test_hover_non_ascii_space_doc() { 1039 fn test_hover_non_ascii_space_doc() {
887 check_hover_result( 1040 check(
888 " 1041 "
889 //- /lib.rs 1042/// <- `\u{3000}` here
890 /// <- `\u{3000}` here 1043fn foo() { }
891 fn foo() {
892 }
893 1044
894 fn bar() { 1045fn bar() { fo<|>o(); }
895 fo<|>o(); 1046",
896 } 1047 expect![[r#"
897 ", 1048 *foo*
898 &["fn foo()\n```\n___\n\n<- `\u{3000}` here"], 1049 ```rust
1050 fn foo()
1051 ```
1052 ___
1053
1054 <- ` ` here
1055 "#]],
899 ); 1056 );
900 } 1057 }
901 1058
902 #[test] 1059 #[test]
903 fn test_hover_function_show_qualifiers() { 1060 fn test_hover_function_show_qualifiers() {
904 check_hover_result( 1061 check(
905 " 1062 r#"async fn foo<|>() {}"#,
906 //- /lib.rs 1063 expect![[r#"
907 async fn foo<|>() {} 1064 *foo*
908 ", 1065 ```rust
909 &["async fn foo()"], 1066 async fn foo()
1067 ```
1068 "#]],
910 ); 1069 );
911 check_hover_result( 1070 check(
912 " 1071 r#"pub const unsafe fn foo<|>() {}"#,
913 //- /lib.rs 1072 expect![[r#"
914 pub const unsafe fn foo<|>() {} 1073 *foo*
915 ", 1074 ```rust
916 &["pub const unsafe fn foo()"], 1075 pub const unsafe fn foo()
1076 ```
1077 "#]],
917 ); 1078 );
918 check_hover_result( 1079 check(
919 r#" 1080 r#"pub(crate) async unsafe extern "C" fn foo<|>() {}"#,
920 //- /lib.rs 1081 expect![[r#"
921 pub(crate) async unsafe extern "C" fn foo<|>() {} 1082 *foo*
922 "#, 1083 ```rust
923 &[r#"pub(crate) async unsafe extern "C" fn foo()"#], 1084 pub(crate) async unsafe extern "C" fn foo()
1085 ```
1086 "#]],
924 ); 1087 );
925 } 1088 }
926 1089
927 #[test] 1090 #[test]
928 fn test_hover_trait_show_qualifiers() { 1091 fn test_hover_trait_show_qualifiers() {
929 check_hover_result( 1092 check_actions(
930 " 1093 r"unsafe trait foo<|>() {}",
931 //- /lib.rs 1094 expect![[r#"
932 unsafe trait foo<|>() {} 1095 [
933 ", 1096 Implementaion(
934 &["unsafe trait foo"], 1097 FilePosition {
1098 file_id: FileId(
1099 1,
1100 ),
1101 offset: 13,
1102 },
1103 ),
1104 ]
1105 "#]],
935 ); 1106 );
936 } 1107 }
937 1108
938 #[test] 1109 #[test]
939 fn test_hover_mod_with_same_name_as_function() { 1110 fn test_hover_mod_with_same_name_as_function() {
940 check_hover_result( 1111 check(
941 " 1112 r#"
942 //- /lib.rs 1113use self::m<|>y::Bar;
943 use self::m<|>y::Bar; 1114mod my { pub struct Bar; }
1115
1116fn my() {}
1117"#,
1118 expect![[r#"
1119 *my*
1120 ```rust
1121 mod my
1122 ```
1123 "#]],
1124 );
1125 }
944 1126
945 mod my { 1127 #[test]
946 pub struct Bar; 1128 fn test_hover_struct_doc_comment() {
947 } 1129 check(
1130 r#"
1131/// bar docs
1132struct Bar;
1133
1134fn foo() { let bar = Ba<|>r; }
1135"#,
1136 expect![[r#"
1137 *Bar*
1138 ```rust
1139 struct Bar
1140 ```
1141 ___
1142
1143 bar docs
1144 "#]],
1145 );
1146 }
1147
1148 #[test]
1149 fn test_hover_struct_doc_attr() {
1150 check(
1151 r#"
1152#[doc = "bar docs"]
1153struct Bar;
1154
1155fn foo() { let bar = Ba<|>r; }
1156"#,
1157 expect![[r#"
1158 *Bar*
1159 ```rust
1160 struct Bar
1161 ```
1162 ___
1163
1164 bar docs
1165 "#]],
1166 );
1167 }
1168
1169 #[test]
1170 fn test_hover_struct_doc_attr_multiple_and_mixed() {
1171 check(
1172 r#"
1173/// bar docs 0
1174#[doc = "bar docs 1"]
1175#[doc = "bar docs 2"]
1176struct Bar;
1177
1178fn foo() { let bar = Ba<|>r; }
1179"#,
1180 expect![[r#"
1181 *Bar*
1182 ```rust
1183 struct Bar
1184 ```
1185 ___
1186
1187 bar docs 0
1188
1189 bar docs 1
1190
1191 bar docs 2
1192 "#]],
1193 );
1194 }
1195
1196 #[test]
1197 fn test_hover_macro_generated_struct_fn_doc_comment() {
1198 mark::check!(hover_macro_generated_struct_fn_doc_comment);
1199
1200 check(
1201 r#"
1202macro_rules! bar {
1203 () => {
1204 struct Bar;
1205 impl Bar {
1206 /// Do the foo
1207 fn foo(&self) {}
1208 }
1209 }
1210}
1211
1212bar!();
1213
1214fn foo() { let bar = Bar; bar.fo<|>o(); }
1215"#,
1216 expect![[r#"
1217 *foo*
1218 ```rust
1219 Bar
1220 ```
1221
1222 ```rust
1223 fn foo(&self)
1224 ```
1225 ___
1226
1227 Do the foo
1228 "#]],
1229 );
1230 }
1231
1232 #[test]
1233 fn test_hover_macro_generated_struct_fn_doc_attr() {
1234 mark::check!(hover_macro_generated_struct_fn_doc_attr);
1235
1236 check(
1237 r#"
1238macro_rules! bar {
1239 () => {
1240 struct Bar;
1241 impl Bar {
1242 #[doc = "Do the foo"]
1243 fn foo(&self) {}
1244 }
1245 }
1246}
1247
1248bar!();
1249
1250fn foo() { let bar = Bar; bar.fo<|>o(); }
1251"#,
1252 expect![[r#"
1253 *foo*
1254 ```rust
1255 Bar
1256 ```
1257
1258 ```rust
1259 fn foo(&self)
1260 ```
1261 ___
1262
1263 Do the foo
1264 "#]],
1265 );
1266 }
1267
1268 #[test]
1269 fn test_hover_trait_has_impl_action() {
1270 check_actions(
1271 r#"trait foo<|>() {}"#,
1272 expect![[r#"
1273 [
1274 Implementaion(
1275 FilePosition {
1276 file_id: FileId(
1277 1,
1278 ),
1279 offset: 6,
1280 },
1281 ),
1282 ]
1283 "#]],
1284 );
1285 }
1286
1287 #[test]
1288 fn test_hover_struct_has_impl_action() {
1289 check_actions(
1290 r"struct foo<|>() {}",
1291 expect![[r#"
1292 [
1293 Implementaion(
1294 FilePosition {
1295 file_id: FileId(
1296 1,
1297 ),
1298 offset: 7,
1299 },
1300 ),
1301 ]
1302 "#]],
1303 );
1304 }
1305
1306 #[test]
1307 fn test_hover_union_has_impl_action() {
1308 check_actions(
1309 r#"union foo<|>() {}"#,
1310 expect![[r#"
1311 [
1312 Implementaion(
1313 FilePosition {
1314 file_id: FileId(
1315 1,
1316 ),
1317 offset: 6,
1318 },
1319 ),
1320 ]
1321 "#]],
1322 );
1323 }
1324
1325 #[test]
1326 fn test_hover_enum_has_impl_action() {
1327 check_actions(
1328 r"enum foo<|>() { A, B }",
1329 expect![[r#"
1330 [
1331 Implementaion(
1332 FilePosition {
1333 file_id: FileId(
1334 1,
1335 ),
1336 offset: 5,
1337 },
1338 ),
1339 ]
1340 "#]],
1341 );
1342 }
1343
1344 #[test]
1345 fn test_hover_test_has_action() {
1346 check_actions(
1347 r#"
1348#[test]
1349fn foo_<|>test() {}
1350"#,
1351 expect![[r#"
1352 [
1353 Runnable(
1354 Runnable {
1355 nav: NavigationTarget {
1356 file_id: FileId(
1357 1,
1358 ),
1359 full_range: 0..24,
1360 name: "foo_test",
1361 kind: FN_DEF,
1362 focus_range: Some(
1363 11..19,
1364 ),
1365 container_name: None,
1366 description: None,
1367 docs: None,
1368 },
1369 kind: Test {
1370 test_id: Path(
1371 "foo_test",
1372 ),
1373 attr: TestAttr {
1374 ignore: false,
1375 },
1376 },
1377 cfg_exprs: [],
1378 },
1379 ),
1380 ]
1381 "#]],
1382 );
1383 }
1384
1385 #[test]
1386 fn test_hover_test_mod_has_action() {
1387 check_actions(
1388 r#"
1389mod tests<|> {
1390 #[test]
1391 fn foo_test() {}
1392}
1393"#,
1394 expect![[r#"
1395 [
1396 Runnable(
1397 Runnable {
1398 nav: NavigationTarget {
1399 file_id: FileId(
1400 1,
1401 ),
1402 full_range: 0..46,
1403 name: "tests",
1404 kind: MODULE,
1405 focus_range: Some(
1406 4..9,
1407 ),
1408 container_name: None,
1409 description: None,
1410 docs: None,
1411 },
1412 kind: TestMod {
1413 path: "tests",
1414 },
1415 cfg_exprs: [],
1416 },
1417 ),
1418 ]
1419 "#]],
1420 );
1421 }
1422
1423 #[test]
1424 fn test_hover_struct_has_goto_type_action() {
1425 check_actions(
1426 r#"
1427struct S{ f1: u32 }
1428
1429fn main() { let s<|>t = S{ f1:0 }; }
1430 "#,
1431 expect![[r#"
1432 [
1433 GoToType(
1434 [
1435 HoverGotoTypeData {
1436 mod_path: "S",
1437 nav: NavigationTarget {
1438 file_id: FileId(
1439 1,
1440 ),
1441 full_range: 0..19,
1442 name: "S",
1443 kind: STRUCT_DEF,
1444 focus_range: Some(
1445 7..8,
1446 ),
1447 container_name: None,
1448 description: Some(
1449 "struct S",
1450 ),
1451 docs: None,
1452 },
1453 },
1454 ],
1455 ),
1456 ]
1457 "#]],
1458 );
1459 }
1460
1461 #[test]
1462 fn test_hover_generic_struct_has_goto_type_actions() {
1463 check_actions(
1464 r#"
1465struct Arg(u32);
1466struct S<T>{ f1: T }
1467
1468fn main() { let s<|>t = S{ f1:Arg(0) }; }
1469"#,
1470 expect![[r#"
1471 [
1472 GoToType(
1473 [
1474 HoverGotoTypeData {
1475 mod_path: "S",
1476 nav: NavigationTarget {
1477 file_id: FileId(
1478 1,
1479 ),
1480 full_range: 17..37,
1481 name: "S",
1482 kind: STRUCT_DEF,
1483 focus_range: Some(
1484 24..25,
1485 ),
1486 container_name: None,
1487 description: Some(
1488 "struct S",
1489 ),
1490 docs: None,
1491 },
1492 },
1493 HoverGotoTypeData {
1494 mod_path: "Arg",
1495 nav: NavigationTarget {
1496 file_id: FileId(
1497 1,
1498 ),
1499 full_range: 0..16,
1500 name: "Arg",
1501 kind: STRUCT_DEF,
1502 focus_range: Some(
1503 7..10,
1504 ),
1505 container_name: None,
1506 description: Some(
1507 "struct Arg",
1508 ),
1509 docs: None,
1510 },
1511 },
1512 ],
1513 ),
1514 ]
1515 "#]],
1516 );
1517 }
1518
1519 #[test]
1520 fn test_hover_generic_struct_has_flattened_goto_type_actions() {
1521 check_actions(
1522 r#"
1523struct Arg(u32);
1524struct S<T>{ f1: T }
1525
1526fn main() { let s<|>t = S{ f1: S{ f1: Arg(0) } }; }
1527 "#,
1528 expect![[r#"
1529 [
1530 GoToType(
1531 [
1532 HoverGotoTypeData {
1533 mod_path: "S",
1534 nav: NavigationTarget {
1535 file_id: FileId(
1536 1,
1537 ),
1538 full_range: 17..37,
1539 name: "S",
1540 kind: STRUCT_DEF,
1541 focus_range: Some(
1542 24..25,
1543 ),
1544 container_name: None,
1545 description: Some(
1546 "struct S",
1547 ),
1548 docs: None,
1549 },
1550 },
1551 HoverGotoTypeData {
1552 mod_path: "Arg",
1553 nav: NavigationTarget {
1554 file_id: FileId(
1555 1,
1556 ),
1557 full_range: 0..16,
1558 name: "Arg",
1559 kind: STRUCT_DEF,
1560 focus_range: Some(
1561 7..10,
1562 ),
1563 container_name: None,
1564 description: Some(
1565 "struct Arg",
1566 ),
1567 docs: None,
1568 },
1569 },
1570 ],
1571 ),
1572 ]
1573 "#]],
1574 );
1575 }
1576
1577 #[test]
1578 fn test_hover_tuple_has_goto_type_actions() {
1579 check_actions(
1580 r#"
1581struct A(u32);
1582struct B(u32);
1583mod M {
1584 pub struct C(u32);
1585}
1586
1587fn main() { let s<|>t = (A(1), B(2), M::C(3) ); }
1588"#,
1589 expect![[r#"
1590 [
1591 GoToType(
1592 [
1593 HoverGotoTypeData {
1594 mod_path: "A",
1595 nav: NavigationTarget {
1596 file_id: FileId(
1597 1,
1598 ),
1599 full_range: 0..14,
1600 name: "A",
1601 kind: STRUCT_DEF,
1602 focus_range: Some(
1603 7..8,
1604 ),
1605 container_name: None,
1606 description: Some(
1607 "struct A",
1608 ),
1609 docs: None,
1610 },
1611 },
1612 HoverGotoTypeData {
1613 mod_path: "B",
1614 nav: NavigationTarget {
1615 file_id: FileId(
1616 1,
1617 ),
1618 full_range: 15..29,
1619 name: "B",
1620 kind: STRUCT_DEF,
1621 focus_range: Some(
1622 22..23,
1623 ),
1624 container_name: None,
1625 description: Some(
1626 "struct B",
1627 ),
1628 docs: None,
1629 },
1630 },
1631 HoverGotoTypeData {
1632 mod_path: "M::C",
1633 nav: NavigationTarget {
1634 file_id: FileId(
1635 1,
1636 ),
1637 full_range: 42..60,
1638 name: "C",
1639 kind: STRUCT_DEF,
1640 focus_range: Some(
1641 53..54,
1642 ),
1643 container_name: None,
1644 description: Some(
1645 "pub struct C",
1646 ),
1647 docs: None,
1648 },
1649 },
1650 ],
1651 ),
1652 ]
1653 "#]],
1654 );
1655 }
1656
1657 #[test]
1658 fn test_hover_return_impl_trait_has_goto_type_action() {
1659 check_actions(
1660 r#"
1661trait Foo {}
1662fn foo() -> impl Foo {}
1663
1664fn main() { let s<|>t = foo(); }
1665"#,
1666 expect![[r#"
1667 [
1668 GoToType(
1669 [
1670 HoverGotoTypeData {
1671 mod_path: "Foo",
1672 nav: NavigationTarget {
1673 file_id: FileId(
1674 1,
1675 ),
1676 full_range: 0..12,
1677 name: "Foo",
1678 kind: TRAIT_DEF,
1679 focus_range: Some(
1680 6..9,
1681 ),
1682 container_name: None,
1683 description: Some(
1684 "trait Foo",
1685 ),
1686 docs: None,
1687 },
1688 },
1689 ],
1690 ),
1691 ]
1692 "#]],
1693 );
1694 }
1695
1696 #[test]
1697 fn test_hover_generic_return_impl_trait_has_goto_type_action() {
1698 check_actions(
1699 r#"
1700trait Foo<T> {}
1701struct S;
1702fn foo() -> impl Foo<S> {}
1703
1704fn main() { let s<|>t = foo(); }
1705"#,
1706 expect![[r#"
1707 [
1708 GoToType(
1709 [
1710 HoverGotoTypeData {
1711 mod_path: "Foo",
1712 nav: NavigationTarget {
1713 file_id: FileId(
1714 1,
1715 ),
1716 full_range: 0..15,
1717 name: "Foo",
1718 kind: TRAIT_DEF,
1719 focus_range: Some(
1720 6..9,
1721 ),
1722 container_name: None,
1723 description: Some(
1724 "trait Foo",
1725 ),
1726 docs: None,
1727 },
1728 },
1729 HoverGotoTypeData {
1730 mod_path: "S",
1731 nav: NavigationTarget {
1732 file_id: FileId(
1733 1,
1734 ),
1735 full_range: 16..25,
1736 name: "S",
1737 kind: STRUCT_DEF,
1738 focus_range: Some(
1739 23..24,
1740 ),
1741 container_name: None,
1742 description: Some(
1743 "struct S",
1744 ),
1745 docs: None,
1746 },
1747 },
1748 ],
1749 ),
1750 ]
1751 "#]],
1752 );
1753 }
1754
1755 #[test]
1756 fn test_hover_return_impl_traits_has_goto_type_action() {
1757 check_actions(
1758 r#"
1759trait Foo {}
1760trait Bar {}
1761fn foo() -> impl Foo + Bar {}
1762
1763fn main() { let s<|>t = foo(); }
1764 "#,
1765 expect![[r#"
1766 [
1767 GoToType(
1768 [
1769 HoverGotoTypeData {
1770 mod_path: "Foo",
1771 nav: NavigationTarget {
1772 file_id: FileId(
1773 1,
1774 ),
1775 full_range: 0..12,
1776 name: "Foo",
1777 kind: TRAIT_DEF,
1778 focus_range: Some(
1779 6..9,
1780 ),
1781 container_name: None,
1782 description: Some(
1783 "trait Foo",
1784 ),
1785 docs: None,
1786 },
1787 },
1788 HoverGotoTypeData {
1789 mod_path: "Bar",
1790 nav: NavigationTarget {
1791 file_id: FileId(
1792 1,
1793 ),
1794 full_range: 13..25,
1795 name: "Bar",
1796 kind: TRAIT_DEF,
1797 focus_range: Some(
1798 19..22,
1799 ),
1800 container_name: None,
1801 description: Some(
1802 "trait Bar",
1803 ),
1804 docs: None,
1805 },
1806 },
1807 ],
1808 ),
1809 ]
1810 "#]],
1811 );
1812 }
1813
1814 #[test]
1815 fn test_hover_generic_return_impl_traits_has_goto_type_action() {
1816 check_actions(
1817 r#"
1818trait Foo<T> {}
1819trait Bar<T> {}
1820struct S1 {}
1821struct S2 {}
1822
1823fn foo() -> impl Foo<S1> + Bar<S2> {}
1824
1825fn main() { let s<|>t = foo(); }
1826"#,
1827 expect![[r#"
1828 [
1829 GoToType(
1830 [
1831 HoverGotoTypeData {
1832 mod_path: "Foo",
1833 nav: NavigationTarget {
1834 file_id: FileId(
1835 1,
1836 ),
1837 full_range: 0..15,
1838 name: "Foo",
1839 kind: TRAIT_DEF,
1840 focus_range: Some(
1841 6..9,
1842 ),
1843 container_name: None,
1844 description: Some(
1845 "trait Foo",
1846 ),
1847 docs: None,
1848 },
1849 },
1850 HoverGotoTypeData {
1851 mod_path: "Bar",
1852 nav: NavigationTarget {
1853 file_id: FileId(
1854 1,
1855 ),
1856 full_range: 16..31,
1857 name: "Bar",
1858 kind: TRAIT_DEF,
1859 focus_range: Some(
1860 22..25,
1861 ),
1862 container_name: None,
1863 description: Some(
1864 "trait Bar",
1865 ),
1866 docs: None,
1867 },
1868 },
1869 HoverGotoTypeData {
1870 mod_path: "S1",
1871 nav: NavigationTarget {
1872 file_id: FileId(
1873 1,
1874 ),
1875 full_range: 32..44,
1876 name: "S1",
1877 kind: STRUCT_DEF,
1878 focus_range: Some(
1879 39..41,
1880 ),
1881 container_name: None,
1882 description: Some(
1883 "struct S1",
1884 ),
1885 docs: None,
1886 },
1887 },
1888 HoverGotoTypeData {
1889 mod_path: "S2",
1890 nav: NavigationTarget {
1891 file_id: FileId(
1892 1,
1893 ),
1894 full_range: 45..57,
1895 name: "S2",
1896 kind: STRUCT_DEF,
1897 focus_range: Some(
1898 52..54,
1899 ),
1900 container_name: None,
1901 description: Some(
1902 "struct S2",
1903 ),
1904 docs: None,
1905 },
1906 },
1907 ],
1908 ),
1909 ]
1910 "#]],
1911 );
1912 }
1913
1914 #[test]
1915 fn test_hover_arg_impl_trait_has_goto_type_action() {
1916 check_actions(
1917 r#"
1918trait Foo {}
1919fn foo(ar<|>g: &impl Foo) {}
1920"#,
1921 expect![[r#"
1922 [
1923 GoToType(
1924 [
1925 HoverGotoTypeData {
1926 mod_path: "Foo",
1927 nav: NavigationTarget {
1928 file_id: FileId(
1929 1,
1930 ),
1931 full_range: 0..12,
1932 name: "Foo",
1933 kind: TRAIT_DEF,
1934 focus_range: Some(
1935 6..9,
1936 ),
1937 container_name: None,
1938 description: Some(
1939 "trait Foo",
1940 ),
1941 docs: None,
1942 },
1943 },
1944 ],
1945 ),
1946 ]
1947 "#]],
1948 );
1949 }
1950
1951 #[test]
1952 fn test_hover_arg_impl_traits_has_goto_type_action() {
1953 check_actions(
1954 r#"
1955trait Foo {}
1956trait Bar<T> {}
1957struct S{}
1958
1959fn foo(ar<|>g: &impl Foo + Bar<S>) {}
1960"#,
1961 expect![[r#"
1962 [
1963 GoToType(
1964 [
1965 HoverGotoTypeData {
1966 mod_path: "Foo",
1967 nav: NavigationTarget {
1968 file_id: FileId(
1969 1,
1970 ),
1971 full_range: 0..12,
1972 name: "Foo",
1973 kind: TRAIT_DEF,
1974 focus_range: Some(
1975 6..9,
1976 ),
1977 container_name: None,
1978 description: Some(
1979 "trait Foo",
1980 ),
1981 docs: None,
1982 },
1983 },
1984 HoverGotoTypeData {
1985 mod_path: "Bar",
1986 nav: NavigationTarget {
1987 file_id: FileId(
1988 1,
1989 ),
1990 full_range: 13..28,
1991 name: "Bar",
1992 kind: TRAIT_DEF,
1993 focus_range: Some(
1994 19..22,
1995 ),
1996 container_name: None,
1997 description: Some(
1998 "trait Bar",
1999 ),
2000 docs: None,
2001 },
2002 },
2003 HoverGotoTypeData {
2004 mod_path: "S",
2005 nav: NavigationTarget {
2006 file_id: FileId(
2007 1,
2008 ),
2009 full_range: 29..39,
2010 name: "S",
2011 kind: STRUCT_DEF,
2012 focus_range: Some(
2013 36..37,
2014 ),
2015 container_name: None,
2016 description: Some(
2017 "struct S",
2018 ),
2019 docs: None,
2020 },
2021 },
2022 ],
2023 ),
2024 ]
2025 "#]],
2026 );
2027 }
2028
2029 #[test]
2030 fn test_hover_arg_generic_impl_trait_has_goto_type_action() {
2031 check_actions(
2032 r#"
2033trait Foo<T> {}
2034struct S {}
2035fn foo(ar<|>g: &impl Foo<S>) {}
2036"#,
2037 expect![[r#"
2038 [
2039 GoToType(
2040 [
2041 HoverGotoTypeData {
2042 mod_path: "Foo",
2043 nav: NavigationTarget {
2044 file_id: FileId(
2045 1,
2046 ),
2047 full_range: 0..15,
2048 name: "Foo",
2049 kind: TRAIT_DEF,
2050 focus_range: Some(
2051 6..9,
2052 ),
2053 container_name: None,
2054 description: Some(
2055 "trait Foo",
2056 ),
2057 docs: None,
2058 },
2059 },
2060 HoverGotoTypeData {
2061 mod_path: "S",
2062 nav: NavigationTarget {
2063 file_id: FileId(
2064 1,
2065 ),
2066 full_range: 16..27,
2067 name: "S",
2068 kind: STRUCT_DEF,
2069 focus_range: Some(
2070 23..24,
2071 ),
2072 container_name: None,
2073 description: Some(
2074 "struct S",
2075 ),
2076 docs: None,
2077 },
2078 },
2079 ],
2080 ),
2081 ]
2082 "#]],
2083 );
2084 }
2085
2086 #[test]
2087 fn test_hover_dyn_return_has_goto_type_action() {
2088 check_actions(
2089 r#"
2090trait Foo {}
2091struct S;
2092impl Foo for S {}
2093
2094struct B<T>{}
2095fn foo() -> B<dyn Foo> {}
2096
2097fn main() { let s<|>t = foo(); }
2098"#,
2099 expect![[r#"
2100 [
2101 GoToType(
2102 [
2103 HoverGotoTypeData {
2104 mod_path: "B",
2105 nav: NavigationTarget {
2106 file_id: FileId(
2107 1,
2108 ),
2109 full_range: 42..55,
2110 name: "B",
2111 kind: STRUCT_DEF,
2112 focus_range: Some(
2113 49..50,
2114 ),
2115 container_name: None,
2116 description: Some(
2117 "struct B",
2118 ),
2119 docs: None,
2120 },
2121 },
2122 HoverGotoTypeData {
2123 mod_path: "Foo",
2124 nav: NavigationTarget {
2125 file_id: FileId(
2126 1,
2127 ),
2128 full_range: 0..12,
2129 name: "Foo",
2130 kind: TRAIT_DEF,
2131 focus_range: Some(
2132 6..9,
2133 ),
2134 container_name: None,
2135 description: Some(
2136 "trait Foo",
2137 ),
2138 docs: None,
2139 },
2140 },
2141 ],
2142 ),
2143 ]
2144 "#]],
2145 );
2146 }
2147
2148 #[test]
2149 fn test_hover_dyn_arg_has_goto_type_action() {
2150 check_actions(
2151 r#"
2152trait Foo {}
2153fn foo(ar<|>g: &dyn Foo) {}
2154"#,
2155 expect![[r#"
2156 [
2157 GoToType(
2158 [
2159 HoverGotoTypeData {
2160 mod_path: "Foo",
2161 nav: NavigationTarget {
2162 file_id: FileId(
2163 1,
2164 ),
2165 full_range: 0..12,
2166 name: "Foo",
2167 kind: TRAIT_DEF,
2168 focus_range: Some(
2169 6..9,
2170 ),
2171 container_name: None,
2172 description: Some(
2173 "trait Foo",
2174 ),
2175 docs: None,
2176 },
2177 },
2178 ],
2179 ),
2180 ]
2181 "#]],
2182 );
2183 }
2184
2185 #[test]
2186 fn test_hover_generic_dyn_arg_has_goto_type_action() {
2187 check_actions(
2188 r#"
2189trait Foo<T> {}
2190struct S {}
2191fn foo(ar<|>g: &dyn Foo<S>) {}
2192"#,
2193 expect![[r#"
2194 [
2195 GoToType(
2196 [
2197 HoverGotoTypeData {
2198 mod_path: "Foo",
2199 nav: NavigationTarget {
2200 file_id: FileId(
2201 1,
2202 ),
2203 full_range: 0..15,
2204 name: "Foo",
2205 kind: TRAIT_DEF,
2206 focus_range: Some(
2207 6..9,
2208 ),
2209 container_name: None,
2210 description: Some(
2211 "trait Foo",
2212 ),
2213 docs: None,
2214 },
2215 },
2216 HoverGotoTypeData {
2217 mod_path: "S",
2218 nav: NavigationTarget {
2219 file_id: FileId(
2220 1,
2221 ),
2222 full_range: 16..27,
2223 name: "S",
2224 kind: STRUCT_DEF,
2225 focus_range: Some(
2226 23..24,
2227 ),
2228 container_name: None,
2229 description: Some(
2230 "struct S",
2231 ),
2232 docs: None,
2233 },
2234 },
2235 ],
2236 ),
2237 ]
2238 "#]],
2239 );
2240 }
2241
2242 #[test]
2243 fn test_hover_goto_type_action_links_order() {
2244 check_actions(
2245 r#"
2246trait ImplTrait<T> {}
2247trait DynTrait<T> {}
2248struct B<T> {}
2249struct S {}
2250
2251fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
2252 "#,
2253 expect![[r#"
2254 [
2255 GoToType(
2256 [
2257 HoverGotoTypeData {
2258 mod_path: "ImplTrait",
2259 nav: NavigationTarget {
2260 file_id: FileId(
2261 1,
2262 ),
2263 full_range: 0..21,
2264 name: "ImplTrait",
2265 kind: TRAIT_DEF,
2266 focus_range: Some(
2267 6..15,
2268 ),
2269 container_name: None,
2270 description: Some(
2271 "trait ImplTrait",
2272 ),
2273 docs: None,
2274 },
2275 },
2276 HoverGotoTypeData {
2277 mod_path: "B",
2278 nav: NavigationTarget {
2279 file_id: FileId(
2280 1,
2281 ),
2282 full_range: 43..57,
2283 name: "B",
2284 kind: STRUCT_DEF,
2285 focus_range: Some(
2286 50..51,
2287 ),
2288 container_name: None,
2289 description: Some(
2290 "struct B",
2291 ),
2292 docs: None,
2293 },
2294 },
2295 HoverGotoTypeData {
2296 mod_path: "DynTrait",
2297 nav: NavigationTarget {
2298 file_id: FileId(
2299 1,
2300 ),
2301 full_range: 22..42,
2302 name: "DynTrait",
2303 kind: TRAIT_DEF,
2304 focus_range: Some(
2305 28..36,
2306 ),
2307 container_name: None,
2308 description: Some(
2309 "trait DynTrait",
2310 ),
2311 docs: None,
2312 },
2313 },
2314 HoverGotoTypeData {
2315 mod_path: "S",
2316 nav: NavigationTarget {
2317 file_id: FileId(
2318 1,
2319 ),
2320 full_range: 58..69,
2321 name: "S",
2322 kind: STRUCT_DEF,
2323 focus_range: Some(
2324 65..66,
2325 ),
2326 container_name: None,
2327 description: Some(
2328 "struct S",
2329 ),
2330 docs: None,
2331 },
2332 },
2333 ],
2334 ),
2335 ]
2336 "#]],
2337 );
2338 }
2339
2340 #[test]
2341 fn test_hover_associated_type_has_goto_type_action() {
2342 check_actions(
2343 r#"
2344trait Foo {
2345 type Item;
2346 fn get(self) -> Self::Item {}
2347}
948 2348
949 fn my() {} 2349struct Bar{}
950 ", 2350struct S{}
951 &["mod my"], 2351
2352impl Foo for S { type Item = Bar; }
2353
2354fn test() -> impl Foo { S {} }
2355
2356fn main() { let s<|>t = test().get(); }
2357"#,
2358 expect![[r#"
2359 [
2360 GoToType(
2361 [
2362 HoverGotoTypeData {
2363 mod_path: "Foo",
2364 nav: NavigationTarget {
2365 file_id: FileId(
2366 1,
2367 ),
2368 full_range: 0..62,
2369 name: "Foo",
2370 kind: TRAIT_DEF,
2371 focus_range: Some(
2372 6..9,
2373 ),
2374 container_name: None,
2375 description: Some(
2376 "trait Foo",
2377 ),
2378 docs: None,
2379 },
2380 },
2381 ],
2382 ),
2383 ]
2384 "#]],
952 ); 2385 );
953 } 2386 }
954} 2387}
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 75bd3c96b..3cbae8a45 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -3,7 +3,7 @@ use ra_ide_db::RootDatabase;
3use ra_prof::profile; 3use ra_prof::profile;
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, 5 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, 6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
7}; 7};
8 8
9use crate::{FileId, FunctionSignature}; 9use crate::{FileId, FunctionSignature};
@@ -112,7 +112,7 @@ fn get_chaining_hints(
112 // Ignoring extra whitespace and comments 112 // Ignoring extra whitespace and comments
113 let next = tokens.next()?.kind(); 113 let next = tokens.next()?.kind();
114 let next_next = tokens.next()?.kind(); 114 let next_next = tokens.next()?.kind();
115 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { 115 if next == SyntaxKind::WHITESPACE && next_next == T![.] {
116 let ty = sema.type_of_expr(&expr)?; 116 let ty = sema.type_of_expr(&expr)?;
117 if ty.is_unknown() { 117 if ty.is_unknown() {
118 return None; 118 return None;
@@ -149,11 +149,10 @@ fn get_param_name_hints(
149 ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(), 149 ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(),
150 _ => return None, 150 _ => return None,
151 }; 151 };
152 let args_count = args.clone().count();
153 152
154 let fn_signature = get_fn_signature(sema, &expr)?; 153 let fn_signature = get_fn_signature(sema, &expr)?;
155 let n_params_to_skip = 154 let n_params_to_skip =
156 if fn_signature.has_self_param && fn_signature.parameter_names.len() > args_count { 155 if fn_signature.has_self_param && matches!(&expr, ast::Expr::MethodCallExpr(_)) {
157 1 156 1
158 } else { 157 } else {
159 0 158 0
@@ -259,6 +258,7 @@ fn should_show_param_name_hint(
259 if param_name.is_empty() 258 if param_name.is_empty()
260 || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) 259 || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_'))
261 || is_argument_similar_to_param_name(sema, argument, param_name) 260 || is_argument_similar_to_param_name(sema, argument, param_name)
261 || param_name.starts_with("ra_fixture")
262 { 262 {
263 return false; 263 return false;
264 } 264 }
@@ -271,7 +271,7 @@ fn should_show_param_name_hint(
271 271
272 // avoid displaying hints for common functions like map, filter, etc. 272 // avoid displaying hints for common functions like map, filter, etc.
273 // or other obvious words used in std 273 // or other obvious words used in std
274 parameters_len != 1 || !is_obvious_param(param_name) 274 !(parameters_len == 1 && is_obvious_param(param_name))
275} 275}
276 276
277fn is_argument_similar_to_param_name( 277fn is_argument_similar_to_param_name(
@@ -313,10 +313,8 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
313} 313}
314 314
315fn is_obvious_param(param_name: &str) -> bool { 315fn is_obvious_param(param_name: &str) -> bool {
316 let is_obvious_param_name = match param_name { 316 let is_obvious_param_name =
317 "predicate" | "value" | "pat" | "rhs" | "other" => true, 317 matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
318 _ => false,
319 };
320 param_name.len() == 1 || is_obvious_param_name 318 param_name.len() == 1 || is_obvious_param_name
321} 319}
322 320
@@ -326,13 +324,13 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
326 // FIXME: Type::as_callable is broken for closures 324 // FIXME: Type::as_callable is broken for closures
327 let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?; 325 let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?;
328 match callable_def { 326 match callable_def {
329 hir::CallableDef::FunctionId(it) => { 327 hir::CallableDefId::FunctionId(it) => {
330 Some(FunctionSignature::from_hir(sema.db, it.into())) 328 Some(FunctionSignature::from_hir(sema.db, it.into()))
331 } 329 }
332 hir::CallableDef::StructId(it) => { 330 hir::CallableDefId::StructId(it) => {
333 FunctionSignature::from_struct(sema.db, it.into()) 331 FunctionSignature::from_struct(sema.db, it.into())
334 } 332 }
335 hir::CallableDef::EnumVariantId(it) => { 333 hir::CallableDefId::EnumVariantId(it) => {
336 FunctionSignature::from_enum_variant(sema.db, it.into()) 334 FunctionSignature::from_enum_variant(sema.db, it.into())
337 } 335 }
338 } 336 }
@@ -347,581 +345,252 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
347 345
348#[cfg(test)] 346#[cfg(test)]
349mod tests { 347mod tests {
350 use crate::inlay_hints::InlayHintsConfig; 348 use expect::{expect, Expect};
351 use insta::assert_debug_snapshot; 349 use test_utils::extract_annotations;
350
351 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file};
352 352
353 use crate::mock_analysis::single_file; 353 fn check(ra_fixture: &str) {
354 check_with_config(InlayHintsConfig::default(), ra_fixture);
355 }
356
357 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
358 let (analysis, file_id) = single_file(ra_fixture);
359 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
360 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
361 let actual =
362 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
363 assert_eq!(expected, actual);
364 }
365
366 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
367 let (analysis, file_id) = single_file(ra_fixture);
368 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
369 expect.assert_debug_eq(&inlay_hints)
370 }
354 371
355 #[test] 372 #[test]
356 fn param_hints_only() { 373 fn param_hints_only() {
357 let (analysis, file_id) = single_file( 374 check_with_config(
375 InlayHintsConfig {
376 parameter_hints: true,
377 type_hints: false,
378 chaining_hints: false,
379 max_length: None,
380 },
358 r#" 381 r#"
359 fn foo(a: i32, b: i32) -> i32 { a + b } 382fn foo(a: i32, b: i32) -> i32 { a + b }
360 fn main() { 383fn main() {
361 let _x = foo(4, 4); 384 let _x = foo(
362 }"#, 385 4,
386 //^ a
387 4,
388 //^ b
389 );
390}"#,
363 ); 391 );
364 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
365 [
366 InlayHint {
367 range: 106..107,
368 kind: ParameterHint,
369 label: "a",
370 },
371 InlayHint {
372 range: 109..110,
373 kind: ParameterHint,
374 label: "b",
375 },
376 ]"###);
377 } 392 }
378 393
379 #[test] 394 #[test]
380 fn hints_disabled() { 395 fn hints_disabled() {
381 let (analysis, file_id) = single_file( 396 check_with_config(
397 InlayHintsConfig {
398 type_hints: false,
399 parameter_hints: false,
400 chaining_hints: false,
401 max_length: None,
402 },
382 r#" 403 r#"
383 fn foo(a: i32, b: i32) -> i32 { a + b } 404fn foo(a: i32, b: i32) -> i32 { a + b }
384 fn main() { 405fn main() {
385 let _x = foo(4, 4); 406 let _x = foo(4, 4);
386 }"#, 407}"#,
387 ); 408 );
388 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
389 } 409 }
390 410
391 #[test] 411 #[test]
392 fn type_hints_only() { 412 fn type_hints_only() {
393 let (analysis, file_id) = single_file( 413 check_with_config(
414 InlayHintsConfig {
415 type_hints: true,
416 parameter_hints: false,
417 chaining_hints: false,
418 max_length: None,
419 },
394 r#" 420 r#"
395 fn foo(a: i32, b: i32) -> i32 { a + b } 421fn foo(a: i32, b: i32) -> i32 { a + b }
396 fn main() { 422fn main() {
397 let _x = foo(4, 4); 423 let _x = foo(4, 4);
398 }"#, 424 //^^ i32
425}"#,
399 ); 426 );
400 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
401 [
402 InlayHint {
403 range: 97..99,
404 kind: TypeHint,
405 label: "i32",
406 },
407 ]"###);
408 } 427 }
428
409 #[test] 429 #[test]
410 fn default_generic_types_should_not_be_displayed() { 430 fn default_generic_types_should_not_be_displayed() {
411 let (analysis, file_id) = single_file( 431 check(
412 r#" 432 r#"
413struct Test<K, T = u8> { 433struct Test<K, T = u8> { k: K, t: T }
414 k: K,
415 t: T,
416}
417 434
418fn main() { 435fn main() {
419 let zz = Test { t: 23, k: 33 }; 436 let zz = Test { t: 23u8, k: 33 };
437 //^^ Test<i32>
420 let zz_ref = &zz; 438 let zz_ref = &zz;
439 //^^^^^^ &Test<i32>
421}"#, 440}"#,
422 ); 441 );
423
424 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
425 [
426 InlayHint {
427 range: 69..71,
428 kind: TypeHint,
429 label: "Test<i32>",
430 },
431 InlayHint {
432 range: 105..111,
433 kind: TypeHint,
434 label: "&Test<i32>",
435 },
436 ]
437 "###
438 );
439 } 442 }
440 443
441 #[test] 444 #[test]
442 fn let_statement() { 445 fn let_statement() {
443 let (analysis, file_id) = single_file( 446 check(
444 r#" 447 r#"
445#[derive(PartialEq)] 448#[derive(PartialEq)]
446enum CustomOption<T> { 449enum Option<T> { None, Some(T) }
447 None,
448 Some(T),
449}
450 450
451#[derive(PartialEq)] 451#[derive(PartialEq)]
452struct Test { 452struct Test { a: Option<u32>, b: u8 }
453 a: CustomOption<u32>,
454 b: u8,
455}
456 453
457fn main() { 454fn main() {
458 struct InnerStruct {} 455 struct InnerStruct {}
459 456
460 let test = 54; 457 let test = 54;
458 //^^^^ i32
461 let test: i32 = 33; 459 let test: i32 = 33;
462 let mut test = 33; 460 let mut test = 33;
461 //^^^^^^^^ i32
463 let _ = 22; 462 let _ = 22;
464 let test = "test"; 463 let test = "test";
464 //^^^^ &str
465 let test = InnerStruct {}; 465 let test = InnerStruct {};
466 466
467 let test = vec![222]; 467 let test = unresolved();
468 let test: Vec<_> = (0..3).collect();
469 let test = (0..3).collect::<Vec<i128>>();
470 let test = (0..3).collect::<Vec<_>>();
471
472 let mut test = Vec::new();
473 test.push(333);
474 468
475 let test = (42, 'a'); 469 let test = (42, 'a');
476 let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); 470 //^^^^ (i32, char)
471 let (a, (b, (c,)) = (2, (3, (9.2,));
472 //^ i32 ^ i32 ^ f64
477 let &x = &92; 473 let &x = &92;
474 //^ i32
478}"#, 475}"#,
479 ); 476 );
480
481 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
482 [
483 InlayHint {
484 range: 193..197,
485 kind: TypeHint,
486 label: "i32",
487 },
488 InlayHint {
489 range: 236..244,
490 kind: TypeHint,
491 label: "i32",
492 },
493 InlayHint {
494 range: 275..279,
495 kind: TypeHint,
496 label: "&str",
497 },
498 InlayHint {
499 range: 539..543,
500 kind: TypeHint,
501 label: "(i32, char)",
502 },
503 InlayHint {
504 range: 566..567,
505 kind: TypeHint,
506 label: "i32",
507 },
508 InlayHint {
509 range: 570..571,
510 kind: TypeHint,
511 label: "i32",
512 },
513 InlayHint {
514 range: 573..574,
515 kind: TypeHint,
516 label: "i32",
517 },
518 InlayHint {
519 range: 577..578,
520 kind: TypeHint,
521 label: "f64",
522 },
523 InlayHint {
524 range: 580..581,
525 kind: TypeHint,
526 label: "f64",
527 },
528 InlayHint {
529 range: 584..585,
530 kind: TypeHint,
531 label: "i32",
532 },
533 InlayHint {
534 range: 627..628,
535 kind: TypeHint,
536 label: "i32",
537 },
538 ]
539 "###
540 );
541 } 477 }
542 478
543 #[test] 479 #[test]
544 fn closure_parameters() { 480 fn closure_parameters() {
545 let (analysis, file_id) = single_file( 481 check(
546 r#" 482 r#"
547fn main() { 483fn main() {
548 let mut start = 0; 484 let mut start = 0;
549 (0..2).for_each(|increment| { 485 //^^^^^^^^^ i32
550 start += increment; 486 (0..2).for_each(|increment| { start += increment; });
551 }); 487 //^^^^^^^^^ i32
552 488
553 let multiply = |a, b, c, d| a * b * c * d; 489 let multiply =
554 let _: i32 = multiply(1, 2, 3, 4); 490 //^^^^^^^^ |…| -> i32
491 | a, b| a * b
492 //^ i32 ^ i32
493 ;
494
495 let _: i32 = multiply(1, 2);
555 let multiply_ref = &multiply; 496 let multiply_ref = &multiply;
497 //^^^^^^^^^^^^ &|…| -> i32
556 498
557 let return_42 = || 42; 499 let return_42 = || 42;
500 //^^^^^^^^^ || -> i32
558}"#, 501}"#,
559 ); 502 );
560
561 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
562 [
563 InlayHint {
564 range: 21..30,
565 kind: TypeHint,
566 label: "i32",
567 },
568 InlayHint {
569 range: 57..66,
570 kind: TypeHint,
571 label: "i32",
572 },
573 InlayHint {
574 range: 115..123,
575 kind: TypeHint,
576 label: "|…| -> i32",
577 },
578 InlayHint {
579 range: 127..128,
580 kind: TypeHint,
581 label: "i32",
582 },
583 InlayHint {
584 range: 130..131,
585 kind: TypeHint,
586 label: "i32",
587 },
588 InlayHint {
589 range: 133..134,
590 kind: TypeHint,
591 label: "i32",
592 },
593 InlayHint {
594 range: 136..137,
595 kind: TypeHint,
596 label: "i32",
597 },
598 InlayHint {
599 range: 201..213,
600 kind: TypeHint,
601 label: "&|…| -> i32",
602 },
603 InlayHint {
604 range: 236..245,
605 kind: TypeHint,
606 label: "|| -> i32",
607 },
608 ]
609 "###
610 );
611 } 503 }
612 504
613 #[test] 505 #[test]
614 fn for_expression() { 506 fn for_expression() {
615 let (analysis, file_id) = single_file( 507 check(
616 r#" 508 r#"
617fn main() { 509fn main() {
618 let mut start = 0; 510 let mut start = 0;
619 for increment in 0..2 { 511 //^^^^^^^^^ i32
620 start += increment; 512 for increment in 0..2 { start += increment; }
621 } 513 //^^^^^^^^^ i32
622}"#, 514}"#,
623 ); 515 );
624
625 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
626 [
627 InlayHint {
628 range: 21..30,
629 kind: TypeHint,
630 label: "i32",
631 },
632 InlayHint {
633 range: 44..53,
634 kind: TypeHint,
635 label: "i32",
636 },
637 ]
638 "###
639 );
640 } 516 }
641 517
642 #[test] 518 #[test]
643 fn if_expr() { 519 fn if_expr() {
644 let (analysis, file_id) = single_file( 520 check(
645 r#" 521 r#"
646#[derive(PartialEq)] 522enum Option<T> { None, Some(T) }
647enum CustomOption<T> { 523use Option::*;
648 None,
649 Some(T),
650}
651
652#[derive(PartialEq)]
653struct Test {
654 a: CustomOption<u32>,
655 b: u8,
656}
657 524
658use CustomOption::*; 525struct Test { a: Option<u32>, b: u8 }
659 526
660fn main() { 527fn main() {
661 let test = Some(Test { a: Some(3), b: 1 }); 528 let test = Some(Test { a: Some(3), b: 1 });
529 //^^^^ Option<Test>
662 if let None = &test {}; 530 if let None = &test {};
663 if let test = &test {}; 531 if let test = &test {};
532 //^^^^ &Option<Test>
664 if let Some(test) = &test {}; 533 if let Some(test) = &test {};
665 if let Some(Test { a, b }) = &test {}; 534 //^^^^ &Test
666 if let Some(Test { a: x, b: y }) = &test {}; 535 if let Some(Test { a, b }) = &test {};
667 if let Some(Test { a: Some(x), b: y }) = &test {}; 536 //^ &Option<u32> ^ &u8
668 if let Some(Test { a: None, b: y }) = &test {}; 537 if let Some(Test { a: x, b: y }) = &test {};
538 //^ &Option<u32> ^ &u8
539 if let Some(Test { a: Some(x), b: y }) = &test {};
540 //^ &u32 ^ &u8
541 if let Some(Test { a: None, b: y }) = &test {};
542 //^ &u8
669 if let Some(Test { b: y, .. }) = &test {}; 543 if let Some(Test { b: y, .. }) = &test {};
670 544 //^ &u8
671 if test == None {} 545 if test == None {}
672}"#, 546}"#,
673 ); 547 );
674
675 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
676 [
677 InlayHint {
678 range: 188..192,
679 kind: TypeHint,
680 label: "CustomOption<Test>",
681 },
682 InlayHint {
683 range: 267..271,
684 kind: TypeHint,
685 label: "&CustomOption<Test>",
686 },
687 InlayHint {
688 range: 300..304,
689 kind: TypeHint,
690 label: "&Test",
691 },
692 InlayHint {
693 range: 341..342,
694 kind: TypeHint,
695 label: "&CustomOption<u32>",
696 },
697 InlayHint {
698 range: 344..345,
699 kind: TypeHint,
700 label: "&u8",
701 },
702 InlayHint {
703 range: 387..388,
704 kind: TypeHint,
705 label: "&CustomOption<u32>",
706 },
707 InlayHint {
708 range: 393..394,
709 kind: TypeHint,
710 label: "&u8",
711 },
712 InlayHint {
713 range: 441..442,
714 kind: TypeHint,
715 label: "&u32",
716 },
717 InlayHint {
718 range: 448..449,
719 kind: TypeHint,
720 label: "&u8",
721 },
722 InlayHint {
723 range: 500..501,
724 kind: TypeHint,
725 label: "&u8",
726 },
727 InlayHint {
728 range: 543..544,
729 kind: TypeHint,
730 label: "&u8",
731 },
732 ]
733 "###
734 );
735 } 548 }
736 549
737 #[test] 550 #[test]
738 fn while_expr() { 551 fn while_expr() {
739 let (analysis, file_id) = single_file( 552 check(
740 r#" 553 r#"
741#[derive(PartialEq)] 554enum Option<T> { None, Some(T) }
742enum CustomOption<T> { 555use Option::*;
743 None,
744 Some(T),
745}
746 556
747#[derive(PartialEq)] 557struct Test { a: Option<u32>, b: u8 }
748struct Test {
749 a: CustomOption<u32>,
750 b: u8,
751}
752
753use CustomOption::*;
754 558
755fn main() { 559fn main() {
756 let test = Some(Test { a: Some(3), b: 1 }); 560 let test = Some(Test { a: Some(3), b: 1 });
757 while let None = &test {}; 561 //^^^^ Option<Test>
758 while let test = &test {}; 562 while let Some(Test { a: Some(x), b: y }) = &test {};
759 while let Some(test) = &test {}; 563 //^ &u32 ^ &u8
760 while let Some(Test { a, b }) = &test {};
761 while let Some(Test { a: x, b: y }) = &test {};
762 while let Some(Test { a: Some(x), b: y }) = &test {};
763 while let Some(Test { a: None, b: y }) = &test {};
764 while let Some(Test { b: y, .. }) = &test {};
765
766 while test == None {}
767}"#, 564}"#,
768 ); 565 );
769
770 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
771 [
772 InlayHint {
773 range: 188..192,
774 kind: TypeHint,
775 label: "CustomOption<Test>",
776 },
777 InlayHint {
778 range: 273..277,
779 kind: TypeHint,
780 label: "&CustomOption<Test>",
781 },
782 InlayHint {
783 range: 309..313,
784 kind: TypeHint,
785 label: "&Test",
786 },
787 InlayHint {
788 range: 353..354,
789 kind: TypeHint,
790 label: "&CustomOption<u32>",
791 },
792 InlayHint {
793 range: 356..357,
794 kind: TypeHint,
795 label: "&u8",
796 },
797 InlayHint {
798 range: 402..403,
799 kind: TypeHint,
800 label: "&CustomOption<u32>",
801 },
802 InlayHint {
803 range: 408..409,
804 kind: TypeHint,
805 label: "&u8",
806 },
807 InlayHint {
808 range: 459..460,
809 kind: TypeHint,
810 label: "&u32",
811 },
812 InlayHint {
813 range: 466..467,
814 kind: TypeHint,
815 label: "&u8",
816 },
817 InlayHint {
818 range: 521..522,
819 kind: TypeHint,
820 label: "&u8",
821 },
822 InlayHint {
823 range: 567..568,
824 kind: TypeHint,
825 label: "&u8",
826 },
827 ]
828 "###
829 );
830 } 566 }
831 567
832 #[test] 568 #[test]
833 fn match_arm_list() { 569 fn match_arm_list() {
834 let (analysis, file_id) = single_file( 570 check(
835 r#" 571 r#"
836#[derive(PartialEq)] 572enum Option<T> { None, Some(T) }
837enum CustomOption<T> { 573use Option::*;
838 None,
839 Some(T),
840}
841
842#[derive(PartialEq)]
843struct Test {
844 a: CustomOption<u32>,
845 b: u8,
846}
847 574
848use CustomOption::*; 575struct Test { a: Option<u32>, b: u8 }
849 576
850fn main() { 577fn main() {
851 match Some(Test { a: Some(3), b: 1 }) { 578 match Some(Test { a: Some(3), b: 1 }) {
852 None => (), 579 None => (),
853 test => (), 580 test => (),
854 Some(test) => (), 581 //^^^^ Option<Test>
855 Some(Test { a, b }) => (),
856 Some(Test { a: x, b: y }) => (),
857 Some(Test { a: Some(x), b: y }) => (), 582 Some(Test { a: Some(x), b: y }) => (),
858 Some(Test { a: None, b: y }) => (), 583 //^ u32 ^ u8
859 Some(Test { b: y, .. }) => (),
860 _ => {} 584 _ => {}
861 } 585 }
862}"#, 586}"#,
863 ); 587 );
864
865 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
866 [
867 InlayHint {
868 range: 252..256,
869 kind: TypeHint,
870 label: "CustomOption<Test>",
871 },
872 InlayHint {
873 range: 277..281,
874 kind: TypeHint,
875 label: "Test",
876 },
877 InlayHint {
878 range: 310..311,
879 kind: TypeHint,
880 label: "CustomOption<u32>",
881 },
882 InlayHint {
883 range: 313..314,
884 kind: TypeHint,
885 label: "u8",
886 },
887 InlayHint {
888 range: 348..349,
889 kind: TypeHint,
890 label: "CustomOption<u32>",
891 },
892 InlayHint {
893 range: 354..355,
894 kind: TypeHint,
895 label: "u8",
896 },
897 InlayHint {
898 range: 394..395,
899 kind: TypeHint,
900 label: "u32",
901 },
902 InlayHint {
903 range: 401..402,
904 kind: TypeHint,
905 label: "u8",
906 },
907 InlayHint {
908 range: 445..446,
909 kind: TypeHint,
910 label: "u8",
911 },
912 InlayHint {
913 range: 480..481,
914 kind: TypeHint,
915 label: "u8",
916 },
917 ]
918 "###
919 );
920 } 588 }
921 589
922 #[test] 590 #[test]
923 fn hint_truncation() { 591 fn hint_truncation() {
924 let (analysis, file_id) = single_file( 592 check_with_config(
593 InlayHintsConfig { max_length: Some(8), ..Default::default() },
925 r#" 594 r#"
926struct Smol<T>(T); 595struct Smol<T>(T);
927 596
@@ -929,52 +598,25 @@ struct VeryLongOuterName<T>(T);
929 598
930fn main() { 599fn main() {
931 let a = Smol(0u32); 600 let a = Smol(0u32);
601 //^ Smol<u32>
932 let b = VeryLongOuterName(0usize); 602 let b = VeryLongOuterName(0usize);
603 //^ VeryLongOuterName<…>
933 let c = Smol(Smol(0u32)) 604 let c = Smol(Smol(0u32))
605 //^ Smol<Smol<…>>
934}"#, 606}"#,
935 ); 607 );
936
937 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
938 [
939 InlayHint {
940 range: 74..75,
941 kind: TypeHint,
942 label: "Smol<u32>",
943 },
944 InlayHint {
945 range: 98..99,
946 kind: TypeHint,
947 label: "VeryLongOuterName<…>",
948 },
949 InlayHint {
950 range: 137..138,
951 kind: TypeHint,
952 label: "Smol<Smol<…>>",
953 },
954 ]
955 "###
956 );
957 } 608 }
958 609
959 #[test] 610 #[test]
960 fn function_call_parameter_hint() { 611 fn function_call_parameter_hint() {
961 let (analysis, file_id) = single_file( 612 check(
962 r#" 613 r#"
963enum CustomOption<T> { 614enum Option<T> { None, Some(T) }
964 None, 615use Option::*;
965 Some(T),
966}
967use CustomOption::*;
968 616
969struct FileId {} 617struct FileId {}
970struct SmolStr {} 618struct SmolStr {}
971 619
972impl From<&str> for SmolStr {
973 fn from(_: &str) -> Self {
974 unimplemented!()
975 }
976}
977
978struct TextRange {} 620struct TextRange {}
979struct SyntaxKind {} 621struct SyntaxKind {}
980struct NavigationTarget {} 622struct NavigationTarget {}
@@ -982,18 +624,15 @@ struct NavigationTarget {}
982struct Test {} 624struct Test {}
983 625
984impl Test { 626impl Test {
985 fn method(&self, mut param: i32) -> i32 { 627 fn method(&self, mut param: i32) -> i32 { param * 2 }
986 param * 2
987 }
988 628
989 fn from_syntax( 629 fn from_syntax(
990 file_id: FileId, 630 file_id: FileId,
991 name: SmolStr, 631 name: SmolStr,
992 focus_range: CustomOption<TextRange>, 632 focus_range: Option<TextRange>,
993 full_range: TextRange, 633 full_range: TextRange,
994 kind: SyntaxKind, 634 kind: SyntaxKind,
995 docs: CustomOption<String>, 635 docs: Option<String>,
996 description: CustomOption<String>,
997 ) -> NavigationTarget { 636 ) -> NavigationTarget {
998 NavigationTarget {} 637 NavigationTarget {}
999 } 638 }
@@ -1005,108 +644,36 @@ fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
1005 644
1006fn main() { 645fn main() {
1007 let not_literal = 1; 646 let not_literal = 1;
1008 let _: i32 = test_func(1, 2, "hello", 3, not_literal); 647 //^^^^^^^^^^^ i32
648 let _: i32 = test_func(1, 2, "hello", 3, not_literal);
649 //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last
1009 let t: Test = Test {}; 650 let t: Test = Test {};
1010 t.method(123); 651 t.method(123);
1011 Test::method(&t, 3456); 652 //^^^ param
1012 653 Test::method(&t, 3456);
654 //^^ &self ^^^^ param
1013 Test::from_syntax( 655 Test::from_syntax(
1014 FileId {}, 656 FileId {},
657 //^^^^^^^^^ file_id
1015 "impl".into(), 658 "impl".into(),
659 //^^^^^^^^^^^^^ name
1016 None, 660 None,
661 //^^^^ focus_range
1017 TextRange {}, 662 TextRange {},
663 //^^^^^^^^^^^^ full_range
1018 SyntaxKind {}, 664 SyntaxKind {},
665 //^^^^^^^^^^^^^ kind
1019 None, 666 None,
1020 None, 667 //^^^^ docs
1021 ); 668 );
1022}"#, 669}"#,
1023 ); 670 );
1024
1025 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
1026 [
1027 InlayHint {
1028 range: 798..809,
1029 kind: TypeHint,
1030 label: "i32",
1031 },
1032 InlayHint {
1033 range: 842..843,
1034 kind: ParameterHint,
1035 label: "foo",
1036 },
1037 InlayHint {
1038 range: 845..846,
1039 kind: ParameterHint,
1040 label: "bar",
1041 },
1042 InlayHint {
1043 range: 848..855,
1044 kind: ParameterHint,
1045 label: "msg",
1046 },
1047 InlayHint {
1048 range: 860..871,
1049 kind: ParameterHint,
1050 label: "last",
1051 },
1052 InlayHint {
1053 range: 914..917,
1054 kind: ParameterHint,
1055 label: "param",
1056 },
1057 InlayHint {
1058 range: 937..939,
1059 kind: ParameterHint,
1060 label: "&self",
1061 },
1062 InlayHint {
1063 range: 941..945,
1064 kind: ParameterHint,
1065 label: "param",
1066 },
1067 InlayHint {
1068 range: 980..989,
1069 kind: ParameterHint,
1070 label: "file_id",
1071 },
1072 InlayHint {
1073 range: 999..1012,
1074 kind: ParameterHint,
1075 label: "name",
1076 },
1077 InlayHint {
1078 range: 1022..1026,
1079 kind: ParameterHint,
1080 label: "focus_range",
1081 },
1082 InlayHint {
1083 range: 1036..1048,
1084 kind: ParameterHint,
1085 label: "full_range",
1086 },
1087 InlayHint {
1088 range: 1058..1071,
1089 kind: ParameterHint,
1090 label: "kind",
1091 },
1092 InlayHint {
1093 range: 1081..1085,
1094 kind: ParameterHint,
1095 label: "docs",
1096 },
1097 InlayHint {
1098 range: 1095..1099,
1099 kind: ParameterHint,
1100 label: "description",
1101 },
1102 ]
1103 "###
1104 );
1105 } 671 }
1106 672
1107 #[test] 673 #[test]
1108 fn omitted_parameters_hints_heuristics() { 674 fn omitted_parameters_hints_heuristics() {
1109 let (analysis, file_id) = single_file( 675 check_with_config(
676 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1110 r#" 677 r#"
1111fn map(f: i32) {} 678fn map(f: i32) {}
1112fn filter(predicate: i32) {} 679fn filter(predicate: i32) {}
@@ -1188,22 +755,15 @@ fn main() {
1188 let _: f64 = a.abs_sub(b); 755 let _: f64 = a.abs_sub(b);
1189}"#, 756}"#,
1190 ); 757 );
1191
1192 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1193 []
1194 "###
1195 );
1196 } 758 }
1197 759
1198 #[test] 760 #[test]
1199 fn unit_structs_have_no_type_hints() { 761 fn unit_structs_have_no_type_hints() {
1200 let (analysis, file_id) = single_file( 762 check_with_config(
763 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1201 r#" 764 r#"
1202enum CustomResult<T, E> { 765enum Result<T, E> { Ok(T), Err(E) }
1203 Ok(T), 766use Result::*;
1204 Err(E),
1205}
1206use CustomResult::*;
1207 767
1208struct SyntheticSyntax; 768struct SyntheticSyntax;
1209 769
@@ -1214,133 +774,155 @@ fn main() {
1214 } 774 }
1215}"#, 775}"#,
1216 ); 776 );
1217
1218 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1219 []
1220 "###
1221 );
1222 } 777 }
1223 778
1224 #[test] 779 #[test]
1225 fn chaining_hints_ignore_comments() { 780 fn chaining_hints_ignore_comments() {
1226 let (analysis, file_id) = single_file( 781 check_expect(
782 InlayHintsConfig {
783 parameter_hints: false,
784 type_hints: false,
785 chaining_hints: true,
786 max_length: None,
787 },
1227 r#" 788 r#"
1228 struct A(B); 789struct A(B);
1229 impl A { fn into_b(self) -> B { self.0 } } 790impl A { fn into_b(self) -> B { self.0 } }
1230 struct B(C); 791struct B(C);
1231 impl B { fn into_c(self) -> C { self.0 } } 792impl B { fn into_c(self) -> C { self.0 } }
1232 struct C; 793struct C;
1233 794
1234 fn main() { 795fn main() {
1235 let c = A(B(C)) 796 let c = A(B(C))
1236 .into_b() // This is a comment 797 .into_b() // This is a comment
1237 .into_c(); 798 .into_c();
1238 }"#, 799}
800"#,
801 expect![[r#"
802 [
803 InlayHint {
804 range: 147..172,
805 kind: ChainingHint,
806 label: "B",
807 },
808 InlayHint {
809 range: 147..154,
810 kind: ChainingHint,
811 label: "A",
812 },
813 ]
814 "#]],
1239 ); 815 );
1240 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1241 [
1242 InlayHint {
1243 range: 232..269,
1244 kind: ChainingHint,
1245 label: "B",
1246 },
1247 InlayHint {
1248 range: 232..239,
1249 kind: ChainingHint,
1250 label: "A",
1251 },
1252 ]"###);
1253 } 816 }
1254 817
1255 #[test] 818 #[test]
1256 fn chaining_hints_without_newlines() { 819 fn chaining_hints_without_newlines() {
1257 let (analysis, file_id) = single_file( 820 check_with_config(
821 InlayHintsConfig {
822 parameter_hints: false,
823 type_hints: false,
824 chaining_hints: true,
825 max_length: None,
826 },
1258 r#" 827 r#"
1259 struct A(B); 828struct A(B);
1260 impl A { fn into_b(self) -> B { self.0 } } 829impl A { fn into_b(self) -> B { self.0 } }
1261 struct B(C); 830struct B(C);
1262 impl B { fn into_c(self) -> C { self.0 } } 831impl B { fn into_c(self) -> C { self.0 } }
1263 struct C; 832struct C;
1264 833
1265 fn main() { 834fn main() {
1266 let c = A(B(C)).into_b().into_c(); 835 let c = A(B(C)).into_b().into_c();
1267 }"#, 836}"#,
1268 ); 837 );
1269 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
1270 } 838 }
1271 839
1272 #[test] 840 #[test]
1273 fn struct_access_chaining_hints() { 841 fn struct_access_chaining_hints() {
1274 let (analysis, file_id) = single_file( 842 check_expect(
843 InlayHintsConfig {
844 parameter_hints: false,
845 type_hints: false,
846 chaining_hints: true,
847 max_length: None,
848 },
1275 r#" 849 r#"
1276 struct A { pub b: B } 850struct A { pub b: B }
1277 struct B { pub c: C } 851struct B { pub c: C }
1278 struct C(pub bool); 852struct C(pub bool);
1279 struct D; 853struct D;
1280 854
1281 impl D { 855impl D {
1282 fn foo(&self) -> i32 { 42 } 856 fn foo(&self) -> i32 { 42 }
1283 } 857}
1284 858
1285 fn main() { 859fn main() {
1286 let x = A { b: B { c: C(true) } } 860 let x = A { b: B { c: C(true) } }
1287 .b 861 .b
1288 .c 862 .c
1289 .0; 863 .0;
1290 let x = D 864 let x = D
1291 .foo(); 865 .foo();
1292 }"#, 866}"#,
867 expect![[r#"
868 [
869 InlayHint {
870 range: 143..190,
871 kind: ChainingHint,
872 label: "C",
873 },
874 InlayHint {
875 range: 143..179,
876 kind: ChainingHint,
877 label: "B",
878 },
879 ]
880 "#]],
1293 ); 881 );
1294 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1295 [
1296 InlayHint {
1297 range: 252..323,
1298 kind: ChainingHint,
1299 label: "C",
1300 },
1301 InlayHint {
1302 range: 252..300,
1303 kind: ChainingHint,
1304 label: "B",
1305 },
1306 ]
1307 "###);
1308 } 882 }
1309 883
1310 #[test] 884 #[test]
1311 fn generic_chaining_hints() { 885 fn generic_chaining_hints() {
1312 let (analysis, file_id) = single_file( 886 check_expect(
887 InlayHintsConfig {
888 parameter_hints: false,
889 type_hints: false,
890 chaining_hints: true,
891 max_length: None,
892 },
1313 r#" 893 r#"
1314 struct A<T>(T); 894struct A<T>(T);
1315 struct B<T>(T); 895struct B<T>(T);
1316 struct C<T>(T); 896struct C<T>(T);
1317 struct X<T,R>(T, R); 897struct X<T,R>(T, R);
1318 898
1319 impl<T> A<T> { 899impl<T> A<T> {
1320 fn new(t: T) -> Self { A(t) } 900 fn new(t: T) -> Self { A(t) }
1321 fn into_b(self) -> B<T> { B(self.0) } 901 fn into_b(self) -> B<T> { B(self.0) }
1322 } 902}
1323 impl<T> B<T> { 903impl<T> B<T> {
1324 fn into_c(self) -> C<T> { C(self.0) } 904 fn into_c(self) -> C<T> { C(self.0) }
1325 } 905}
1326 fn main() { 906fn main() {
1327 let c = A::new(X(42, true)) 907 let c = A::new(X(42, true))
1328 .into_b() 908 .into_b()
1329 .into_c(); 909 .into_c();
1330 }"#, 910}
911"#,
912 expect![[r#"
913 [
914 InlayHint {
915 range: 246..283,
916 kind: ChainingHint,
917 label: "B<X<i32, bool>>",
918 },
919 InlayHint {
920 range: 246..265,
921 kind: ChainingHint,
922 label: "A<X<i32, bool>>",
923 },
924 ]
925 "#]],
1331 ); 926 );
1332 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1333 [
1334 InlayHint {
1335 range: 403..452,
1336 kind: ChainingHint,
1337 label: "B<X<i32, bool>>",
1338 },
1339 InlayHint {
1340 range: 403..422,
1341 kind: ChainingHint,
1342 label: "A<X<i32, bool>>",
1343 },
1344 ]"###);
1345 } 927 }
1346} 928}
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs
index 5036c1fb0..6907c09e8 100644
--- a/crates/ra_ide/src/join_lines.rs
+++ b/crates/ra_ide/src/join_lines.rs
@@ -165,10 +165,7 @@ fn join_single_use_tree(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Opti
165} 165}
166 166
167fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool { 167fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool {
168 match (left, right) { 168 matches!((left, right), (T![,], T![')']) | (T![,], T![']']))
169 (T![,], T![')']) | (T![,], T![']']) => true,
170 _ => false,
171 }
172} 169}
173 170
174#[cfg(test)] 171#[cfg(test)]
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 12d5716e8..5d1f64e19 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -17,6 +17,7 @@ macro_rules! eprintln {
17 17
18pub mod mock_analysis; 18pub mod mock_analysis;
19 19
20mod markup;
20mod prime_caches; 21mod prime_caches;
21mod status; 22mod status;
22mod completion; 23mod completion;
@@ -47,7 +48,7 @@ use std::sync::Arc;
47use ra_cfg::CfgOptions; 48use ra_cfg::CfgOptions;
48use ra_db::{ 49use ra_db::{
49 salsa::{self, ParallelDatabase}, 50 salsa::{self, ParallelDatabase},
50 CheckCanceled, Env, FileLoader, SourceDatabase, 51 CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
51}; 52};
52use ra_ide_db::{ 53use ra_ide_db::{
53 symbol_index::{self, FileSymbol}, 54 symbol_index::{self, FileSymbol},
@@ -59,6 +60,7 @@ use crate::display::ToNav;
59 60
60pub use crate::{ 61pub use crate::{
61 call_hierarchy::CallItem, 62 call_hierarchy::CallItem,
63 call_info::CallInfo,
62 completion::{ 64 completion::{
63 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat, 65 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat,
64 }, 66 },
@@ -66,29 +68,31 @@ pub use crate::{
66 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 68 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
67 expand_macro::ExpandedMacro, 69 expand_macro::ExpandedMacro,
68 folding_ranges::{Fold, FoldKind}, 70 folding_ranges::{Fold, FoldKind},
69 hover::HoverResult, 71 hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult},
70 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, 72 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
73 markup::Markup,
71 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, 74 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
72 runnables::{Runnable, RunnableKind, TestId}, 75 runnables::{Runnable, RunnableKind, TestId},
73 ssr::SsrError,
74 syntax_highlighting::{ 76 syntax_highlighting::{
75 Highlight, HighlightModifier, HighlightModifiers, HighlightTag, HighlightedRange, 77 Highlight, HighlightModifier, HighlightModifiers, HighlightTag, HighlightedRange,
76 }, 78 },
77}; 79};
78 80
79pub use hir::Documentation; 81pub use hir::{Documentation, Semantics};
80pub use ra_assists::{AssistConfig, AssistId}; 82pub use ra_assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist};
81pub use ra_db::{ 83pub use ra_db::{
82 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, 84 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot,
85 SourceRootId,
83}; 86};
84pub use ra_ide_db::{ 87pub use ra_ide_db::{
85 change::{AnalysisChange, LibraryData}, 88 change::AnalysisChange,
86 line_index::{LineCol, LineIndex}, 89 line_index::{LineCol, LineIndex},
87 search::SearchScope, 90 search::SearchScope,
88 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 91 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
89 symbol_index::Query, 92 symbol_index::Query,
90 RootDatabase, 93 RootDatabase,
91}; 94};
95pub use ra_ssr::SsrError;
92pub use ra_text_edit::{Indel, TextEdit}; 96pub use ra_text_edit::{Indel, TextEdit};
93 97
94pub type Cancelable<T> = Result<T, Canceled>; 98pub type Cancelable<T> = Result<T, Canceled>;
@@ -128,28 +132,12 @@ impl<T> RangeInfo<T> {
128 } 132 }
129} 133}
130 134
131/// Contains information about a call site. Specifically the
132/// `FunctionSignature`and current parameter.
133#[derive(Debug)]
134pub struct CallInfo {
135 pub signature: FunctionSignature,
136 pub active_parameter: Option<usize>,
137}
138
139/// `AnalysisHost` stores the current state of the world. 135/// `AnalysisHost` stores the current state of the world.
140#[derive(Debug)] 136#[derive(Debug)]
141pub struct AnalysisHost { 137pub struct AnalysisHost {
142 db: RootDatabase, 138 db: RootDatabase,
143} 139}
144 140
145#[derive(Debug)]
146pub struct Assist {
147 pub id: AssistId,
148 pub label: String,
149 pub group_label: Option<String>,
150 pub source_change: SourceChange,
151}
152
153impl AnalysisHost { 141impl AnalysisHost {
154 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost { 142 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
155 AnalysisHost { db: RootDatabase::new(lru_capacity) } 143 AnalysisHost { db: RootDatabase::new(lru_capacity) }
@@ -220,11 +208,14 @@ impl Analysis {
220 // `AnalysisHost` for creating a fully-featured analysis. 208 // `AnalysisHost` for creating a fully-featured analysis.
221 pub fn from_single_file(text: String) -> (Analysis, FileId) { 209 pub fn from_single_file(text: String) -> (Analysis, FileId) {
222 let mut host = AnalysisHost::default(); 210 let mut host = AnalysisHost::default();
223 let source_root = SourceRootId(0); 211 let file_id = FileId(0);
212 let mut file_set = FileSet::default();
213 file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_string()));
214 let source_root = SourceRoot::new_local(file_set);
215
224 let mut change = AnalysisChange::new(); 216 let mut change = AnalysisChange::new();
225 change.add_root(source_root, true); 217 change.set_roots(vec![source_root]);
226 let mut crate_graph = CrateGraph::default(); 218 let mut crate_graph = CrateGraph::default();
227 let file_id = FileId(0);
228 // FIXME: cfg options 219 // FIXME: cfg options
229 // Default to enable test for single file. 220 // Default to enable test for single file.
230 let mut cfg_options = CfgOptions::default(); 221 let mut cfg_options = CfgOptions::default();
@@ -236,9 +227,8 @@ impl Analysis {
236 cfg_options, 227 cfg_options,
237 Env::default(), 228 Env::default(),
238 Default::default(), 229 Default::default(),
239 Default::default(),
240 ); 230 );
241 change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); 231 change.change_file(file_id, Some(Arc::new(text)));
242 change.set_crate_graph(crate_graph); 232 change.set_crate_graph(crate_graph);
243 host.apply_change(change); 233 host.apply_change(change);
244 (host.analysis(), file_id) 234 (host.analysis(), file_id)
@@ -390,7 +380,9 @@ impl Analysis {
390 position: FilePosition, 380 position: FilePosition,
391 search_scope: Option<SearchScope>, 381 search_scope: Option<SearchScope>,
392 ) -> Cancelable<Option<ReferenceSearchResult>> { 382 ) -> Cancelable<Option<ReferenceSearchResult>> {
393 self.with_db(|db| references::find_all_refs(db, position, search_scope).map(|it| it.info)) 383 self.with_db(|db| {
384 references::find_all_refs(&Semantics::new(db), position, search_scope).map(|it| it.info)
385 })
394 } 386 }
395 387
396 /// Returns a short text describing element at position. 388 /// Returns a short text describing element at position.
@@ -448,12 +440,14 @@ impl Analysis {
448 440
449 /// Computes syntax highlighting for the given file 441 /// Computes syntax highlighting for the given file
450 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 442 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
451 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None)) 443 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
452 } 444 }
453 445
454 /// Computes syntax highlighting for the given file range. 446 /// Computes syntax highlighting for the given file range.
455 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> { 447 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> {
456 self.with_db(|db| syntax_highlighting::highlight(db, frange.file_id, Some(frange.range))) 448 self.with_db(|db| {
449 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
450 })
457 } 451 }
458 452
459 /// Computes syntax highlighting for the given file. 453 /// Computes syntax highlighting for the given file.
@@ -470,20 +464,23 @@ impl Analysis {
470 self.with_db(|db| completion::completions(db, config, position).map(Into::into)) 464 self.with_db(|db| completion::completions(db, config, position).map(Into::into))
471 } 465 }
472 466
473 /// Computes assists (aka code actions aka intentions) for the given 467 /// Computes resolved assists with source changes for the given position.
468 pub fn resolved_assists(
469 &self,
470 config: &AssistConfig,
471 frange: FileRange,
472 ) -> Cancelable<Vec<ResolvedAssist>> {
473 self.with_db(|db| ra_assists::Assist::resolved(db, config, frange))
474 }
475
476 /// Computes unresolved assists (aka code actions aka intentions) for the given
474 /// position. 477 /// position.
475 pub fn assists(&self, config: &AssistConfig, frange: FileRange) -> Cancelable<Vec<Assist>> { 478 pub fn unresolved_assists(
476 self.with_db(|db| { 479 &self,
477 ra_assists::Assist::resolved(db, config, frange) 480 config: &AssistConfig,
478 .into_iter() 481 frange: FileRange,
479 .map(|assist| Assist { 482 ) -> Cancelable<Vec<Assist>> {
480 id: assist.assist.id, 483 self.with_db(|db| Assist::unresolved(db, config, frange))
481 label: assist.assist.label,
482 group_label: assist.assist.group.map(|it| it.0),
483 source_change: assist.source_change,
484 })
485 .collect()
486 })
487 } 484 }
488 485
489 /// Computes the set of diagnostics for the given file. 486 /// Computes the set of diagnostics for the given file.
@@ -508,7 +505,7 @@ impl Analysis {
508 ) -> Cancelable<Result<SourceChange, SsrError>> { 505 ) -> Cancelable<Result<SourceChange, SsrError>> {
509 self.with_db(|db| { 506 self.with_db(|db| {
510 let edits = ssr::parse_search_replace(query, parse_only, db)?; 507 let edits = ssr::parse_search_replace(query, parse_only, db)?;
511 Ok(SourceChange::source_file_edits(edits)) 508 Ok(SourceChange::from(edits))
512 }) 509 })
513 } 510 }
514 511
diff --git a/crates/ra_ide/src/markup.rs b/crates/ra_ide/src/markup.rs
new file mode 100644
index 000000000..60c193c40
--- /dev/null
+++ b/crates/ra_ide/src/markup.rs
@@ -0,0 +1,38 @@
1//! Markdown formatting.
2//!
3//! Sometimes, we want to display a "rich text" in the UI. At the moment, we use
4//! markdown for this purpose. It doesn't feel like a right option, but that's
5//! what is used by LSP, so let's keep it simple.
6use std::fmt;
7
8#[derive(Default, Debug)]
9pub struct Markup {
10 text: String,
11}
12
13impl From<Markup> for String {
14 fn from(markup: Markup) -> Self {
15 markup.text
16 }
17}
18
19impl From<String> for Markup {
20 fn from(text: String) -> Self {
21 Markup { text }
22 }
23}
24
25impl fmt::Display for Markup {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 fmt::Display::fmt(&self.text, f)
28 }
29}
30
31impl Markup {
32 pub fn as_str(&self) -> &str {
33 self.text.as_str()
34 }
35 pub fn fenced_block(contents: &impl fmt::Display) -> Markup {
36 format!("```rust\n{}\n```", contents).into()
37 }
38}
diff --git a/crates/ra_ide/src/matching_brace.rs b/crates/ra_ide/src/matching_brace.rs
index 407a9636d..742d70c9c 100644
--- a/crates/ra_ide/src/matching_brace.rs
+++ b/crates/ra_ide/src/matching_brace.rs
@@ -1,8 +1,12 @@
1use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T}; 1use ra_syntax::{
2 ast::{self, AstNode},
3 SourceFile, SyntaxKind, TextSize, T,
4};
5use test_utils::mark;
2 6
3// Feature: Matching Brace 7// Feature: Matching Brace
4// 8//
5// If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair, 9// If the cursor is on any brace (`<>(){}[]||`) which is a part of a brace-pair,
6// moves cursor to the matching brace. It uses the actual parser to determine 10// moves cursor to the matching brace. It uses the actual parser to determine
7// braces, so it won't confuse generics with comparisons. 11// braces, so it won't confuse generics with comparisons.
8// 12//
@@ -13,8 +17,8 @@ use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T};
13// |=== 17// |===
14pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { 18pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
15 const BRACES: &[SyntaxKind] = 19 const BRACES: &[SyntaxKind] =
16 &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>]]; 20 &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]];
17 let (brace_node, brace_idx) = file 21 let (brace_token, brace_idx) = file
18 .syntax() 22 .syntax()
19 .token_at_offset(offset) 23 .token_at_offset(offset)
20 .filter_map(|node| { 24 .filter_map(|node| {
@@ -22,9 +26,16 @@ pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
22 Some((node, idx)) 26 Some((node, idx))
23 }) 27 })
24 .next()?; 28 .next()?;
25 let parent = brace_node.parent(); 29 let parent = brace_token.parent();
30 if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) {
31 mark::hit!(pipes_not_braces);
32 return None;
33 }
26 let matching_kind = BRACES[brace_idx ^ 1]; 34 let matching_kind = BRACES[brace_idx ^ 1];
27 let matching_node = parent.children_with_tokens().find(|node| node.kind() == matching_kind)?; 35 let matching_node = parent
36 .children_with_tokens()
37 .filter_map(|it| it.into_token())
38 .find(|node| node.kind() == matching_kind && node != &brace_token)?;
28 Some(matching_node.text_range().start()) 39 Some(matching_node.text_range().start())
29} 40}
30 41
@@ -48,5 +59,15 @@ mod tests {
48 } 59 }
49 60
50 do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }"); 61 do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }");
62 do_check("fn main() { |x: i32|<|> x * 2;}", "fn main() { <|>|x: i32| x * 2;}");
63 do_check("fn main() { <|>|x: i32| x * 2;}", "fn main() { |x: i32<|>| x * 2;}");
64
65 {
66 mark::check!(pipes_not_braces);
67 do_check(
68 "fn main() { match 92 { 1 | 2 |<|> 3 => 92 } }",
69 "fn main() { match 92 { 1 | 2 |<|> 3 => 92 } }",
70 );
71 }
51 } 72 }
52} 73}
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
index ad78d2d93..b28054688 100644
--- a/crates/ra_ide/src/mock_analysis.rs
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -1,87 +1,24 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2
3use std::str::FromStr;
4use std::sync::Arc; 2use std::sync::Arc;
5 3
6use ra_cfg::CfgOptions; 4use ra_cfg::CfgOptions;
7use ra_db::{CrateName, Env, RelativePathBuf}; 5use ra_db::{CrateName, Env, FileSet, SourceRoot, VfsPath};
8use test_utils::{extract_offset, extract_range, parse_fixture, FixtureEntry, CURSOR_MARKER}; 6use test_utils::{
7 extract_annotations, extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER,
8};
9 9
10use crate::{ 10use crate::{
11 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, 11 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange,
12 SourceRootId,
13}; 12};
14 13
15#[derive(Debug)]
16enum MockFileData {
17 Plain { path: String, content: String },
18 Fixture(FixtureEntry),
19}
20
21impl MockFileData {
22 fn new(path: String, content: String) -> Self {
23 // `Self::Plain` causes a false warning: 'variant is never constructed: `Plain` '
24 // see https://github.com/rust-lang/rust/issues/69018
25 MockFileData::Plain { path, content }
26 }
27
28 fn path(&self) -> &str {
29 match self {
30 MockFileData::Plain { path, .. } => path.as_str(),
31 MockFileData::Fixture(f) => f.meta.path().as_str(),
32 }
33 }
34
35 fn content(&self) -> &str {
36 match self {
37 MockFileData::Plain { content, .. } => content,
38 MockFileData::Fixture(f) => f.text.as_str(),
39 }
40 }
41
42 fn cfg_options(&self) -> CfgOptions {
43 match self {
44 MockFileData::Fixture(f) => {
45 f.meta.cfg_options().map_or_else(Default::default, |o| o.clone())
46 }
47 _ => CfgOptions::default(),
48 }
49 }
50
51 fn edition(&self) -> Edition {
52 match self {
53 MockFileData::Fixture(f) => {
54 f.meta.edition().map_or(Edition::Edition2018, |v| Edition::from_str(v).unwrap())
55 }
56 _ => Edition::Edition2018,
57 }
58 }
59
60 fn env(&self) -> Env {
61 match self {
62 MockFileData::Fixture(f) => Env::from(f.meta.env()),
63 _ => Env::default(),
64 }
65 }
66}
67
68impl From<FixtureEntry> for MockFileData {
69 fn from(fixture: FixtureEntry) -> Self {
70 Self::Fixture(fixture)
71 }
72}
73
74/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis 14/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
75/// from a set of in-memory files. 15/// from a set of in-memory files.
76#[derive(Debug, Default)] 16#[derive(Debug, Default)]
77pub struct MockAnalysis { 17pub struct MockAnalysis {
78 files: Vec<MockFileData>, 18 files: Vec<Fixture>,
79} 19}
80 20
81impl MockAnalysis { 21impl MockAnalysis {
82 pub fn new() -> MockAnalysis {
83 MockAnalysis::default()
84 }
85 /// Creates `MockAnalysis` using a fixture data in the following format: 22 /// Creates `MockAnalysis` using a fixture data in the following format:
86 /// 23 ///
87 /// ```not_rust 24 /// ```not_rust
@@ -92,106 +29,107 @@ impl MockAnalysis {
92 /// //- /foo.rs 29 /// //- /foo.rs
93 /// struct Baz; 30 /// struct Baz;
94 /// ``` 31 /// ```
95 pub fn with_files(fixture: &str) -> MockAnalysis { 32 pub fn with_files(ra_fixture: &str) -> MockAnalysis {
96 let mut res = MockAnalysis::new(); 33 let (res, pos) = MockAnalysis::with_fixture(ra_fixture);
97 for entry in parse_fixture(fixture) { 34 assert!(pos.is_none());
98 res.add_file_fixture(entry);
99 }
100 res 35 res
101 } 36 }
102 37
103 /// Same as `with_files`, but requires that a single file contains a `<|>` marker, 38 /// Same as `with_files`, but requires that a single file contains a `<|>` marker,
104 /// whose position is also returned. 39 /// whose position is also returned.
105 pub fn with_files_and_position(fixture: &str) -> (MockAnalysis, FilePosition) { 40 pub fn with_files_and_position(fixture: &str) -> (MockAnalysis, FilePosition) {
41 let (res, position) = MockAnalysis::with_fixture(fixture);
42 let (file_id, range_or_offset) = position.expect("expected a marker (<|>)");
43 let offset = match range_or_offset {
44 RangeOrOffset::Range(_) => panic!(),
45 RangeOrOffset::Offset(it) => it,
46 };
47 (res, FilePosition { file_id, offset })
48 }
49
50 fn with_fixture(fixture: &str) -> (MockAnalysis, Option<(FileId, RangeOrOffset)>) {
106 let mut position = None; 51 let mut position = None;
107 let mut res = MockAnalysis::new(); 52 let mut res = MockAnalysis::default();
108 for entry in parse_fixture(fixture) { 53 for mut entry in Fixture::parse(fixture) {
109 if entry.text.contains(CURSOR_MARKER) { 54 if entry.text.contains(CURSOR_MARKER) {
110 assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); 55 assert!(position.is_none(), "only one marker (<|>) per fixture is allowed");
111 position = Some(res.add_file_fixture_with_position(entry)); 56 let (range_or_offset, text) = extract_range_or_offset(&entry.text);
57 entry.text = text;
58 let file_id = res.add_file_fixture(entry);
59 position = Some((file_id, range_or_offset));
112 } else { 60 } else {
113 res.add_file_fixture(entry); 61 res.add_file_fixture(entry);
114 } 62 }
115 } 63 }
116 let position = position.expect("expected a marker (<|>)");
117 (res, position) 64 (res, position)
118 } 65 }
119 66
120 pub fn add_file_fixture(&mut self, fixture: FixtureEntry) -> FileId { 67 fn add_file_fixture(&mut self, fixture: Fixture) -> FileId {
121 let file_id = self.next_id(); 68 let file_id = FileId((self.files.len() + 1) as u32);
122 self.files.push(MockFileData::from(fixture)); 69 self.files.push(fixture);
123 file_id 70 file_id
124 } 71 }
125 72
126 pub fn add_file_fixture_with_position(&mut self, mut fixture: FixtureEntry) -> FilePosition { 73 pub fn id_of(&self, path: &str) -> FileId {
127 let (offset, text) = extract_offset(&fixture.text); 74 let (file_id, _) =
128 fixture.text = text; 75 self.files().find(|(_, data)| path == data.path).expect("no file in this mock");
129 let file_id = self.next_id();
130 self.files.push(MockFileData::from(fixture));
131 FilePosition { file_id, offset }
132 }
133
134 pub fn add_file(&mut self, path: &str, text: &str) -> FileId {
135 let file_id = self.next_id();
136 self.files.push(MockFileData::new(path.to_string(), text.to_string()));
137 file_id 76 file_id
138 } 77 }
139 pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition { 78 pub fn annotations(&self) -> Vec<(FileRange, String)> {
140 let (offset, text) = extract_offset(text); 79 self.files()
141 let file_id = self.next_id(); 80 .flat_map(|(file_id, fixture)| {
142 self.files.push(MockFileData::new(path.to_string(), text)); 81 let annotations = extract_annotations(&fixture.text);
143 FilePosition { file_id, offset } 82 annotations
83 .into_iter()
84 .map(move |(range, data)| (FileRange { file_id, range }, data))
85 })
86 .collect()
144 } 87 }
145 pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { 88 pub fn files(&self) -> impl Iterator<Item = (FileId, &Fixture)> + '_ {
146 let (range, text) = extract_range(text); 89 self.files.iter().enumerate().map(|(idx, fixture)| (FileId(idx as u32 + 1), fixture))
147 let file_id = self.next_id();
148 self.files.push(MockFileData::new(path.to_string(), text));
149 FileRange { file_id, range }
150 } 90 }
151 pub fn id_of(&self, path: &str) -> FileId { 91 pub fn annotation(&self) -> (FileRange, String) {
152 let (idx, _) = self 92 let mut all = self.annotations();
153 .files 93 assert_eq!(all.len(), 1);
154 .iter() 94 all.pop().unwrap()
155 .enumerate()
156 .find(|(_, data)| path == data.path())
157 .expect("no file in this mock");
158 FileId(idx as u32 + 1)
159 } 95 }
160 pub fn analysis_host(self) -> AnalysisHost { 96 pub fn analysis_host(self) -> AnalysisHost {
161 let mut host = AnalysisHost::default(); 97 let mut host = AnalysisHost::default();
162 let source_root = SourceRootId(0);
163 let mut change = AnalysisChange::new(); 98 let mut change = AnalysisChange::new();
164 change.add_root(source_root, true); 99 let mut file_set = FileSet::default();
165 let mut crate_graph = CrateGraph::default(); 100 let mut crate_graph = CrateGraph::default();
166 let mut root_crate = None; 101 let mut root_crate = None;
167 for (i, data) in self.files.into_iter().enumerate() { 102 for (i, data) in self.files.into_iter().enumerate() {
168 let path = data.path(); 103 let path = data.path;
169 assert!(path.starts_with('/')); 104 assert!(path.starts_with('/'));
170 let path = RelativePathBuf::from_path(&path[1..]).unwrap(); 105
171 let cfg_options = data.cfg_options(); 106 let mut cfg = CfgOptions::default();
107 data.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into()));
108 data.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into()));
109 let edition: Edition =
110 data.edition.and_then(|it| it.parse().ok()).unwrap_or(Edition::Edition2018);
111
172 let file_id = FileId(i as u32 + 1); 112 let file_id = FileId(i as u32 + 1);
173 let edition = data.edition(); 113 let env = Env::from(data.env.iter());
174 let env = data.env();
175 if path == "/lib.rs" || path == "/main.rs" { 114 if path == "/lib.rs" || path == "/main.rs" {
176 root_crate = Some(crate_graph.add_crate_root( 115 root_crate = Some(crate_graph.add_crate_root(
177 file_id, 116 file_id,
178 edition, 117 edition,
179 None, 118 None,
180 cfg_options, 119 cfg,
181 env, 120 env,
182 Default::default(), 121 Default::default(),
183 Default::default(),
184 )); 122 ));
185 } else if path.ends_with("/lib.rs") { 123 } else if path.ends_with("/lib.rs") {
186 let crate_name = path.parent().unwrap().file_name().unwrap(); 124 let base = &path[..path.len() - "/lib.rs".len()];
125 let crate_name = &base[base.rfind('/').unwrap() + '/'.len_utf8()..];
187 let other_crate = crate_graph.add_crate_root( 126 let other_crate = crate_graph.add_crate_root(
188 file_id, 127 file_id,
189 edition, 128 edition,
190 Some(CrateName::new(crate_name).unwrap()), 129 Some(crate_name.to_string()),
191 cfg_options, 130 cfg,
192 env, 131 env,
193 Default::default(), 132 Default::default(),
194 Default::default(),
195 ); 133 );
196 if let Some(root_crate) = root_crate { 134 if let Some(root_crate) = root_crate {
197 crate_graph 135 crate_graph
@@ -199,19 +137,18 @@ impl MockAnalysis {
199 .unwrap(); 137 .unwrap();
200 } 138 }
201 } 139 }
202 change.add_file(source_root, file_id, path, Arc::new(data.content().to_owned())); 140 let path = VfsPath::new_virtual_path(path.to_string());
141 file_set.insert(file_id, path);
142 change.change_file(file_id, Some(Arc::new(data.text).to_owned()));
203 } 143 }
204 change.set_crate_graph(crate_graph); 144 change.set_crate_graph(crate_graph);
145 change.set_roots(vec![SourceRoot::new_local(file_set)]);
205 host.apply_change(change); 146 host.apply_change(change);
206 host 147 host
207 } 148 }
208 pub fn analysis(self) -> Analysis { 149 pub fn analysis(self) -> Analysis {
209 self.analysis_host().analysis() 150 self.analysis_host().analysis()
210 } 151 }
211
212 fn next_id(&self) -> FileId {
213 FileId((self.files.len() + 1) as u32)
214 }
215} 152}
216 153
217/// Creates analysis from a multi-file fixture, returns positions marked with <|>. 154/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
@@ -222,21 +159,18 @@ pub fn analysis_and_position(ra_fixture: &str) -> (Analysis, FilePosition) {
222 159
223/// Creates analysis for a single file. 160/// Creates analysis for a single file.
224pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) { 161pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) {
225 let mut mock = MockAnalysis::new(); 162 let mock = MockAnalysis::with_files(ra_fixture);
226 let file_id = mock.add_file("/main.rs", ra_fixture); 163 let file_id = mock.id_of("/main.rs");
227 (mock.analysis(), file_id) 164 (mock.analysis(), file_id)
228} 165}
229 166
230/// Creates analysis for a single file, returns position marked with <|>.
231pub fn single_file_with_position(ra_fixture: &str) -> (Analysis, FilePosition) {
232 let mut mock = MockAnalysis::new();
233 let pos = mock.add_file_with_position("/main.rs", ra_fixture);
234 (mock.analysis(), pos)
235}
236
237/// Creates analysis for a single file, returns range marked with a pair of <|>. 167/// Creates analysis for a single file, returns range marked with a pair of <|>.
238pub fn single_file_with_range(ra_fixture: &str) -> (Analysis, FileRange) { 168pub fn analysis_and_range(ra_fixture: &str) -> (Analysis, FileRange) {
239 let mut mock = MockAnalysis::new(); 169 let (res, position) = MockAnalysis::with_fixture(ra_fixture);
240 let pos = mock.add_file_with_range("/main.rs", ra_fixture); 170 let (file_id, range_or_offset) = position.expect("expected a marker (<|>)");
241 (mock.analysis(), pos) 171 let range = match range_or_offset {
172 RangeOrOffset::Range(it) => it,
173 RangeOrOffset::Offset(_) => panic!(),
174 };
175 (res.analysis(), FileRange { file_id, range })
242} 176}
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs
index fa1535da5..e3e0c7639 100644
--- a/crates/ra_ide/src/parent_module.rs
+++ b/crates/ra_ide/src/parent_module.rs
@@ -125,12 +125,12 @@ mod tests {
125 #[test] 125 #[test]
126 fn test_resolve_crate_root() { 126 fn test_resolve_crate_root() {
127 let mock = MockAnalysis::with_files( 127 let mock = MockAnalysis::with_files(
128 " 128 r#"
129 //- /bar.rs 129//- /bar.rs
130 mod foo; 130mod foo;
131 //- /foo.rs 131//- /foo.rs
132 // empty <|> 132// empty
133 ", 133"#,
134 ); 134 );
135 let root_file = mock.id_of("/bar.rs"); 135 let root_file = mock.id_of("/bar.rs");
136 let mod_file = mock.id_of("/foo.rs"); 136 let mod_file = mock.id_of("/foo.rs");
@@ -145,7 +145,6 @@ mod tests {
145 CfgOptions::default(), 145 CfgOptions::default(),
146 Env::default(), 146 Env::default(),
147 Default::default(), 147 Default::default(),
148 Default::default(),
149 ); 148 );
150 let mut change = AnalysisChange::new(); 149 let mut change = AnalysisChange::new();
151 change.set_crate_graph(crate_graph); 150 change.set_crate_graph(crate_graph);
diff --git a/crates/ra_ide/src/prime_caches.rs b/crates/ra_ide/src/prime_caches.rs
index 90bf7d25f..c5ab5a1d8 100644
--- a/crates/ra_ide/src/prime_caches.rs
+++ b/crates/ra_ide/src/prime_caches.rs
@@ -7,6 +7,6 @@ use crate::{FileId, RootDatabase};
7 7
8pub(crate) fn prime_caches(db: &RootDatabase, files: Vec<FileId>) { 8pub(crate) fn prime_caches(db: &RootDatabase, files: Vec<FileId>) {
9 for file in files { 9 for file in files {
10 let _ = crate::syntax_highlighting::highlight(db, file, None); 10 let _ = crate::syntax_highlighting::highlight(db, file, None, false);
11 } 11 }
12} 12}
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index bb40d2043..c2b0d5efe 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -86,12 +86,11 @@ impl IntoIterator for ReferenceSearchResult {
86} 86}
87 87
88pub(crate) fn find_all_refs( 88pub(crate) fn find_all_refs(
89 db: &RootDatabase, 89 sema: &Semantics<RootDatabase>,
90 position: FilePosition, 90 position: FilePosition,
91 search_scope: Option<SearchScope>, 91 search_scope: Option<SearchScope>,
92) -> Option<RangeInfo<ReferenceSearchResult>> { 92) -> Option<RangeInfo<ReferenceSearchResult>> {
93 let _p = profile("find_all_refs"); 93 let _p = profile("find_all_refs");
94 let sema = Semantics::new(db);
95 let syntax = sema.parse(position.file_id).syntax().clone(); 94 let syntax = sema.parse(position.file_id).syntax().clone();
96 95
97 let (opt_name, search_kind) = if let Some(name) = 96 let (opt_name, search_kind) = if let Some(name) =
@@ -108,15 +107,15 @@ pub(crate) fn find_all_refs(
108 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; 107 let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?;
109 108
110 let references = def 109 let references = def
111 .find_usages(db, search_scope) 110 .find_usages(sema, search_scope)
112 .into_iter() 111 .into_iter()
113 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) 112 .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)
114 .collect(); 113 .collect();
115 114
116 let decl_range = def.try_to_nav(db)?.range(); 115 let decl_range = def.try_to_nav(sema.db)?.range();
117 116
118 let declaration = Declaration { 117 let declaration = Declaration {
119 nav: def.try_to_nav(db)?, 118 nav: def.try_to_nav(sema.db)?,
120 kind: ReferenceKind::Other, 119 kind: ReferenceKind::Other,
121 access: decl_access(&def, &syntax, decl_range), 120 access: decl_access(&def, &syntax, decl_range),
122 }; 121 };
@@ -191,244 +190,249 @@ fn get_struct_def_name_for_struct_literal_search(
191#[cfg(test)] 190#[cfg(test)]
192mod tests { 191mod tests {
193 use crate::{ 192 use crate::{
194 mock_analysis::{analysis_and_position, single_file_with_position, MockAnalysis}, 193 mock_analysis::{analysis_and_position, MockAnalysis},
195 Declaration, Reference, ReferenceSearchResult, SearchScope, 194 Declaration, Reference, ReferenceSearchResult, SearchScope,
196 }; 195 };
197 196
198 #[test] 197 #[test]
199 fn test_struct_literal_after_space() { 198 fn test_struct_literal_after_space() {
200 let code = r#" 199 let refs = get_all_refs(
201 struct Foo <|>{ 200 r#"
202 a: i32, 201struct Foo <|>{
203 } 202 a: i32,
204 impl Foo { 203}
205 fn f() -> i32 { 42 } 204impl Foo {
206 } 205 fn f() -> i32 { 42 }
207 fn main() { 206}
208 let f: Foo; 207fn main() {
209 f = Foo {a: Foo::f()}; 208 let f: Foo;
210 }"#; 209 f = Foo {a: Foo::f()};
211 210}
212 let refs = get_all_refs(code); 211"#,
212 );
213 check_result( 213 check_result(
214 refs, 214 refs,
215 "Foo STRUCT_DEF FileId(1) 5..39 12..15 Other", 215 "Foo STRUCT_DEF FileId(1) 0..26 7..10 Other",
216 &["FileId(1) 138..141 StructLiteral"], 216 &["FileId(1) 101..104 StructLiteral"],
217 ); 217 );
218 } 218 }
219 219
220 #[test] 220 #[test]
221 fn test_struct_literal_befor_space() { 221 fn test_struct_literal_before_space() {
222 let code = r#" 222 let refs = get_all_refs(
223 struct Foo<|> {} 223 r#"
224 fn main() { 224struct Foo<|> {}
225 let f: Foo; 225 fn main() {
226 f = Foo {}; 226 let f: Foo;
227 }"#; 227 f = Foo {};
228 228}
229 let refs = get_all_refs(code); 229"#,
230 );
230 check_result( 231 check_result(
231 refs, 232 refs,
232 "Foo STRUCT_DEF FileId(1) 5..18 12..15 Other", 233 "Foo STRUCT_DEF FileId(1) 0..13 7..10 Other",
233 &["FileId(1) 54..57 Other", "FileId(1) 71..74 StructLiteral"], 234 &["FileId(1) 41..44 Other", "FileId(1) 54..57 StructLiteral"],
234 ); 235 );
235 } 236 }
236 237
237 #[test] 238 #[test]
238 fn test_struct_literal_with_generic_type() { 239 fn test_struct_literal_with_generic_type() {
239 let code = r#" 240 let refs = get_all_refs(
240 struct Foo<T> <|>{} 241 r#"
241 fn main() { 242struct Foo<T> <|>{}
242 let f: Foo::<i32>; 243 fn main() {
243 f = Foo {}; 244 let f: Foo::<i32>;
244 }"#; 245 f = Foo {};
245 246}
246 let refs = get_all_refs(code); 247"#,
248 );
247 check_result( 249 check_result(
248 refs, 250 refs,
249 "Foo STRUCT_DEF FileId(1) 5..21 12..15 Other", 251 "Foo STRUCT_DEF FileId(1) 0..16 7..10 Other",
250 &["FileId(1) 81..84 StructLiteral"], 252 &["FileId(1) 64..67 StructLiteral"],
251 ); 253 );
252 } 254 }
253 255
254 #[test] 256 #[test]
255 fn test_struct_literal_for_tuple() { 257 fn test_struct_literal_for_tuple() {
256 let code = r#" 258 let refs = get_all_refs(
257 struct Foo<|>(i32); 259 r#"
260struct Foo<|>(i32);
258 261
259 fn main() { 262fn main() {
260 let f: Foo; 263 let f: Foo;
261 f = Foo(1); 264 f = Foo(1);
262 }"#; 265}
263 266"#,
264 let refs = get_all_refs(code); 267 );
265 check_result( 268 check_result(
266 refs, 269 refs,
267 "Foo STRUCT_DEF FileId(1) 5..21 12..15 Other", 270 "Foo STRUCT_DEF FileId(1) 0..16 7..10 Other",
268 &["FileId(1) 71..74 StructLiteral"], 271 &["FileId(1) 54..57 StructLiteral"],
269 ); 272 );
270 } 273 }
271 274
272 #[test] 275 #[test]
273 fn test_find_all_refs_for_local() { 276 fn test_find_all_refs_for_local() {
274 let code = r#" 277 let refs = get_all_refs(
275 fn main() { 278 r#"
276 let mut i = 1; 279fn main() {
277 let j = 1; 280 let mut i = 1;
278 i = i<|> + j; 281 let j = 1;
282 i = i<|> + j;
279 283
280 { 284 {
281 i = 0; 285 i = 0;
282 } 286 }
283
284 i = 5;
285 }"#;
286 287
287 let refs = get_all_refs(code); 288 i = 5;
289}"#,
290 );
288 check_result( 291 check_result(
289 refs, 292 refs,
290 "i BIND_PAT FileId(1) 33..34 Other Write", 293 "i BIND_PAT FileId(1) 24..25 Other Write",
291 &[ 294 &[
292 "FileId(1) 67..68 Other Write", 295 "FileId(1) 50..51 Other Write",
293 "FileId(1) 71..72 Other Read", 296 "FileId(1) 54..55 Other Read",
294 "FileId(1) 101..102 Other Write", 297 "FileId(1) 76..77 Other Write",
295 "FileId(1) 127..128 Other Write", 298 "FileId(1) 94..95 Other Write",
296 ], 299 ],
297 ); 300 );
298 } 301 }
299 302
300 #[test] 303 #[test]
301 fn search_filters_by_range() { 304 fn search_filters_by_range() {
302 let code = r#" 305 let refs = get_all_refs(
303 fn foo() { 306 r#"
304 let spam<|> = 92; 307fn foo() {
305 spam + spam 308 let spam<|> = 92;
306 } 309 spam + spam
307 fn bar() { 310}
308 let spam = 92; 311fn bar() {
309 spam + spam 312 let spam = 92;
310 } 313 spam + spam
311 "#; 314}
312 let refs = get_all_refs(code); 315"#,
316 );
313 check_result( 317 check_result(
314 refs, 318 refs,
315 "spam BIND_PAT FileId(1) 44..48 Other", 319 "spam BIND_PAT FileId(1) 19..23 Other",
316 &["FileId(1) 71..75 Other Read", "FileId(1) 78..82 Other Read"], 320 &["FileId(1) 34..38 Other Read", "FileId(1) 41..45 Other Read"],
317 ); 321 );
318 } 322 }
319 323
320 #[test] 324 #[test]
321 fn test_find_all_refs_for_param_inside() { 325 fn test_find_all_refs_for_param_inside() {
322 let code = r#" 326 let refs = get_all_refs(
323 fn foo(i : u32) -> u32 { 327 r#"
324 i<|> 328fn foo(i : u32) -> u32 {
325 }"#; 329 i<|>
326 330}
327 let refs = get_all_refs(code); 331"#,
328 check_result(refs, "i BIND_PAT FileId(1) 12..13 Other", &["FileId(1) 38..39 Other Read"]); 332 );
333 check_result(refs, "i BIND_PAT FileId(1) 7..8 Other", &["FileId(1) 29..30 Other Read"]);
329 } 334 }
330 335
331 #[test] 336 #[test]
332 fn test_find_all_refs_for_fn_param() { 337 fn test_find_all_refs_for_fn_param() {
333 let code = r#" 338 let refs = get_all_refs(
334 fn foo(i<|> : u32) -> u32 { 339 r#"
335 i 340fn foo(i<|> : u32) -> u32 {
336 }"#; 341 i
337 342}
338 let refs = get_all_refs(code); 343"#,
339 check_result(refs, "i BIND_PAT FileId(1) 12..13 Other", &["FileId(1) 38..39 Other Read"]); 344 );
345 check_result(refs, "i BIND_PAT FileId(1) 7..8 Other", &["FileId(1) 29..30 Other Read"]);
340 } 346 }
341 347
342 #[test] 348 #[test]
343 fn test_find_all_refs_field_name() { 349 fn test_find_all_refs_field_name() {
344 let code = r#" 350 let refs = get_all_refs(
345 //- /lib.rs 351 r#"
346 struct Foo { 352//- /lib.rs
347 pub spam<|>: u32, 353struct Foo {
348 } 354 pub spam<|>: u32,
349 355}
350 fn main(s: Foo) {
351 let f = s.spam;
352 }
353 "#;
354 356
355 let refs = get_all_refs(code); 357fn main(s: Foo) {
358 let f = s.spam;
359}
360"#,
361 );
356 check_result( 362 check_result(
357 refs, 363 refs,
358 "spam RECORD_FIELD_DEF FileId(1) 66..79 70..74 Other", 364 "spam RECORD_FIELD_DEF FileId(1) 17..30 21..25 Other",
359 &["FileId(1) 152..156 Other Read"], 365 &["FileId(1) 67..71 Other Read"],
360 ); 366 );
361 } 367 }
362 368
363 #[test] 369 #[test]
364 fn test_find_all_refs_impl_item_name() { 370 fn test_find_all_refs_impl_item_name() {
365 let code = r#" 371 let refs = get_all_refs(
366 //- /lib.rs 372 r#"
367 struct Foo; 373struct Foo;
368 impl Foo { 374impl Foo {
369 fn f<|>(&self) { } 375 fn f<|>(&self) { }
370 } 376}
371 "#; 377"#,
372 378 );
373 let refs = get_all_refs(code); 379 check_result(refs, "f FN_DEF FileId(1) 27..43 30..31 Other", &[]);
374 check_result(refs, "f FN_DEF FileId(1) 88..104 91..92 Other", &[]);
375 } 380 }
376 381
377 #[test] 382 #[test]
378 fn test_find_all_refs_enum_var_name() { 383 fn test_find_all_refs_enum_var_name() {
379 let code = r#" 384 let refs = get_all_refs(
380 //- /lib.rs 385 r#"
381 enum Foo { 386enum Foo {
382 A, 387 A,
383 B<|>, 388 B<|>,
384 C, 389 C,
385 } 390}
386 "#; 391"#,
387 392 );
388 let refs = get_all_refs(code); 393 check_result(refs, "B ENUM_VARIANT FileId(1) 22..23 22..23 Other", &[]);
389 check_result(refs, "B ENUM_VARIANT FileId(1) 83..84 83..84 Other", &[]);
390 } 394 }
391 395
392 #[test] 396 #[test]
393 fn test_find_all_refs_two_modules() { 397 fn test_find_all_refs_two_modules() {
394 let code = r#" 398 let (analysis, pos) = analysis_and_position(
395 //- /lib.rs 399 r#"
396 pub mod foo; 400//- /lib.rs
397 pub mod bar; 401pub mod foo;
398 402pub mod bar;
399 fn f() { 403
400 let i = foo::Foo { n: 5 }; 404fn f() {
401 } 405 let i = foo::Foo { n: 5 };
402 406}
403 //- /foo.rs
404 use crate::bar;
405 407
406 pub struct Foo { 408//- /foo.rs
407 pub n: u32, 409use crate::bar;
408 }
409 410
410 fn f() { 411pub struct Foo {
411 let i = bar::Bar { n: 5 }; 412 pub n: u32,
412 } 413}
413 414
414 //- /bar.rs 415fn f() {
415 use crate::foo; 416 let i = bar::Bar { n: 5 };
417}
416 418
417 pub struct Bar { 419//- /bar.rs
418 pub n: u32, 420use crate::foo;
419 }
420 421
421 fn f() { 422pub struct Bar {
422 let i = foo::Foo<|> { n: 5 }; 423 pub n: u32,
423 } 424}
424 "#;
425 425
426 let (analysis, pos) = analysis_and_position(code); 426fn f() {
427 let i = foo::Foo<|> { n: 5 };
428}
429"#,
430 );
427 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 431 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
428 check_result( 432 check_result(
429 refs, 433 refs,
430 "Foo STRUCT_DEF FileId(2) 16..50 27..30 Other", 434 "Foo STRUCT_DEF FileId(2) 17..51 28..31 Other",
431 &["FileId(1) 52..55 StructLiteral", "FileId(3) 77..80 StructLiteral"], 435 &["FileId(1) 53..56 StructLiteral", "FileId(3) 79..82 StructLiteral"],
432 ); 436 );
433 } 437 }
434 438
@@ -437,53 +441,53 @@ mod tests {
437 // which is the whole `foo.rs`, and the second one is in `use foo::Foo`. 441 // which is the whole `foo.rs`, and the second one is in `use foo::Foo`.
438 #[test] 442 #[test]
439 fn test_find_all_refs_decl_module() { 443 fn test_find_all_refs_decl_module() {
440 let code = r#" 444 let (analysis, pos) = analysis_and_position(
441 //- /lib.rs 445 r#"
442 mod foo<|>; 446//- /lib.rs
447mod foo<|>;
443 448
444 use foo::Foo; 449use foo::Foo;
445 450
446 fn f() { 451fn f() {
447 let i = Foo { n: 5 }; 452 let i = Foo { n: 5 };
448 } 453}
449
450 //- /foo.rs
451 pub struct Foo {
452 pub n: u32,
453 }
454 "#;
455 454
456 let (analysis, pos) = analysis_and_position(code); 455//- /foo.rs
456pub struct Foo {
457 pub n: u32,
458}
459"#,
460 );
457 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 461 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
458 check_result(refs, "foo SOURCE_FILE FileId(2) 0..35 Other", &["FileId(1) 13..16 Other"]); 462 check_result(refs, "foo SOURCE_FILE FileId(2) 0..35 Other", &["FileId(1) 14..17 Other"]);
459 } 463 }
460 464
461 #[test] 465 #[test]
462 fn test_find_all_refs_super_mod_vis() { 466 fn test_find_all_refs_super_mod_vis() {
463 let code = r#" 467 let (analysis, pos) = analysis_and_position(
464 //- /lib.rs 468 r#"
465 mod foo; 469//- /lib.rs
466 470mod foo;
467 //- /foo.rs
468 mod some;
469 use some::Foo;
470 471
471 fn f() { 472//- /foo.rs
472 let i = Foo { n: 5 }; 473mod some;
473 } 474use some::Foo;
474 475
475 //- /foo/some.rs 476fn f() {
476 pub(super) struct Foo<|> { 477 let i = Foo { n: 5 };
477 pub n: u32, 478}
478 }
479 "#;
480 479
481 let (analysis, pos) = analysis_and_position(code); 480//- /foo/some.rs
481pub(super) struct Foo<|> {
482 pub n: u32,
483}
484"#,
485 );
482 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 486 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
483 check_result( 487 check_result(
484 refs, 488 refs,
485 "Foo STRUCT_DEF FileId(3) 0..41 18..21 Other", 489 "Foo STRUCT_DEF FileId(3) 0..41 18..21 Other",
486 &["FileId(2) 20..23 Other", "FileId(2) 46..49 StructLiteral"], 490 &["FileId(2) 20..23 Other", "FileId(2) 47..50 StructLiteral"],
487 ); 491 );
488 } 492 }
489 493
@@ -510,7 +514,7 @@ mod tests {
510 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 514 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
511 check_result( 515 check_result(
512 refs, 516 refs,
513 "quux FN_DEF FileId(1) 18..34 25..29 Other", 517 "quux FN_DEF FileId(1) 19..35 26..30 Other",
514 &["FileId(2) 16..20 StructLiteral", "FileId(3) 16..20 StructLiteral"], 518 &["FileId(2) 16..20 StructLiteral", "FileId(3) 16..20 StructLiteral"],
515 ); 519 );
516 520
@@ -518,100 +522,105 @@ mod tests {
518 analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); 522 analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap();
519 check_result( 523 check_result(
520 refs, 524 refs,
521 "quux FN_DEF FileId(1) 18..34 25..29 Other", 525 "quux FN_DEF FileId(1) 19..35 26..30 Other",
522 &["FileId(3) 16..20 StructLiteral"], 526 &["FileId(3) 16..20 StructLiteral"],
523 ); 527 );
524 } 528 }
525 529
526 #[test] 530 #[test]
527 fn test_find_all_refs_macro_def() { 531 fn test_find_all_refs_macro_def() {
528 let code = r#" 532 let refs = get_all_refs(
529 #[macro_export] 533 r#"
530 macro_rules! m1<|> { () => (()) } 534#[macro_export]
531 535macro_rules! m1<|> { () => (()) }
532 fn foo() { 536
533 m1(); 537fn foo() {
534 m1(); 538 m1();
535 }"#; 539 m1();
536 540}
537 let refs = get_all_refs(code); 541"#,
542 );
538 check_result( 543 check_result(
539 refs, 544 refs,
540 "m1 MACRO_CALL FileId(1) 9..63 46..48 Other", 545 "m1 MACRO_CALL FileId(1) 0..46 29..31 Other",
541 &["FileId(1) 96..98 StructLiteral", "FileId(1) 114..116 StructLiteral"], 546 &["FileId(1) 63..65 StructLiteral", "FileId(1) 73..75 StructLiteral"],
542 ); 547 );
543 } 548 }
544 549
545 #[test] 550 #[test]
546 fn test_basic_highlight_read_write() { 551 fn test_basic_highlight_read_write() {
547 let code = r#" 552 let refs = get_all_refs(
548 fn foo() { 553 r#"
549 let mut i<|> = 0; 554fn foo() {
550 i = i + 1; 555 let mut i<|> = 0;
551 }"#; 556 i = i + 1;
552 557}
553 let refs = get_all_refs(code); 558"#,
559 );
554 check_result( 560 check_result(
555 refs, 561 refs,
556 "i BIND_PAT FileId(1) 40..41 Other Write", 562 "i BIND_PAT FileId(1) 23..24 Other Write",
557 &["FileId(1) 59..60 Other Write", "FileId(1) 63..64 Other Read"], 563 &["FileId(1) 34..35 Other Write", "FileId(1) 38..39 Other Read"],
558 ); 564 );
559 } 565 }
560 566
561 #[test] 567 #[test]
562 fn test_basic_highlight_field_read_write() { 568 fn test_basic_highlight_field_read_write() {
563 let code = r#" 569 let refs = get_all_refs(
564 struct S { 570 r#"
565 f: u32, 571struct S {
566 } 572 f: u32,
567 573}
568 fn foo() {
569 let mut s = S{f: 0};
570 s.f<|> = 0;
571 }"#;
572 574
573 let refs = get_all_refs(code); 575fn foo() {
576 let mut s = S{f: 0};
577 s.f<|> = 0;
578}
579"#,
580 );
574 check_result( 581 check_result(
575 refs, 582 refs,
576 "f RECORD_FIELD_DEF FileId(1) 32..38 32..33 Other", 583 "f RECORD_FIELD_DEF FileId(1) 15..21 15..16 Other",
577 &["FileId(1) 96..97 Other Read", "FileId(1) 117..118 Other Write"], 584 &["FileId(1) 55..56 Other Read", "FileId(1) 68..69 Other Write"],
578 ); 585 );
579 } 586 }
580 587
581 #[test] 588 #[test]
582 fn test_basic_highlight_decl_no_write() { 589 fn test_basic_highlight_decl_no_write() {
583 let code = r#" 590 let refs = get_all_refs(
584 fn foo() { 591 r#"
585 let i<|>; 592fn foo() {
586 i = 1; 593 let i<|>;
587 }"#; 594 i = 1;
588 595}
589 let refs = get_all_refs(code); 596"#,
590 check_result(refs, "i BIND_PAT FileId(1) 36..37 Other", &["FileId(1) 51..52 Other Write"]); 597 );
598 check_result(refs, "i BIND_PAT FileId(1) 19..20 Other", &["FileId(1) 26..27 Other Write"]);
591 } 599 }
592 600
593 #[test] 601 #[test]
594 fn test_find_struct_function_refs_outside_module() { 602 fn test_find_struct_function_refs_outside_module() {
595 let code = r#" 603 let refs = get_all_refs(
596 mod foo { 604 r#"
597 pub struct Foo; 605mod foo {
606 pub struct Foo;
598 607
599 impl Foo { 608 impl Foo {
600 pub fn new<|>() -> Foo { 609 pub fn new<|>() -> Foo {
601 Foo 610 Foo
602 }
603 }
604 } 611 }
612 }
613}
605 614
606 fn main() { 615fn main() {
607 let _f = foo::Foo::new(); 616 let _f = foo::Foo::new();
608 }"#; 617}
609 618"#,
610 let refs = get_all_refs(code); 619 );
611 check_result( 620 check_result(
612 refs, 621 refs,
613 "new FN_DEF FileId(1) 87..150 94..97 Other", 622 "new FN_DEF FileId(1) 54..101 61..64 Other",
614 &["FileId(1) 227..230 StructLiteral"], 623 &["FileId(1) 146..149 StructLiteral"],
615 ); 624 );
616 } 625 }
617 626
@@ -637,13 +646,13 @@ mod tests {
637 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); 646 let refs = analysis.find_all_refs(pos, None).unwrap().unwrap();
638 check_result( 647 check_result(
639 refs, 648 refs,
640 "f FN_DEF FileId(1) 25..34 28..29 Other", 649 "f FN_DEF FileId(1) 26..35 29..30 Other",
641 &["FileId(2) 11..12 Other", "FileId(2) 27..28 StructLiteral"], 650 &["FileId(2) 11..12 Other", "FileId(2) 28..29 StructLiteral"],
642 ); 651 );
643 } 652 }
644 653
645 fn get_all_refs(text: &str) -> ReferenceSearchResult { 654 fn get_all_refs(ra_fixture: &str) -> ReferenceSearchResult {
646 let (analysis, position) = single_file_with_position(text); 655 let (analysis, position) = analysis_and_position(ra_fixture);
647 analysis.find_all_refs(position, None).unwrap().unwrap() 656 analysis.find_all_refs(position, None).unwrap().unwrap()
648 } 657 }
649 658
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 28c6349b1..8735ec53c 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -1,11 +1,14 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{ModuleSource, Semantics}; 3use hir::{Module, ModuleDef, ModuleSource, Semantics};
4use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt}; 4use ra_db::SourceDatabaseExt;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::{
6 defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
7 RootDatabase,
8};
6use ra_syntax::{ 9use ra_syntax::{
7 algo::find_node_at_offset, ast, ast::TypeAscriptionOwner, lex_single_valid_syntax_kind, 10 algo::find_node_at_offset, ast, ast::NameOwner, ast::TypeAscriptionOwner,
8 AstNode, SyntaxKind, SyntaxNode, SyntaxToken, 11 lex_single_valid_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
9}; 12};
10use ra_text_edit::TextEdit; 13use ra_text_edit::TextEdit;
11use std::convert::TryInto; 14use std::convert::TryInto;
@@ -21,35 +24,53 @@ pub(crate) fn rename(
21 position: FilePosition, 24 position: FilePosition,
22 new_name: &str, 25 new_name: &str,
23) -> Option<RangeInfo<SourceChange>> { 26) -> Option<RangeInfo<SourceChange>> {
27 let sema = Semantics::new(db);
28
24 match lex_single_valid_syntax_kind(new_name)? { 29 match lex_single_valid_syntax_kind(new_name)? {
25 SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), 30 SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (),
26 SyntaxKind::SELF_KW => return rename_to_self(db, position), 31 SyntaxKind::SELF_KW => return rename_to_self(&sema, position),
27 _ => return None, 32 _ => return None,
28 } 33 }
29 34
30 let sema = Semantics::new(db);
31 let source_file = sema.parse(position.file_id); 35 let source_file = sema.parse(position.file_id);
32 let syntax = source_file.syntax(); 36 let syntax = source_file.syntax();
33 if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) { 37 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
34 let range = ast_name.syntax().text_range(); 38 rename_mod(&sema, position, module, new_name)
35 rename_mod(&sema, &ast_name, &ast_module, position, new_name)
36 .map(|info| RangeInfo::new(range, info))
37 } else if let Some(self_token) = 39 } else if let Some(self_token) =
38 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 40 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
39 { 41 {
40 rename_self_to_param(db, position, self_token, new_name) 42 rename_self_to_param(&sema, position, self_token, new_name)
41 } else { 43 } else {
42 rename_reference(sema.db, position, new_name) 44 rename_reference(&sema, position, new_name)
43 } 45 }
44} 46}
45 47
46fn find_name_and_module_at_offset( 48fn find_module_at_offset(
47 syntax: &SyntaxNode, 49 sema: &Semantics<RootDatabase>,
48 position: FilePosition, 50 position: FilePosition,
49) -> Option<(ast::Name, ast::Module)> { 51 syntax: &SyntaxNode,
50 let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset)?; 52) -> Option<Module> {
51 let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?; 53 let ident = syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::IDENT)?;
52 Some((ast_name, ast_module)) 54
55 let module = match_ast! {
56 match (ident.parent()) {
57 ast::NameRef(name_ref) => {
58 match classify_name_ref(sema, &name_ref)? {
59 NameRefClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
60 _ => return None,
61 }
62 },
63 ast::Name(name) => {
64 match classify_name(&sema, &name)? {
65 NameClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
66 _ => return None,
67 }
68 },
69 _ => return None,
70 }
71 };
72
73 Some(module)
53} 74}
54 75
55fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit { 76fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit {
@@ -78,61 +99,53 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
78 99
79fn rename_mod( 100fn rename_mod(
80 sema: &Semantics<RootDatabase>, 101 sema: &Semantics<RootDatabase>,
81 ast_name: &ast::Name,
82 ast_module: &ast::Module,
83 position: FilePosition, 102 position: FilePosition,
103 module: Module,
84 new_name: &str, 104 new_name: &str,
85) -> Option<SourceChange> { 105) -> Option<RangeInfo<SourceChange>> {
86 let mut source_file_edits = Vec::new(); 106 let mut source_file_edits = Vec::new();
87 let mut file_system_edits = Vec::new(); 107 let mut file_system_edits = Vec::new();
88 if let Some(module) = sema.to_def(ast_module) { 108
89 let src = module.definition_source(sema.db); 109 let src = module.definition_source(sema.db);
90 let file_id = src.file_id.original_file(sema.db); 110 let file_id = src.file_id.original_file(sema.db);
91 match src.value { 111 match src.value {
92 ModuleSource::SourceFile(..) => { 112 ModuleSource::SourceFile(..) => {
93 let mod_path: RelativePathBuf = sema.db.file_relative_path(file_id); 113 // mod is defined in path/to/dir/mod.rs
94 // mod is defined in path/to/dir/mod.rs 114 let dst = if module.is_mod_rs(sema.db) {
95 let dst_path = if mod_path.file_stem() == Some("mod") { 115 format!("../{}/mod.rs", new_name)
96 mod_path 116 } else {
97 .parent() 117 format!("{}.rs", new_name)
98 .and_then(|p| p.parent()) 118 };
99 .or_else(|| Some(RelativePath::new(""))) 119 let move_file = FileSystemEdit::MoveFile { src: file_id, anchor: file_id, dst };
100 .map(|p| p.join(new_name).join("mod.rs")) 120 file_system_edits.push(move_file);
101 } else {
102 Some(mod_path.with_file_name(new_name).with_extension("rs"))
103 };
104 if let Some(path) = dst_path {
105 let move_file = FileSystemEdit::MoveFile {
106 src: file_id,
107 dst_source_root: sema.db.file_source_root(position.file_id),
108 dst_path: path,
109 };
110 file_system_edits.push(move_file);
111 }
112 }
113 ModuleSource::Module(..) => {}
114 } 121 }
122 ModuleSource::Module(..) => {}
115 } 123 }
116 124
117 let edit = SourceFileEdit { 125 if let Some(src) = module.declaration_source(sema.db) {
118 file_id: position.file_id, 126 let file_id = src.file_id.original_file(sema.db);
119 edit: TextEdit::replace(ast_name.syntax().text_range(), new_name.into()), 127 let name = src.value.name()?;
120 }; 128 let edit = SourceFileEdit {
121 source_file_edits.push(edit); 129 file_id,
122 130 edit: TextEdit::replace(name.syntax().text_range(), new_name.into()),
123 if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(sema.db, position, None) { 131 };
124 let ref_edits = refs 132 source_file_edits.push(edit);
125 .references
126 .into_iter()
127 .map(|reference| source_edit_from_reference(reference, new_name));
128 source_file_edits.extend(ref_edits);
129 } 133 }
130 134
131 Some(SourceChange::from_edits(source_file_edits, file_system_edits)) 135 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
136 let ref_edits = refs
137 .references
138 .into_iter()
139 .map(|reference| source_edit_from_reference(reference, new_name));
140 source_file_edits.extend(ref_edits);
141
142 Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
132} 143}
133 144
134fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { 145fn rename_to_self(
135 let sema = Semantics::new(db); 146 sema: &Semantics<RootDatabase>,
147 position: FilePosition,
148) -> Option<RangeInfo<SourceChange>> {
136 let source_file = sema.parse(position.file_id); 149 let source_file = sema.parse(position.file_id);
137 let syn = source_file.syntax(); 150 let syn = source_file.syntax();
138 151
@@ -147,7 +160,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo
147 _ => return None, // not renaming other types 160 _ => return None, // not renaming other types
148 }; 161 };
149 162
150 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; 163 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
151 164
152 let param_range = first_param.syntax().text_range(); 165 let param_range = first_param.syntax().text_range();
153 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs 166 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
@@ -171,7 +184,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo
171 ), 184 ),
172 }); 185 });
173 186
174 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) 187 Some(RangeInfo::new(range, SourceChange::from(edits)))
175} 188}
176 189
177fn text_edit_from_self_param( 190fn text_edit_from_self_param(
@@ -199,16 +212,15 @@ fn text_edit_from_self_param(
199} 212}
200 213
201fn rename_self_to_param( 214fn rename_self_to_param(
202 db: &RootDatabase, 215 sema: &Semantics<RootDatabase>,
203 position: FilePosition, 216 position: FilePosition,
204 self_token: SyntaxToken, 217 self_token: SyntaxToken,
205 new_name: &str, 218 new_name: &str,
206) -> Option<RangeInfo<SourceChange>> { 219) -> Option<RangeInfo<SourceChange>> {
207 let sema = Semantics::new(db);
208 let source_file = sema.parse(position.file_id); 220 let source_file = sema.parse(position.file_id);
209 let syn = source_file.syntax(); 221 let syn = source_file.syntax();
210 222
211 let text = db.file_text(position.file_id); 223 let text = sema.db.file_text(position.file_id);
212 let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?; 224 let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?;
213 let search_range = fn_def.syntax().text_range(); 225 let search_range = fn_def.syntax().text_range();
214 226
@@ -234,15 +246,15 @@ fn rename_self_to_param(
234 let range = ast::SelfParam::cast(self_token.parent()) 246 let range = ast::SelfParam::cast(self_token.parent())
235 .map_or(self_token.text_range(), |p| p.syntax().text_range()); 247 .map_or(self_token.text_range(), |p| p.syntax().text_range());
236 248
237 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) 249 Some(RangeInfo::new(range, SourceChange::from(edits)))
238} 250}
239 251
240fn rename_reference( 252fn rename_reference(
241 db: &RootDatabase, 253 sema: &Semantics<RootDatabase>,
242 position: FilePosition, 254 position: FilePosition,
243 new_name: &str, 255 new_name: &str,
244) -> Option<RangeInfo<SourceChange>> { 256) -> Option<RangeInfo<SourceChange>> {
245 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; 257 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
246 258
247 let edit = refs 259 let edit = refs
248 .into_iter() 260 .into_iter()
@@ -253,57 +265,56 @@ fn rename_reference(
253 return None; 265 return None;
254 } 266 }
255 267
256 Some(RangeInfo::new(range, SourceChange::source_file_edits(edit))) 268 Some(RangeInfo::new(range, SourceChange::from(edit)))
257} 269}
258 270
259#[cfg(test)] 271#[cfg(test)]
260mod tests { 272mod tests {
261 use insta::assert_debug_snapshot; 273 use expect::{expect, Expect};
262 use ra_text_edit::TextEditBuilder; 274 use ra_text_edit::TextEditBuilder;
275 use stdx::trim_indent;
263 use test_utils::{assert_eq_text, mark}; 276 use test_utils::{assert_eq_text, mark};
264 277
265 use crate::{ 278 use crate::{mock_analysis::analysis_and_position, FileId};
266 mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, 279
267 }; 280 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
281 let ra_fixture_after = &trim_indent(ra_fixture_after);
282 let (analysis, position) = analysis_and_position(ra_fixture_before);
283 let source_change = analysis.rename(position, new_name).unwrap();
284 let mut text_edit_builder = TextEditBuilder::default();
285 let mut file_id: Option<FileId> = None;
286 if let Some(change) = source_change {
287 for edit in change.info.source_file_edits {
288 file_id = Some(edit.file_id);
289 for indel in edit.edit.into_iter() {
290 text_edit_builder.replace(indel.delete, indel.insert);
291 }
292 }
293 }
294 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string();
295 text_edit_builder.finish().apply(&mut result);
296 assert_eq_text!(ra_fixture_after, &*result);
297 }
298
299 fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
300 let (analysis, position) = analysis_and_position(ra_fixture);
301 let source_change = analysis.rename(position, new_name).unwrap().unwrap();
302 expect.assert_debug_eq(&source_change)
303 }
268 304
269 #[test] 305 #[test]
270 fn test_rename_to_underscore() { 306 fn test_rename_to_underscore() {
271 test_rename( 307 check("_", r#"fn main() { let i<|> = 1; }"#, r#"fn main() { let _ = 1; }"#);
272 r#"
273 fn main() {
274 let i<|> = 1;
275 }"#,
276 "_",
277 r#"
278 fn main() {
279 let _ = 1;
280 }"#,
281 );
282 } 308 }
283 309
284 #[test] 310 #[test]
285 fn test_rename_to_raw_identifier() { 311 fn test_rename_to_raw_identifier() {
286 test_rename( 312 check("r#fn", r#"fn main() { let i<|> = 1; }"#, r#"fn main() { let r#fn = 1; }"#);
287 r#"
288 fn main() {
289 let i<|> = 1;
290 }"#,
291 "r#fn",
292 r#"
293 fn main() {
294 let r#fn = 1;
295 }"#,
296 );
297 } 313 }
298 314
299 #[test] 315 #[test]
300 fn test_rename_to_invalid_identifier() { 316 fn test_rename_to_invalid_identifier() {
301 let (analysis, position) = single_file_with_position( 317 let (analysis, position) = analysis_and_position(r#"fn main() { let i<|> = 1; }"#);
302 "
303 fn main() {
304 let i<|> = 1;
305 }",
306 );
307 let new_name = "invalid!"; 318 let new_name = "invalid!";
308 let source_change = analysis.rename(position, new_name).unwrap(); 319 let source_change = analysis.rename(position, new_name).unwrap();
309 assert!(source_change.is_none()); 320 assert!(source_change.is_none());
@@ -311,682 +322,689 @@ mod tests {
311 322
312 #[test] 323 #[test]
313 fn test_rename_for_local() { 324 fn test_rename_for_local() {
314 test_rename( 325 check(
326 "k",
315 r#" 327 r#"
316 fn main() { 328fn main() {
317 let mut i = 1; 329 let mut i = 1;
318 let j = 1; 330 let j = 1;
319 i = i<|> + j; 331 i = i<|> + j;
320 332
321 { 333 { i = 0; }
322 i = 0;
323 }
324 334
325 i = 5; 335 i = 5;
326 }"#, 336}
327 "k", 337"#,
328 r#" 338 r#"
329 fn main() { 339fn main() {
330 let mut k = 1; 340 let mut k = 1;
331 let j = 1; 341 let j = 1;
332 k = k + j; 342 k = k + j;
333 343
334 { 344 { k = 0; }
335 k = 0;
336 }
337 345
338 k = 5; 346 k = 5;
339 }"#, 347}
348"#,
340 ); 349 );
341 } 350 }
342 351
343 #[test] 352 #[test]
344 fn test_rename_for_macro_args() { 353 fn test_rename_for_macro_args() {
345 test_rename( 354 check(
346 r#"
347 macro_rules! foo {($i:ident) => {$i} }
348 fn main() {
349 let a<|> = "test";
350 foo!(a);
351 }"#,
352 "b", 355 "b",
353 r#" 356 r#"
354 macro_rules! foo {($i:ident) => {$i} } 357macro_rules! foo {($i:ident) => {$i} }
355 fn main() { 358fn main() {
356 let b = "test"; 359 let a<|> = "test";
357 foo!(b); 360 foo!(a);
358 }"#, 361}
362"#,
363 r#"
364macro_rules! foo {($i:ident) => {$i} }
365fn main() {
366 let b = "test";
367 foo!(b);
368}
369"#,
359 ); 370 );
360 } 371 }
361 372
362 #[test] 373 #[test]
363 fn test_rename_for_macro_args_rev() { 374 fn test_rename_for_macro_args_rev() {
364 test_rename( 375 check(
365 r#"
366 macro_rules! foo {($i:ident) => {$i} }
367 fn main() {
368 let a = "test";
369 foo!(a<|>);
370 }"#,
371 "b", 376 "b",
372 r#" 377 r#"
373 macro_rules! foo {($i:ident) => {$i} } 378macro_rules! foo {($i:ident) => {$i} }
374 fn main() { 379fn main() {
375 let b = "test"; 380 let a = "test";
376 foo!(b); 381 foo!(a<|>);
377 }"#, 382}
383"#,
384 r#"
385macro_rules! foo {($i:ident) => {$i} }
386fn main() {
387 let b = "test";
388 foo!(b);
389}
390"#,
378 ); 391 );
379 } 392 }
380 393
381 #[test] 394 #[test]
382 fn test_rename_for_macro_define_fn() { 395 fn test_rename_for_macro_define_fn() {
383 test_rename( 396 check(
384 r#"
385 macro_rules! define_fn {($id:ident) => { fn $id{} }}
386 define_fn!(foo);
387 fn main() {
388 fo<|>o();
389 }"#,
390 "bar", 397 "bar",
391 r#" 398 r#"
392 macro_rules! define_fn {($id:ident) => { fn $id{} }} 399macro_rules! define_fn {($id:ident) => { fn $id{} }}
393 define_fn!(bar); 400define_fn!(foo);
394 fn main() { 401fn main() {
395 bar(); 402 fo<|>o();
396 }"#, 403}
404"#,
405 r#"
406macro_rules! define_fn {($id:ident) => { fn $id{} }}
407define_fn!(bar);
408fn main() {
409 bar();
410}
411"#,
397 ); 412 );
398 } 413 }
399 414
400 #[test] 415 #[test]
401 fn test_rename_for_macro_define_fn_rev() { 416 fn test_rename_for_macro_define_fn_rev() {
402 test_rename( 417 check(
403 r#"
404 macro_rules! define_fn {($id:ident) => { fn $id{} }}
405 define_fn!(fo<|>o);
406 fn main() {
407 foo();
408 }"#,
409 "bar", 418 "bar",
410 r#" 419 r#"
411 macro_rules! define_fn {($id:ident) => { fn $id{} }} 420macro_rules! define_fn {($id:ident) => { fn $id{} }}
412 define_fn!(bar); 421define_fn!(fo<|>o);
413 fn main() { 422fn main() {
414 bar(); 423 foo();
415 }"#, 424}
425"#,
426 r#"
427macro_rules! define_fn {($id:ident) => { fn $id{} }}
428define_fn!(bar);
429fn main() {
430 bar();
431}
432"#,
416 ); 433 );
417 } 434 }
418 435
419 #[test] 436 #[test]
420 fn test_rename_for_param_inside() { 437 fn test_rename_for_param_inside() {
421 test_rename( 438 check("j", r#"fn foo(i : u32) -> u32 { i<|> }"#, r#"fn foo(j : u32) -> u32 { j }"#);
422 r#"
423 fn foo(i : u32) -> u32 {
424 i<|>
425 }"#,
426 "j",
427 r#"
428 fn foo(j : u32) -> u32 {
429 j
430 }"#,
431 );
432 } 439 }
433 440
434 #[test] 441 #[test]
435 fn test_rename_refs_for_fn_param() { 442 fn test_rename_refs_for_fn_param() {
436 test_rename( 443 check("j", r#"fn foo(i<|> : u32) -> u32 { i }"#, r#"fn foo(j : u32) -> u32 { j }"#);
437 r#"
438 fn foo(i<|> : u32) -> u32 {
439 i
440 }"#,
441 "new_name",
442 r#"
443 fn foo(new_name : u32) -> u32 {
444 new_name
445 }"#,
446 );
447 } 444 }
448 445
449 #[test] 446 #[test]
450 fn test_rename_for_mut_param() { 447 fn test_rename_for_mut_param() {
451 test_rename( 448 check("j", r#"fn foo(mut i<|> : u32) -> u32 { i }"#, r#"fn foo(mut j : u32) -> u32 { j }"#);
452 r#"
453 fn foo(mut i<|> : u32) -> u32 {
454 i
455 }"#,
456 "new_name",
457 r#"
458 fn foo(mut new_name : u32) -> u32 {
459 new_name
460 }"#,
461 );
462 } 449 }
463 450
464 #[test] 451 #[test]
465 fn test_rename_struct_field() { 452 fn test_rename_struct_field() {
466 test_rename( 453 check(
454 "j",
467 r#" 455 r#"
468 struct Foo { 456struct Foo { i<|>: i32 }
469 i<|>: i32,
470 }
471 457
472 impl Foo { 458impl Foo {
473 fn new(i: i32) -> Self { 459 fn new(i: i32) -> Self {
474 Self { i: i } 460 Self { i: i }
475 }
476 } 461 }
477 "#, 462}
478 "j", 463"#,
479 r#" 464 r#"
480 struct Foo { 465struct Foo { j: i32 }
481 j: i32,
482 }
483 466
484 impl Foo { 467impl Foo {
485 fn new(i: i32) -> Self { 468 fn new(i: i32) -> Self {
486 Self { j: i } 469 Self { j: i }
487 }
488 } 470 }
489 "#, 471}
472"#,
490 ); 473 );
491 } 474 }
492 475
493 #[test] 476 #[test]
494 fn test_rename_struct_field_for_shorthand() { 477 fn test_rename_struct_field_for_shorthand() {
495 mark::check!(test_rename_struct_field_for_shorthand); 478 mark::check!(test_rename_struct_field_for_shorthand);
496 test_rename( 479 check(
480 "j",
497 r#" 481 r#"
498 struct Foo { 482struct Foo { i<|>: i32 }
499 i<|>: i32,
500 }
501 483
502 impl Foo { 484impl Foo {
503 fn new(i: i32) -> Self { 485 fn new(i: i32) -> Self {
504 Self { i } 486 Self { i }
505 }
506 } 487 }
507 "#, 488}
508 "j", 489"#,
509 r#" 490 r#"
510 struct Foo { 491struct Foo { j: i32 }
511 j: i32,
512 }
513 492
514 impl Foo { 493impl Foo {
515 fn new(i: i32) -> Self { 494 fn new(i: i32) -> Self {
516 Self { j: i } 495 Self { j: i }
517 }
518 } 496 }
519 "#, 497}
498"#,
520 ); 499 );
521 } 500 }
522 501
523 #[test] 502 #[test]
524 fn test_rename_local_for_field_shorthand() { 503 fn test_rename_local_for_field_shorthand() {
525 mark::check!(test_rename_local_for_field_shorthand); 504 mark::check!(test_rename_local_for_field_shorthand);
526 test_rename( 505 check(
506 "j",
527 r#" 507 r#"
528 struct Foo { 508struct Foo { i: i32 }
529 i: i32,
530 }
531 509
532 impl Foo { 510impl Foo {
533 fn new(i<|>: i32) -> Self { 511 fn new(i<|>: i32) -> Self {
534 Self { i } 512 Self { i }
535 }
536 } 513 }
537 "#, 514}
538 "j", 515"#,
539 r#" 516 r#"
540 struct Foo { 517struct Foo { i: i32 }
541 i: i32,
542 }
543 518
544 impl Foo { 519impl Foo {
545 fn new(j: i32) -> Self { 520 fn new(j: i32) -> Self {
546 Self { i: j } 521 Self { i: j }
547 }
548 } 522 }
549 "#, 523}
524"#,
550 ); 525 );
551 } 526 }
552 527
553 #[test] 528 #[test]
554 fn test_field_shorthand_correct_struct() { 529 fn test_field_shorthand_correct_struct() {
555 test_rename( 530 check(
556 r#"
557 struct Foo {
558 i<|>: i32,
559 }
560
561 struct Bar {
562 i: i32,
563 }
564
565 impl Bar {
566 fn new(i: i32) -> Self {
567 Self { i }
568 }
569 }
570 "#,
571 "j", 531 "j",
572 r#" 532 r#"
573 struct Foo { 533struct Foo { i<|>: i32 }
574 j: i32, 534struct Bar { i: i32 }
575 }
576 535
577 struct Bar { 536impl Bar {
578 i: i32, 537 fn new(i: i32) -> Self {
538 Self { i }
579 } 539 }
540}
541"#,
542 r#"
543struct Foo { j: i32 }
544struct Bar { i: i32 }
580 545
581 impl Bar { 546impl Bar {
582 fn new(i: i32) -> Self { 547 fn new(i: i32) -> Self {
583 Self { i } 548 Self { i }
584 }
585 } 549 }
586 "#, 550}
551"#,
587 ); 552 );
588 } 553 }
589 554
590 #[test] 555 #[test]
591 fn test_shadow_local_for_struct_shorthand() { 556 fn test_shadow_local_for_struct_shorthand() {
592 test_rename( 557 check(
558 "j",
593 r#" 559 r#"
594 struct Foo { 560struct Foo { i: i32 }
595 i: i32,
596 }
597 561
598 fn baz(i<|>: i32) -> Self { 562fn baz(i<|>: i32) -> Self {
599 let x = Foo { i }; 563 let x = Foo { i };
600 { 564 {
601 let i = 0; 565 let i = 0;
602 Foo { i } 566 Foo { i }
603 }
604 } 567 }
605 "#, 568}
606 "j", 569"#,
607 r#" 570 r#"
608 struct Foo { 571struct Foo { i: i32 }
609 i: i32,
610 }
611 572
612 fn baz(j: i32) -> Self { 573fn baz(j: i32) -> Self {
613 let x = Foo { i: j }; 574 let x = Foo { i: j };
614 { 575 {
615 let i = 0; 576 let i = 0;
616 Foo { i } 577 Foo { i }
617 }
618 } 578 }
619 "#, 579}
580"#,
620 ); 581 );
621 } 582 }
622 583
623 #[test] 584 #[test]
624 fn test_rename_mod() { 585 fn test_rename_mod() {
625 let (analysis, position) = analysis_and_position( 586 check_expect(
626 " 587 "foo2",
627 //- /lib.rs 588 r#"
628 mod bar; 589//- /lib.rs
629 590mod bar;
630 //- /bar.rs 591
631 mod foo<|>; 592//- /bar.rs
632 593mod foo<|>;
633 //- /bar/foo.rs 594
634 // emtpy 595//- /bar/foo.rs
635 ", 596// empty
597"#,
598 expect![[r#"
599 RangeInfo {
600 range: 4..7,
601 info: SourceChange {
602 source_file_edits: [
603 SourceFileEdit {
604 file_id: FileId(
605 2,
606 ),
607 edit: TextEdit {
608 indels: [
609 Indel {
610 insert: "foo2",
611 delete: 4..7,
612 },
613 ],
614 },
615 },
616 ],
617 file_system_edits: [
618 MoveFile {
619 src: FileId(
620 3,
621 ),
622 anchor: FileId(
623 3,
624 ),
625 dst: "foo2.rs",
626 },
627 ],
628 is_snippet: false,
629 },
630 }
631 "#]],
636 ); 632 );
637 let new_name = "foo2"; 633 }
638 let source_change = analysis.rename(position, new_name).unwrap(); 634
639 assert_debug_snapshot!(&source_change, 635 #[test]
640@r###" 636 fn test_rename_mod_in_use_tree() {
641 Some( 637 check_expect(
642 RangeInfo { 638 "quux",
643 range: 4..7, 639 r#"
644 info: SourceChange { 640//- /main.rs
645 source_file_edits: [ 641pub mod foo;
646 SourceFileEdit { 642pub mod bar;
647 file_id: FileId( 643fn main() {}
648 2, 644
649 ), 645//- /foo.rs
650 edit: TextEdit { 646pub struct FooContent;
651 indels: [ 647
652 Indel { 648//- /bar.rs
653 insert: "foo2", 649use crate::foo<|>::FooContent;
654 delete: 4..7, 650"#,
655 }, 651 expect![[r#"
656 ], 652 RangeInfo {
653 range: 11..14,
654 info: SourceChange {
655 source_file_edits: [
656 SourceFileEdit {
657 file_id: FileId(
658 1,
659 ),
660 edit: TextEdit {
661 indels: [
662 Indel {
663 insert: "quux",
664 delete: 8..11,
665 },
666 ],
667 },
657 }, 668 },
658 }, 669 SourceFileEdit {
659 ], 670 file_id: FileId(
660 file_system_edits: [ 671 3,
661 MoveFile { 672 ),
662 src: FileId( 673 edit: TextEdit {
663 3, 674 indels: [
664 ), 675 Indel {
665 dst_source_root: SourceRootId( 676 insert: "quux",
666 0, 677 delete: 11..14,
667 ), 678 },
668 dst_path: "bar/foo2.rs", 679 ],
669 }, 680 },
670 ], 681 },
671 is_snippet: false, 682 ],
672 }, 683 file_system_edits: [
673 }, 684 MoveFile {
674 ) 685 src: FileId(
675 "###); 686 2,
687 ),
688 anchor: FileId(
689 2,
690 ),
691 dst: "quux.rs",
692 },
693 ],
694 is_snippet: false,
695 },
696 }
697 "#]],
698 );
676 } 699 }
677 700
678 #[test] 701 #[test]
679 fn test_rename_mod_in_dir() { 702 fn test_rename_mod_in_dir() {
680 let (analysis, position) = analysis_and_position( 703 check_expect(
681 " 704 "foo2",
682 //- /lib.rs 705 r#"
683 mod fo<|>o; 706//- /lib.rs
684 //- /foo/mod.rs 707mod fo<|>o;
685 // emtpy 708//- /foo/mod.rs
686 ", 709// emtpy
687 ); 710"#,
688 let new_name = "foo2"; 711 expect![[r#"
689 let source_change = analysis.rename(position, new_name).unwrap(); 712 RangeInfo {
690 assert_debug_snapshot!(&source_change, 713 range: 4..7,
691 @r###" 714 info: SourceChange {
692 Some( 715 source_file_edits: [
693 RangeInfo { 716 SourceFileEdit {
694 range: 4..7, 717 file_id: FileId(
695 info: SourceChange { 718 1,
696 source_file_edits: [ 719 ),
697 SourceFileEdit { 720 edit: TextEdit {
698 file_id: FileId( 721 indels: [
699 1, 722 Indel {
700 ), 723 insert: "foo2",
701 edit: TextEdit { 724 delete: 4..7,
702 indels: [ 725 },
703 Indel { 726 ],
704 insert: "foo2", 727 },
705 delete: 4..7,
706 },
707 ],
708 }, 728 },
709 }, 729 ],
710 ], 730 file_system_edits: [
711 file_system_edits: [ 731 MoveFile {
712 MoveFile { 732 src: FileId(
713 src: FileId( 733 2,
714 2, 734 ),
715 ), 735 anchor: FileId(
716 dst_source_root: SourceRootId( 736 2,
717 0, 737 ),
718 ), 738 dst: "../foo2/mod.rs",
719 dst_path: "foo2/mod.rs", 739 },
720 }, 740 ],
721 ], 741 is_snippet: false,
722 is_snippet: false, 742 },
723 }, 743 }
724 }, 744 "#]],
725 ) 745 );
726 "###
727 );
728 } 746 }
729 747
730 #[test] 748 #[test]
731 fn test_module_rename_in_path() { 749 fn test_rename_unusually_nested_mod() {
732 test_rename( 750 check_expect(
751 "bar",
733 r#" 752 r#"
734 mod <|>foo { 753//- /lib.rs
735 pub fn bar() {} 754mod outer { mod fo<|>o; }
755
756//- /outer/foo.rs
757// emtpy
758"#,
759 expect![[r#"
760 RangeInfo {
761 range: 16..19,
762 info: SourceChange {
763 source_file_edits: [
764 SourceFileEdit {
765 file_id: FileId(
766 1,
767 ),
768 edit: TextEdit {
769 indels: [
770 Indel {
771 insert: "bar",
772 delete: 16..19,
773 },
774 ],
775 },
776 },
777 ],
778 file_system_edits: [
779 MoveFile {
780 src: FileId(
781 2,
782 ),
783 anchor: FileId(
784 2,
785 ),
786 dst: "bar.rs",
787 },
788 ],
789 is_snippet: false,
790 },
791 }
792 "#]],
793 );
736 } 794 }
737 795
738 fn main() { 796 #[test]
739 foo::bar(); 797 fn test_module_rename_in_path() {
740 }"#, 798 check(
741 "baz", 799 "baz",
742 r#" 800 r#"
743 mod baz { 801mod <|>foo { pub fn bar() {} }
744 pub fn bar() {}
745 }
746 802
747 fn main() { 803fn main() { foo::bar(); }
748 baz::bar(); 804"#,
749 }"#, 805 r#"
806mod baz { pub fn bar() {} }
807
808fn main() { baz::bar(); }
809"#,
750 ); 810 );
751 } 811 }
752 812
753 #[test] 813 #[test]
754 fn test_rename_mod_filename_and_path() { 814 fn test_rename_mod_filename_and_path() {
755 let (analysis, position) = analysis_and_position( 815 check_expect(
756 " 816 "foo2",
757 //- /lib.rs 817 r#"
758 mod bar; 818//- /lib.rs
759 fn f() { 819mod bar;
760 bar::foo::fun() 820fn f() {
761 } 821 bar::foo::fun()
762 822}
763 //- /bar.rs
764 pub mod foo<|>;
765 823
766 //- /bar/foo.rs 824//- /bar.rs
767 // pub fn fun() {} 825pub mod foo<|>;
768 ", 826
769 ); 827//- /bar/foo.rs
770 let new_name = "foo2"; 828// pub fn fun() {}
771 let source_change = analysis.rename(position, new_name).unwrap(); 829"#,
772 assert_debug_snapshot!(&source_change, 830 expect![[r#"
773@r###" 831 RangeInfo {
774 Some( 832 range: 8..11,
775 RangeInfo { 833 info: SourceChange {
776 range: 8..11, 834 source_file_edits: [
777 info: SourceChange { 835 SourceFileEdit {
778 source_file_edits: [ 836 file_id: FileId(
779 SourceFileEdit { 837 2,
780 file_id: FileId( 838 ),
781 2, 839 edit: TextEdit {
782 ), 840 indels: [
783 edit: TextEdit { 841 Indel {
784 indels: [ 842 insert: "foo2",
785 Indel { 843 delete: 8..11,
786 insert: "foo2", 844 },
787 delete: 8..11, 845 ],
788 }, 846 },
789 ],
790 }, 847 },
791 }, 848 SourceFileEdit {
792 SourceFileEdit { 849 file_id: FileId(
793 file_id: FileId( 850 1,
794 1, 851 ),
795 ), 852 edit: TextEdit {
796 edit: TextEdit { 853 indels: [
797 indels: [ 854 Indel {
798 Indel { 855 insert: "foo2",
799 insert: "foo2", 856 delete: 27..30,
800 delete: 27..30, 857 },
801 }, 858 ],
802 ], 859 },
803 }, 860 },
804 }, 861 ],
805 ], 862 file_system_edits: [
806 file_system_edits: [ 863 MoveFile {
807 MoveFile { 864 src: FileId(
808 src: FileId( 865 3,
809 3, 866 ),
810 ), 867 anchor: FileId(
811 dst_source_root: SourceRootId( 868 3,
812 0, 869 ),
813 ), 870 dst: "foo2.rs",
814 dst_path: "bar/foo2.rs", 871 },
815 }, 872 ],
816 ], 873 is_snippet: false,
817 is_snippet: false, 874 },
818 }, 875 }
819 }, 876 "#]],
820 ) 877 );
821 "###);
822 } 878 }
823 879
824 #[test] 880 #[test]
825 fn test_enum_variant_from_module_1() { 881 fn test_enum_variant_from_module_1() {
826 test_rename( 882 check(
883 "Baz",
827 r#" 884 r#"
828 mod foo { 885mod foo {
829 pub enum Foo { 886 pub enum Foo { Bar<|> }
830 Bar<|>, 887}
831 }
832 }
833 888
834 fn func(f: foo::Foo) { 889fn func(f: foo::Foo) {
835 match f { 890 match f {
836 foo::Foo::Bar => {} 891 foo::Foo::Bar => {}
837 }
838 } 892 }
839 "#, 893}
840 "Baz", 894"#,
841 r#" 895 r#"
842 mod foo { 896mod foo {
843 pub enum Foo { 897 pub enum Foo { Baz }
844 Baz, 898}
845 }
846 }
847 899
848 fn func(f: foo::Foo) { 900fn func(f: foo::Foo) {
849 match f { 901 match f {
850 foo::Foo::Baz => {} 902 foo::Foo::Baz => {}
851 }
852 } 903 }
853 "#, 904}
905"#,
854 ); 906 );
855 } 907 }
856 908
857 #[test] 909 #[test]
858 fn test_enum_variant_from_module_2() { 910 fn test_enum_variant_from_module_2() {
859 test_rename( 911 check(
912 "baz",
860 r#" 913 r#"
861 mod foo { 914mod foo {
862 pub struct Foo { 915 pub struct Foo { pub bar<|>: uint }
863 pub bar<|>: uint, 916}
864 }
865 }
866 917
867 fn foo(f: foo::Foo) { 918fn foo(f: foo::Foo) {
868 let _ = f.bar; 919 let _ = f.bar;
869 } 920}
870 "#, 921"#,
871 "baz",
872 r#" 922 r#"
873 mod foo { 923mod foo {
874 pub struct Foo { 924 pub struct Foo { pub baz: uint }
875 pub baz: uint, 925}
876 }
877 }
878 926
879 fn foo(f: foo::Foo) { 927fn foo(f: foo::Foo) {
880 let _ = f.baz; 928 let _ = f.baz;
881 } 929}
882 "#, 930"#,
883 ); 931 );
884 } 932 }
885 933
886 #[test] 934 #[test]
887 fn test_parameter_to_self() { 935 fn test_parameter_to_self() {
888 test_rename( 936 check(
937 "self",
889 r#" 938 r#"
890 struct Foo { 939struct Foo { i: i32 }
891 i: i32,
892 }
893 940
894 impl Foo { 941impl Foo {
895 fn f(foo<|>: &mut Foo) -> i32 { 942 fn f(foo<|>: &mut Foo) -> i32 {
896 foo.i 943 foo.i
897 }
898 } 944 }
899 "#, 945}
900 "self", 946"#,
901 r#" 947 r#"
902 struct Foo { 948struct Foo { i: i32 }
903 i: i32,
904 }
905 949
906 impl Foo { 950impl Foo {
907 fn f(&mut self) -> i32 { 951 fn f(&mut self) -> i32 {
908 self.i 952 self.i
909 }
910 } 953 }
911 "#, 954}
955"#,
912 ); 956 );
913 } 957 }
914 958
915 #[test] 959 #[test]
916 fn test_self_to_parameter() { 960 fn test_self_to_parameter() {
917 test_rename( 961 check(
962 "foo",
918 r#" 963 r#"
919 struct Foo { 964struct Foo { i: i32 }
920 i: i32,
921 }
922 965
923 impl Foo { 966impl Foo {
924 fn f(&mut <|>self) -> i32 { 967 fn f(&mut <|>self) -> i32 {
925 self.i 968 self.i
926 }
927 } 969 }
928 "#, 970}
929 "foo", 971"#,
930 r#" 972 r#"
931 struct Foo { 973struct Foo { i: i32 }
932 i: i32,
933 }
934 974
935 impl Foo { 975impl Foo {
936 fn f(foo: &mut Foo) -> i32 { 976 fn f(foo: &mut Foo) -> i32 {
937 foo.i 977 foo.i
938 }
939 } 978 }
940 "#, 979}
980"#,
941 ); 981 );
942 } 982 }
943 983
944 #[test] 984 #[test]
945 fn test_self_in_path_to_parameter() { 985 fn test_self_in_path_to_parameter() {
946 test_rename( 986 check(
987 "foo",
947 r#" 988 r#"
948 struct Foo { 989struct Foo { i: i32 }
949 i: i32,
950 }
951 990
952 impl Foo { 991impl Foo {
953 fn f(&self) -> i32 { 992 fn f(&self) -> i32 {
954 let self_var = 1; 993 let self_var = 1;
955 self<|>.i 994 self<|>.i
956 }
957 } 995 }
958 "#, 996}
959 "foo", 997"#,
960 r#" 998 r#"
961 struct Foo { 999struct Foo { i: i32 }
962 i: i32,
963 }
964 1000
965 impl Foo { 1001impl Foo {
966 fn f(foo: &Foo) -> i32 { 1002 fn f(foo: &Foo) -> i32 {
967 let self_var = 1; 1003 let self_var = 1;
968 foo.i 1004 foo.i
969 }
970 } 1005 }
971 "#, 1006}
1007"#,
972 ); 1008 );
973 } 1009 }
974
975 fn test_rename(text: &str, new_name: &str, expected: &str) {
976 let (analysis, position) = single_file_with_position(text);
977 let source_change = analysis.rename(position, new_name).unwrap();
978 let mut text_edit_builder = TextEditBuilder::default();
979 let mut file_id: Option<FileId> = None;
980 if let Some(change) = source_change {
981 for edit in change.info.source_file_edits {
982 file_id = Some(edit.file_id);
983 for indel in edit.edit.into_iter() {
984 text_edit_builder.replace(indel.delete, indel.insert);
985 }
986 }
987 }
988 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string();
989 text_edit_builder.finish().apply(&mut result);
990 assert_eq_text!(expected, &*result);
991 }
992} 1010}
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 286d45eee..ed15d6494 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,31 +1,31 @@
1use std::fmt;
2
1use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; 3use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
2use itertools::Itertools; 4use itertools::Itertools;
5use ra_cfg::CfgExpr;
3use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
4use ra_syntax::{ 7use ra_syntax::{
5 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, 8 ast::{self, AstNode, AttrsOwner, DocCommentsOwner, ModuleItemOwner, NameOwner},
6 match_ast, SyntaxNode, TextRange, 9 match_ast, SyntaxNode,
7}; 10};
8 11
9use crate::FileId; 12use crate::{display::ToNav, FileId, NavigationTarget};
10use ast::DocCommentsOwner;
11use ra_cfg::CfgExpr;
12use std::fmt::Display;
13 13
14#[derive(Debug)] 14#[derive(Debug, Clone)]
15pub struct Runnable { 15pub struct Runnable {
16 pub range: TextRange, 16 pub nav: NavigationTarget,
17 pub kind: RunnableKind, 17 pub kind: RunnableKind,
18 pub cfg_exprs: Vec<CfgExpr>, 18 pub cfg_exprs: Vec<CfgExpr>,
19} 19}
20 20
21#[derive(Debug)] 21#[derive(Debug, Clone)]
22pub enum TestId { 22pub enum TestId {
23 Name(String), 23 Name(String),
24 Path(String), 24 Path(String),
25} 25}
26 26
27impl Display for TestId { 27impl fmt::Display for TestId {
28 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 28 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29 match self { 29 match self {
30 TestId::Name(name) => write!(f, "{}", name), 30 TestId::Name(name) => write!(f, "{}", name),
31 TestId::Path(path) => write!(f, "{}", path), 31 TestId::Path(path) => write!(f, "{}", path),
@@ -33,7 +33,7 @@ impl Display for TestId {
33 } 33 }
34} 34}
35 35
36#[derive(Debug)] 36#[derive(Debug, Clone)]
37pub enum RunnableKind { 37pub enum RunnableKind {
38 Test { test_id: TestId, attr: TestAttr }, 38 Test { test_id: TestId, attr: TestAttr },
39 TestMod { path: String }, 39 TestMod { path: String },
@@ -42,6 +42,42 @@ pub enum RunnableKind {
42 Bin, 42 Bin,
43} 43}
44 44
45#[derive(Debug, Eq, PartialEq)]
46pub struct RunnableAction {
47 pub run_title: &'static str,
48 pub debugee: bool,
49}
50
51const TEST: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Test", debugee: true };
52const DOCTEST: RunnableAction =
53 RunnableAction { run_title: "▶\u{fe0e} Run Doctest", debugee: false };
54const BENCH: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Bench", debugee: true };
55const BIN: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run", debugee: true };
56
57impl Runnable {
58 // test package::module::testname
59 pub fn label(&self, target: Option<String>) -> String {
60 match &self.kind {
61 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
62 RunnableKind::TestMod { path } => format!("test-mod {}", path),
63 RunnableKind::Bench { test_id } => format!("bench {}", test_id),
64 RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
65 RunnableKind::Bin => {
66 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
67 }
68 }
69 }
70
71 pub fn action(&self) -> &'static RunnableAction {
72 match &self.kind {
73 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => &TEST,
74 RunnableKind::DocTest { .. } => &DOCTEST,
75 RunnableKind::Bench { .. } => &BENCH,
76 RunnableKind::Bin => &BIN,
77 }
78 }
79}
80
45// Feature: Run 81// Feature: Run
46// 82//
47// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor 83// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
@@ -59,7 +95,11 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
59 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect() 95 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect()
60} 96}
61 97
62fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> { 98pub(crate) fn runnable(
99 sema: &Semantics<RootDatabase>,
100 item: SyntaxNode,
101 file_id: FileId,
102) -> Option<Runnable> {
63 match_ast! { 103 match_ast! {
64 match item { 104 match item {
65 ast::FnDef(it) => runnable_fn(sema, it, file_id), 105 ast::FnDef(it) => runnable_fn(sema, it, file_id),
@@ -131,10 +171,19 @@ fn runnable_fn(
131 let cfg_exprs = 171 let cfg_exprs =
132 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 172 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
133 173
134 Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs }) 174 let nav = if let RunnableKind::DocTest { .. } = kind {
175 NavigationTarget::from_doc_commented(
176 sema.db,
177 InFile::new(file_id.into(), &fn_def),
178 InFile::new(file_id.into(), &fn_def),
179 )
180 } else {
181 NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def))
182 };
183 Some(Runnable { nav, kind, cfg_exprs })
135} 184}
136 185
137#[derive(Debug)] 186#[derive(Debug, Copy, Clone)]
138pub struct TestAttr { 187pub struct TestAttr {
139 pub ignore: bool, 188 pub ignore: bool,
140} 189}
@@ -183,7 +232,6 @@ fn runnable_mod(
183 if !has_test_function { 232 if !has_test_function {
184 return None; 233 return None;
185 } 234 }
186 let range = module.syntax().text_range();
187 let module_def = sema.to_def(&module)?; 235 let module_def = sema.to_def(&module)?;
188 236
189 let path = module_def 237 let path = module_def
@@ -197,366 +245,586 @@ fn runnable_mod(
197 let cfg_exprs = 245 let cfg_exprs =
198 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 246 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
199 247
200 Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs }) 248 let nav = module_def.to_nav(sema.db);
249 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
201} 250}
202 251
203#[cfg(test)] 252#[cfg(test)]
204mod tests { 253mod tests {
205 use insta::assert_debug_snapshot; 254 use expect::{expect, Expect};
206 255
207 use crate::mock_analysis::analysis_and_position; 256 use crate::mock_analysis::analysis_and_position;
208 257
258 use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST};
259
260 fn check(
261 ra_fixture: &str,
262 // FIXME: fold this into `expect` as well
263 actions: &[&RunnableAction],
264 expect: Expect,
265 ) {
266 let (analysis, position) = analysis_and_position(ra_fixture);
267 let runnables = analysis.runnables(position.file_id).unwrap();
268 expect.assert_debug_eq(&runnables);
269 assert_eq!(
270 actions,
271 runnables.into_iter().map(|it| it.action()).collect::<Vec<_>>().as_slice()
272 );
273 }
274
209 #[test] 275 #[test]
210 fn test_runnables() { 276 fn test_runnables() {
211 let (analysis, pos) = analysis_and_position( 277 check(
212 r#" 278 r#"
213 //- /lib.rs 279//- /lib.rs
214 <|> //empty 280<|>
215 fn main() {} 281fn main() {}
216 282
217 #[test] 283#[test]
218 fn test_foo() {} 284fn test_foo() {}
219 285
220 #[test] 286#[test]
221 #[ignore] 287#[ignore]
222 fn test_foo() {} 288fn test_foo() {}
223 "#, 289
224 ); 290#[bench]
225 let runnables = analysis.runnables(pos.file_id).unwrap(); 291fn bench() {}
226 assert_debug_snapshot!(&runnables, 292"#,
227 @r###" 293 &[&BIN, &TEST, &TEST, &BENCH],
228 [ 294 expect![[r#"
229 Runnable { 295 [
230 range: 1..21, 296 Runnable {
231 kind: Bin, 297 nav: NavigationTarget {
232 cfg_exprs: [], 298 file_id: FileId(
233 }, 299 1,
234 Runnable { 300 ),
235 range: 22..46, 301 full_range: 1..13,
236 kind: Test { 302 name: "main",
237 test_id: Path( 303 kind: FN_DEF,
238 "test_foo", 304 focus_range: Some(
239 ), 305 4..8,
240 attr: TestAttr { 306 ),
241 ignore: false, 307 container_name: None,
308 description: None,
309 docs: None,
310 },
311 kind: Bin,
312 cfg_exprs: [],
313 },
314 Runnable {
315 nav: NavigationTarget {
316 file_id: FileId(
317 1,
318 ),
319 full_range: 15..39,
320 name: "test_foo",
321 kind: FN_DEF,
322 focus_range: Some(
323 26..34,
324 ),
325 container_name: None,
326 description: None,
327 docs: None,
328 },
329 kind: Test {
330 test_id: Path(
331 "test_foo",
332 ),
333 attr: TestAttr {
334 ignore: false,
335 },
336 },
337 cfg_exprs: [],
338 },
339 Runnable {
340 nav: NavigationTarget {
341 file_id: FileId(
342 1,
343 ),
344 full_range: 41..75,
345 name: "test_foo",
346 kind: FN_DEF,
347 focus_range: Some(
348 62..70,
349 ),
350 container_name: None,
351 description: None,
352 docs: None,
353 },
354 kind: Test {
355 test_id: Path(
356 "test_foo",
357 ),
358 attr: TestAttr {
359 ignore: true,
360 },
361 },
362 cfg_exprs: [],
242 }, 363 },
243 }, 364 Runnable {
244 cfg_exprs: [], 365 nav: NavigationTarget {
245 }, 366 file_id: FileId(
246 Runnable { 367 1,
247 range: 47..81, 368 ),
248 kind: Test { 369 full_range: 77..99,
249 test_id: Path( 370 name: "bench",
250 "test_foo", 371 kind: FN_DEF,
251 ), 372 focus_range: Some(
252 attr: TestAttr { 373 89..94,
253 ignore: true, 374 ),
375 container_name: None,
376 description: None,
377 docs: None,
378 },
379 kind: Bench {
380 test_id: Path(
381 "bench",
382 ),
383 },
384 cfg_exprs: [],
254 }, 385 },
255 }, 386 ]
256 cfg_exprs: [], 387 "#]],
257 }, 388 );
258 ]
259 "###
260 );
261 } 389 }
262 390
263 #[test] 391 #[test]
264 fn test_runnables_doc_test() { 392 fn test_runnables_doc_test() {
265 let (analysis, pos) = analysis_and_position( 393 check(
266 r#" 394 r#"
267 //- /lib.rs 395//- /lib.rs
268 <|> //empty 396<|>
269 fn main() {} 397fn main() {}
270 398
271 /// ``` 399/// ```
272 /// let x = 5; 400/// let x = 5;
273 /// ``` 401/// ```
274 fn foo() {} 402fn foo() {}
275 "#, 403"#,
404 &[&BIN, &DOCTEST],
405 expect![[r#"
406 [
407 Runnable {
408 nav: NavigationTarget {
409 file_id: FileId(
410 1,
411 ),
412 full_range: 1..13,
413 name: "main",
414 kind: FN_DEF,
415 focus_range: Some(
416 4..8,
417 ),
418 container_name: None,
419 description: None,
420 docs: None,
421 },
422 kind: Bin,
423 cfg_exprs: [],
424 },
425 Runnable {
426 nav: NavigationTarget {
427 file_id: FileId(
428 1,
429 ),
430 full_range: 15..57,
431 name: "foo",
432 kind: FN_DEF,
433 focus_range: None,
434 container_name: None,
435 description: None,
436 docs: None,
437 },
438 kind: DocTest {
439 test_id: Path(
440 "foo",
441 ),
442 },
443 cfg_exprs: [],
444 },
445 ]
446 "#]],
276 ); 447 );
277 let runnables = analysis.runnables(pos.file_id).unwrap();
278 assert_debug_snapshot!(&runnables,
279 @r###"
280 [
281 Runnable {
282 range: 1..21,
283 kind: Bin,
284 cfg_exprs: [],
285 },
286 Runnable {
287 range: 22..64,
288 kind: DocTest {
289 test_id: Path(
290 "foo",
291 ),
292 },
293 cfg_exprs: [],
294 },
295 ]
296 "###
297 );
298 } 448 }
299 449
300 #[test] 450 #[test]
301 fn test_runnables_doc_test_in_impl() { 451 fn test_runnables_doc_test_in_impl() {
302 let (analysis, pos) = analysis_and_position( 452 check(
303 r#" 453 r#"
304 //- /lib.rs 454//- /lib.rs
305 <|> //empty 455<|>
306 fn main() {} 456fn main() {}
307 457
308 struct Data; 458struct Data;
309 impl Data { 459impl Data {
310 /// ``` 460 /// ```
311 /// let x = 5; 461 /// let x = 5;
312 /// ``` 462 /// ```
313 fn foo() {} 463 fn foo() {}
314 } 464}
315 "#, 465"#,
466 &[&BIN, &DOCTEST],
467 expect![[r#"
468 [
469 Runnable {
470 nav: NavigationTarget {
471 file_id: FileId(
472 1,
473 ),
474 full_range: 1..13,
475 name: "main",
476 kind: FN_DEF,
477 focus_range: Some(
478 4..8,
479 ),
480 container_name: None,
481 description: None,
482 docs: None,
483 },
484 kind: Bin,
485 cfg_exprs: [],
486 },
487 Runnable {
488 nav: NavigationTarget {
489 file_id: FileId(
490 1,
491 ),
492 full_range: 44..98,
493 name: "foo",
494 kind: FN_DEF,
495 focus_range: None,
496 container_name: None,
497 description: None,
498 docs: None,
499 },
500 kind: DocTest {
501 test_id: Path(
502 "Data::foo",
503 ),
504 },
505 cfg_exprs: [],
506 },
507 ]
508 "#]],
316 ); 509 );
317 let runnables = analysis.runnables(pos.file_id).unwrap();
318 assert_debug_snapshot!(&runnables,
319 @r###"
320 [
321 Runnable {
322 range: 1..21,
323 kind: Bin,
324 cfg_exprs: [],
325 },
326 Runnable {
327 range: 51..105,
328 kind: DocTest {
329 test_id: Path(
330 "Data::foo",
331 ),
332 },
333 cfg_exprs: [],
334 },
335 ]
336 "###
337 );
338 } 510 }
339 511
340 #[test] 512 #[test]
341 fn test_runnables_module() { 513 fn test_runnables_module() {
342 let (analysis, pos) = analysis_and_position( 514 check(
343 r#" 515 r#"
344 //- /lib.rs 516//- /lib.rs
345 <|> //empty 517<|>
346 mod test_mod { 518mod test_mod {
347 #[test] 519 #[test]
348 fn test_foo1() {} 520 fn test_foo1() {}
349 } 521}
350 "#, 522"#,
351 ); 523 &[&TEST, &TEST],
352 let runnables = analysis.runnables(pos.file_id).unwrap(); 524 expect![[r#"
353 assert_debug_snapshot!(&runnables, 525 [
354 @r###" 526 Runnable {
355 [ 527 nav: NavigationTarget {
356 Runnable { 528 file_id: FileId(
357 range: 1..59, 529 1,
358 kind: TestMod { 530 ),
359 path: "test_mod", 531 full_range: 1..51,
360 }, 532 name: "test_mod",
361 cfg_exprs: [], 533 kind: MODULE,
362 }, 534 focus_range: Some(
363 Runnable { 535 5..13,
364 range: 28..57, 536 ),
365 kind: Test { 537 container_name: None,
366 test_id: Path( 538 description: None,
367 "test_mod::test_foo1", 539 docs: None,
368 ), 540 },
369 attr: TestAttr { 541 kind: TestMod {
370 ignore: false, 542 path: "test_mod",
543 },
544 cfg_exprs: [],
371 }, 545 },
372 }, 546 Runnable {
373 cfg_exprs: [], 547 nav: NavigationTarget {
374 }, 548 file_id: FileId(
375 ] 549 1,
376 "### 550 ),
377 ); 551 full_range: 20..49,
552 name: "test_foo1",
553 kind: FN_DEF,
554 focus_range: Some(
555 35..44,
556 ),
557 container_name: None,
558 description: None,
559 docs: None,
560 },
561 kind: Test {
562 test_id: Path(
563 "test_mod::test_foo1",
564 ),
565 attr: TestAttr {
566 ignore: false,
567 },
568 },
569 cfg_exprs: [],
570 },
571 ]
572 "#]],
573 );
378 } 574 }
379 575
380 #[test] 576 #[test]
381 fn test_runnables_one_depth_layer_module() { 577 fn test_runnables_one_depth_layer_module() {
382 let (analysis, pos) = analysis_and_position( 578 check(
383 r#" 579 r#"
384 //- /lib.rs 580//- /lib.rs
385 <|> //empty 581<|>
386 mod foo { 582mod foo {
387 mod test_mod { 583 mod test_mod {
388 #[test] 584 #[test]
389 fn test_foo1() {} 585 fn test_foo1() {}
390 } 586 }
391 } 587}
392 "#, 588"#,
393 ); 589 &[&TEST, &TEST],
394 let runnables = analysis.runnables(pos.file_id).unwrap(); 590 expect![[r#"
395 assert_debug_snapshot!(&runnables, 591 [
396 @r###" 592 Runnable {
397 [ 593 nav: NavigationTarget {
398 Runnable { 594 file_id: FileId(
399 range: 23..85, 595 1,
400 kind: TestMod { 596 ),
401 path: "foo::test_mod", 597 full_range: 15..77,
402 }, 598 name: "test_mod",
403 cfg_exprs: [], 599 kind: MODULE,
404 }, 600 focus_range: Some(
405 Runnable { 601 19..27,
406 range: 46..79, 602 ),
407 kind: Test { 603 container_name: None,
408 test_id: Path( 604 description: None,
409 "foo::test_mod::test_foo1", 605 docs: None,
410 ), 606 },
411 attr: TestAttr { 607 kind: TestMod {
412 ignore: false, 608 path: "foo::test_mod",
609 },
610 cfg_exprs: [],
611 },
612 Runnable {
613 nav: NavigationTarget {
614 file_id: FileId(
615 1,
616 ),
617 full_range: 38..71,
618 name: "test_foo1",
619 kind: FN_DEF,
620 focus_range: Some(
621 57..66,
622 ),
623 container_name: None,
624 description: None,
625 docs: None,
626 },
627 kind: Test {
628 test_id: Path(
629 "foo::test_mod::test_foo1",
630 ),
631 attr: TestAttr {
632 ignore: false,
633 },
634 },
635 cfg_exprs: [],
413 }, 636 },
414 }, 637 ]
415 cfg_exprs: [], 638 "#]],
416 }, 639 );
417 ]
418 "###
419 );
420 } 640 }
421 641
422 #[test] 642 #[test]
423 fn test_runnables_multiple_depth_module() { 643 fn test_runnables_multiple_depth_module() {
424 let (analysis, pos) = analysis_and_position( 644 check(
425 r#" 645 r#"
426 //- /lib.rs 646//- /lib.rs
427 <|> //empty 647<|>
428 mod foo { 648mod foo {
429 mod bar { 649 mod bar {
430 mod test_mod { 650 mod test_mod {
431 #[test] 651 #[test]
432 fn test_foo1() {} 652 fn test_foo1() {}
433 }
434 }
435 } 653 }
436 "#, 654 }
437 ); 655}
438 let runnables = analysis.runnables(pos.file_id).unwrap(); 656"#,
439 assert_debug_snapshot!(&runnables, 657 &[&TEST, &TEST],
440 @r###" 658 expect![[r#"
441 [ 659 [
442 Runnable { 660 Runnable {
443 range: 41..115, 661 nav: NavigationTarget {
444 kind: TestMod { 662 file_id: FileId(
445 path: "foo::bar::test_mod", 663 1,
446 }, 664 ),
447 cfg_exprs: [], 665 full_range: 33..107,
448 }, 666 name: "test_mod",
449 Runnable { 667 kind: MODULE,
450 range: 68..105, 668 focus_range: Some(
451 kind: Test { 669 37..45,
452 test_id: Path( 670 ),
453 "foo::bar::test_mod::test_foo1", 671 container_name: None,
454 ), 672 description: None,
455 attr: TestAttr { 673 docs: None,
456 ignore: false, 674 },
675 kind: TestMod {
676 path: "foo::bar::test_mod",
677 },
678 cfg_exprs: [],
457 }, 679 },
458 }, 680 Runnable {
459 cfg_exprs: [], 681 nav: NavigationTarget {
460 }, 682 file_id: FileId(
461 ] 683 1,
462 "### 684 ),
463 ); 685 full_range: 60..97,
686 name: "test_foo1",
687 kind: FN_DEF,
688 focus_range: Some(
689 83..92,
690 ),
691 container_name: None,
692 description: None,
693 docs: None,
694 },
695 kind: Test {
696 test_id: Path(
697 "foo::bar::test_mod::test_foo1",
698 ),
699 attr: TestAttr {
700 ignore: false,
701 },
702 },
703 cfg_exprs: [],
704 },
705 ]
706 "#]],
707 );
464 } 708 }
465 709
466 #[test] 710 #[test]
467 fn test_runnables_with_feature() { 711 fn test_runnables_with_feature() {
468 let (analysis, pos) = analysis_and_position( 712 check(
469 r#" 713 r#"
470 //- /lib.rs crate:foo cfg:feature=foo 714//- /lib.rs crate:foo cfg:feature=foo
471 <|> //empty 715<|>
472 #[test] 716#[test]
473 #[cfg(feature = "foo")] 717#[cfg(feature = "foo")]
474 fn test_foo1() {} 718fn test_foo1() {}
475 "#, 719"#,
476 ); 720 &[&TEST],
477 let runnables = analysis.runnables(pos.file_id).unwrap(); 721 expect![[r#"
478 assert_debug_snapshot!(&runnables, 722 [
479 @r###" 723 Runnable {
480 [ 724 nav: NavigationTarget {
481 Runnable { 725 file_id: FileId(
482 range: 1..58, 726 1,
483 kind: Test { 727 ),
484 test_id: Path( 728 full_range: 1..50,
485 "test_foo1", 729 name: "test_foo1",
486 ), 730 kind: FN_DEF,
487 attr: TestAttr { 731 focus_range: Some(
488 ignore: false, 732 36..45,
489 }, 733 ),
490 }, 734 container_name: None,
491 cfg_exprs: [ 735 description: None,
492 KeyValue { 736 docs: None,
493 key: "feature", 737 },
494 value: "foo", 738 kind: Test {
739 test_id: Path(
740 "test_foo1",
741 ),
742 attr: TestAttr {
743 ignore: false,
744 },
745 },
746 cfg_exprs: [
747 KeyValue {
748 key: "feature",
749 value: "foo",
750 },
751 ],
495 }, 752 },
496 ], 753 ]
497 }, 754 "#]],
498 ] 755 );
499 "###
500 );
501 } 756 }
502 757
503 #[test] 758 #[test]
504 fn test_runnables_with_features() { 759 fn test_runnables_with_features() {
505 let (analysis, pos) = analysis_and_position( 760 check(
506 r#" 761 r#"
507 //- /lib.rs crate:foo cfg:feature=foo,feature=bar 762//- /lib.rs crate:foo cfg:feature=foo,feature=bar
508 <|> //empty 763<|>
509 #[test] 764#[test]
510 #[cfg(all(feature = "foo", feature = "bar"))] 765#[cfg(all(feature = "foo", feature = "bar"))]
511 fn test_foo1() {} 766fn test_foo1() {}
512 "#, 767"#,
513 ); 768 &[&TEST],
514 let runnables = analysis.runnables(pos.file_id).unwrap(); 769 expect![[r#"
515 assert_debug_snapshot!(&runnables, 770 [
516 @r###" 771 Runnable {
517 [ 772 nav: NavigationTarget {
518 Runnable { 773 file_id: FileId(
519 range: 1..80, 774 1,
520 kind: Test { 775 ),
521 test_id: Path( 776 full_range: 1..72,
522 "test_foo1", 777 name: "test_foo1",
523 ), 778 kind: FN_DEF,
524 attr: TestAttr { 779 focus_range: Some(
525 ignore: false, 780 58..67,
526 }, 781 ),
527 }, 782 container_name: None,
528 cfg_exprs: [ 783 description: None,
529 All( 784 docs: None,
530 [ 785 },
531 KeyValue { 786 kind: Test {
532 key: "feature", 787 test_id: Path(
533 value: "foo", 788 "test_foo1",
534 }, 789 ),
535 KeyValue { 790 attr: TestAttr {
536 key: "feature", 791 ignore: false,
537 value: "bar",
538 }, 792 },
793 },
794 cfg_exprs: [
795 All(
796 [
797 KeyValue {
798 key: "feature",
799 value: "foo",
800 },
801 KeyValue {
802 key: "feature",
803 value: "bar",
804 },
805 ],
806 ),
539 ], 807 ],
540 ), 808 },
541 ], 809 ]
542 }, 810 "#]],
543 ] 811 );
544 "###
545 );
546 } 812 }
547 813
548 #[test] 814 #[test]
549 fn test_runnables_no_test_function_in_module() { 815 fn test_runnables_no_test_function_in_module() {
550 let (analysis, pos) = analysis_and_position( 816 check(
551 r#" 817 r#"
552 //- /lib.rs 818//- /lib.rs
553 <|> //empty 819<|>
554 mod test_mod { 820mod test_mod {
555 fn foo1() {} 821 fn foo1() {}
556 } 822}
557 "#, 823"#,
824 &[],
825 expect![[r#"
826 []
827 "#]],
558 ); 828 );
559 let runnables = analysis.runnables(pos.file_id).unwrap();
560 assert!(runnables.is_empty())
561 } 829 }
562} 830}
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
deleted file mode 100644
index 68fc589bc..000000000
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ /dev/null
@@ -1,41 +0,0 @@
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.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; }
15.type { color: #7CB8BB; }
16.builtin_type { color: #8CD0D3; }
17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
21.macro { color: #94BFF3; }
22.module { color: #AFD8AF; }
23.variable { color: #DCDCCC; }
24.format_specifier { color: #CC696B; }
25.mutable { text-decoration: underline; }
26
27.keyword { color: #F0DFAF; font-weight: bold; }
28.keyword.unsafe { color: #BC8383; font-weight: bold; }
29.control { font-style: italic; }
30</style>
31<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span>(<span class="variable declaration">ra_fixture</span>: &<span class="builtin_type">str</span>) {}
32
33<span class="keyword">fn</span> <span class="function declaration">main</span>() {
34 <span class="function">fixture</span>(<span class="string_literal">r#"</span>
35 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> {
36 <span class="keyword">fn</span> <span class="function declaration">foo</span>() {
37 <span class="macro">println!</span>(<span class="string_literal">"2 + 2 = {}"</span>, <span class="numeric_literal">4</span>);
38 }
39 }<span class="string_literal">"#</span>
40 );
41}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
deleted file mode 100644
index 326744361..000000000
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ /dev/null
@@ -1,84 +0,0 @@
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.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; }
15.type { color: #7CB8BB; }
16.builtin_type { color: #8CD0D3; }
17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
21.macro { color: #94BFF3; }
22.module { color: #AFD8AF; }
23.variable { color: #DCDCCC; }
24.format_specifier { color: #CC696B; }
25.mutable { text-decoration: underline; }
26
27.keyword { color: #F0DFAF; font-weight: bold; }
28.keyword.unsafe { color: #BC8383; font-weight: bold; }
29.control { font-style: italic; }
30</style>
31<pre><code><span class="macro">macro_rules!</span> <span class="macro declaration">println</span> {
32 ($($arg:tt)*) =&gt; ({
33 $<span class="keyword">crate</span>::io::_print($<span class="keyword">crate</span>::format_args_nl!($($arg)*));
34 })
35}
36#[rustc_builtin_macro]
37<span class="macro">macro_rules!</span> <span class="macro declaration">format_args_nl</span> {
38 ($fmt:expr) =&gt; {{ <span class="comment">/* compiler built-in */</span> }};
39 ($fmt:expr, $($args:tt)*) =&gt; {{ <span class="comment">/* compiler built-in */</span> }};
40}
41
42<span class="keyword">fn</span> <span class="function declaration">main</span>() {
43 <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
44 <span class="macro">println!</span>(<span class="string_literal">"Hello"</span>); <span class="comment">// =&gt; "Hello"</span>
45 <span class="macro">println!</span>(<span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); <span class="comment">// =&gt; "Hello, world!"</span>
46 <span class="macro">println!</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="numeric_literal">1</span>); <span class="comment">// =&gt; "The number is 1"</span>
47 <span class="macro">println!</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="numeric_literal">3</span>, <span class="numeric_literal">4</span>)); <span class="comment">// =&gt; "(3, 4)"</span>
48 <span class="macro">println!</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>, value=<span class="numeric_literal">4</span>); <span class="comment">// =&gt; "4"</span>
49 <span class="macro">println!</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="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "1 2"</span>
50 <span class="macro">println!</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="numeric_literal">42</span>); <span class="comment">// =&gt; "0042" with leading zerosV</span>
51 <span class="macro">println!</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="numeric_literal">1</span>, <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1 1 2"</span>
52 <span class="macro">println!</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>, argument = <span class="string_literal">"test"</span>); <span class="comment">// =&gt; "test"</span>
53 <span class="macro">println!</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="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// =&gt; "2 1"</span>
54 <span class="macro">println!</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>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// =&gt; "a 3 b"</span>
55 <span class="macro">println!</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="string_literal">"x"</span>);
56 <span class="macro">println!</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="string_literal">"x"</span>, <span class="numeric_literal">5</span>);
57 <span class="macro">println!</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="numeric_literal">5</span>, <span class="string_literal">"x"</span>);
58 <span class="macro">println!</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="string_literal">"x"</span>, width = <span class="numeric_literal">5</span>);
59 <span class="macro">println!</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="string_literal">"x"</span>);
60 <span class="macro">println!</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="string_literal">"x"</span>);
61 <span class="macro">println!</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="string_literal">"x"</span>);
62 <span class="macro">println!</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="string_literal">"x"</span>);
63 <span class="macro">println!</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="numeric_literal">5</span>);
64 <span class="macro">println!</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="string_literal">}!"</span>, <span class="numeric_literal">27</span>);
65 <span class="macro">println!</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="numeric_literal">5</span>);
66 <span class="macro">println!</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="numeric_literal">5</span>);
67 <span class="macro">println!</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="numeric_literal">27</span>);
68 <span class="macro">println!</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="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
69 <span class="macro">println!</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="numeric_literal">5</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">0.01</span>);
70 <span class="macro">println!</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="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
71 <span class="macro">println!</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="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
72 <span class="macro">println!</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="string_literal">"x"</span>, <span class="numeric_literal">5</span>, <span class="numeric_literal">0.01</span>);
73 <span class="macro">println!</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="string_literal">"x"</span>, prec = <span class="numeric_literal">5</span>, number = <span class="numeric_literal">0.01</span>);
74 <span class="macro">println!</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="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="numeric_literal">1234.56</span>);
75 <span class="macro">println!</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="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
76 <span class="macro">println!</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="string_literal">"Hello"</span>, <span class="numeric_literal">3</span>, name=<span class="string_literal">"1234.56"</span>);
77 <span class="macro">println!</span>(<span class="string_literal">"Hello {{}}"</span>);
78 <span class="macro">println!</span>(<span class="string_literal">"{{ Hello"</span>);
79
80 <span class="macro">println!</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="string_literal">"world"</span>);
81
82 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>);
83 <span class="macro">println!</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="numeric_literal">92</span>);
84}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
deleted file mode 100644
index 352e35095..000000000
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ /dev/null
@@ -1,101 +0,0 @@
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.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; }
15.type { color: #7CB8BB; }
16.builtin_type { color: #8CD0D3; }
17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
21.macro { color: #94BFF3; }
22.module { color: #AFD8AF; }
23.variable { color: #DCDCCC; }
24.format_specifier { color: #CC696B; }
25.mutable { text-decoration: underline; }
26
27.keyword { color: #F0DFAF; font-weight: bold; }
28.keyword.unsafe { color: #BC8383; font-weight: bold; }
29.control { font-style: italic; }
30</style>
31<pre><code><span class="attribute">#[</span><span class="function attribute">derive</span><span class="attribute">(Clone, Debug)]</span>
32<span class="keyword">struct</span> <span class="struct declaration">Foo</span> {
33 <span class="keyword">pub</span> <span class="field declaration">x</span>: <span class="builtin_type">i32</span>,
34 <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>,
35}
36
37<span class="keyword">trait</span> <span class="trait declaration">Bar</span> {
38 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">i32</span>;
39}
40
41<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> {
42 <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">i32</span> {
43 <span class="self_keyword">self</span>.<span class="field">x</span>
44 }
45}
46
47<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable">STATIC_MUT</span>: <span class="builtin_type">i32</span> = <span class="numeric_literal">0</span>;
48
49<span class="keyword">fn</span> <span class="function declaration">foo</span>&lt;<span class="lifetime declaration">'a</span>, <span class="type_param declaration">T</span>&gt;() -&gt; <span class="type_param">T</span> {
50 <span class="function">foo</span>::&lt;<span class="lifetime">'a</span>, <span class="builtin_type">i32</span>&gt;()
51}
52
53<span class="macro">macro_rules!</span> <span class="macro declaration">def_fn</span> {
54 ($($tt:tt)*) =&gt; {$($tt)*}
55}
56
57<span class="macro">def_fn!</span> {
58 <span class="keyword">fn</span> <span class="function declaration">bar</span>() -&gt; <span class="builtin_type">u32</span> {
59 <span class="numeric_literal">100</span>
60 }
61}
62
63<span class="comment">// comment</span>
64<span class="keyword">fn</span> <span class="function declaration">main</span>() {
65 <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>);
66
67 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = <span class="unresolved_reference">Vec</span>::<span class="unresolved_reference">new</span>();
68 <span class="keyword control">if</span> <span class="bool_literal">true</span> {
69 <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>;
70 <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> });
71 }
72 <span class="keyword unsafe">unsafe</span> {
73 <span class="variable mutable">vec</span>.<span class="unresolved_reference">set_len</span>(<span class="numeric_literal">0</span>);
74 <span class="static mutable">STATIC_MUT</span> = <span class="numeric_literal">1</span>;
75 }
76
77 <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> {
78 <span class="comment">// Do nothing</span>
79 }
80
81 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> = <span class="numeric_literal">42</span>;
82 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>;
83 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>;
84
85 <span class="variable mutable">y</span>;
86}
87
88<span class="keyword">enum</span> <span class="enum declaration">Option</span>&lt;<span class="type_param declaration">T</span>&gt; {
89 <span class="enum_variant declaration">Some</span>(<span class="type_param">T</span>),
90 <span class="enum_variant declaration">None</span>,
91}
92<span class="keyword">use</span> <span class="enum">Option</span>::*;
93
94<span class="keyword">impl</span>&lt;<span class="type_param declaration">T</span>&gt; <span class="enum">Option</span>&lt;<span class="type_param">T</span>&gt; {
95 <span class="keyword">fn</span> <span class="function declaration">and</span>&lt;<span class="type_param declaration">U</span>&gt;(<span class="self_keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span>&lt;<span class="type_param">U</span>&gt;) -&gt; <span class="enum">Option</span>&lt;(<span class="type_param">T</span>, <span class="type_param">U</span>)&gt; {
96 <span class="keyword control">match</span> <span class="variable">other</span> {
97 <span class="enum_variant">None</span> =&gt; <span class="macro">unimplemented!</span>(),
98 <span class="variable declaration">Nope</span> =&gt; <span class="variable">Nope</span>,
99 }
100 }
101}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
deleted file mode 100644
index 2a0294f71..000000000
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ /dev/null
@@ -1,42 +0,0 @@
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.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; }
15.type { color: #7CB8BB; }
16.builtin_type { color: #8CD0D3; }
17.type_param { color: #DFAF8F; }
18.attribute { color: #94BFF3; }
19.numeric_literal { color: #BFEBBF; }
20.bool_literal { color: #BFE6EB; }
21.macro { color: #94BFF3; }
22.module { color: #AFD8AF; }
23.variable { color: #DCDCCC; }
24.format_specifier { color: #CC696B; }
25.mutable { text-decoration: underline; }
26
27.keyword { color: #F0DFAF; font-weight: bold; }
28.keyword.unsafe { color: #BC8383; font-weight: bold; }
29.control { font-style: italic; }
30</style>
31<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span>() {
32 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string_literal">"hello"</span>;
33 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(17,51%,74%);">x</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.<span class="unresolved_reference">to_string</span>();
34 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="3365759661443752373" style="color: hsl(127,76%,66%);">y</span> = <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span>.<span class="unresolved_reference">to_string</span>();
35
36 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span> = <span class="string_literal">"other color please!"</span>;
37 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="6717528807933952652" style="color: hsl(85,49%,84%);">y</span> = <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span>.<span class="unresolved_reference">to_string</span>();
38}
39
40<span class="keyword">fn</span> <span class="function declaration">bar</span>() {
41 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> = <span class="string_literal">"hello"</span>;
42}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 93e9aee1d..b3e9e5dfe 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -1,37 +1,31 @@
1use std::{collections::HashMap, iter::once, str::FromStr}; 1use ra_db::SourceDatabaseExt;
2
3use ra_db::{SourceDatabase, SourceDatabaseExt};
4use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; 2use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
5use ra_syntax::ast::{
6 make::try_expr_from_text, ArgList, AstToken, CallExpr, Comment, Expr, MethodCallExpr,
7 RecordField, RecordLit,
8};
9use ra_syntax::{AstNode, SyntaxElement, SyntaxKind, SyntaxNode};
10use ra_text_edit::{TextEdit, TextEditBuilder};
11use rustc_hash::FxHashMap;
12 3
13use crate::SourceFileEdit; 4use crate::SourceFileEdit;
5use ra_ssr::{MatchFinder, SsrError, SsrRule};
14 6
15#[derive(Debug, PartialEq)] 7// Feature: Structural Search and Replace
16pub struct SsrError(String);
17
18impl std::fmt::Display for SsrError {
19 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
20 write!(f, "Parse error: {}", self.0)
21 }
22}
23
24impl std::error::Error for SsrError {}
25
26// Feature: Structural Seach and Replace
27// 8//
28// Search and replace with named wildcards that will match any expression. 9// Search and replace with named wildcards that will match any expression, type, path, pattern or item.
29// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. 10// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
30// A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement. 11// A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement.
12// Within a macro call, a placeholder will match up until whatever token follows the placeholder.
13//
14// Placeholders may be given constraints by writing them as `${<name>:<constraint1>:<constraint2>...}`.
15//
16// Supported constraints:
17//
18// |===
19// | Constraint | Restricts placeholder
20//
21// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`)
22// | not(a) | Negates the constraint `a`
23// |===
24//
31// Available via the command `rust-analyzer.ssr`. 25// Available via the command `rust-analyzer.ssr`.
32// 26//
33// ```rust 27// ```rust
34// // Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)] 28// // Using structural search replace command [foo($a, $b) ==>> ($a).foo($b)]
35// 29//
36// // BEFORE 30// // BEFORE
37// String::from(foo(y + 5, z)) 31// String::from(foo(y + 5, z))
@@ -46,584 +40,24 @@ impl std::error::Error for SsrError {}
46// | VS Code | **Rust Analyzer: Structural Search Replace** 40// | VS Code | **Rust Analyzer: Structural Search Replace**
47// |=== 41// |===
48pub fn parse_search_replace( 42pub fn parse_search_replace(
49 query: &str, 43 rule: &str,
50 parse_only: bool, 44 parse_only: bool,
51 db: &RootDatabase, 45 db: &RootDatabase,
52) -> Result<Vec<SourceFileEdit>, SsrError> { 46) -> Result<Vec<SourceFileEdit>, SsrError> {
53 let mut edits = vec![]; 47 let mut edits = vec![];
54 let query: SsrQuery = query.parse()?; 48 let rule: SsrRule = rule.parse()?;
55 if parse_only { 49 if parse_only {
56 return Ok(edits); 50 return Ok(edits);
57 } 51 }
52 let mut match_finder = MatchFinder::new(db);
53 match_finder.add_rule(rule);
58 for &root in db.local_roots().iter() { 54 for &root in db.local_roots().iter() {
59 let sr = db.source_root(root); 55 let sr = db.source_root(root);
60 for file_id in sr.walk() { 56 for file_id in sr.iter() {
61 let matches = find(&query.pattern, db.parse(file_id).tree().syntax()); 57 if let Some(edit) = match_finder.edits_for_file(file_id) {
62 if !matches.matches.is_empty() { 58 edits.push(SourceFileEdit { file_id, edit });
63 edits.push(SourceFileEdit { file_id, edit: replace(&matches, &query.template) });
64 } 59 }
65 } 60 }
66 } 61 }
67 Ok(edits) 62 Ok(edits)
68} 63}
69
70#[derive(Debug)]
71struct SsrQuery {
72 pattern: SsrPattern,
73 template: SsrTemplate,
74}
75
76#[derive(Debug)]
77struct SsrPattern {
78 pattern: SyntaxNode,
79 vars: Vec<Var>,
80}
81
82/// represents an `$var` in an SSR query
83#[derive(Debug, Clone, PartialEq, Eq, Hash)]
84struct Var(String);
85
86#[derive(Debug)]
87struct SsrTemplate {
88 template: SyntaxNode,
89 placeholders: FxHashMap<SyntaxNode, Var>,
90}
91
92type Binding = HashMap<Var, SyntaxNode>;
93
94#[derive(Debug)]
95struct Match {
96 place: SyntaxNode,
97 binding: Binding,
98 ignored_comments: Vec<Comment>,
99}
100
101#[derive(Debug)]
102struct SsrMatches {
103 matches: Vec<Match>,
104}
105
106impl FromStr for SsrQuery {
107 type Err = SsrError;
108
109 fn from_str(query: &str) -> Result<SsrQuery, SsrError> {
110 let mut it = query.split("==>>");
111 let pattern = it.next().expect("at least empty string").trim();
112 let mut template = it
113 .next()
114 .ok_or_else(|| SsrError("Cannot find delemiter `==>>`".into()))?
115 .trim()
116 .to_string();
117 if it.next().is_some() {
118 return Err(SsrError("More than one delimiter found".into()));
119 }
120 let mut vars = vec![];
121 let mut it = pattern.split('$');
122 let mut pattern = it.next().expect("something").to_string();
123
124 for part in it.map(split_by_var) {
125 let (var, var_type, remainder) = part?;
126 is_expr(var_type)?;
127 let new_var = create_name(var, &mut vars)?;
128 pattern.push_str(new_var);
129 pattern.push_str(remainder);
130 template = replace_in_template(template, var, new_var);
131 }
132
133 let template = try_expr_from_text(&template)
134 .ok_or(SsrError("Template is not an expression".into()))?
135 .syntax()
136 .clone();
137 let mut placeholders = FxHashMap::default();
138
139 traverse(&template, &mut |n| {
140 if let Some(v) = vars.iter().find(|v| v.0.as_str() == n.text()) {
141 placeholders.insert(n.clone(), v.clone());
142 false
143 } else {
144 true
145 }
146 });
147
148 let pattern = SsrPattern {
149 pattern: try_expr_from_text(&pattern)
150 .ok_or(SsrError("Pattern is not an expression".into()))?
151 .syntax()
152 .clone(),
153 vars,
154 };
155 let template = SsrTemplate { template, placeholders };
156 Ok(SsrQuery { pattern, template })
157 }
158}
159
160fn traverse(node: &SyntaxNode, go: &mut impl FnMut(&SyntaxNode) -> bool) {
161 if !go(node) {
162 return;
163 }
164 for ref child in node.children() {
165 traverse(child, go);
166 }
167}
168
169fn split_by_var(s: &str) -> Result<(&str, &str, &str), SsrError> {
170 let end_of_name = s.find(':').ok_or_else(|| SsrError("Use $<name>:expr".into()))?;
171 let name = &s[0..end_of_name];
172 is_name(name)?;
173 let type_begin = end_of_name + 1;
174 let type_length =
175 s[type_begin..].find(|c| !char::is_ascii_alphanumeric(&c)).unwrap_or_else(|| s.len());
176 let type_name = &s[type_begin..type_begin + type_length];
177 Ok((name, type_name, &s[type_begin + type_length..]))
178}
179
180fn is_name(s: &str) -> Result<(), SsrError> {
181 if s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
182 Ok(())
183 } else {
184 Err(SsrError("Name can contain only alphanumerics and _".into()))
185 }
186}
187
188fn is_expr(s: &str) -> Result<(), SsrError> {
189 if s == "expr" {
190 Ok(())
191 } else {
192 Err(SsrError("Only $<name>:expr is supported".into()))
193 }
194}
195
196fn replace_in_template(template: String, var: &str, new_var: &str) -> String {
197 let name = format!("${}", var);
198 template.replace(&name, new_var)
199}
200
201fn create_name<'a>(name: &str, vars: &'a mut Vec<Var>) -> Result<&'a str, SsrError> {
202 let sanitized_name = format!("__search_pattern_{}", name);
203 if vars.iter().any(|a| a.0 == sanitized_name) {
204 return Err(SsrError(format!("Name `{}` repeats more than once", name)));
205 }
206 vars.push(Var(sanitized_name));
207 Ok(&vars.last().unwrap().0)
208}
209
210fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
211 fn check_record_lit(
212 pattern: RecordLit,
213 code: RecordLit,
214 placeholders: &[Var],
215 match_: Match,
216 ) -> Option<Match> {
217 let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?;
218
219 let mut pattern_fields: Vec<RecordField> =
220 pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or_default();
221 let mut code_fields: Vec<RecordField> =
222 code.record_field_list().map(|x| x.fields().collect()).unwrap_or_default();
223
224 if pattern_fields.len() != code_fields.len() {
225 return None;
226 }
227
228 let by_name = |a: &RecordField, b: &RecordField| {
229 a.name_ref()
230 .map(|x| x.syntax().text().to_string())
231 .cmp(&b.name_ref().map(|x| x.syntax().text().to_string()))
232 };
233 pattern_fields.sort_by(by_name);
234 code_fields.sort_by(by_name);
235
236 pattern_fields.into_iter().zip(code_fields.into_iter()).fold(
237 Some(match_),
238 |accum, (a, b)| {
239 accum.and_then(|match_| check_opt_nodes(Some(a), Some(b), placeholders, match_))
240 },
241 )
242 }
243
244 fn check_call_and_method_call(
245 pattern: CallExpr,
246 code: MethodCallExpr,
247 placeholders: &[Var],
248 match_: Match,
249 ) -> Option<Match> {
250 let (pattern_name, pattern_type_args) = if let Some(Expr::PathExpr(path_exr)) =
251 pattern.expr()
252 {
253 let segment = path_exr.path().and_then(|p| p.segment());
254 (segment.as_ref().and_then(|s| s.name_ref()), segment.and_then(|s| s.type_arg_list()))
255 } else {
256 (None, None)
257 };
258 let match_ = check_opt_nodes(pattern_name, code.name_ref(), placeholders, match_)?;
259 let match_ =
260 check_opt_nodes(pattern_type_args, code.type_arg_list(), placeholders, match_)?;
261 let pattern_args = pattern.syntax().children().find_map(ArgList::cast)?.args();
262 let code_args = code.syntax().children().find_map(ArgList::cast)?.args();
263 let code_args = once(code.expr()?).chain(code_args);
264 check_iter(pattern_args, code_args, placeholders, match_)
265 }
266
267 fn check_method_call_and_call(
268 pattern: MethodCallExpr,
269 code: CallExpr,
270 placeholders: &[Var],
271 match_: Match,
272 ) -> Option<Match> {
273 let (code_name, code_type_args) = if let Some(Expr::PathExpr(path_exr)) = code.expr() {
274 let segment = path_exr.path().and_then(|p| p.segment());
275 (segment.as_ref().and_then(|s| s.name_ref()), segment.and_then(|s| s.type_arg_list()))
276 } else {
277 (None, None)
278 };
279 let match_ = check_opt_nodes(pattern.name_ref(), code_name, placeholders, match_)?;
280 let match_ =
281 check_opt_nodes(pattern.type_arg_list(), code_type_args, placeholders, match_)?;
282 let code_args = code.syntax().children().find_map(ArgList::cast)?.args();
283 let pattern_args = pattern.syntax().children().find_map(ArgList::cast)?.args();
284 let pattern_args = once(pattern.expr()?).chain(pattern_args);
285 check_iter(pattern_args, code_args, placeholders, match_)
286 }
287
288 fn check_opt_nodes(
289 pattern: Option<impl AstNode>,
290 code: Option<impl AstNode>,
291 placeholders: &[Var],
292 match_: Match,
293 ) -> Option<Match> {
294 match (pattern, code) {
295 (Some(pattern), Some(code)) => check(
296 &pattern.syntax().clone().into(),
297 &code.syntax().clone().into(),
298 placeholders,
299 match_,
300 ),
301 (None, None) => Some(match_),
302 _ => None,
303 }
304 }
305
306 fn check_iter<T, I1, I2>(
307 mut pattern: I1,
308 mut code: I2,
309 placeholders: &[Var],
310 match_: Match,
311 ) -> Option<Match>
312 where
313 T: AstNode,
314 I1: Iterator<Item = T>,
315 I2: Iterator<Item = T>,
316 {
317 pattern
318 .by_ref()
319 .zip(code.by_ref())
320 .fold(Some(match_), |accum, (a, b)| {
321 accum.and_then(|match_| {
322 check(
323 &a.syntax().clone().into(),
324 &b.syntax().clone().into(),
325 placeholders,
326 match_,
327 )
328 })
329 })
330 .filter(|_| pattern.next().is_none() && code.next().is_none())
331 }
332
333 fn check(
334 pattern: &SyntaxElement,
335 code: &SyntaxElement,
336 placeholders: &[Var],
337 mut match_: Match,
338 ) -> Option<Match> {
339 match (&pattern, &code) {
340 (SyntaxElement::Token(pattern), SyntaxElement::Token(code)) => {
341 if pattern.text() == code.text() {
342 Some(match_)
343 } else {
344 None
345 }
346 }
347 (SyntaxElement::Node(pattern), SyntaxElement::Node(code)) => {
348 if placeholders.iter().any(|n| n.0.as_str() == pattern.text()) {
349 match_.binding.insert(Var(pattern.text().to_string()), code.clone());
350 Some(match_)
351 } else {
352 if let (Some(pattern), Some(code)) =
353 (RecordLit::cast(pattern.clone()), RecordLit::cast(code.clone()))
354 {
355 check_record_lit(pattern, code, placeholders, match_)
356 } else if let (Some(pattern), Some(code)) =
357 (CallExpr::cast(pattern.clone()), MethodCallExpr::cast(code.clone()))
358 {
359 check_call_and_method_call(pattern, code, placeholders, match_)
360 } else if let (Some(pattern), Some(code)) =
361 (MethodCallExpr::cast(pattern.clone()), CallExpr::cast(code.clone()))
362 {
363 check_method_call_and_call(pattern, code, placeholders, match_)
364 } else {
365 let mut pattern_children = pattern
366 .children_with_tokens()
367 .filter(|element| !element.kind().is_trivia());
368 let mut code_children = code
369 .children_with_tokens()
370 .filter(|element| !element.kind().is_trivia());
371 let new_ignored_comments =
372 code.children_with_tokens().filter_map(|element| {
373 element.as_token().and_then(|token| Comment::cast(token.clone()))
374 });
375 match_.ignored_comments.extend(new_ignored_comments);
376 pattern_children
377 .by_ref()
378 .zip(code_children.by_ref())
379 .fold(Some(match_), |accum, (a, b)| {
380 accum.and_then(|match_| check(&a, &b, placeholders, match_))
381 })
382 .filter(|_| {
383 pattern_children.next().is_none() && code_children.next().is_none()
384 })
385 }
386 }
387 }
388 _ => None,
389 }
390 }
391 let kind = pattern.pattern.kind();
392 let matches = code
393 .descendants()
394 .filter(|n| {
395 n.kind() == kind
396 || (kind == SyntaxKind::CALL_EXPR && n.kind() == SyntaxKind::METHOD_CALL_EXPR)
397 || (kind == SyntaxKind::METHOD_CALL_EXPR && n.kind() == SyntaxKind::CALL_EXPR)
398 })
399 .filter_map(|code| {
400 let match_ =
401 Match { place: code.clone(), binding: HashMap::new(), ignored_comments: vec![] };
402 check(&pattern.pattern.clone().into(), &code.into(), &pattern.vars, match_)
403 })
404 .collect();
405 SsrMatches { matches }
406}
407
408fn replace(matches: &SsrMatches, template: &SsrTemplate) -> TextEdit {
409 let mut builder = TextEditBuilder::default();
410 for match_ in &matches.matches {
411 builder.replace(
412 match_.place.text_range(),
413 render_replace(&match_.binding, &match_.ignored_comments, template),
414 );
415 }
416 builder.finish()
417}
418
419fn render_replace(
420 binding: &Binding,
421 ignored_comments: &Vec<Comment>,
422 template: &SsrTemplate,
423) -> String {
424 let edit = {
425 let mut builder = TextEditBuilder::default();
426 for element in template.template.descendants() {
427 if let Some(var) = template.placeholders.get(&element) {
428 builder.replace(element.text_range(), binding[var].to_string())
429 }
430 }
431 for comment in ignored_comments {
432 builder.insert(template.template.text_range().end(), comment.syntax().to_string())
433 }
434 builder.finish()
435 };
436
437 let mut text = template.template.text().to_string();
438 edit.apply(&mut text);
439 text
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445 use ra_syntax::SourceFile;
446
447 fn parse_error_text(query: &str) -> String {
448 format!("{}", query.parse::<SsrQuery>().unwrap_err())
449 }
450
451 #[test]
452 fn parser_happy_case() {
453 let result: SsrQuery = "foo($a:expr, $b:expr) ==>> bar($b, $a)".parse().unwrap();
454 assert_eq!(&result.pattern.pattern.text(), "foo(__search_pattern_a, __search_pattern_b)");
455 assert_eq!(result.pattern.vars.len(), 2);
456 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a");
457 assert_eq!(result.pattern.vars[1].0, "__search_pattern_b");
458 assert_eq!(&result.template.template.text(), "bar(__search_pattern_b, __search_pattern_a)");
459 }
460
461 #[test]
462 fn parser_empty_query() {
463 assert_eq!(parse_error_text(""), "Parse error: Cannot find delemiter `==>>`");
464 }
465
466 #[test]
467 fn parser_no_delimiter() {
468 assert_eq!(parse_error_text("foo()"), "Parse error: Cannot find delemiter `==>>`");
469 }
470
471 #[test]
472 fn parser_two_delimiters() {
473 assert_eq!(
474 parse_error_text("foo() ==>> a ==>> b "),
475 "Parse error: More than one delimiter found"
476 );
477 }
478
479 #[test]
480 fn parser_no_pattern_type() {
481 assert_eq!(parse_error_text("foo($a) ==>>"), "Parse error: Use $<name>:expr");
482 }
483
484 #[test]
485 fn parser_invalid_name() {
486 assert_eq!(
487 parse_error_text("foo($a+:expr) ==>>"),
488 "Parse error: Name can contain only alphanumerics and _"
489 );
490 }
491
492 #[test]
493 fn parser_invalid_type() {
494 assert_eq!(
495 parse_error_text("foo($a:ident) ==>>"),
496 "Parse error: Only $<name>:expr is supported"
497 );
498 }
499
500 #[test]
501 fn parser_repeated_name() {
502 assert_eq!(
503 parse_error_text("foo($a:expr, $a:expr) ==>>"),
504 "Parse error: Name `a` repeats more than once"
505 );
506 }
507
508 #[test]
509 fn parser_invlid_pattern() {
510 assert_eq!(parse_error_text(" ==>> ()"), "Parse error: Pattern is not an expression");
511 }
512
513 #[test]
514 fn parser_invlid_template() {
515 assert_eq!(parse_error_text("() ==>> )"), "Parse error: Template is not an expression");
516 }
517
518 #[test]
519 fn parse_match_replace() {
520 let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap();
521 let input = "fn main() { foo(1+2); }";
522
523 let code = SourceFile::parse(input).tree();
524 let matches = find(&query.pattern, code.syntax());
525 assert_eq!(matches.matches.len(), 1);
526 assert_eq!(matches.matches[0].place.text(), "foo(1+2)");
527 assert_eq!(matches.matches[0].binding.len(), 1);
528 assert_eq!(
529 matches.matches[0].binding[&Var("__search_pattern_x".to_string())].text(),
530 "1+2"
531 );
532
533 let edit = replace(&matches, &query.template);
534 let mut after = input.to_string();
535 edit.apply(&mut after);
536 assert_eq!(after, "fn main() { bar(1+2); }");
537 }
538
539 fn assert_ssr_transform(query: &str, input: &str, result: &str) {
540 let query: SsrQuery = query.parse().unwrap();
541 let code = SourceFile::parse(input).tree();
542 let matches = find(&query.pattern, code.syntax());
543 let edit = replace(&matches, &query.template);
544 let mut after = input.to_string();
545 edit.apply(&mut after);
546 assert_eq!(after, result);
547 }
548
549 #[test]
550 fn ssr_function_to_method() {
551 assert_ssr_transform(
552 "my_function($a:expr, $b:expr) ==>> ($a).my_method($b)",
553 "loop { my_function( other_func(x, y), z + w) }",
554 "loop { (other_func(x, y)).my_method(z + w) }",
555 )
556 }
557
558 #[test]
559 fn ssr_nested_function() {
560 assert_ssr_transform(
561 "foo($a:expr, $b:expr, $c:expr) ==>> bar($c, baz($a, $b))",
562 "fn main { foo (x + value.method(b), x+y-z, true && false) }",
563 "fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }",
564 )
565 }
566
567 #[test]
568 fn ssr_expected_spacing() {
569 assert_ssr_transform(
570 "foo($x:expr) + bar() ==>> bar($x)",
571 "fn main() { foo(5) + bar() }",
572 "fn main() { bar(5) }",
573 );
574 }
575
576 #[test]
577 fn ssr_with_extra_space() {
578 assert_ssr_transform(
579 "foo($x:expr ) + bar() ==>> bar($x)",
580 "fn main() { foo( 5 ) +bar( ) }",
581 "fn main() { bar(5) }",
582 );
583 }
584
585 #[test]
586 fn ssr_keeps_nested_comment() {
587 assert_ssr_transform(
588 "foo($x:expr) ==>> bar($x)",
589 "fn main() { foo(other(5 /* using 5 */)) }",
590 "fn main() { bar(other(5 /* using 5 */)) }",
591 )
592 }
593
594 #[test]
595 fn ssr_keeps_comment() {
596 assert_ssr_transform(
597 "foo($x:expr) ==>> bar($x)",
598 "fn main() { foo(5 /* using 5 */) }",
599 "fn main() { bar(5)/* using 5 */ }",
600 )
601 }
602
603 #[test]
604 fn ssr_struct_lit() {
605 assert_ssr_transform(
606 "foo{a: $a:expr, b: $b:expr} ==>> foo::new($a, $b)",
607 "fn main() { foo{b:2, a:1} }",
608 "fn main() { foo::new(1, 2) }",
609 )
610 }
611
612 #[test]
613 fn ssr_call_and_method_call() {
614 assert_ssr_transform(
615 "foo::<'a>($a:expr, $b:expr)) ==>> foo2($a, $b)",
616 "fn main() { get().bar.foo::<'a>(1); }",
617 "fn main() { foo2(get().bar, 1); }",
618 )
619 }
620
621 #[test]
622 fn ssr_method_call_and_call() {
623 assert_ssr_transform(
624 "$o:expr.foo::<i32>($a:expr)) ==>> $o.foo2($a)",
625 "fn main() { X::foo::<i32>(x, 1); }",
626 "fn main() { x.foo2(1); }",
627 )
628 }
629}
diff --git a/crates/ra_ide/src/status.rs b/crates/ra_ide/src/status.rs
index 5b7992920..08e6f69cb 100644
--- a/crates/ra_ide/src/status.rs
+++ b/crates/ra_ide/src/status.rs
@@ -2,10 +2,7 @@ use std::{fmt, iter::FromIterator, sync::Arc};
2 2
3use hir::MacroFile; 3use hir::MacroFile;
4use ra_db::{ 4use ra_db::{
5 salsa::{ 5 salsa::debug::{DebugQueryTable, TableEntry},
6 debug::{DebugQueryTable, TableEntry},
7 Database,
8 },
9 FileTextQuery, SourceRootId, 6 FileTextQuery, SourceRootId,
10}; 7};
11use ra_ide_db::{ 8use ra_ide_db::{
@@ -14,14 +11,15 @@ use ra_ide_db::{
14}; 11};
15use ra_prof::{memory_usage, Bytes}; 12use ra_prof::{memory_usage, Bytes};
16use ra_syntax::{ast, Parse, SyntaxNode}; 13use ra_syntax::{ast, Parse, SyntaxNode};
14use rustc_hash::FxHashMap;
17 15
18use crate::FileId; 16use crate::FileId;
19 17
20fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { 18fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
21 db.query(ra_db::ParseQuery).entries::<SyntaxTreeStats>() 19 ra_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>()
22} 20}
23fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { 21fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
24 db.query(hir::db::ParseMacroQuery).entries::<SyntaxTreeStats>() 22 hir::db::ParseMacroQuery.in_db(db).entries::<SyntaxTreeStats>()
25} 23}
26 24
27// Feature: Status 25// Feature: Status
@@ -34,10 +32,10 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
34// | VS Code | **Rust Analyzer: Status** 32// | VS Code | **Rust Analyzer: Status**
35// |=== 33// |===
36pub(crate) fn status(db: &RootDatabase) -> String { 34pub(crate) fn status(db: &RootDatabase) -> String {
37 let files_stats = db.query(FileTextQuery).entries::<FilesStats>(); 35 let files_stats = FileTextQuery.in_db(db).entries::<FilesStats>();
38 let syntax_tree_stats = syntax_tree_stats(db); 36 let syntax_tree_stats = syntax_tree_stats(db);
39 let macro_syntax_tree_stats = macro_syntax_tree_stats(db); 37 let macro_syntax_tree_stats = macro_syntax_tree_stats(db);
40 let symbols_stats = db.query(LibrarySymbolsQuery).entries::<LibrarySymbolsStats>(); 38 let symbols_stats = LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>();
41 format!( 39 format!(
42 "{}\n{}\n{}\n{} (macros)\n\n\nmemory:\n{}\ngc {:?} seconds ago", 40 "{}\n{}\n{}\n{} (macros)\n\n\nmemory:\n{}\ngc {:?} seconds ago",
43 files_stats, 41 files_stats,
@@ -123,20 +121,24 @@ struct LibrarySymbolsStats {
123 121
124impl fmt::Display for LibrarySymbolsStats { 122impl fmt::Display for LibrarySymbolsStats {
125 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 123 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
126 write!(fmt, "{} ({}) symbols", self.total, self.size,) 124 write!(fmt, "{} ({}) symbols", self.total, self.size)
127 } 125 }
128} 126}
129 127
130impl FromIterator<TableEntry<SourceRootId, Arc<SymbolIndex>>> for LibrarySymbolsStats { 128impl FromIterator<TableEntry<(), Arc<FxHashMap<SourceRootId, SymbolIndex>>>>
129 for LibrarySymbolsStats
130{
131 fn from_iter<T>(iter: T) -> LibrarySymbolsStats 131 fn from_iter<T>(iter: T) -> LibrarySymbolsStats
132 where 132 where
133 T: IntoIterator<Item = TableEntry<SourceRootId, Arc<SymbolIndex>>>, 133 T: IntoIterator<Item = TableEntry<(), Arc<FxHashMap<SourceRootId, SymbolIndex>>>>,
134 { 134 {
135 let mut res = LibrarySymbolsStats::default(); 135 let mut res = LibrarySymbolsStats::default();
136 for entry in iter { 136 for entry in iter {
137 let value = entry.value.unwrap(); 137 let value = entry.value.unwrap();
138 res.total += value.len(); 138 for symbols in value.values() {
139 res.size += value.memory_size(); 139 res.total += symbols.len();
140 res.size += symbols.memory_size();
141 }
140 } 142 }
141 res 143 res
142 } 144 }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 0b53ebe69..6ac44c2c0 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -1,5 +1,6 @@
1mod tags; 1mod tags;
2mod html; 2mod html;
3mod injection;
3#[cfg(test)] 4#[cfg(test)]
4mod tests; 5mod tests;
5 6
@@ -10,14 +11,14 @@ use ra_ide_db::{
10}; 11};
11use ra_prof::profile; 12use ra_prof::profile;
12use ra_syntax::{ 13use ra_syntax::{
13 ast::{self, HasFormatSpecifier, HasQuotes, HasStringValue}, 14 ast::{self, HasFormatSpecifier},
14 AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, 15 AstNode, AstToken, Direction, NodeOrToken, SyntaxElement,
15 SyntaxKind::*, 16 SyntaxKind::*,
16 SyntaxToken, TextRange, WalkEvent, T, 17 TextRange, WalkEvent, T,
17}; 18};
18use rustc_hash::FxHashMap; 19use rustc_hash::FxHashMap;
19 20
20use crate::{call_info::ActiveParameter, Analysis, FileId}; 21use crate::FileId;
21 22
22use ast::FormatSpecifier; 23use ast::FormatSpecifier;
23pub(crate) use html::highlight_as_html; 24pub(crate) use html::highlight_as_html;
@@ -43,6 +44,7 @@ pub(crate) fn highlight(
43 db: &RootDatabase, 44 db: &RootDatabase,
44 file_id: FileId, 45 file_id: FileId,
45 range_to_highlight: Option<TextRange>, 46 range_to_highlight: Option<TextRange>,
47 syntactic_name_ref_highlighting: bool,
46) -> Vec<HighlightedRange> { 48) -> Vec<HighlightedRange> {
47 let _p = profile("highlight"); 49 let _p = profile("highlight");
48 let sema = Semantics::new(db); 50 let sema = Semantics::new(db);
@@ -103,6 +105,7 @@ pub(crate) fn highlight(
103 if let Some((highlight, binding_hash)) = highlight_element( 105 if let Some((highlight, binding_hash)) = highlight_element(
104 &sema, 106 &sema,
105 &mut bindings_shadow_count, 107 &mut bindings_shadow_count,
108 syntactic_name_ref_highlighting,
106 name.syntax().clone().into(), 109 name.syntax().clone().into(),
107 ) { 110 ) {
108 stack.add(HighlightedRange { 111 stack.add(HighlightedRange {
@@ -118,7 +121,23 @@ pub(crate) fn highlight(
118 assert!(current_macro_call == Some(mc)); 121 assert!(current_macro_call == Some(mc));
119 current_macro_call = None; 122 current_macro_call = None;
120 format_string = None; 123 format_string = None;
121 continue; 124 }
125 _ => (),
126 }
127
128 // Check for Rust code in documentation
129 match &event {
130 WalkEvent::Leave(NodeOrToken::Node(node)) => {
131 if let Some((doctest, range_mapping, new_comments)) =
132 injection::extract_doc_comments(node)
133 {
134 injection::highlight_doc_comment(
135 doctest,
136 range_mapping,
137 new_comments,
138 &mut stack,
139 );
140 }
122 } 141 }
123 _ => (), 142 _ => (),
124 } 143 }
@@ -130,7 +149,7 @@ pub(crate) fn highlight(
130 149
131 let range = element.text_range(); 150 let range = element.text_range();
132 151
133 let element_to_highlight = if current_macro_call.is_some() { 152 let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT {
134 // Inside a macro -- expand it first 153 // Inside a macro -- expand it first
135 let token = match element.clone().into_token() { 154 let token = match element.clone().into_token() {
136 Some(it) if it.parent().kind() == TOKEN_TREE => it, 155 Some(it) if it.parent().kind() == TOKEN_TREE => it,
@@ -142,23 +161,25 @@ pub(crate) fn highlight(
142 // Check if macro takes a format string and remember it for highlighting later. 161 // Check if macro takes a format string and remember it for highlighting later.
143 // The macros that accept a format string expand to a compiler builtin macros 162 // The macros that accept a format string expand to a compiler builtin macros
144 // `format_args` and `format_args_nl`. 163 // `format_args` and `format_args_nl`.
145 if let Some(fmt_macro_call) = parent.parent().and_then(ast::MacroCall::cast) { 164 if let Some(name) = parent
146 if let Some(name) = 165 .parent()
147 fmt_macro_call.path().and_then(|p| p.segment()).and_then(|s| s.name_ref()) 166 .and_then(ast::MacroCall::cast)
148 { 167 .and_then(|mc| mc.path())
149 match name.text().as_str() { 168 .and_then(|p| p.segment())
150 "format_args" | "format_args_nl" => { 169 .and_then(|s| s.name_ref())
151 format_string = parent 170 {
152 .children_with_tokens() 171 match name.text().as_str() {
153 .filter(|t| t.kind() != WHITESPACE) 172 "format_args" | "format_args_nl" => {
154 .nth(1) 173 format_string = parent
155 .filter(|e| { 174 .children_with_tokens()
156 ast::String::can_cast(e.kind()) 175 .filter(|t| t.kind() != WHITESPACE)
157 || ast::RawString::can_cast(e.kind()) 176 .nth(1)
158 }) 177 .filter(|e| {
159 } 178 ast::String::can_cast(e.kind())
160 _ => {} 179 || ast::RawString::can_cast(e.kind())
180 })
161 } 181 }
182 _ => {}
162 } 183 }
163 } 184 }
164 185
@@ -173,22 +194,25 @@ pub(crate) fn highlight(
173 194
174 if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) { 195 if let Some(token) = element.as_token().cloned().and_then(ast::RawString::cast) {
175 let expanded = element_to_highlight.as_token().unwrap().clone(); 196 let expanded = element_to_highlight.as_token().unwrap().clone();
176 if highlight_injection(&mut stack, &sema, token, expanded).is_some() { 197 if injection::highlight_injection(&mut stack, &sema, token, expanded).is_some() {
177 continue; 198 continue;
178 } 199 }
179 } 200 }
180 201
181 let is_format_string = format_string.as_ref() == Some(&element_to_highlight); 202 let is_format_string = format_string.as_ref() == Some(&element_to_highlight);
182 203
183 if let Some((highlight, binding_hash)) = 204 if let Some((highlight, binding_hash)) = highlight_element(
184 highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight.clone()) 205 &sema,
185 { 206 &mut bindings_shadow_count,
207 syntactic_name_ref_highlighting,
208 element_to_highlight.clone(),
209 ) {
186 stack.add(HighlightedRange { range, highlight, binding_hash }); 210 stack.add(HighlightedRange { range, highlight, binding_hash });
187 if let Some(string) = 211 if let Some(string) =
188 element_to_highlight.as_token().cloned().and_then(ast::String::cast) 212 element_to_highlight.as_token().cloned().and_then(ast::String::cast)
189 { 213 {
190 stack.push();
191 if is_format_string { 214 if is_format_string {
215 stack.push();
192 string.lex_format_specifier(|piece_range, kind| { 216 string.lex_format_specifier(|piece_range, kind| {
193 if let Some(highlight) = highlight_format_specifier(kind) { 217 if let Some(highlight) = highlight_format_specifier(kind) {
194 stack.add(HighlightedRange { 218 stack.add(HighlightedRange {
@@ -198,13 +222,27 @@ pub(crate) fn highlight(
198 }); 222 });
199 } 223 }
200 }); 224 });
225 stack.pop();
226 }
227 // Highlight escape sequences
228 if let Some(char_ranges) = string.char_ranges() {
229 stack.push();
230 for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) {
231 if string.text()[piece_range.start().into()..].starts_with('\\') {
232 stack.add(HighlightedRange {
233 range: piece_range + range.start(),
234 highlight: HighlightTag::EscapeSequence.into(),
235 binding_hash: None,
236 });
237 }
238 }
239 stack.pop_and_inject(None);
201 } 240 }
202 stack.pop();
203 } else if let Some(string) = 241 } else if let Some(string) =
204 element_to_highlight.as_token().cloned().and_then(ast::RawString::cast) 242 element_to_highlight.as_token().cloned().and_then(ast::RawString::cast)
205 { 243 {
206 stack.push();
207 if is_format_string { 244 if is_format_string {
245 stack.push();
208 string.lex_format_specifier(|piece_range, kind| { 246 string.lex_format_specifier(|piece_range, kind| {
209 if let Some(highlight) = highlight_format_specifier(kind) { 247 if let Some(highlight) = highlight_format_specifier(kind) {
210 stack.add(HighlightedRange { 248 stack.add(HighlightedRange {
@@ -214,8 +252,8 @@ pub(crate) fn highlight(
214 }); 252 });
215 } 253 }
216 }); 254 });
255 stack.pop();
217 } 256 }
218 stack.pop();
219 } 257 }
220 } 258 }
221 } 259 }
@@ -259,9 +297,8 @@ impl HighlightedRangeStack {
259 let mut parent = prev.pop().unwrap(); 297 let mut parent = prev.pop().unwrap();
260 for ele in children { 298 for ele in children {
261 assert!(parent.range.contains_range(ele.range)); 299 assert!(parent.range.contains_range(ele.range));
262 let mut cloned = parent.clone(); 300
263 parent.range = TextRange::new(parent.range.start(), ele.range.start()); 301 let cloned = Self::intersect(&mut parent, &ele);
264 cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
265 if !parent.range.is_empty() { 302 if !parent.range.is_empty() {
266 prev.push(parent); 303 prev.push(parent);
267 } 304 }
@@ -274,6 +311,92 @@ impl HighlightedRangeStack {
274 } 311 }
275 } 312 }
276 313
314 /// Intersects the `HighlightedRange` `parent` with `child`.
315 /// `parent` is mutated in place, becoming the range before `child`.
316 /// Returns the range (of the same type as `parent`) *after* `child`.
317 fn intersect(parent: &mut HighlightedRange, child: &HighlightedRange) -> HighlightedRange {
318 assert!(parent.range.contains_range(child.range));
319
320 let mut cloned = parent.clone();
321 parent.range = TextRange::new(parent.range.start(), child.range.start());
322 cloned.range = TextRange::new(child.range.end(), cloned.range.end());
323
324 cloned
325 }
326
327 /// Remove the `HighlightRange` of `parent` that's currently covered by `child`.
328 fn intersect_partial(parent: &mut HighlightedRange, child: &HighlightedRange) {
329 assert!(
330 parent.range.start() <= child.range.start()
331 && parent.range.end() >= child.range.start()
332 && child.range.end() > parent.range.end()
333 );
334
335 parent.range = TextRange::new(parent.range.start(), child.range.start());
336 }
337
338 /// Similar to `pop`, but can modify arbitrary prior ranges (where `pop`)
339 /// can only modify the last range currently on the stack.
340 /// Can be used to do injections that span multiple ranges, like the
341 /// doctest injection below.
342 /// If `overwrite_parent` is non-optional, the highlighting of the parent range
343 /// is overwritten with the argument.
344 ///
345 /// Note that `pop` can be simulated by `pop_and_inject(false)` but the
346 /// latter is computationally more expensive.
347 fn pop_and_inject(&mut self, overwrite_parent: Option<Highlight>) {
348 let mut children = self.stack.pop().unwrap();
349 let prev = self.stack.last_mut().unwrap();
350 children.sort_by_key(|range| range.range.start());
351 prev.sort_by_key(|range| range.range.start());
352
353 for child in children {
354 if let Some(idx) =
355 prev.iter().position(|parent| parent.range.contains_range(child.range))
356 {
357 if let Some(tag) = overwrite_parent {
358 prev[idx].highlight = tag;
359 }
360
361 let cloned = Self::intersect(&mut prev[idx], &child);
362 let insert_idx = if prev[idx].range.is_empty() {
363 prev.remove(idx);
364 idx
365 } else {
366 idx + 1
367 };
368 prev.insert(insert_idx, child);
369 if !cloned.range.is_empty() {
370 prev.insert(insert_idx + 1, cloned);
371 }
372 } else {
373 let maybe_idx =
374 prev.iter().position(|parent| parent.range.contains(child.range.start()));
375 match (overwrite_parent, maybe_idx) {
376 (Some(_), Some(idx)) => {
377 Self::intersect_partial(&mut prev[idx], &child);
378 let insert_idx = if prev[idx].range.is_empty() {
379 prev.remove(idx);
380 idx
381 } else {
382 idx + 1
383 };
384 prev.insert(insert_idx, child);
385 }
386 (_, None) => {
387 let idx = prev
388 .binary_search_by_key(&child.range.start(), |range| range.range.start())
389 .unwrap_or_else(|x| x);
390 prev.insert(idx, child);
391 }
392 _ => {
393 unreachable!("child range should be completely contained in parent range");
394 }
395 }
396 }
397 }
398 }
399
277 fn add(&mut self, range: HighlightedRange) { 400 fn add(&mut self, range: HighlightedRange) {
278 self.stack 401 self.stack
279 .last_mut() 402 .last_mut()
@@ -335,6 +458,7 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
335fn highlight_element( 458fn highlight_element(
336 sema: &Semantics<RootDatabase>, 459 sema: &Semantics<RootDatabase>,
337 bindings_shadow_count: &mut FxHashMap<Name, u32>, 460 bindings_shadow_count: &mut FxHashMap<Name, u32>,
461 syntactic_name_ref_highlighting: bool,
338 element: SyntaxElement, 462 element: SyntaxElement,
339) -> Option<(Highlight, Option<u64>)> { 463) -> Option<(Highlight, Option<u64>)> {
340 let db = sema.db; 464 let db = sema.db;
@@ -363,6 +487,7 @@ fn highlight_element(
363 highlight_name(db, def) | HighlightModifier::Definition 487 highlight_name(db, def) | HighlightModifier::Definition
364 } 488 }
365 Some(NameClass::ConstReference(def)) => highlight_name(db, def), 489 Some(NameClass::ConstReference(def)) => highlight_name(db, def),
490 Some(NameClass::FieldShorthand { .. }) => HighlightTag::Field.into(),
366 None => highlight_name_by_syntax(name) | HighlightModifier::Definition, 491 None => highlight_name_by_syntax(name) | HighlightModifier::Definition,
367 } 492 }
368 } 493 }
@@ -387,12 +512,20 @@ fn highlight_element(
387 } 512 }
388 NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), 513 NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
389 }, 514 },
515 None if syntactic_name_ref_highlighting => highlight_name_ref_by_syntax(name_ref),
390 None => HighlightTag::UnresolvedReference.into(), 516 None => HighlightTag::UnresolvedReference.into(),
391 } 517 }
392 } 518 }
393 519
394 // Simple token-based highlighting 520 // Simple token-based highlighting
395 COMMENT => HighlightTag::Comment.into(), 521 COMMENT => {
522 let comment = element.into_token().and_then(ast::Comment::cast)?;
523 let h = HighlightTag::Comment;
524 match comment.kind().doc {
525 Some(_) => h | HighlightModifier::Documentation,
526 None => h.into(),
527 }
528 }
396 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::StringLiteral.into(), 529 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::StringLiteral.into(),
397 ATTR => HighlightTag::Attribute.into(), 530 ATTR => HighlightTag::Attribute.into(),
398 INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(), 531 INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(),
@@ -406,6 +539,21 @@ fn highlight_element(
406 _ => h, 539 _ => h,
407 } 540 }
408 } 541 }
542 T![*] => {
543 let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?;
544
545 let expr = prefix_expr.expr()?;
546 let ty = sema.type_of_expr(&expr)?;
547 if !ty.is_raw_ptr() {
548 return None;
549 } else {
550 HighlightTag::Operator | HighlightModifier::Unsafe
551 }
552 }
553 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
554 Highlight::new(HighlightTag::Macro)
555 }
556 p if p.is_punct() => HighlightTag::Punctuation.into(),
409 557
410 k if k.is_keyword() => { 558 k if k.is_keyword() => {
411 let h = Highlight::new(HighlightTag::Keyword); 559 let h = Highlight::new(HighlightTag::Keyword);
@@ -419,10 +567,31 @@ fn highlight_element(
419 | T![return] 567 | T![return]
420 | T![while] 568 | T![while]
421 | T![in] => h | HighlightModifier::ControlFlow, 569 | T![in] => h | HighlightModifier::ControlFlow,
422 T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow, 570 T![for] if !is_child_of_impl(&element) => h | HighlightModifier::ControlFlow,
423 T![unsafe] => h | HighlightModifier::Unsafe, 571 T![unsafe] => h | HighlightModifier::Unsafe,
424 T![true] | T![false] => HighlightTag::BoolLiteral.into(), 572 T![true] | T![false] => HighlightTag::BoolLiteral.into(),
425 T![self] => HighlightTag::SelfKeyword.into(), 573 T![self] => {
574 let self_param_is_mut = element
575 .parent()
576 .and_then(ast::SelfParam::cast)
577 .and_then(|p| p.mut_token())
578 .is_some();
579 // closure to enforce lazyness
580 let self_path = || {
581 sema.resolve_path(&element.parent()?.parent().and_then(ast::Path::cast)?)
582 };
583 if self_param_is_mut
584 || matches!(self_path(),
585 Some(hir::PathResolution::Local(local))
586 if local.is_self(db)
587 && (local.is_mut(db) || local.ty(db).is_mutable_reference())
588 )
589 {
590 HighlightTag::SelfKeyword | HighlightModifier::Mutable
591 } else {
592 HighlightTag::SelfKeyword.into()
593 }
594 }
426 _ => h, 595 _ => h,
427 } 596 }
428 } 597 }
@@ -445,7 +614,7 @@ fn highlight_element(
445 } 614 }
446} 615}
447 616
448fn is_child_of_impl(element: SyntaxElement) -> bool { 617fn is_child_of_impl(element: &SyntaxElement) -> bool {
449 match element.parent() { 618 match element.parent() {
450 Some(e) => e.kind() == IMPL_DEF, 619 Some(e) => e.kind() == IMPL_DEF,
451 _ => false, 620 _ => false,
@@ -458,7 +627,13 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
458 Definition::Field(_) => HighlightTag::Field, 627 Definition::Field(_) => HighlightTag::Field,
459 Definition::ModuleDef(def) => match def { 628 Definition::ModuleDef(def) => match def {
460 hir::ModuleDef::Module(_) => HighlightTag::Module, 629 hir::ModuleDef::Module(_) => HighlightTag::Module,
461 hir::ModuleDef::Function(_) => HighlightTag::Function, 630 hir::ModuleDef::Function(func) => {
631 let mut h = HighlightTag::Function.into();
632 if func.is_unsafe(db) {
633 h |= HighlightModifier::Unsafe;
634 }
635 return h;
636 }
462 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, 637 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
463 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, 638 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum,
464 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, 639 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union,
@@ -477,9 +652,10 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
477 }, 652 },
478 Definition::SelfType(_) => HighlightTag::SelfType, 653 Definition::SelfType(_) => HighlightTag::SelfType,
479 Definition::TypeParam(_) => HighlightTag::TypeParam, 654 Definition::TypeParam(_) => HighlightTag::TypeParam,
480 // FIXME: distinguish between locals and parameters
481 Definition::Local(local) => { 655 Definition::Local(local) => {
482 let mut h = Highlight::new(HighlightTag::Local); 656 let tag =
657 if local.is_param(db) { HighlightTag::ValueParam } else { HighlightTag::Local };
658 let mut h = Highlight::new(tag);
483 if local.is_mut(db) || local.ty(db).is_mutable_reference() { 659 if local.is_mut(db) || local.ty(db).is_mutable_reference() {
484 h |= HighlightModifier::Mutable; 660 h |= HighlightModifier::Mutable;
485 } 661 }
@@ -517,41 +693,52 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
517 tag.into() 693 tag.into()
518} 694}
519 695
520fn highlight_injection( 696fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight {
521 acc: &mut HighlightedRangeStack, 697 let default = HighlightTag::UnresolvedReference;
522 sema: &Semantics<RootDatabase>,
523 literal: ast::RawString,
524 expanded: SyntaxToken,
525) -> Option<()> {
526 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
527 if !active_parameter.name.starts_with("ra_fixture") {
528 return None;
529 }
530 let value = literal.value()?;
531 let (analysis, tmp_file_id) = Analysis::from_single_file(value);
532
533 if let Some(range) = literal.open_quote_text_range() {
534 acc.add(HighlightedRange {
535 range,
536 highlight: HighlightTag::StringLiteral.into(),
537 binding_hash: None,
538 })
539 }
540 698
541 for mut h in analysis.highlight(tmp_file_id).unwrap() { 699 let parent = match name.syntax().parent() {
542 if let Some(r) = literal.map_range_up(h.range) { 700 Some(it) => it,
543 h.range = r; 701 _ => return default.into(),
544 acc.add(h) 702 };
545 }
546 }
547 703
548 if let Some(range) = literal.close_quote_text_range() { 704 let tag = match parent.kind() {
549 acc.add(HighlightedRange { 705 METHOD_CALL_EXPR => HighlightTag::Function,
550 range, 706 FIELD_EXPR => HighlightTag::Field,
551 highlight: HighlightTag::StringLiteral.into(), 707 PATH_SEGMENT => {
552 binding_hash: None, 708 let path = match parent.parent().and_then(ast::Path::cast) {
553 }) 709 Some(it) => it,
554 } 710 _ => return default.into(),
711 };
712 let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) {
713 Some(it) => it,
714 _ => {
715 // within path, decide whether it is module or adt by checking for uppercase name
716 return if name.text().chars().next().unwrap_or_default().is_uppercase() {
717 HighlightTag::Struct
718 } else {
719 HighlightTag::Module
720 }
721 .into();
722 }
723 };
724 let parent = match expr.syntax().parent() {
725 Some(it) => it,
726 None => return default.into(),
727 };
555 728
556 Some(()) 729 match parent.kind() {
730 CALL_EXPR => HighlightTag::Function,
731 _ => {
732 if name.text().chars().next().unwrap_or_default().is_uppercase() {
733 HighlightTag::Struct
734 } else {
735 HighlightTag::Constant
736 }
737 }
738 }
739 }
740 _ => default,
741 };
742
743 tag.into()
557} 744}
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index edfe61f39..0be55bca9 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -19,7 +19,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
19 ) 19 )
20 } 20 }
21 21
22 let ranges = highlight(db, file_id, None); 22 let ranges = highlight(db, file_id, None, false);
23 let text = parse.tree().syntax().to_string(); 23 let text = parse.tree().syntax().to_string();
24 let mut prev_pos = TextSize::from(0); 24 let mut prev_pos = TextSize::from(0);
25 let mut buf = String::new(); 25 let mut buf = String::new();
@@ -64,11 +64,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
64 64
65.lifetime { color: #DFAF8F; font-style: italic; } 65.lifetime { color: #DFAF8F; font-style: italic; }
66.comment { color: #7F9F7F; } 66.comment { color: #7F9F7F; }
67.documentation { color: #629755; }
68.injected { opacity: 0.65 ; }
67.struct, .enum { color: #7CB8BB; } 69.struct, .enum { color: #7CB8BB; }
68.enum_variant { color: #BDE0F3; } 70.enum_variant { color: #BDE0F3; }
69.string_literal { color: #CC9393; } 71.string_literal { color: #CC9393; }
70.field { color: #94BFF3; } 72.field { color: #94BFF3; }
71.function { color: #93E0E3; } 73.function { color: #93E0E3; }
74.function.unsafe { color: #BC8383; }
75.operator.unsafe { color: #BC8383; }
72.parameter { color: #94BFF3; } 76.parameter { color: #94BFF3; }
73.text { color: #DCDCCC; } 77.text { color: #DCDCCC; }
74.type { color: #7CB8BB; } 78.type { color: #7CB8BB; }
@@ -79,12 +83,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
79.bool_literal { color: #BFE6EB; } 83.bool_literal { color: #BFE6EB; }
80.macro { color: #94BFF3; } 84.macro { color: #94BFF3; }
81.module { color: #AFD8AF; } 85.module { color: #AFD8AF; }
86.value_param { color: #DCDCCC; }
82.variable { color: #DCDCCC; } 87.variable { color: #DCDCCC; }
83.format_specifier { color: #CC696B; } 88.format_specifier { color: #CC696B; }
84.mutable { text-decoration: underline; } 89.mutable { text-decoration: underline; }
85 90.escape_sequence { color: #94BFF3; }
86.keyword { color: #F0DFAF; font-weight: bold; } 91.keyword { color: #F0DFAF; font-weight: bold; }
87.keyword.unsafe { color: #BC8383; font-weight: bold; } 92.keyword.unsafe { color: #BC8383; font-weight: bold; }
88.control { font-style: italic; } 93.control { font-style: italic; }
94
95.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
89</style> 96</style>
90"; 97";
diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs
new file mode 100644
index 000000000..8665b480f
--- /dev/null
+++ b/crates/ra_ide/src/syntax_highlighting/injection.rs
@@ -0,0 +1,188 @@
1//! Syntax highlighting injections such as highlighting of documentation tests.
2
3use std::{collections::BTreeMap, convert::TryFrom};
4
5use ast::{HasQuotes, HasStringValue};
6use hir::Semantics;
7use ra_syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
8use stdx::SepBy;
9
10use crate::{
11 call_info::ActiveParameter, Analysis, Highlight, HighlightModifier, HighlightTag,
12 HighlightedRange, RootDatabase,
13};
14
15use super::HighlightedRangeStack;
16
17pub(super) fn highlight_injection(
18 acc: &mut HighlightedRangeStack,
19 sema: &Semantics<RootDatabase>,
20 literal: ast::RawString,
21 expanded: SyntaxToken,
22) -> Option<()> {
23 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
24 if !active_parameter.name.starts_with("ra_fixture") {
25 return None;
26 }
27 let value = literal.value()?;
28 let (analysis, tmp_file_id) = Analysis::from_single_file(value.into_owned());
29
30 if let Some(range) = literal.open_quote_text_range() {
31 acc.add(HighlightedRange {
32 range,
33 highlight: HighlightTag::StringLiteral.into(),
34 binding_hash: None,
35 })
36 }
37
38 for mut h in analysis.highlight(tmp_file_id).unwrap() {
39 if let Some(r) = literal.map_range_up(h.range) {
40 h.range = r;
41 acc.add(h)
42 }
43 }
44
45 if let Some(range) = literal.close_quote_text_range() {
46 acc.add(HighlightedRange {
47 range,
48 highlight: HighlightTag::StringLiteral.into(),
49 binding_hash: None,
50 })
51 }
52
53 Some(())
54}
55
56/// Mapping from extracted documentation code to original code
57type RangesMap = BTreeMap<TextSize, TextSize>;
58
59const RUSTDOC_FENCE: &'static str = "```";
60const RUSTDOC_FENCE_TOKENS: &[&'static str] =
61 &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"];
62
63/// Extracts Rust code from documentation comments as well as a mapping from
64/// the extracted source code back to the original source ranges.
65/// Lastly, a vector of new comment highlight ranges (spanning only the
66/// comment prefix) is returned which is used in the syntax highlighting
67/// injection to replace the previous (line-spanning) comment ranges.
68pub(super) fn extract_doc_comments(
69 node: &SyntaxNode,
70) -> Option<(String, RangesMap, Vec<HighlightedRange>)> {
71 // wrap the doctest into function body to get correct syntax highlighting
72 let prefix = "fn doctest() {\n";
73 let suffix = "}\n";
74 // Mapping from extracted documentation code to original code
75 let mut range_mapping: RangesMap = BTreeMap::new();
76 let mut line_start = TextSize::try_from(prefix.len()).unwrap();
77 let mut is_codeblock = false;
78 let mut is_doctest = false;
79 // Replace the original, line-spanning comment ranges by new, only comment-prefix
80 // spanning comment ranges.
81 let mut new_comments = Vec::new();
82 let doctest = node
83 .children_with_tokens()
84 .filter_map(|el| el.into_token().and_then(ast::Comment::cast))
85 .filter(|comment| comment.kind().doc.is_some())
86 .filter(|comment| {
87 if let Some(idx) = comment.text().find(RUSTDOC_FENCE) {
88 is_codeblock = !is_codeblock;
89 // Check whether code is rust by inspecting fence guards
90 let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..];
91 let is_rust =
92 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
93 is_doctest = is_codeblock && is_rust;
94 false
95 } else {
96 is_doctest
97 }
98 })
99 .map(|comment| {
100 let prefix_len = comment.prefix().len();
101 let line: &str = comment.text().as_str();
102 let range = comment.syntax().text_range();
103
104 // whitespace after comment is ignored
105 let pos = if let Some(ws) = line.chars().nth(prefix_len).filter(|c| c.is_whitespace()) {
106 prefix_len + ws.len_utf8()
107 } else {
108 prefix_len
109 };
110
111 // lines marked with `#` should be ignored in output, we skip the `#` char
112 let pos = if let Some(ws) = line.chars().nth(pos).filter(|&c| c == '#') {
113 pos + ws.len_utf8()
114 } else {
115 pos
116 };
117
118 range_mapping.insert(line_start, range.start() + TextSize::try_from(pos).unwrap());
119 new_comments.push(HighlightedRange {
120 range: TextRange::new(
121 range.start(),
122 range.start() + TextSize::try_from(pos).unwrap(),
123 ),
124 highlight: HighlightTag::Comment | HighlightModifier::Documentation,
125 binding_hash: None,
126 });
127 line_start += range.len() - TextSize::try_from(pos).unwrap();
128 line_start += TextSize::try_from('\n'.len_utf8()).unwrap();
129
130 line[pos..].to_owned()
131 })
132 .sep_by("\n")
133 .to_string();
134
135 if doctest.is_empty() {
136 return None;
137 }
138
139 let doctest = format!("{}{}{}", prefix, doctest, suffix);
140 Some((doctest, range_mapping, new_comments))
141}
142
143/// Injection of syntax highlighting of doctests.
144pub(super) fn highlight_doc_comment(
145 text: String,
146 range_mapping: RangesMap,
147 new_comments: Vec<HighlightedRange>,
148 stack: &mut HighlightedRangeStack,
149) {
150 let (analysis, tmp_file_id) = Analysis::from_single_file(text);
151
152 stack.push();
153 for mut h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() {
154 // Determine start offset and end offset in case of multi-line ranges
155 let mut start_offset = None;
156 let mut end_offset = None;
157 for (line_start, orig_line_start) in range_mapping.range(..h.range.end()).rev() {
158 // It's possible for orig_line_start - line_start to be negative. Add h.range.start()
159 // here and remove it from the end range after the loop below so that the values are
160 // always non-negative.
161 let offset = h.range.start() + orig_line_start - line_start;
162 if line_start <= &h.range.start() {
163 start_offset.get_or_insert(offset);
164 break;
165 } else {
166 end_offset.get_or_insert(offset);
167 }
168 }
169 if let Some(start_offset) = start_offset {
170 h.range = TextRange::new(
171 start_offset,
172 h.range.end() + end_offset.unwrap_or(start_offset) - h.range.start(),
173 );
174
175 h.highlight |= HighlightModifier::Injected;
176 stack.add(h);
177 }
178 }
179
180 // Inject the comment prefix highlight ranges
181 stack.push();
182 for comment in new_comments {
183 stack.add(comment);
184 }
185 stack.pop_and_inject(None);
186 stack
187 .pop_and_inject(Some(Highlight::from(HighlightTag::Generic) | HighlightModifier::Injected));
188}
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 1514531de..49ec94bdc 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -23,13 +23,16 @@ pub enum HighlightTag {
23 Constant, 23 Constant,
24 Enum, 24 Enum,
25 EnumVariant, 25 EnumVariant,
26 EscapeSequence,
26 Field, 27 Field,
27 Function, 28 Function,
29 Generic,
28 Keyword, 30 Keyword,
29 Lifetime, 31 Lifetime,
30 Macro, 32 Macro,
31 Module, 33 Module,
32 NumericLiteral, 34 NumericLiteral,
35 Punctuation,
33 SelfKeyword, 36 SelfKeyword,
34 SelfType, 37 SelfType,
35 Static, 38 Static,
@@ -39,6 +42,7 @@ pub enum HighlightTag {
39 TypeAlias, 42 TypeAlias,
40 TypeParam, 43 TypeParam,
41 Union, 44 Union,
45 ValueParam,
42 Local, 46 Local,
43 UnresolvedReference, 47 UnresolvedReference,
44 FormatSpecifier, 48 FormatSpecifier,
@@ -55,6 +59,8 @@ pub enum HighlightModifier {
55 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is 59 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is
56 /// not. 60 /// not.
57 Definition, 61 Definition,
62 Documentation,
63 Injected,
58 Mutable, 64 Mutable,
59 Unsafe, 65 Unsafe,
60} 66}
@@ -71,13 +77,18 @@ impl HighlightTag {
71 HighlightTag::Constant => "constant", 77 HighlightTag::Constant => "constant",
72 HighlightTag::Enum => "enum", 78 HighlightTag::Enum => "enum",
73 HighlightTag::EnumVariant => "enum_variant", 79 HighlightTag::EnumVariant => "enum_variant",
80 HighlightTag::EscapeSequence => "escape_sequence",
74 HighlightTag::Field => "field", 81 HighlightTag::Field => "field",
82 HighlightTag::FormatSpecifier => "format_specifier",
75 HighlightTag::Function => "function", 83 HighlightTag::Function => "function",
84 HighlightTag::Generic => "generic",
76 HighlightTag::Keyword => "keyword", 85 HighlightTag::Keyword => "keyword",
77 HighlightTag::Lifetime => "lifetime", 86 HighlightTag::Lifetime => "lifetime",
87 HighlightTag::Punctuation => "punctuation",
78 HighlightTag::Macro => "macro", 88 HighlightTag::Macro => "macro",
79 HighlightTag::Module => "module", 89 HighlightTag::Module => "module",
80 HighlightTag::NumericLiteral => "numeric_literal", 90 HighlightTag::NumericLiteral => "numeric_literal",
91 HighlightTag::Operator => "operator",
81 HighlightTag::SelfKeyword => "self_keyword", 92 HighlightTag::SelfKeyword => "self_keyword",
82 HighlightTag::SelfType => "self_type", 93 HighlightTag::SelfType => "self_type",
83 HighlightTag::Static => "static", 94 HighlightTag::Static => "static",
@@ -87,10 +98,9 @@ impl HighlightTag {
87 HighlightTag::TypeAlias => "type_alias", 98 HighlightTag::TypeAlias => "type_alias",
88 HighlightTag::TypeParam => "type_param", 99 HighlightTag::TypeParam => "type_param",
89 HighlightTag::Union => "union", 100 HighlightTag::Union => "union",
101 HighlightTag::ValueParam => "value_param",
90 HighlightTag::Local => "variable", 102 HighlightTag::Local => "variable",
91 HighlightTag::UnresolvedReference => "unresolved_reference", 103 HighlightTag::UnresolvedReference => "unresolved_reference",
92 HighlightTag::FormatSpecifier => "format_specifier",
93 HighlightTag::Operator => "operator",
94 } 104 }
95 } 105 }
96} 106}
@@ -106,6 +116,8 @@ impl HighlightModifier {
106 HighlightModifier::Attribute, 116 HighlightModifier::Attribute,
107 HighlightModifier::ControlFlow, 117 HighlightModifier::ControlFlow,
108 HighlightModifier::Definition, 118 HighlightModifier::Definition,
119 HighlightModifier::Documentation,
120 HighlightModifier::Injected,
109 HighlightModifier::Mutable, 121 HighlightModifier::Mutable,
110 HighlightModifier::Unsafe, 122 HighlightModifier::Unsafe,
111 ]; 123 ];
@@ -115,6 +127,8 @@ impl HighlightModifier {
115 HighlightModifier::Attribute => "attribute", 127 HighlightModifier::Attribute => "attribute",
116 HighlightModifier::ControlFlow => "control", 128 HighlightModifier::ControlFlow => "control",
117 HighlightModifier::Definition => "declaration", 129 HighlightModifier::Definition => "declaration",
130 HighlightModifier::Documentation => "documentation",
131 HighlightModifier::Injected => "injected",
118 HighlightModifier::Mutable => "mutable", 132 HighlightModifier::Mutable => "mutable",
119 HighlightModifier::Unsafe => "unsafe", 133 HighlightModifier::Unsafe => "unsafe",
120 } 134 }
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index eb43a23da..87a6e2523 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -1,15 +1,13 @@
1use std::fs; 1use std::fs;
2 2
3use test_utils::{assert_eq_text, project_dir, read_text}; 3use expect::{expect_file, ExpectFile};
4use test_utils::project_dir;
4 5
5use crate::{ 6use crate::{mock_analysis::single_file, FileRange, TextRange};
6 mock_analysis::{single_file, MockAnalysis},
7 FileRange, TextRange,
8};
9 7
10#[test] 8#[test]
11fn test_highlighting() { 9fn test_highlighting() {
12 let (analysis, file_id) = single_file( 10 check_highlighting(
13 r#" 11 r#"
14#[derive(Clone, Debug)] 12#[derive(Clone, Debug)]
15struct Foo { 13struct Foo {
@@ -27,6 +25,16 @@ impl Bar for Foo {
27 } 25 }
28} 26}
29 27
28impl Foo {
29 fn baz(mut self) -> i32 {
30 self.x
31 }
32
33 fn qux(&mut self) {
34 self.x = 0;
35 }
36}
37
30static mut STATIC_MUT: i32 = 0; 38static mut STATIC_MUT: i32 = 0;
31 39
32fn foo<'a, T>() -> T { 40fn foo<'a, T>() -> T {
@@ -43,6 +51,12 @@ def_fn! {
43 } 51 }
44} 52}
45 53
54macro_rules! noop {
55 ($expr:expr) => {
56 $expr
57 }
58}
59
46// comment 60// comment
47fn main() { 61fn main() {
48 println!("Hello, {}!", 92); 62 println!("Hello, {}!", 92);
@@ -61,10 +75,14 @@ fn main() {
61 // Do nothing 75 // Do nothing
62 } 76 }
63 77
78 noop!(noop!(1));
79
64 let mut x = 42; 80 let mut x = 42;
65 let y = &mut x; 81 let y = &mut x;
66 let z = &y; 82 let z = &y;
67 83
84 let Foo { x: z, y } = Foo { x: z, y };
85
68 y; 86 y;
69} 87}
70 88
@@ -84,17 +102,14 @@ impl<T> Option<T> {
84} 102}
85"# 103"#
86 .trim(), 104 .trim(),
105 expect_file!["crates/ra_ide/test_data/highlighting.html"],
106 false,
87 ); 107 );
88 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlighting.html");
89 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
90 let expected_html = &read_text(&dst_file);
91 fs::write(dst_file, &actual_html).unwrap();
92 assert_eq_text!(expected_html, actual_html);
93} 108}
94 109
95#[test] 110#[test]
96fn test_rainbow_highlighting() { 111fn test_rainbow_highlighting() {
97 let (analysis, file_id) = single_file( 112 check_highlighting(
98 r#" 113 r#"
99fn main() { 114fn main() {
100 let hello = "hello"; 115 let hello = "hello";
@@ -110,12 +125,9 @@ fn bar() {
110} 125}
111"# 126"#
112 .trim(), 127 .trim(),
128 expect_file!["crates/ra_ide/test_data/rainbow_highlighting.html"],
129 true,
113 ); 130 );
114 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/rainbow_highlighting.html");
115 let actual_html = &analysis.highlight_as_html(file_id, true).unwrap();
116 let expected_html = &read_text(&dst_file);
117 fs::write(dst_file, &actual_html).unwrap();
118 assert_eq_text!(expected_html, actual_html);
119} 131}
120 132
121#[test] 133#[test]
@@ -123,12 +135,10 @@ fn accidentally_quadratic() {
123 let file = project_dir().join("crates/ra_syntax/test_data/accidentally_quadratic"); 135 let file = project_dir().join("crates/ra_syntax/test_data/accidentally_quadratic");
124 let src = fs::read_to_string(file).unwrap(); 136 let src = fs::read_to_string(file).unwrap();
125 137
126 let mut mock = MockAnalysis::new(); 138 let (analysis, file_id) = single_file(&src);
127 let file_id = mock.add_file("/main.rs", &src);
128 let host = mock.analysis_host();
129 139
130 // let t = std::time::Instant::now(); 140 // let t = std::time::Instant::now();
131 let _ = host.analysis().highlight(file_id).unwrap(); 141 let _ = analysis.highlight(file_id).unwrap();
132 // eprintln!("elapsed: {:?}", t.elapsed()); 142 // eprintln!("elapsed: {:?}", t.elapsed());
133} 143}
134 144
@@ -136,16 +146,17 @@ fn accidentally_quadratic() {
136fn test_ranges() { 146fn test_ranges() {
137 let (analysis, file_id) = single_file( 147 let (analysis, file_id) = single_file(
138 r#" 148 r#"
139 #[derive(Clone, Debug)] 149#[derive(Clone, Debug)]
140 struct Foo { 150struct Foo {
141 pub x: i32, 151 pub x: i32,
142 pub y: i32, 152 pub y: i32,
143 }"#, 153}
154"#,
144 ); 155 );
145 156
146 // The "x" 157 // The "x"
147 let highlights = &analysis 158 let highlights = &analysis
148 .highlight_range(FileRange { file_id, range: TextRange::at(82.into(), 1.into()) }) 159 .highlight_range(FileRange { file_id, range: TextRange::at(45.into(), 1.into()) })
149 .unwrap(); 160 .unwrap();
150 161
151 assert_eq!(&highlights[0].highlight.to_string(), "field.declaration"); 162 assert_eq!(&highlights[0].highlight.to_string(), "field.declaration");
@@ -153,7 +164,7 @@ fn test_ranges() {
153 164
154#[test] 165#[test]
155fn test_flattening() { 166fn test_flattening() {
156 let (analysis, file_id) = single_file( 167 check_highlighting(
157 r##" 168 r##"
158fn fixture(ra_fixture: &str) {} 169fn fixture(ra_fixture: &str) {}
159 170
@@ -167,13 +178,9 @@ fn main() {
167 ); 178 );
168}"## 179}"##
169 .trim(), 180 .trim(),
181 expect_file!["crates/ra_ide/test_data/highlight_injection.html"],
182 false,
170 ); 183 );
171
172 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_injection.html");
173 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
174 let expected_html = &read_text(&dst_file);
175 fs::write(dst_file, &actual_html).unwrap();
176 assert_eq_text!(expected_html, actual_html);
177} 184}
178 185
179#[test] 186#[test]
@@ -192,7 +199,7 @@ macro_rules! test {}
192fn test_string_highlighting() { 199fn test_string_highlighting() {
193 // The format string detection is based on macro-expansion, 200 // The format string detection is based on macro-expansion,
194 // thus, we have to copy the macro definition from `std` 201 // thus, we have to copy the macro definition from `std`
195 let (analysis, file_id) = single_file( 202 check_highlighting(
196 r#" 203 r#"
197macro_rules! println { 204macro_rules! println {
198 ($($arg:tt)*) => ({ 205 ($($arg:tt)*) => ({
@@ -218,6 +225,7 @@ fn main() {
218 println!("{argument}", argument = "test"); // => "test" 225 println!("{argument}", argument = "test"); // => "test"
219 println!("{name} {}", 1, name = 2); // => "2 1" 226 println!("{name} {}", 1, name = 2); // => "2 1"
220 println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" 227 println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b"
228 println!("{{{}}}", 2); // => "{2}"
221 println!("Hello {:5}!", "x"); 229 println!("Hello {:5}!", "x");
222 println!("Hello {:1$}!", "x", 5); 230 println!("Hello {:1$}!", "x", 5);
223 println!("Hello {1:0$}!", 5, "x"); 231 println!("Hello {1:0$}!", 5, "x");
@@ -245,15 +253,128 @@ fn main() {
245 253
246 println!(r"Hello, {}!", "world"); 254 println!(r"Hello, {}!", "world");
247 255
256 // escape sequences
257 println!("Hello\nWorld");
258 println!("\u{48}\x65\x6C\x6C\x6F World");
259
248 println!("{\x41}", A = 92); 260 println!("{\x41}", A = 92);
249 println!("{ничоси}", ничоси = 92); 261 println!("{ничоси}", ничоси = 92);
250}"# 262}"#
251 .trim(), 263 .trim(),
264 expect_file!["crates/ra_ide/test_data/highlight_strings.html"],
265 false,
266 );
267}
268
269#[test]
270fn test_unsafe_highlighting() {
271 check_highlighting(
272 r#"
273unsafe fn unsafe_fn() {}
274
275struct HasUnsafeFn;
276
277impl HasUnsafeFn {
278 unsafe fn unsafe_method(&self) {}
279}
280
281fn main() {
282 let x = &5 as *const usize;
283 unsafe {
284 unsafe_fn();
285 HasUnsafeFn.unsafe_method();
286 let y = *(x);
287 let z = -x;
288 }
289}
290"#
291 .trim(),
292 expect_file!["crates/ra_ide/test_data/highlight_unsafe.html"],
293 false,
294 );
295}
296
297#[test]
298fn test_highlight_doctest() {
299 check_highlighting(
300 r#"
301/// ```
302/// let _ = "early doctests should not go boom";
303/// ```
304struct Foo {
305 bar: bool,
306}
307
308impl Foo {
309 pub const bar: bool = true;
310
311 /// Constructs a new `Foo`.
312 ///
313 /// # Examples
314 ///
315 /// ```
316 /// # #![allow(unused_mut)]
317 /// let mut foo: Foo = Foo::new();
318 /// ```
319 pub const fn new() -> Foo {
320 Foo { bar: true }
321 }
322
323 /// `bar` method on `Foo`.
324 ///
325 /// # Examples
326 ///
327 /// ```
328 /// use x::y;
329 ///
330 /// let foo = Foo::new();
331 ///
332 /// // calls bar on foo
333 /// assert!(foo.bar());
334 ///
335 /// let bar = foo.bar || Foo::bar;
336 ///
337 /// /* multi-line
338 /// comment */
339 ///
340 /// let multi_line_string = "Foo
341 /// bar
342 /// ";
343 ///
344 /// ```
345 ///
346 /// ```rust,no_run
347 /// let foobar = Foo::new().bar();
348 /// ```
349 ///
350 /// ```sh
351 /// echo 1
352 /// ```
353 pub fn foo(&self) -> bool {
354 true
355 }
356}
357
358/// ```
359/// noop!(1);
360/// ```
361macro_rules! noop {
362 ($expr:expr) => {
363 $expr
364 }
365}
366"#
367 .trim(),
368 expect_file!["crates/ra_ide/test_data/highlight_doctest.html"],
369 false,
252 ); 370 );
371}
253 372
254 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_strings.html"); 373/// Highlights the code given by the `ra_fixture` argument, renders the
255 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); 374/// result as HTML, and compares it with the HTML file given as `snapshot`.
256 let expected_html = &read_text(&dst_file); 375/// Note that the `snapshot` file is overwritten by the rendered HTML.
257 fs::write(dst_file, &actual_html).unwrap(); 376fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) {
258 assert_eq_text!(expected_html, actual_html); 377 let (analysis, file_id) = single_file(ra_fixture);
378 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
379 expect.assert_eq(actual_html)
259} 380}
diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs
index a341684fd..f716a3861 100644
--- a/crates/ra_ide/src/syntax_tree.rs
+++ b/crates/ra_ide/src/syntax_tree.rs
@@ -104,7 +104,7 @@ fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<St
104mod tests { 104mod tests {
105 use test_utils::assert_eq_text; 105 use test_utils::assert_eq_text;
106 106
107 use crate::mock_analysis::{single_file, single_file_with_range}; 107 use crate::mock_analysis::{analysis_and_range, single_file};
108 108
109 #[test] 109 #[test]
110 fn test_syntax_tree_without_range() { 110 fn test_syntax_tree_without_range() {
@@ -184,7 +184,7 @@ [email protected]
184 184
185 #[test] 185 #[test]
186 fn test_syntax_tree_with_range() { 186 fn test_syntax_tree_with_range() {
187 let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); 187 let (analysis, range) = analysis_and_range(r#"<|>fn foo() {}<|>"#.trim());
188 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); 188 let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap();
189 189
190 assert_eq_text!( 190 assert_eq_text!(
@@ -206,7 +206,7 @@ [email protected]
206 .trim() 206 .trim()
207 ); 207 );
208 208
209 let (analysis, range) = single_file_with_range( 209 let (analysis, range) = analysis_and_range(
210 r#"fn test() { 210 r#"fn test() {
211 <|>assert!(" 211 <|>assert!("
212 fn foo() { 212 fn foo() {
@@ -242,7 +242,7 @@ [email protected]
242 242
243 #[test] 243 #[test]
244 fn test_syntax_tree_inside_string() { 244 fn test_syntax_tree_inside_string() {
245 let (analysis, range) = single_file_with_range( 245 let (analysis, range) = analysis_and_range(
246 r#"fn test() { 246 r#"fn test() {
247 assert!(" 247 assert!("
248<|>fn foo() { 248<|>fn foo() {
@@ -276,7 +276,7 @@ [email protected]
276 ); 276 );
277 277
278 // With a raw string 278 // With a raw string
279 let (analysis, range) = single_file_with_range( 279 let (analysis, range) = analysis_and_range(
280 r###"fn test() { 280 r###"fn test() {
281 assert!(r#" 281 assert!(r#"
282<|>fn foo() { 282<|>fn foo() {
@@ -310,7 +310,7 @@ [email protected]
310 ); 310 );
311 311
312 // With a raw string 312 // With a raw string
313 let (analysis, range) = single_file_with_range( 313 let (analysis, range) = analysis_and_range(
314 r###"fn test() { 314 r###"fn test() {
315 assert!(r<|>#" 315 assert!(r<|>#"
316fn foo() { 316fn foo() {
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 67e2c33a0..83776d2b6 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -17,11 +17,13 @@ mod on_enter;
17 17
18use ra_db::{FilePosition, SourceDatabase}; 18use ra_db::{FilePosition, SourceDatabase};
19use ra_fmt::leading_indent; 19use ra_fmt::leading_indent;
20use ra_ide_db::RootDatabase; 20use ra_ide_db::{source_change::SourceFileEdit, RootDatabase};
21use ra_syntax::{ 21use ra_syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, AstToken}, 23 ast::{self, AstToken},
24 AstNode, SourceFile, TextRange, TextSize, 24 AstNode, SourceFile,
25 SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR},
26 TextRange, TextSize,
25}; 27};
26 28
27use ra_text_edit::TextEdit; 29use ra_text_edit::TextEdit;
@@ -47,8 +49,8 @@ pub(crate) fn on_char_typed(
47 assert!(TRIGGER_CHARS.contains(char_typed)); 49 assert!(TRIGGER_CHARS.contains(char_typed));
48 let file = &db.parse(position.file_id).tree(); 50 let file = &db.parse(position.file_id).tree();
49 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); 51 assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
50 let text_edit = on_char_typed_inner(file, position.offset, char_typed)?; 52 let edit = on_char_typed_inner(file, position.offset, char_typed)?;
51 Some(SourceChange::source_file_edit_from(position.file_id, text_edit)) 53 Some(SourceFileEdit { file_id: position.file_id, edit }.into())
52} 54}
53 55
54fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { 56fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> {
@@ -98,9 +100,12 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
98 }; 100 };
99 let current_indent_len = TextSize::of(current_indent); 101 let current_indent_len = TextSize::of(current_indent);
100 102
103 let parent = whitespace.syntax().parent();
101 // Make sure dot is a part of call chain 104 // Make sure dot is a part of call chain
102 let field_expr = ast::FieldExpr::cast(whitespace.syntax().parent())?; 105 if !matches!(parent.kind(), FIELD_EXPR | METHOD_CALL_EXPR) {
103 let prev_indent = leading_indent(field_expr.syntax())?; 106 return None;
107 }
108 let prev_indent = leading_indent(&parent)?;
104 let target_indent = format!(" {}", prev_indent); 109 let target_indent = format!(" {}", prev_indent);
105 let target_indent_len = TextSize::of(&target_indent); 110 let target_indent_len = TextSize::of(&target_indent);
106 if current_indent_len == target_indent_len { 111 if current_indent_len == target_indent_len {
@@ -143,11 +148,11 @@ mod tests {
143 }) 148 })
144 } 149 }
145 150
146 fn type_char(char_typed: char, before: &str, after: &str) { 151 fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) {
147 let actual = do_type_char(char_typed, before) 152 let actual = do_type_char(char_typed, ra_fixture_before)
148 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); 153 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));
149 154
150 assert_eq_text!(after, &actual); 155 assert_eq_text!(ra_fixture_after, &actual);
151 } 156 }
152 157
153 fn type_char_noop(char_typed: char, before: &str) { 158 fn type_char_noop(char_typed: char, before: &str) {
@@ -249,6 +254,27 @@ fn foo() {
249 } 254 }
250 255
251 #[test] 256 #[test]
257 fn indents_new_chain_call_with_let() {
258 type_char(
259 '.',
260 r#"
261fn main() {
262 let _ = foo
263 <|>
264 bar()
265}
266"#,
267 r#"
268fn main() {
269 let _ = foo
270 .
271 bar()
272}
273"#,
274 );
275 }
276
277 #[test]
252 fn indents_continued_chain_call() { 278 fn indents_continued_chain_call() {
253 type_char( 279 type_char(
254 '.', 280 '.',
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs
index a40d8af9c..2faaa8ff0 100644
--- a/crates/ra_ide/src/typing/on_enter.rs
+++ b/crates/ra_ide/src/typing/on_enter.rs
@@ -75,23 +75,22 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
75 75
76#[cfg(test)] 76#[cfg(test)]
77mod tests { 77mod tests {
78 use test_utils::{assert_eq_text, extract_offset}; 78 use test_utils::assert_eq_text;
79 79
80 use crate::mock_analysis::single_file; 80 use crate::mock_analysis::analysis_and_position;
81 81 use stdx::trim_indent;
82 use super::*;
83 82
84 fn apply_on_enter(before: &str) -> Option<String> { 83 fn apply_on_enter(before: &str) -> Option<String> {
85 let (offset, before) = extract_offset(before); 84 let (analysis, position) = analysis_and_position(&before);
86 let (analysis, file_id) = single_file(&before); 85 let result = analysis.on_enter(position).unwrap()?;
87 let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?;
88 86
89 let mut actual = before.to_string(); 87 let mut actual = analysis.file_text(position.file_id).unwrap().to_string();
90 result.apply(&mut actual); 88 result.apply(&mut actual);
91 Some(actual) 89 Some(actual)
92 } 90 }
93 91
94 fn do_check(ra_fixture_before: &str, ra_fixture_after: &str) { 92 fn do_check(ra_fixture_before: &str, ra_fixture_after: &str) {
93 let ra_fixture_after = &trim_indent(ra_fixture_after);
95 let actual = apply_on_enter(ra_fixture_before).unwrap(); 94 let actual = apply_on_enter(ra_fixture_before).unwrap();
96 assert_eq_text!(ra_fixture_after, &actual); 95 assert_eq_text!(ra_fixture_after, &actual);
97 } 96 }
diff --git a/crates/ra_ide/test_data/highlight_doctest.html b/crates/ra_ide/test_data/highlight_doctest.html
new file mode 100644
index 000000000..1cc17d6d0
--- /dev/null
+++ b/crates/ra_ide/test_data/highlight_doctest.html
@@ -0,0 +1,102 @@
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.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.value_param { color: #DCDCCC; }
28.variable { color: #DCDCCC; }
29.format_specifier { color: #CC696B; }
30.mutable { text-decoration: underline; }
31.escape_sequence { color: #94BFF3; }
32.keyword { color: #F0DFAF; font-weight: bold; }
33.keyword.unsafe { color: #BC8383; font-weight: bold; }
34.control { font-style: italic; }
35
36.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
37</style>
38<pre><code><span class="comment documentation">/// ```</span>
39<span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="punctuation injected">_</span><span class="generic injected"> </span><span class="punctuation injected">=</span><span class="generic 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><span class="comment documentation">/// ```</span>
41<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span>
42 <span class="field declaration">bar</span><span class="punctuation">:</span> <span class="builtin_type">bool</span><span class="punctuation">,</span>
43<span class="punctuation">}</span>
44
45<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
46 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration">bar</span><span class="punctuation">:</span> <span class="builtin_type">bool</span> <span class="punctuation">=</span> <span class="bool_literal">true</span><span class="punctuation">;</span>
47
48 <span class="comment documentation">/// Constructs a new `Foo`.</span>
49 <span class="comment documentation">///</span>
50 <span class="comment documentation">/// # Examples</span>
51 <span class="comment documentation">///</span>
52 <span class="comment documentation">/// ```</span>
53 <span class="comment documentation">/// #</span><span class="generic injected"> </span><span class="punctuation injected">#</span><span class="punctuation injected">!</span><span class="punctuation injected">[</span><span class="function attribute injected">allow</span><span class="punctuation injected">(</span><span class="attribute injected">unused_mut</span><span class="punctuation injected">)</span><span class="punctuation injected">]</span>
54 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="keyword injected">mut</span><span class="generic injected"> </span><span class="variable declaration injected mutable">foo</span><span class="punctuation injected">:</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="generic injected"> </span><span class="punctuation injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="punctuation 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">
55</span> <span class="comment documentation">/// ```</span>
56 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">-&gt;</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
57 <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>
58 <span class="punctuation">}</span>
59
60 <span class="comment documentation">/// `bar` method on `Foo`.</span>
61 <span class="comment documentation">///</span>
62 <span class="comment documentation">/// # Examples</span>
63 <span class="comment documentation">///</span>
64 <span class="comment documentation">/// ```</span>
65 <span class="comment documentation">/// </span><span class="keyword injected">use</span><span class="generic injected"> </span><span class="module injected">x</span><span class="punctuation injected">::</span><span class="module injected">y</span><span class="punctuation injected">;</span>
66 <span class="comment documentation">///</span>
67 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foo</span><span class="generic injected"> </span><span class="punctuation injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="punctuation injected">::</span><span class="function injected">new</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span>
68 <span class="comment documentation">///</span>
69 <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span>
70 <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="punctuation injected">(</span><span class="generic injected">foo</span><span class="punctuation injected">.</span><span class="generic injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span>
71 <span class="comment documentation">///</span>
72 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">bar</span><span class="generic injected"> </span><span class="punctuation injected">=</span><span class="generic injected"> </span><span class="variable injected">foo</span><span class="punctuation injected">.</span><span class="field injected">bar</span><span class="generic injected"> </span><span class="punctuation injected">||</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="punctuation injected">::</span><span class="constant injected">bar</span><span class="punctuation injected">;</span>
73 <span class="comment documentation">///</span>
74 <span class="comment documentation">/// </span><span class="comment injected">/* multi-line
75 </span><span class="comment documentation">/// </span><span class="comment injected"> comment */</span>
76 <span class="comment documentation">///</span>
77 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">multi_line_string</span><span class="generic injected"> </span><span class="punctuation injected">=</span><span class="generic injected"> </span><span class="string_literal injected">"Foo
78 </span><span class="comment documentation">/// </span><span class="string_literal injected"> bar
79 </span><span class="comment documentation">/// </span><span class="string_literal injected"> "</span><span class="punctuation injected">;</span>
80 <span class="comment documentation">///</span>
81 <span class="comment documentation">/// ```</span>
82 <span class="comment documentation">///</span>
83 <span class="comment documentation">/// ```rust,no_run</span>
84 <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="generic injected"> </span><span class="variable declaration injected">foobar</span><span class="generic injected"> </span><span class="punctuation injected">=</span><span class="generic injected"> </span><span class="struct injected">Foo</span><span class="punctuation 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="function injected">bar</span><span class="punctuation injected">(</span><span class="punctuation injected">)</span><span class="punctuation injected">;</span><span class="punctuation injected">
85</span> <span class="comment documentation">/// ```</span>
86 <span class="comment documentation">///</span>
87 <span class="comment documentation">/// ```sh</span>
88 <span class="comment documentation">/// echo 1</span>
89 <span class="comment documentation">/// ```</span>
90 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">-&gt;</span> <span class="builtin_type">bool</span> <span class="punctuation">{</span>
91 <span class="bool_literal">true</span>
92 <span class="punctuation">}</span>
93<span class="punctuation">}</span>
94
95<span class="comment documentation">/// ```</span>
96<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">
97</span><span class="comment documentation">/// ```</span>
98<span class="macro">macro_rules!</span> <span class="macro declaration">noop</span> <span class="punctuation">{</span>
99 <span class="punctuation">(</span><span class="punctuation">$</span>expr<span class="punctuation">:</span>expr<span class="punctuation">)</span> <span class="punctuation">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span>
100 <span class="punctuation">$</span>expr
101 <span class="punctuation">}</span>
102<span class="punctuation">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/test_data/highlight_injection.html b/crates/ra_ide/test_data/highlight_injection.html
new file mode 100644
index 000000000..461cfc437
--- /dev/null
+++ b/crates/ra_ide/test_data/highlight_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.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.value_param { color: #DCDCCC; }
28.variable { color: #DCDCCC; }
29.format_specifier { color: #CC696B; }
30.mutable { text-decoration: underline; }
31.escape_sequence { color: #94BFF3; }
32.keyword { color: #F0DFAF; font-weight: bold; }
33.keyword.unsafe { color: #BC8383; font-weight: bold; }
34.control { font-style: italic; }
35
36.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
37</style>
38<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="punctuation">&</span><span class="builtin_type">str</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
39
40<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="function">fixture</span><span class="punctuation">(</span><span class="string_literal">r#"</span>
42 <span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="punctuation">{</span>
43 <span class="keyword">fn</span> <span class="function declaration">foo</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
44 <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="punctuation">}</span>
46 <span class="punctuation">}</span><span class="string_literal">"#</span>
47 <span class="punctuation">)</span><span class="punctuation">;</span>
48<span class="punctuation">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/test_data/highlight_strings.html b/crates/ra_ide/test_data/highlight_strings.html
new file mode 100644
index 000000000..9f98e73e7
--- /dev/null
+++ b/crates/ra_ide/test_data/highlight_strings.html
@@ -0,0 +1,96 @@
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.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.value_param { color: #DCDCCC; }
28.variable { color: #DCDCCC; }
29.format_specifier { color: #CC696B; }
30.mutable { text-decoration: underline; }
31.escape_sequence { color: #94BFF3; }
32.keyword { color: #F0DFAF; font-weight: bold; }
33.keyword.unsafe { color: #BC8383; font-weight: bold; }
34.control { font-style: italic; }
35
36.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
37</style>
38<pre><code><span class="macro">macro_rules!</span> <span class="macro declaration">println</span> <span class="punctuation">{</span>
39 <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="punctuation">&gt;</span> <span class="punctuation">(</span><span class="punctuation">{</span>
40 <span class="punctuation">$</span><span class="keyword">crate</span><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><span class="keyword">crate</span><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>
41 <span class="punctuation">}</span><span class="punctuation">)</span>
42<span class="punctuation">}</span>
43#[rustc_builtin_macro]
44<span class="macro">macro_rules!</span> <span class="macro declaration">format_args_nl</span> <span class="punctuation">{</span>
45 <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">&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="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="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="punctuation">}</span>
48
49<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="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
51 <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="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="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="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="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="punctuation">=</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="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="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="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="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="punctuation">=</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="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="punctuation">=</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="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="punctuation">=</span><span class="string_literal">"a"</span><span class="punctuation">,</span> b<span class="punctuation">=</span><span class="char_literal">'b'</span><span class="punctuation">,</span> c<span class="punctuation">=</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="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="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="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="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="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="punctuation">=</span> <span class="numeric_literal">5</span><span class="punctuation">)</span><span class="punctuation">;</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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="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="punctuation">=</span> <span class="numeric_literal">5</span><span class="punctuation">,</span> number <span class="punctuation">=</span> <span class="numeric_literal">0.01</span><span class="punctuation">)</span><span class="punctuation">;</span>
82 <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="punctuation">=</span><span class="numeric_literal">1234.56</span><span class="punctuation">)</span><span class="punctuation">;</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 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="punctuation">=</span><span class="string_literal">"1234.56"</span><span class="punctuation">)</span><span class="punctuation">;</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">&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="punctuation">=</span><span class="string_literal">"1234.56"</span><span class="punctuation">)</span><span class="punctuation">;</span>
85 <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="punctuation">(</span><span class="string_literal">"{{ Hello"</span><span class="punctuation">)</span><span class="punctuation">;</span>
87
88 <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
90 <span class="comment">// escape sequences</span>
91 <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="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
94 <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="punctuation">=</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span>
95 <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="punctuation">=</span> <span class="numeric_literal">92</span><span class="punctuation">)</span><span class="punctuation">;</span>
96<span class="punctuation">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
new file mode 100644
index 000000000..88ac91f9a
--- /dev/null
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -0,0 +1,54 @@
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.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.value_param { color: #DCDCCC; }
28.variable { color: #DCDCCC; }
29.format_specifier { color: #CC696B; }
30.mutable { text-decoration: underline; }
31.escape_sequence { color: #94BFF3; }
32.keyword { color: #F0DFAF; font-weight: bold; }
33.keyword.unsafe { color: #BC8383; font-weight: bold; }
34.control { font-style: italic; }
35
36.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
37</style>
38<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
40<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span><span class="punctuation">;</span>
41
42<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> <span class="punctuation">{</span>
43 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">{</span><span class="punctuation">}</span>
44<span class="punctuation">}</span>
45
46<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
47 <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="punctuation">=</span> <span class="punctuation">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> *<span class="keyword">const</span> <span class="builtin_type">usize</span><span class="punctuation">;</span>
48 <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
49 <span class="function unsafe">unsafe_fn</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
50 <span class="struct">HasUnsafeFn</span><span class="punctuation">.</span><span class="function unsafe">unsafe_method</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
51 <span class="keyword">let</span> <span class="variable declaration">y</span> <span class="punctuation">=</span> <span class="operator unsafe">*</span><span class="punctuation">(</span><span class="variable">x</span><span class="punctuation">)</span><span class="punctuation">;</span>
52 <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="punctuation">=</span> <span class="punctuation">-</span><span class="variable">x</span><span class="punctuation">;</span>
53 <span class="punctuation">}</span>
54<span class="punctuation">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/test_data/highlighting.html b/crates/ra_ide/test_data/highlighting.html
new file mode 100644
index 000000000..767e82f9d
--- /dev/null
+++ b/crates/ra_ide/test_data/highlighting.html
@@ -0,0 +1,128 @@
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.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.value_param { color: #DCDCCC; }
28.variable { color: #DCDCCC; }
29.format_specifier { color: #CC696B; }
30.mutable { text-decoration: underline; }
31.escape_sequence { color: #94BFF3; }
32.keyword { color: #F0DFAF; font-weight: bold; }
33.keyword.unsafe { color: #BC8383; font-weight: bold; }
34.control { font-style: italic; }
35
36.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
37</style>
38<pre><code><span class="punctuation">#</span><span class="punctuation">[</span><span class="function attribute">derive</span><span class="punctuation">(</span><span class="attribute">Clone</span><span class="punctuation">,</span><span class="attribute"> Debug</span><span class="punctuation">)</span><span class="punctuation">]</span>
39<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="punctuation">{</span>
40 <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>
41 <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>
42<span class="punctuation">}</span>
43
44<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="punctuation">{</span>
45 <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">-&gt;</span> <span class="builtin_type">i32</span><span class="punctuation">;</span>
46<span class="punctuation">}</span>
47
48<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
49 <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">&</span><span class="self_keyword">self</span><span class="punctuation">)</span> <span class="punctuation">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
50 <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
51 <span class="punctuation">}</span>
52<span class="punctuation">}</span>
53
54<span class="keyword">impl</span> <span class="struct">Foo</span> <span class="punctuation">{</span>
55 <span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">(</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">-&gt;</span> <span class="builtin_type">i32</span> <span class="punctuation">{</span>
56 <span class="self_keyword">self</span><span class="punctuation">.</span><span class="field">x</span>
57 <span class="punctuation">}</span>
58
59 <span class="keyword">fn</span> <span class="function declaration">qux</span><span class="punctuation">(</span><span class="punctuation">&</span><span class="keyword">mut</span> <span class="self_keyword mutable">self</span><span class="punctuation">)</span> <span class="punctuation">{</span>
60 <span class="self_keyword mutable">self</span><span class="punctuation">.</span><span class="field">x</span> <span class="punctuation">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
61 <span class="punctuation">}</span>
62<span class="punctuation">}</span>
63
64<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable">STATIC_MUT</span><span class="punctuation">:</span> <span class="builtin_type">i32</span> <span class="punctuation">=</span> <span class="numeric_literal">0</span><span class="punctuation">;</span>
65
66<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="punctuation">-&gt;</span> <span class="type_param">T</span> <span class="punctuation">{</span>
67 <span class="function">foo</span><span class="punctuation">::</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>
68<span class="punctuation">}</span>
69
70<span class="macro">macro_rules!</span> <span class="macro declaration">def_fn</span> <span class="punctuation">{</span>
71 <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="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>
72<span class="punctuation">}</span>
73
74<span class="macro">def_fn!</span> <span class="punctuation">{</span>
75 <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">-</span><span class="punctuation">&gt;</span> <span class="builtin_type">u32</span> <span class="punctuation">{</span>
76 <span class="numeric_literal">100</span>
77 <span class="punctuation">}</span>
78<span class="punctuation">}</span>
79
80<span class="macro">macro_rules!</span> <span class="macro declaration">noop</span> <span class="punctuation">{</span>
81 <span class="punctuation">(</span><span class="punctuation">$</span>expr<span class="punctuation">:</span>expr<span class="punctuation">)</span> <span class="punctuation">=</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span>
82 <span class="punctuation">$</span>expr
83 <span class="punctuation">}</span>
84<span class="punctuation">}</span>
85
86<span class="comment">// comment</span>
87<span class="keyword">fn</span> <span class="function declaration">main</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="punctuation">{</span>
88 <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>
89
90 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> <span class="punctuation">=</span> <span class="unresolved_reference">Vec</span><span class="punctuation">::</span><span class="unresolved_reference">new</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
91 <span class="keyword control">if</span> <span class="bool_literal">true</span> <span class="punctuation">{</span>
92 <span class="keyword">let</span> <span class="variable declaration">x</span> <span class="punctuation">=</span> <span class="numeric_literal">92</span><span class="punctuation">;</span>
93 <span class="variable mutable">vec</span><span class="punctuation">.</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>
94 <span class="punctuation">}</span>
95 <span class="keyword unsafe">unsafe</span> <span class="punctuation">{</span>
96 <span class="variable mutable">vec</span><span class="punctuation">.</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>
97 <span class="static mutable">STATIC_MUT</span> <span class="punctuation">=</span> <span class="numeric_literal">1</span><span class="punctuation">;</span>
98 <span class="punctuation">}</span>
99
100 <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>
101 <span class="comment">// Do nothing</span>
102 <span class="punctuation">}</span>
103
104 <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>
105
106 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> <span class="punctuation">=</span> <span class="numeric_literal">42</span><span class="punctuation">;</span>
107 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> <span class="punctuation">=</span> <span class="punctuation">&</span><span class="keyword">mut</span> <span class="variable mutable">x</span><span class="punctuation">;</span>
108 <span class="keyword">let</span> <span class="variable declaration">z</span> <span class="punctuation">=</span> <span class="punctuation">&</span><span class="variable mutable">y</span><span class="punctuation">;</span>
109
110 <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="punctuation">=</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>
111
112 <span class="variable">y</span><span class="punctuation">;</span>
113<span class="punctuation">}</span>
114
115<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>
116 <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>
117 <span class="enum_variant declaration">None</span><span class="punctuation">,</span>
118<span class="punctuation">}</span>
119<span class="keyword">use</span> <span class="enum">Option</span><span class="punctuation">::</span>*<span class="punctuation">;</span>
120
121<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>
122 <span class="keyword">fn</span> <span class="function declaration">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="punctuation">-&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>
123 <span class="keyword control">match</span> <span class="value_param">other</span> <span class="punctuation">{</span>
124 <span class="enum_variant">None</span> <span class="punctuation">=&gt;</span> <span class="macro">unimplemented!</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">,</span>
125 <span class="variable declaration">Nope</span> <span class="punctuation">=&gt;</span> <span class="variable">Nope</span><span class="punctuation">,</span>
126 <span class="punctuation">}</span>
127 <span class="punctuation">}</span>
128<span class="punctuation">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/test_data/rainbow_highlighting.html b/crates/ra_ide/test_data/rainbow_highlighting.html
new file mode 100644
index 000000000..2fed04a44
--- /dev/null
+++ b/crates/ra_ide/test_data/rainbow_highlighting.html
@@ -0,0 +1,49 @@
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.comment { color: #7F9F7F; }
8.documentation { color: #629755; }
9.injected { opacity: 0.65 ; }
10.struct, .enum { color: #7CB8BB; }
11.enum_variant { color: #BDE0F3; }
12.string_literal { color: #CC9393; }
13.field { color: #94BFF3; }
14.function { color: #93E0E3; }
15.function.unsafe { color: #BC8383; }
16.operator.unsafe { color: #BC8383; }
17.parameter { color: #94BFF3; }
18.text { color: #DCDCCC; }
19.type { color: #7CB8BB; }
20.builtin_type { color: #8CD0D3; }
21.type_param { color: #DFAF8F; }
22.attribute { color: #94BFF3; }
23.numeric_literal { color: #BFEBBF; }
24.bool_literal { color: #BFE6EB; }
25.macro { color: #94BFF3; }
26.module { color: #AFD8AF; }
27.value_param { color: #DCDCCC; }
28.variable { color: #DCDCCC; }
29.format_specifier { color: #CC696B; }
30.mutable { text-decoration: underline; }
31.escape_sequence { color: #94BFF3; }
32.keyword { color: #F0DFAF; font-weight: bold; }
33.keyword.unsafe { color: #BC8383; font-weight: bold; }
34.control { font-style: italic; }
35
36.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
37</style>
38<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 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> <span class="punctuation">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span>
40 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2705725358298919760" style="color: hsl(17,51%,74%);">x</span> <span class="punctuation">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span><span class="punctuation">.</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="3365759661443752373" style="color: hsl(127,76%,66%);">y</span> <span class="punctuation">=</span> <span class="variable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
42
43 <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span> <span class="punctuation">=</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="6717528807933952652" style="color: hsl(85,49%,84%);">y</span> <span class="punctuation">=</span> <span class="variable" data-binding-hash="794745962933817518" style="color: hsl(19,74%,76%);">x</span><span class="punctuation">.</span><span class="unresolved_reference">to_string</span><span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">;</span>
45<span class="punctuation">}</span>
46
47<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">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable" data-binding-hash="8121853618659664005" style="color: hsl(261,57%,61%);">hello</span> <span class="punctuation">=</span> <span class="string_literal">"hello"</span><span class="punctuation">;</span>
49<span class="punctuation">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml
index c3921bd40..bcddad60c 100644
--- a/crates/ra_ide_db/Cargo.toml
+++ b/crates/ra_ide_db/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_ide_db" 3name = "ra_ide_db"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs
index 8446ef88e..a1bb3043b 100644
--- a/crates/ra_ide_db/src/change.rs
+++ b/crates/ra_ide_db/src/change.rs
@@ -5,45 +5,29 @@ use std::{fmt, sync::Arc, time};
5 5
6use ra_db::{ 6use ra_db::{
7 salsa::{Database, Durability, SweepStrategy}, 7 salsa::{Database, Durability, SweepStrategy},
8 CrateGraph, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot, 8 CrateGraph, FileId, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId,
9 SourceRootId,
10}; 9};
11use ra_prof::{memory_usage, profile, Bytes}; 10use ra_prof::{memory_usage, profile, Bytes};
12use ra_syntax::SourceFile; 11use rustc_hash::FxHashSet;
13#[cfg(not(feature = "wasm"))] 12
14use rayon::prelude::*; 13use crate::{symbol_index::SymbolsDatabase, RootDatabase};
15use rustc_hash::FxHashMap;
16
17use crate::{
18 symbol_index::{SymbolIndex, SymbolsDatabase},
19 DebugData, RootDatabase,
20};
21 14
22#[derive(Default)] 15#[derive(Default)]
23pub struct AnalysisChange { 16pub struct AnalysisChange {
24 new_roots: Vec<(SourceRootId, bool)>, 17 roots: Option<Vec<SourceRoot>>,
25 roots_changed: FxHashMap<SourceRootId, RootChange>, 18 files_changed: Vec<(FileId, Option<Arc<String>>)>,
26 files_changed: Vec<(FileId, Arc<String>)>,
27 libraries_added: Vec<LibraryData>,
28 crate_graph: Option<CrateGraph>, 19 crate_graph: Option<CrateGraph>,
29 debug_data: DebugData,
30} 20}
31 21
32impl fmt::Debug for AnalysisChange { 22impl fmt::Debug for AnalysisChange {
33 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 23 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
34 let mut d = fmt.debug_struct("AnalysisChange"); 24 let mut d = fmt.debug_struct("AnalysisChange");
35 if !self.new_roots.is_empty() { 25 if let Some(roots) = &self.roots {
36 d.field("new_roots", &self.new_roots); 26 d.field("roots", roots);
37 }
38 if !self.roots_changed.is_empty() {
39 d.field("roots_changed", &self.roots_changed);
40 } 27 }
41 if !self.files_changed.is_empty() { 28 if !self.files_changed.is_empty() {
42 d.field("files_changed", &self.files_changed.len()); 29 d.field("files_changed", &self.files_changed.len());
43 } 30 }
44 if !self.libraries_added.is_empty() {
45 d.field("libraries_added", &self.libraries_added.len());
46 }
47 if self.crate_graph.is_some() { 31 if self.crate_graph.is_some() {
48 d.field("crate_graph", &self.crate_graph); 32 d.field("crate_graph", &self.crate_graph);
49 } 33 }
@@ -56,54 +40,30 @@ impl AnalysisChange {
56 AnalysisChange::default() 40 AnalysisChange::default()
57 } 41 }
58 42
59 pub fn add_root(&mut self, root_id: SourceRootId, is_local: bool) { 43 pub fn set_roots(&mut self, roots: Vec<SourceRoot>) {
60 self.new_roots.push((root_id, is_local)); 44 self.roots = Some(roots);
61 } 45 }
62 46
63 pub fn add_file( 47 pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<String>>) {
64 &mut self,
65 root_id: SourceRootId,
66 file_id: FileId,
67 path: RelativePathBuf,
68 text: Arc<String>,
69 ) {
70 let file = AddFile { file_id, path, text };
71 self.roots_changed.entry(root_id).or_default().added.push(file);
72 }
73
74 pub fn change_file(&mut self, file_id: FileId, new_text: Arc<String>) {
75 self.files_changed.push((file_id, new_text)) 48 self.files_changed.push((file_id, new_text))
76 } 49 }
77 50
78 pub fn remove_file(&mut self, root_id: SourceRootId, file_id: FileId, path: RelativePathBuf) {
79 let file = RemoveFile { file_id, path };
80 self.roots_changed.entry(root_id).or_default().removed.push(file);
81 }
82
83 pub fn add_library(&mut self, data: LibraryData) {
84 self.libraries_added.push(data)
85 }
86
87 pub fn set_crate_graph(&mut self, graph: CrateGraph) { 51 pub fn set_crate_graph(&mut self, graph: CrateGraph) {
88 self.crate_graph = Some(graph); 52 self.crate_graph = Some(graph);
89 } 53 }
90
91 pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) {
92 self.debug_data.root_paths.insert(source_root_id, path);
93 }
94} 54}
95 55
96#[derive(Debug)] 56#[derive(Debug)]
97struct AddFile { 57struct AddFile {
98 file_id: FileId, 58 file_id: FileId,
99 path: RelativePathBuf, 59 path: String,
100 text: Arc<String>, 60 text: Arc<String>,
101} 61}
102 62
103#[derive(Debug)] 63#[derive(Debug)]
104struct RemoveFile { 64struct RemoveFile {
105 file_id: FileId, 65 file_id: FileId,
106 path: RelativePathBuf, 66 path: String,
107} 67}
108 68
109#[derive(Default)] 69#[derive(Default)]
@@ -121,47 +81,6 @@ impl fmt::Debug for RootChange {
121 } 81 }
122} 82}
123 83
124pub struct LibraryData {
125 root_id: SourceRootId,
126 root_change: RootChange,
127 symbol_index: SymbolIndex,
128}
129
130impl fmt::Debug for LibraryData {
131 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
132 f.debug_struct("LibraryData")
133 .field("root_id", &self.root_id)
134 .field("root_change", &self.root_change)
135 .field("n_symbols", &self.symbol_index.len())
136 .finish()
137 }
138}
139
140impl LibraryData {
141 pub fn prepare(
142 root_id: SourceRootId,
143 files: Vec<(FileId, RelativePathBuf, Arc<String>)>,
144 ) -> LibraryData {
145 let _p = profile("LibraryData::prepare");
146
147 #[cfg(not(feature = "wasm"))]
148 let iter = files.par_iter();
149 #[cfg(feature = "wasm")]
150 let iter = files.iter();
151
152 let symbol_index = SymbolIndex::for_files(iter.map(|(file_id, _, text)| {
153 let parse = SourceFile::parse(text);
154 (*file_id, parse)
155 }));
156 let mut root_change = RootChange::default();
157 root_change.added = files
158 .into_iter()
159 .map(|(file_id, path, text)| AddFile { file_id, path, text })
160 .collect();
161 LibraryData { root_id, root_change, symbol_index }
162 }
163}
164
165const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); 84const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100);
166 85
167impl RootDatabase { 86impl RootDatabase {
@@ -174,72 +93,37 @@ impl RootDatabase {
174 let _p = profile("RootDatabase::apply_change"); 93 let _p = profile("RootDatabase::apply_change");
175 self.request_cancellation(); 94 self.request_cancellation();
176 log::info!("apply_change {:?}", change); 95 log::info!("apply_change {:?}", change);
177 if !change.new_roots.is_empty() { 96 if let Some(roots) = change.roots {
178 let mut local_roots = Vec::clone(&self.local_roots()); 97 let mut local_roots = FxHashSet::default();
179 for (root_id, is_local) in change.new_roots { 98 let mut library_roots = FxHashSet::default();
180 let root = 99 for (idx, root) in roots.into_iter().enumerate() {
181 if is_local { SourceRoot::new_local() } else { SourceRoot::new_library() }; 100 let root_id = SourceRootId(idx as u32);
182 let durability = durability(&root); 101 let durability = durability(&root);
183 self.set_source_root_with_durability(root_id, Arc::new(root), durability); 102 if root.is_library {
184 if is_local { 103 library_roots.insert(root_id);
185 local_roots.push(root_id); 104 } else {
105 local_roots.insert(root_id);
186 } 106 }
107 for file_id in root.iter() {
108 self.set_file_source_root_with_durability(file_id, root_id, durability);
109 }
110 self.set_source_root_with_durability(root_id, Arc::new(root), durability);
187 } 111 }
188 self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); 112 self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
113 self.set_library_roots_with_durability(Arc::new(library_roots), Durability::HIGH);
189 } 114 }
190 115
191 for (root_id, root_change) in change.roots_changed {
192 self.apply_root_change(root_id, root_change);
193 }
194 for (file_id, text) in change.files_changed { 116 for (file_id, text) in change.files_changed {
195 let source_root_id = self.file_source_root(file_id); 117 let source_root_id = self.file_source_root(file_id);
196 let source_root = self.source_root(source_root_id); 118 let source_root = self.source_root(source_root_id);
197 let durability = durability(&source_root); 119 let durability = durability(&source_root);
120 // XXX: can't actually remove the file, just reset the text
121 let text = text.unwrap_or_default();
198 self.set_file_text_with_durability(file_id, text, durability) 122 self.set_file_text_with_durability(file_id, text, durability)
199 } 123 }
200 if !change.libraries_added.is_empty() {
201 let mut libraries = Vec::clone(&self.library_roots());
202 for library in change.libraries_added {
203 libraries.push(library.root_id);
204 self.set_source_root_with_durability(
205 library.root_id,
206 Arc::new(SourceRoot::new_library()),
207 Durability::HIGH,
208 );
209 self.set_library_symbols_with_durability(
210 library.root_id,
211 Arc::new(library.symbol_index),
212 Durability::HIGH,
213 );
214 self.apply_root_change(library.root_id, library.root_change);
215 }
216 self.set_library_roots_with_durability(Arc::new(libraries), Durability::HIGH);
217 }
218 if let Some(crate_graph) = change.crate_graph { 124 if let Some(crate_graph) = change.crate_graph {
219 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) 125 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
220 } 126 }
221
222 Arc::make_mut(&mut self.debug_data).merge(change.debug_data)
223 }
224
225 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {
226 let mut source_root = SourceRoot::clone(&self.source_root(root_id));
227 let durability = durability(&source_root);
228 for add_file in root_change.added {
229 self.set_file_text_with_durability(add_file.file_id, add_file.text, durability);
230 self.set_file_relative_path_with_durability(
231 add_file.file_id,
232 add_file.path.clone(),
233 durability,
234 );
235 self.set_file_source_root_with_durability(add_file.file_id, root_id, durability);
236 source_root.insert_file(add_file.path, add_file.file_id);
237 }
238 for remove_file in root_change.removed {
239 self.set_file_text_with_durability(remove_file.file_id, Default::default(), durability);
240 source_root.remove_file(&remove_file.path);
241 }
242 self.set_source_root_with_durability(root_id, Arc::new(source_root), durability);
243 } 127 }
244 128
245 pub fn maybe_collect_garbage(&mut self) { 129 pub fn maybe_collect_garbage(&mut self) {
@@ -262,37 +146,46 @@ impl RootDatabase {
262 146
263 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); 147 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
264 148
265 self.query(ra_db::ParseQuery).sweep(sweep); 149 ra_db::ParseQuery.in_db(self).sweep(sweep);
266 self.query(hir::db::ParseMacroQuery).sweep(sweep); 150 hir::db::ParseMacroQuery.in_db(self).sweep(sweep);
267 151
268 // Macros do take significant space, but less then the syntax trees 152 // Macros do take significant space, but less then the syntax trees
269 // self.query(hir::db::MacroDefQuery).sweep(sweep); 153 // self.query(hir::db::MacroDefQuery).sweep(sweep);
270 // self.query(hir::db::MacroArgQuery).sweep(sweep); 154 // self.query(hir::db::MacroArgQuery).sweep(sweep);
271 // self.query(hir::db::MacroExpandQuery).sweep(sweep); 155 // self.query(hir::db::MacroExpandQuery).sweep(sweep);
272 156
273 self.query(hir::db::AstIdMapQuery).sweep(sweep); 157 hir::db::AstIdMapQuery.in_db(self).sweep(sweep);
274 158
275 self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep); 159 hir::db::BodyWithSourceMapQuery.in_db(self).sweep(sweep);
276 160
277 self.query(hir::db::ExprScopesQuery).sweep(sweep); 161 hir::db::ExprScopesQuery.in_db(self).sweep(sweep);
278 self.query(hir::db::InferQueryQuery).sweep(sweep); 162 hir::db::InferQueryQuery.in_db(self).sweep(sweep);
279 self.query(hir::db::BodyQuery).sweep(sweep); 163 hir::db::BodyQuery.in_db(self).sweep(sweep);
280 } 164 }
281 165
166 // Feature: Memory Usage
167 //
168 // Clears rust-analyzer's internal database and prints memory usage statistics.
169 //
170 // |===
171 // | Editor | Action Name
172 //
173 // | VS Code | **Rust Analyzer: Memory Usage (Clears Database)**
174 // |===
282 pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> { 175 pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
283 let mut acc: Vec<(String, Bytes)> = vec![]; 176 let mut acc: Vec<(String, Bytes)> = vec![];
284 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); 177 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
285 macro_rules! sweep_each_query { 178 macro_rules! sweep_each_query {
286 ($($q:path)*) => {$( 179 ($($q:path)*) => {$(
287 let before = memory_usage().allocated; 180 let before = memory_usage().allocated;
288 self.query($q).sweep(sweep); 181 $q.in_db(self).sweep(sweep);
289 let after = memory_usage().allocated; 182 let after = memory_usage().allocated;
290 let q: $q = Default::default(); 183 let q: $q = Default::default();
291 let name = format!("{:?}", q); 184 let name = format!("{:?}", q);
292 acc.push((name, before - after)); 185 acc.push((name, before - after));
293 186
294 let before = memory_usage().allocated; 187 let before = memory_usage().allocated;
295 self.query($q).sweep(sweep.discard_everything()); 188 $q.in_db(self).sweep(sweep.discard_everything());
296 let after = memory_usage().allocated; 189 let after = memory_usage().allocated;
297 let q: $q = Default::default(); 190 let q: $q = Default::default();
298 let name = format!("{:?} (deps)", q); 191 let name = format!("{:?} (deps)", q);
@@ -306,15 +199,13 @@ impl RootDatabase {
306 199
307 // AstDatabase 200 // AstDatabase
308 hir::db::AstIdMapQuery 201 hir::db::AstIdMapQuery
309 hir::db::InternMacroQuery
310 hir::db::MacroArgQuery 202 hir::db::MacroArgQuery
311 hir::db::MacroDefQuery 203 hir::db::MacroDefQuery
312 hir::db::ParseMacroQuery 204 hir::db::ParseMacroQuery
313 hir::db::MacroExpandQuery 205 hir::db::MacroExpandQuery
314 hir::db::InternEagerExpansionQuery
315 206
316 // DefDatabase 207 // DefDatabase
317 hir::db::RawItemsQuery 208 hir::db::ItemTreeQuery
318 hir::db::CrateDefMapQueryQuery 209 hir::db::CrateDefMapQueryQuery
319 hir::db::StructDataQuery 210 hir::db::StructDataQuery
320 hir::db::UnionDataQuery 211 hir::db::UnionDataQuery
@@ -334,17 +225,7 @@ impl RootDatabase {
334 hir::db::CrateLangItemsQuery 225 hir::db::CrateLangItemsQuery
335 hir::db::LangItemQuery 226 hir::db::LangItemQuery
336 hir::db::DocumentationQuery 227 hir::db::DocumentationQuery
337 228 hir::db::ImportMapQuery
338 // InternDatabase
339 hir::db::InternFunctionQuery
340 hir::db::InternStructQuery
341 hir::db::InternUnionQuery
342 hir::db::InternEnumQuery
343 hir::db::InternConstQuery
344 hir::db::InternStaticQuery
345 hir::db::InternTraitQuery
346 hir::db::InternTypeAliasQuery
347 hir::db::InternImplQuery
348 229
349 // HirDatabase 230 // HirDatabase
350 hir::db::InferQueryQuery 231 hir::db::InferQueryQuery
@@ -357,18 +238,16 @@ impl RootDatabase {
357 hir::db::GenericPredicatesForParamQuery 238 hir::db::GenericPredicatesForParamQuery
358 hir::db::GenericPredicatesQuery 239 hir::db::GenericPredicatesQuery
359 hir::db::GenericDefaultsQuery 240 hir::db::GenericDefaultsQuery
360 hir::db::ImplsInCrateQuery 241 hir::db::InherentImplsInCrateQuery
361 hir::db::ImplsForTraitQuery 242 hir::db::TraitImplsInCrateQuery
362 hir::db::InternTypeCtorQuery 243 hir::db::TraitImplsInDepsQuery
363 hir::db::InternTypeParamIdQuery
364 hir::db::InternChalkImplQuery
365 hir::db::InternAssocTyValueQuery
366 hir::db::AssociatedTyDataQuery 244 hir::db::AssociatedTyDataQuery
367 hir::db::TraitDatumQuery 245 hir::db::TraitDatumQuery
368 hir::db::StructDatumQuery 246 hir::db::StructDatumQuery
369 hir::db::ImplDatumQuery 247 hir::db::ImplDatumQuery
370 hir::db::AssociatedTyValueQuery 248 hir::db::AssociatedTyValueQuery
371 hir::db::TraitSolveQuery 249 hir::db::TraitSolveQuery
250 hir::db::ReturnTypeImplTraitsQuery
372 251
373 // SymbolsDatabase 252 // SymbolsDatabase
374 crate::symbol_index::FileSymbolsQuery 253 crate::symbol_index::FileSymbolsQuery
@@ -376,6 +255,33 @@ impl RootDatabase {
376 // LineIndexDatabase 255 // LineIndexDatabase
377 crate::LineIndexQuery 256 crate::LineIndexQuery
378 ]; 257 ];
258
259 // To collect interned data, we need to bump the revision counter by performing a synthetic
260 // write.
261 // We do this after collecting the non-interned queries to correctly attribute memory used
262 // by interned data.
263 self.salsa_runtime_mut().synthetic_write(Durability::HIGH);
264
265 sweep_each_query![
266 // AstDatabase
267 hir::db::InternMacroQuery
268 hir::db::InternEagerExpansionQuery
269
270 // InternDatabase
271 hir::db::InternFunctionQuery
272 hir::db::InternStructQuery
273 hir::db::InternUnionQuery
274 hir::db::InternEnumQuery
275 hir::db::InternConstQuery
276 hir::db::InternStaticQuery
277 hir::db::InternTraitQuery
278 hir::db::InternTypeAliasQuery
279 hir::db::InternImplQuery
280
281 // HirDatabase
282 hir::db::InternTypeParamIdQuery
283 ];
284
379 acc.sort_by_key(|it| std::cmp::Reverse(it.1)); 285 acc.sort_by_key(|it| std::cmp::Reverse(it.1));
380 acc 286 acc
381 } 287 }
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index 8b06cbfc5..bcaabca92 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -18,7 +18,7 @@ use ra_syntax::{
18use crate::RootDatabase; 18use crate::RootDatabase;
19 19
20// FIXME: a more precise name would probably be `Symbol`? 20// FIXME: a more precise name would probably be `Symbol`?
21#[derive(Debug, PartialEq, Eq)] 21#[derive(Debug, PartialEq, Eq, Copy, Clone)]
22pub enum Definition { 22pub enum Definition {
23 Macro(MacroDef), 23 Macro(MacroDef),
24 Field(Field), 24 Field(Field),
@@ -78,10 +78,15 @@ impl Definition {
78 } 78 }
79} 79}
80 80
81#[derive(Debug)]
81pub enum NameClass { 82pub enum NameClass {
82 Definition(Definition), 83 Definition(Definition),
83 /// `None` in `if let None = Some(82) {}` 84 /// `None` in `if let None = Some(82) {}`
84 ConstReference(Definition), 85 ConstReference(Definition),
86 FieldShorthand {
87 local: Local,
88 field: Definition,
89 },
85} 90}
86 91
87impl NameClass { 92impl NameClass {
@@ -89,12 +94,14 @@ impl NameClass {
89 match self { 94 match self {
90 NameClass::Definition(it) => Some(it), 95 NameClass::Definition(it) => Some(it),
91 NameClass::ConstReference(_) => None, 96 NameClass::ConstReference(_) => None,
97 NameClass::FieldShorthand { local, field: _ } => Some(Definition::Local(local)),
92 } 98 }
93 } 99 }
94 100
95 pub fn definition(self) -> Definition { 101 pub fn definition(self) -> Definition {
96 match self { 102 match self {
97 NameClass::Definition(it) | NameClass::ConstReference(it) => it, 103 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
104 NameClass::FieldShorthand { local: _, field } => field,
98 } 105 }
99 } 106 }
100} 107}
@@ -102,18 +109,14 @@ impl NameClass {
102pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> { 109pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
103 let _p = profile("classify_name"); 110 let _p = profile("classify_name");
104 111
105 if let Some(bind_pat) = name.syntax().parent().and_then(ast::BindPat::cast) { 112 let parent = name.syntax().parent()?;
113
114 if let Some(bind_pat) = ast::BindPat::cast(parent.clone()) {
106 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) { 115 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
107 return Some(NameClass::ConstReference(Definition::ModuleDef(def))); 116 return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
108 } 117 }
109 } 118 }
110 119
111 classify_name_inner(sema, name).map(NameClass::Definition)
112}
113
114fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<Definition> {
115 let parent = name.syntax().parent()?;
116
117 match_ast! { 120 match_ast! {
118 match parent { 121 match parent {
119 ast::Alias(it) => { 122 ast::Alias(it) => {
@@ -123,63 +126,73 @@ fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Opti
123 let name_ref = path_segment.name_ref()?; 126 let name_ref = path_segment.name_ref()?;
124 let name_ref_class = classify_name_ref(sema, &name_ref)?; 127 let name_ref_class = classify_name_ref(sema, &name_ref)?;
125 128
126 Some(name_ref_class.definition()) 129 Some(NameClass::Definition(name_ref_class.definition()))
127 }, 130 },
128 ast::BindPat(it) => { 131 ast::BindPat(it) => {
129 let local = sema.to_def(&it)?; 132 let local = sema.to_def(&it)?;
130 Some(Definition::Local(local)) 133
134 if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordFieldPat::cast) {
135 if record_field_pat.name_ref().is_none() {
136 if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
137 let field = Definition::Field(field);
138 return Some(NameClass::FieldShorthand { local, field });
139 }
140 }
141 }
142
143 Some(NameClass::Definition(Definition::Local(local)))
131 }, 144 },
132 ast::RecordFieldDef(it) => { 145 ast::RecordFieldDef(it) => {
133 let field: hir::Field = sema.to_def(&it)?; 146 let field: hir::Field = sema.to_def(&it)?;
134 Some(Definition::Field(field)) 147 Some(NameClass::Definition(Definition::Field(field)))
135 }, 148 },
136 ast::Module(it) => { 149 ast::Module(it) => {
137 let def = sema.to_def(&it)?; 150 let def = sema.to_def(&it)?;
138 Some(Definition::ModuleDef(def.into())) 151 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
139 }, 152 },
140 ast::StructDef(it) => { 153 ast::StructDef(it) => {
141 let def: hir::Struct = sema.to_def(&it)?; 154 let def: hir::Struct = sema.to_def(&it)?;
142 Some(Definition::ModuleDef(def.into())) 155 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
143 }, 156 },
144 ast::UnionDef(it) => { 157 ast::UnionDef(it) => {
145 let def: hir::Union = sema.to_def(&it)?; 158 let def: hir::Union = sema.to_def(&it)?;
146 Some(Definition::ModuleDef(def.into())) 159 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
147 }, 160 },
148 ast::EnumDef(it) => { 161 ast::EnumDef(it) => {
149 let def: hir::Enum = sema.to_def(&it)?; 162 let def: hir::Enum = sema.to_def(&it)?;
150 Some(Definition::ModuleDef(def.into())) 163 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
151 }, 164 },
152 ast::TraitDef(it) => { 165 ast::TraitDef(it) => {
153 let def: hir::Trait = sema.to_def(&it)?; 166 let def: hir::Trait = sema.to_def(&it)?;
154 Some(Definition::ModuleDef(def.into())) 167 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
155 }, 168 },
156 ast::StaticDef(it) => { 169 ast::StaticDef(it) => {
157 let def: hir::Static = sema.to_def(&it)?; 170 let def: hir::Static = sema.to_def(&it)?;
158 Some(Definition::ModuleDef(def.into())) 171 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
159 }, 172 },
160 ast::EnumVariant(it) => { 173 ast::EnumVariant(it) => {
161 let def: hir::EnumVariant = sema.to_def(&it)?; 174 let def: hir::EnumVariant = sema.to_def(&it)?;
162 Some(Definition::ModuleDef(def.into())) 175 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
163 }, 176 },
164 ast::FnDef(it) => { 177 ast::FnDef(it) => {
165 let def: hir::Function = sema.to_def(&it)?; 178 let def: hir::Function = sema.to_def(&it)?;
166 Some(Definition::ModuleDef(def.into())) 179 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
167 }, 180 },
168 ast::ConstDef(it) => { 181 ast::ConstDef(it) => {
169 let def: hir::Const = sema.to_def(&it)?; 182 let def: hir::Const = sema.to_def(&it)?;
170 Some(Definition::ModuleDef(def.into())) 183 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
171 }, 184 },
172 ast::TypeAliasDef(it) => { 185 ast::TypeAliasDef(it) => {
173 let def: hir::TypeAlias = sema.to_def(&it)?; 186 let def: hir::TypeAlias = sema.to_def(&it)?;
174 Some(Definition::ModuleDef(def.into())) 187 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
175 }, 188 },
176 ast::MacroCall(it) => { 189 ast::MacroCall(it) => {
177 let def = sema.to_def(&it)?; 190 let def = sema.to_def(&it)?;
178 Some(Definition::Macro(def)) 191 Some(NameClass::Definition(Definition::Macro(def)))
179 }, 192 },
180 ast::TypeParam(it) => { 193 ast::TypeParam(it) => {
181 let def = sema.to_def(&it)?; 194 let def = sema.to_def(&it)?;
182 Some(Definition::TypeParam(def)) 195 Some(NameClass::Definition(Definition::TypeParam(def)))
183 }, 196 },
184 _ => None, 197 _ => None,
185 } 198 }
@@ -242,8 +255,14 @@ pub fn classify_name_ref(
242 } 255 }
243 256
244 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) { 257 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
245 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) { 258 if let Some(path) = macro_call.path() {
246 return Some(NameRefClass::Definition(Definition::Macro(macro_def))); 259 if path.qualifier().is_none() {
260 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
261 // paths are handled below (allowing `log<|>::info!` to resolve to the log crate).
262 if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
263 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
264 }
265 }
247 } 266 }
248 } 267 }
249 268
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs
index bf0d8db60..1fba71ff8 100644
--- a/crates/ra_ide_db/src/imports_locator.rs
+++ b/crates/ra_ide_db/src/imports_locator.rs
@@ -1,7 +1,7 @@
1//! This module contains an import search funcionality that is provided to the ra_assists module. 1//! This module contains an import search funcionality that is provided to the ra_assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.
3 3
4use hir::{MacroDef, ModuleDef, Semantics}; 4use hir::{Crate, MacroDef, ModuleDef, Semantics};
5use ra_prof::profile; 5use ra_prof::profile;
6use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; 6use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
7 7
@@ -11,57 +11,55 @@ use crate::{
11 RootDatabase, 11 RootDatabase,
12}; 12};
13use either::Either; 13use either::Either;
14use rustc_hash::FxHashSet;
14 15
15pub struct ImportsLocator<'a> { 16pub fn find_imports<'a>(
16 sema: Semantics<'a, RootDatabase>, 17 sema: &Semantics<'a, RootDatabase>,
17} 18 krate: Crate,
18 19 name_to_import: &str,
19impl<'a> ImportsLocator<'a> { 20) -> Vec<Either<ModuleDef, MacroDef>> {
20 pub fn new(db: &'a RootDatabase) -> Self { 21 let _p = profile("search_for_imports");
21 Self { sema: Semantics::new(db) } 22 let db = sema.db;
22 }
23 23
24 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> { 24 // Query dependencies first.
25 let _p = profile("search_for_imports"); 25 let mut candidates: FxHashSet<_> =
26 let db = self.sema.db; 26 krate.query_external_importables(db, name_to_import).collect();
27 27
28 let project_results = { 28 // Query the local crate using the symbol index.
29 let mut query = Query::new(name_to_import.to_string()); 29 let local_results = {
30 query.exact(); 30 let mut query = Query::new(name_to_import.to_string());
31 query.limit(40); 31 query.exact();
32 symbol_index::world_symbols(db, query) 32 query.limit(40);
33 }; 33 symbol_index::crate_symbols(db, krate.into(), query)
34 let lib_results = { 34 };
35 let mut query = Query::new(name_to_import.to_string());
36 query.libs();
37 query.exact();
38 query.limit(40);
39 symbol_index::world_symbols(db, query)
40 };
41 35
42 project_results 36 candidates.extend(
37 local_results
43 .into_iter() 38 .into_iter()
44 .chain(lib_results.into_iter()) 39 .filter_map(|import_candidate| get_name_definition(sema, &import_candidate))
45 .filter_map(|import_candidate| self.get_name_definition(&import_candidate))
46 .filter_map(|name_definition_to_import| match name_definition_to_import { 40 .filter_map(|name_definition_to_import| match name_definition_to_import {
47 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), 41 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
48 Definition::Macro(macro_def) => Some(Either::Right(macro_def)), 42 Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
49 _ => None, 43 _ => None,
50 }) 44 }),
51 .collect() 45 );
52 } 46
47 candidates.into_iter().collect()
48}
53 49
54 fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> { 50fn get_name_definition<'a>(
55 let _p = profile("get_name_definition"); 51 sema: &Semantics<'a, RootDatabase>,
56 let file_id = import_candidate.file_id; 52 import_candidate: &FileSymbol,
53) -> Option<Definition> {
54 let _p = profile("get_name_definition");
55 let file_id = import_candidate.file_id;
57 56
58 let candidate_node = import_candidate.ptr.to_node(self.sema.parse(file_id).syntax()); 57 let candidate_node = import_candidate.ptr.to_node(sema.parse(file_id).syntax());
59 let candidate_name_node = if candidate_node.kind() != NAME { 58 let candidate_name_node = if candidate_node.kind() != NAME {
60 candidate_node.children().find(|it| it.kind() == NAME)? 59 candidate_node.children().find(|it| it.kind() == NAME)?
61 } else { 60 } else {
62 candidate_node 61 candidate_node
63 }; 62 };
64 let name = ast::Name::cast(candidate_name_node)?; 63 let name = ast::Name::cast(candidate_name_node)?;
65 classify_name(&self.sema, &name)?.into_definition() 64 classify_name(sema, &name)?.into_definition()
66 }
67} 65}
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index 1b74e6558..6900cac73 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -11,15 +11,15 @@ pub mod imports_locator;
11pub mod source_change; 11pub mod source_change;
12mod wasm_shims; 12mod wasm_shims;
13 13
14use std::sync::Arc; 14use std::{fmt, sync::Arc};
15 15
16use hir::db::{AstDatabase, DefDatabase}; 16use hir::db::{AstDatabase, DefDatabase, HirDatabase};
17use ra_db::{ 17use ra_db::{
18 salsa::{self, Database, Durability}, 18 salsa::{self, Durability},
19 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath, 19 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase,
20 SourceDatabase, SourceRootId, Upcast, 20 Upcast,
21}; 21};
22use rustc_hash::FxHashMap; 22use rustc_hash::FxHashSet;
23 23
24use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase}; 24use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
25 25
@@ -33,14 +33,18 @@ use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
33 hir::db::DefDatabaseStorage, 33 hir::db::DefDatabaseStorage,
34 hir::db::HirDatabaseStorage 34 hir::db::HirDatabaseStorage
35)] 35)]
36#[derive(Debug)]
37pub struct RootDatabase { 36pub struct RootDatabase {
38 runtime: salsa::Runtime<RootDatabase>, 37 storage: salsa::Storage<RootDatabase>,
39 pub(crate) debug_data: Arc<DebugData>,
40 pub last_gc: crate::wasm_shims::Instant, 38 pub last_gc: crate::wasm_shims::Instant,
41 pub last_gc_check: crate::wasm_shims::Instant, 39 pub last_gc_check: crate::wasm_shims::Instant,
42} 40}
43 41
42impl fmt::Debug for RootDatabase {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 f.debug_struct("RootDatabase").finish()
45 }
46}
47
44impl Upcast<dyn AstDatabase> for RootDatabase { 48impl Upcast<dyn AstDatabase> for RootDatabase {
45 fn upcast(&self) -> &(dyn AstDatabase + 'static) { 49 fn upcast(&self) -> &(dyn AstDatabase + 'static) {
46 &*self 50 &*self
@@ -53,41 +57,30 @@ impl Upcast<dyn DefDatabase> for RootDatabase {
53 } 57 }
54} 58}
55 59
60impl Upcast<dyn HirDatabase> for RootDatabase {
61 fn upcast(&self) -> &(dyn HirDatabase + 'static) {
62 &*self
63 }
64}
65
56impl FileLoader for RootDatabase { 66impl FileLoader for RootDatabase {
57 fn file_text(&self, file_id: FileId) -> Arc<String> { 67 fn file_text(&self, file_id: FileId) -> Arc<String> {
58 FileLoaderDelegate(self).file_text(file_id) 68 FileLoaderDelegate(self).file_text(file_id)
59 } 69 }
60 fn resolve_relative_path( 70 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
61 &self, 71 FileLoaderDelegate(self).resolve_path(anchor, path)
62 anchor: FileId,
63 relative_path: &RelativePath,
64 ) -> Option<FileId> {
65 FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
66 } 72 }
67 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 73 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
68 FileLoaderDelegate(self).relevant_crates(file_id) 74 FileLoaderDelegate(self).relevant_crates(file_id)
69 } 75 }
70 fn resolve_extern_path(
71 &self,
72 extern_id: ra_db::ExternSourceId,
73 relative_path: &RelativePath,
74 ) -> Option<FileId> {
75 FileLoaderDelegate(self).resolve_extern_path(extern_id, relative_path)
76 }
77} 76}
78 77
79impl salsa::Database for RootDatabase { 78impl salsa::Database for RootDatabase {
80 fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> {
81 &self.runtime
82 }
83 fn salsa_runtime_mut(&mut self) -> &mut salsa::Runtime<Self> {
84 &mut self.runtime
85 }
86 fn on_propagated_panic(&self) -> ! { 79 fn on_propagated_panic(&self) -> ! {
87 Canceled::throw() 80 Canceled::throw()
88 } 81 }
89 fn salsa_event(&self, event: impl Fn() -> salsa::Event<RootDatabase>) { 82 fn salsa_event(&self, event: salsa::Event) {
90 match event().kind { 83 match event.kind {
91 salsa::EventKind::DidValidateMemoizedValue { .. } 84 salsa::EventKind::DidValidateMemoizedValue { .. }
92 | salsa::EventKind::WillExecute { .. } => { 85 | salsa::EventKind::WillExecute { .. } => {
93 self.check_canceled(); 86 self.check_canceled();
@@ -106,10 +99,9 @@ impl Default for RootDatabase {
106impl RootDatabase { 99impl RootDatabase {
107 pub fn new(lru_capacity: Option<usize>) -> RootDatabase { 100 pub fn new(lru_capacity: Option<usize>) -> RootDatabase {
108 let mut db = RootDatabase { 101 let mut db = RootDatabase {
109 runtime: salsa::Runtime::default(), 102 storage: salsa::Storage::default(),
110 last_gc: crate::wasm_shims::Instant::now(), 103 last_gc: crate::wasm_shims::Instant::now(),
111 last_gc_check: crate::wasm_shims::Instant::now(), 104 last_gc_check: crate::wasm_shims::Instant::now(),
112 debug_data: Default::default(),
113 }; 105 };
114 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); 106 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
115 db.set_local_roots_with_durability(Default::default(), Durability::HIGH); 107 db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
@@ -120,19 +112,18 @@ impl RootDatabase {
120 112
121 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) { 113 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
122 let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP); 114 let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP);
123 self.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity); 115 ra_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
124 self.query_mut(hir::db::ParseMacroQuery).set_lru_capacity(lru_capacity); 116 hir::db::ParseMacroQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
125 self.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity); 117 hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
126 } 118 }
127} 119}
128 120
129impl salsa::ParallelDatabase for RootDatabase { 121impl salsa::ParallelDatabase for RootDatabase {
130 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> { 122 fn snapshot(&self) -> salsa::Snapshot<RootDatabase> {
131 salsa::Snapshot::new(RootDatabase { 123 salsa::Snapshot::new(RootDatabase {
132 runtime: self.runtime.snapshot(self), 124 storage: self.storage.snapshot(),
133 last_gc: self.last_gc, 125 last_gc: self.last_gc,
134 last_gc_check: self.last_gc_check, 126 last_gc_check: self.last_gc_check,
135 debug_data: Arc::clone(&self.debug_data),
136 }) 127 })
137 } 128 }
138} 129}
@@ -142,18 +133,7 @@ pub trait LineIndexDatabase: ra_db::SourceDatabase + CheckCanceled {
142 fn line_index(&self, file_id: FileId) -> Arc<LineIndex>; 133 fn line_index(&self, file_id: FileId) -> Arc<LineIndex>;
143} 134}
144 135
145fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> { 136fn line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
146 let text = db.file_text(file_id); 137 let text = db.file_text(file_id);
147 Arc::new(LineIndex::new(&*text)) 138 Arc::new(LineIndex::new(&*text))
148} 139}
149
150#[derive(Debug, Default, Clone)]
151pub(crate) struct DebugData {
152 pub(crate) root_paths: FxHashMap<SourceRootId, String>,
153}
154
155impl DebugData {
156 pub(crate) fn merge(&mut self, other: DebugData) {
157 self.root_paths.extend(other.root_paths.into_iter());
158 }
159}
diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs
index 335a1ad03..81553150b 100644
--- a/crates/ra_ide_db/src/search.rs
+++ b/crates/ra_ide_db/src/search.rs
@@ -157,14 +157,14 @@ impl Definition {
157 if let Some(Visibility::Public) = vis { 157 if let Some(Visibility::Public) = vis {
158 let source_root_id = db.file_source_root(file_id); 158 let source_root_id = db.file_source_root(file_id);
159 let source_root = db.source_root(source_root_id); 159 let source_root = db.source_root(source_root_id);
160 let mut res = source_root.walk().map(|id| (id, None)).collect::<FxHashMap<_, _>>(); 160 let mut res = source_root.iter().map(|id| (id, None)).collect::<FxHashMap<_, _>>();
161 161
162 let krate = module.krate(); 162 let krate = module.krate();
163 for rev_dep in krate.reverse_dependencies(db) { 163 for rev_dep in krate.reverse_dependencies(db) {
164 let root_file = rev_dep.root_file(db); 164 let root_file = rev_dep.root_file(db);
165 let source_root_id = db.file_source_root(root_file); 165 let source_root_id = db.file_source_root(root_file);
166 let source_root = db.source_root(source_root_id); 166 let source_root = db.source_root(source_root_id);
167 res.extend(source_root.walk().map(|id| (id, None))); 167 res.extend(source_root.iter().map(|id| (id, None)));
168 } 168 }
169 return SearchScope::new(res); 169 return SearchScope::new(res);
170 } 170 }
@@ -180,20 +180,20 @@ impl Definition {
180 180
181 pub fn find_usages( 181 pub fn find_usages(
182 &self, 182 &self,
183 db: &RootDatabase, 183 sema: &Semantics<RootDatabase>,
184 search_scope: Option<SearchScope>, 184 search_scope: Option<SearchScope>,
185 ) -> Vec<Reference> { 185 ) -> Vec<Reference> {
186 let _p = profile("Definition::find_usages"); 186 let _p = profile("Definition::find_usages");
187 187
188 let search_scope = { 188 let search_scope = {
189 let base = self.search_scope(db); 189 let base = self.search_scope(sema.db);
190 match search_scope { 190 match search_scope {
191 None => base, 191 None => base,
192 Some(scope) => base.intersection(&scope), 192 Some(scope) => base.intersection(&scope),
193 } 193 }
194 }; 194 };
195 195
196 let name = match self.name(db) { 196 let name = match self.name(sema.db) {
197 None => return Vec::new(), 197 None => return Vec::new(),
198 Some(it) => it.to_string(), 198 Some(it) => it.to_string(),
199 }; 199 };
@@ -202,11 +202,10 @@ impl Definition {
202 let mut refs = vec![]; 202 let mut refs = vec![];
203 203
204 for (file_id, search_range) in search_scope { 204 for (file_id, search_range) in search_scope {
205 let text = db.file_text(file_id); 205 let text = sema.db.file_text(file_id);
206 let search_range = 206 let search_range =
207 search_range.unwrap_or(TextRange::up_to(TextSize::of(text.as_str()))); 207 search_range.unwrap_or(TextRange::up_to(TextSize::of(text.as_str())));
208 208
209 let sema = Semantics::new(db);
210 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); 209 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
211 210
212 for (idx, _) in text.match_indices(pat) { 211 for (idx, _) in text.match_indices(pat) {
@@ -222,9 +221,6 @@ impl Definition {
222 continue; 221 continue;
223 }; 222 };
224 223
225 // FIXME: reuse sb
226 // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098
227
228 match classify_name_ref(&sema, &name_ref) { 224 match classify_name_ref(&sema, &name_ref) {
229 Some(NameRefClass::Definition(def)) if &def == self => { 225 Some(NameRefClass::Definition(def)) if &def == self => {
230 let kind = if is_record_lit_name_ref(&name_ref) 226 let kind = if is_record_lit_name_ref(&name_ref)
diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs
index e713f4b7e..abb83f421 100644
--- a/crates/ra_ide_db/src/source_change.rs
+++ b/crates/ra_ide_db/src/source_change.rs
@@ -3,10 +3,10 @@
3//! 3//!
4//! It can be viewed as a dual for `AnalysisChange`. 4//! It can be viewed as a dual for `AnalysisChange`.
5 5
6use ra_db::{FileId, RelativePathBuf, SourceRootId}; 6use ra_db::FileId;
7use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
8 8
9#[derive(Debug, Clone)] 9#[derive(Default, Debug, Clone)]
10pub struct SourceChange { 10pub struct SourceChange {
11 pub source_file_edits: Vec<SourceFileEdit>, 11 pub source_file_edits: Vec<SourceFileEdit>,
12 pub file_system_edits: Vec<FileSystemEdit>, 12 pub file_system_edits: Vec<FileSystemEdit>,
@@ -22,17 +22,6 @@ impl SourceChange {
22 ) -> Self { 22 ) -> Self {
23 SourceChange { source_file_edits, file_system_edits, is_snippet: false } 23 SourceChange { source_file_edits, file_system_edits, is_snippet: false }
24 } 24 }
25
26 /// Creates a new SourceChange with the given label,
27 /// containing only the given `SourceFileEdits`.
28 pub fn source_file_edits(edits: Vec<SourceFileEdit>) -> Self {
29 SourceChange { source_file_edits: edits, file_system_edits: vec![], is_snippet: false }
30 }
31 /// Creates a new SourceChange with the given label
32 /// from the given `FileId` and `TextEdit`
33 pub fn source_file_edit_from(file_id: FileId, edit: TextEdit) -> Self {
34 SourceFileEdit { file_id, edit }.into()
35 }
36} 25}
37 26
38#[derive(Debug, Clone)] 27#[derive(Debug, Clone)]
@@ -43,18 +32,20 @@ pub struct SourceFileEdit {
43 32
44impl From<SourceFileEdit> for SourceChange { 33impl From<SourceFileEdit> for SourceChange {
45 fn from(edit: SourceFileEdit) -> SourceChange { 34 fn from(edit: SourceFileEdit) -> SourceChange {
46 SourceChange { 35 vec![edit].into()
47 source_file_edits: vec![edit], 36 }
48 file_system_edits: Vec::new(), 37}
49 is_snippet: false, 38
50 } 39impl From<Vec<SourceFileEdit>> for SourceChange {
40 fn from(source_file_edits: Vec<SourceFileEdit>) -> SourceChange {
41 SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false }
51 } 42 }
52} 43}
53 44
54#[derive(Debug, Clone)] 45#[derive(Debug, Clone)]
55pub enum FileSystemEdit { 46pub enum FileSystemEdit {
56 CreateFile { source_root: SourceRootId, path: RelativePathBuf }, 47 CreateFile { anchor: FileId, dst: String },
57 MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf }, 48 MoveFile { src: FileId, anchor: FileId, dst: String },
58} 49}
59 50
60impl From<FileSystemEdit> for SourceChange { 51impl From<FileSystemEdit> for SourceChange {
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs
index acc31fe3b..131e2a128 100644
--- a/crates/ra_ide_db/src/symbol_index.rs
+++ b/crates/ra_ide_db/src/symbol_index.rs
@@ -29,18 +29,20 @@ use std::{
29}; 29};
30 30
31use fst::{self, Streamer}; 31use fst::{self, Streamer};
32use hir::db::DefDatabase;
32use ra_db::{ 33use ra_db::{
33 salsa::{self, ParallelDatabase}, 34 salsa::{self, ParallelDatabase},
34 FileId, SourceDatabaseExt, SourceRootId, 35 CrateId, FileId, SourceDatabaseExt, SourceRootId,
35}; 36};
37use ra_prof::profile;
36use ra_syntax::{ 38use ra_syntax::{
37 ast::{self, NameOwner}, 39 ast::{self, NameOwner},
38 match_ast, AstNode, Parse, SmolStr, SourceFile, 40 match_ast, AstNode, Parse, SmolStr, SourceFile,
39 SyntaxKind::{self, *}, 41 SyntaxKind::{self, *},
40 SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent, 42 SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent,
41}; 43};
42#[cfg(not(feature = "wasm"))]
43use rayon::prelude::*; 44use rayon::prelude::*;
45use rustc_hash::{FxHashMap, FxHashSet};
44 46
45use crate::RootDatabase; 47use crate::RootDatabase;
46 48
@@ -85,21 +87,41 @@ impl Query {
85} 87}
86 88
87#[salsa::query_group(SymbolsDatabaseStorage)] 89#[salsa::query_group(SymbolsDatabaseStorage)]
88pub trait SymbolsDatabase: hir::db::HirDatabase { 90pub trait SymbolsDatabase: hir::db::HirDatabase + SourceDatabaseExt {
89 fn file_symbols(&self, file_id: FileId) -> Arc<SymbolIndex>; 91 fn file_symbols(&self, file_id: FileId) -> Arc<SymbolIndex>;
90 #[salsa::input] 92 fn library_symbols(&self) -> Arc<FxHashMap<SourceRootId, SymbolIndex>>;
91 fn library_symbols(&self, id: SourceRootId) -> Arc<SymbolIndex>;
92 /// The set of "local" (that is, from the current workspace) roots. 93 /// The set of "local" (that is, from the current workspace) roots.
93 /// Files in local roots are assumed to change frequently. 94 /// Files in local roots are assumed to change frequently.
94 #[salsa::input] 95 #[salsa::input]
95 fn local_roots(&self) -> Arc<Vec<SourceRootId>>; 96 fn local_roots(&self) -> Arc<FxHashSet<SourceRootId>>;
96 /// The set of roots for crates.io libraries. 97 /// The set of roots for crates.io libraries.
97 /// Files in libraries are assumed to never change. 98 /// Files in libraries are assumed to never change.
98 #[salsa::input] 99 #[salsa::input]
99 fn library_roots(&self) -> Arc<Vec<SourceRootId>>; 100 fn library_roots(&self) -> Arc<FxHashSet<SourceRootId>>;
101}
102
103fn library_symbols(db: &dyn SymbolsDatabase) -> Arc<FxHashMap<SourceRootId, SymbolIndex>> {
104 let _p = profile("library_symbols");
105
106 let roots = db.library_roots();
107 let res = roots
108 .iter()
109 .map(|&root_id| {
110 let root = db.source_root(root_id);
111 let files = root
112 .iter()
113 .map(|it| (it, SourceDatabaseExt::file_text(db, it)))
114 .collect::<Vec<_>>();
115 let symbol_index = SymbolIndex::for_files(
116 files.into_par_iter().map(|(file, text)| (file, SourceFile::parse(&text))),
117 );
118 (root_id, symbol_index)
119 })
120 .collect();
121 Arc::new(res)
100} 122}
101 123
102fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> { 124fn file_symbols(db: &dyn SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> {
103 db.check_canceled(); 125 db.check_canceled();
104 let parse = db.parse(file_id); 126 let parse = db.parse(file_id);
105 127
@@ -110,6 +132,14 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
110 Arc::new(SymbolIndex::new(symbols)) 132 Arc::new(SymbolIndex::new(symbols))
111} 133}
112 134
135/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
136struct Snap<DB>(DB);
137impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> {
138 fn clone(&self) -> Snap<salsa::Snapshot<DB>> {
139 Snap(self.0.snapshot())
140 }
141}
142
113// Feature: Workspace Symbol 143// Feature: Workspace Symbol
114// 144//
115// Uses fuzzy-search to find types, modules and functions by name across your 145// Uses fuzzy-search to find types, modules and functions by name across your
@@ -132,44 +162,51 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
132// | VS Code | kbd:[Ctrl+T] 162// | VS Code | kbd:[Ctrl+T]
133// |=== 163// |===
134pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { 164pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
135 /// Need to wrap Snapshot to provide `Clone` impl for `map_with` 165 let _p = ra_prof::profile("world_symbols").detail(|| query.query.clone());
136 struct Snap(salsa::Snapshot<RootDatabase>);
137 impl Clone for Snap {
138 fn clone(&self) -> Snap {
139 Snap(self.0.snapshot())
140 }
141 }
142 166
143 let buf: Vec<Arc<SymbolIndex>> = if query.libs { 167 let tmp1;
144 let snap = Snap(db.snapshot()); 168 let tmp2;
145 #[cfg(not(feature = "wasm"))] 169 let buf: Vec<&SymbolIndex> = if query.libs {
146 let buf = db 170 tmp1 = db.library_symbols();
147 .library_roots() 171 tmp1.values().collect()
148 .par_iter()
149 .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id))
150 .collect();
151
152 #[cfg(feature = "wasm")]
153 let buf = db.library_roots().iter().map(|&lib_id| snap.0.library_symbols(lib_id)).collect();
154
155 buf
156 } else { 172 } else {
157 let mut files = Vec::new(); 173 let mut files = Vec::new();
158 for &root in db.local_roots().iter() { 174 for &root in db.local_roots().iter() {
159 let sr = db.source_root(root); 175 let sr = db.source_root(root);
160 files.extend(sr.walk()) 176 files.extend(sr.iter())
161 } 177 }
162 178
163 let snap = Snap(db.snapshot()); 179 let snap = Snap(db.snapshot());
164 #[cfg(not(feature = "wasm"))] 180 tmp2 = files
165 let buf = 181 .par_iter()
166 files.par_iter().map_with(snap, |db, &file_id| db.0.file_symbols(file_id)).collect(); 182 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
183 .collect::<Vec<_>>();
184 tmp2.iter().map(|it| &**it).collect()
185 };
186 query.search(&buf)
187}
167 188
168 #[cfg(feature = "wasm")] 189pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> {
169 let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect(); 190 // FIXME(#4842): This now depends on CrateDefMap, why not build the entire symbol index from
191 // that instead?
192
193 let def_map = db.crate_def_map(krate);
194 let mut files = Vec::new();
195 let mut modules = vec![def_map.root];
196 while let Some(module) = modules.pop() {
197 let data = &def_map[module];
198 files.extend(data.origin.file_id());
199 modules.extend(data.children.values());
200 }
201
202 let snap = Snap(db.snapshot());
203
204 let buf = files
205 .par_iter()
206 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
207 .collect::<Vec<_>>();
208 let buf = buf.iter().map(|it| &**it).collect::<Vec<_>>();
170 209
171 buf
172 };
173 query.search(&buf) 210 query.search(&buf)
174} 211}
175 212
@@ -215,12 +252,8 @@ impl SymbolIndex {
215 lhs_chars.cmp(rhs_chars) 252 lhs_chars.cmp(rhs_chars)
216 } 253 }
217 254
218 #[cfg(not(feature = "wasm"))]
219 symbols.par_sort_by(cmp); 255 symbols.par_sort_by(cmp);
220 256
221 #[cfg(feature = "wasm")]
222 symbols.sort_by(cmp);
223
224 let mut builder = fst::MapBuilder::memory(); 257 let mut builder = fst::MapBuilder::memory();
225 258
226 let mut last_batch_start = 0; 259 let mut last_batch_start = 0;
@@ -254,7 +287,6 @@ impl SymbolIndex {
254 self.map.as_fst().size() + self.symbols.len() * mem::size_of::<FileSymbol>() 287 self.map.as_fst().size() + self.symbols.len() * mem::size_of::<FileSymbol>()
255 } 288 }
256 289
257 #[cfg(not(feature = "wasm"))]
258 pub(crate) fn for_files( 290 pub(crate) fn for_files(
259 files: impl ParallelIterator<Item = (FileId, Parse<ast::SourceFile>)>, 291 files: impl ParallelIterator<Item = (FileId, Parse<ast::SourceFile>)>,
260 ) -> SymbolIndex { 292 ) -> SymbolIndex {
@@ -264,16 +296,6 @@ impl SymbolIndex {
264 SymbolIndex::new(symbols) 296 SymbolIndex::new(symbols)
265 } 297 }
266 298
267 #[cfg(feature = "wasm")]
268 pub(crate) fn for_files(
269 files: impl Iterator<Item = (FileId, Parse<ast::SourceFile>)>,
270 ) -> SymbolIndex {
271 let symbols = files
272 .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
273 .collect::<Vec<_>>();
274 SymbolIndex::new(symbols)
275 }
276
277 fn range_to_map_value(start: usize, end: usize) -> u64 { 299 fn range_to_map_value(start: usize, end: usize) -> u64 {
278 debug_assert![start <= (std::u32::MAX as usize)]; 300 debug_assert![start <= (std::u32::MAX as usize)];
279 debug_assert![end <= (std::u32::MAX as usize)]; 301 debug_assert![end <= (std::u32::MAX as usize)];
@@ -289,7 +311,7 @@ impl SymbolIndex {
289} 311}
290 312
291impl Query { 313impl Query {
292 pub(crate) fn search(self, indices: &[Arc<SymbolIndex>]) -> Vec<FileSymbol> { 314 pub(crate) fn search(self, indices: &[&SymbolIndex]) -> Vec<FileSymbol> {
293 let mut op = fst::map::OpBuilder::new(); 315 let mut op = fst::map::OpBuilder::new();
294 for file_symbols in indices.iter() { 316 for file_symbols in indices.iter() {
295 let automaton = fst::automaton::Subsequence::new(&self.lowercased); 317 let automaton = fst::automaton::Subsequence::new(&self.lowercased);
@@ -298,9 +320,6 @@ impl Query {
298 let mut stream = op.union(); 320 let mut stream = op.union();
299 let mut res = Vec::new(); 321 let mut res = Vec::new();
300 while let Some((_, indexed_values)) = stream.next() { 322 while let Some((_, indexed_values)) = stream.next() {
301 if res.len() >= self.limit {
302 break;
303 }
304 for indexed_value in indexed_values { 323 for indexed_value in indexed_values {
305 let symbol_index = &indices[indexed_value.index]; 324 let symbol_index = &indices[indexed_value.index];
306 let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); 325 let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);
@@ -312,7 +331,11 @@ impl Query {
312 if self.exact && symbol.name != self.query { 331 if self.exact && symbol.name != self.query {
313 continue; 332 continue;
314 } 333 }
334
315 res.push(symbol.clone()); 335 res.push(symbol.clone());
336 if res.len() >= self.limit {
337 return res;
338 }
316 } 339 }
317 } 340 }
318 } 341 }
@@ -321,10 +344,7 @@ impl Query {
321} 344}
322 345
323fn is_type(kind: SyntaxKind) -> bool { 346fn is_type(kind: SyntaxKind) -> bool {
324 match kind { 347 matches!(kind, STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF)
325 STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => true,
326 _ => false,
327 }
328} 348}
329 349
330/// The actual data that is stored in the index. It should be as compact as 350/// The actual data that is stored in the index. It should be as compact as
diff --git a/crates/ra_mbe/Cargo.toml b/crates/ra_mbe/Cargo.toml
index 4dec24914..a26746a19 100644
--- a/crates/ra_mbe/Cargo.toml
+++ b/crates/ra_mbe/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_mbe" 3name = "ra_mbe"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs
index 9c450eaba..dec7ba22e 100644
--- a/crates/ra_mbe/src/lib.rs
+++ b/crates/ra_mbe/src/lib.rs
@@ -9,6 +9,9 @@ mod syntax_bridge;
9mod tt_iter; 9mod tt_iter;
10mod subtree_source; 10mod subtree_source;
11 11
12#[cfg(test)]
13mod tests;
14
12pub use tt::{Delimiter, Punct}; 15pub use tt::{Delimiter, Punct};
13 16
14use crate::{ 17use crate::{
@@ -273,6 +276,3 @@ impl<T: Default> From<Result<T, ExpandError>> for ExpandResult<T> {
273 .map_or_else(|e| ExpandResult(Default::default(), Some(e)), |it| ExpandResult(it, None)) 276 .map_or_else(|e| ExpandResult(Default::default(), Some(e)), |it| ExpandResult(it, None))
274 } 277 }
275} 278}
276
277#[cfg(test)]
278mod tests;
diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs
index 78f9efa1b..f9e515b81 100644
--- a/crates/ra_mbe/src/mbe_expander/matcher.rs
+++ b/crates/ra_mbe/src/mbe_expander/matcher.rs
@@ -260,7 +260,7 @@ impl<'a> TtIter<'a> {
260 | ('|', '=', None) 260 | ('|', '=', None)
261 | ('|', '|', None) => { 261 | ('|', '|', None) => {
262 let tt2 = self.next().unwrap().clone(); 262 let tt2 = self.next().unwrap().clone();
263 Ok(tt::Subtree { delimiter: None, token_trees: vec![tt.clone(), tt2] }.into()) 263 Ok(tt::Subtree { delimiter: None, token_trees: vec![tt, tt2] }.into())
264 } 264 }
265 _ => Ok(tt), 265 _ => Ok(tt),
266 } 266 }
diff --git a/crates/ra_mbe/src/parser.rs b/crates/ra_mbe/src/parser.rs
index 034150432..1e5dafbdf 100644
--- a/crates/ra_mbe/src/parser.rs
+++ b/crates/ra_mbe/src/parser.rs
@@ -137,10 +137,7 @@ fn eat_fragment_kind<'a>(
137} 137}
138 138
139fn is_boolean_literal(lit: &tt::Literal) -> bool { 139fn is_boolean_literal(lit: &tt::Literal) -> bool {
140 match lit.text.as_str() { 140 matches!(lit.text.as_str(), "true" | "false")
141 "true" | "false" => true,
142 _ => false,
143 }
144} 141}
145 142
146fn parse_repeat(src: &mut TtIter) -> Result<(Option<Separator>, RepeatKind), ExpandError> { 143fn parse_repeat(src: &mut TtIter) -> Result<(Option<Separator>, RepeatKind), ExpandError> {
diff --git a/crates/ra_parser/Cargo.toml b/crates/ra_parser/Cargo.toml
index 0da581fd5..72ec3e4d9 100644
--- a/crates/ra_parser/Cargo.toml
+++ b/crates/ra_parser/Cargo.toml
@@ -4,6 +4,7 @@ name = "ra_parser"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = false
diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs
index be0cd5661..caedeead0 100644
--- a/crates/ra_parser/src/grammar.rs
+++ b/crates/ra_parser/src/grammar.rs
@@ -18,9 +18,10 @@
18//! // fn foo() {} 18//! // fn foo() {}
19//! ``` 19//! ```
20//! 20//!
21//! After adding a new inline-test, run `cargo collect-tests` to extract 21//! After adding a new inline-test, run `cargo xtask codegen` to
22//! it as a standalone text-fixture into `tests/data/parser/inline`, and 22//! extract it as a standalone text-fixture into
23//! run `cargo test` once to create the "gold" value. 23//! `crates/ra_syntax/test_data/parser/`, and run `cargo test` once to
24//! create the "gold" value.
24//! 25//!
25//! Coding convention: rules like `where_clause` always produce either a 26//! Coding convention: rules like `where_clause` always produce either a
26//! node or an error, rules like `opt_where_clause` may produce nothing. 27//! node or an error, rules like `opt_where_clause` may produce nothing.
@@ -72,10 +73,7 @@ pub(crate) mod fragments {
72 // Parse a meta item , which excluded [], e.g : #[ MetaItem ] 73 // Parse a meta item , which excluded [], e.g : #[ MetaItem ]
73 pub(crate) fn meta_item(p: &mut Parser) { 74 pub(crate) fn meta_item(p: &mut Parser) {
74 fn is_delimiter(p: &mut Parser) -> bool { 75 fn is_delimiter(p: &mut Parser) -> bool {
75 match p.current() { 76 matches!(p.current(), T!['{'] | T!['('] | T!['['])
76 T!['{'] | T!['('] | T!['['] => true,
77 _ => false,
78 }
79 } 77 }
80 78
81 if is_delimiter(p) { 79 if is_delimiter(p) {
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index d6e8df32a..6e72eea66 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -50,10 +50,8 @@ fn expr_no_struct(p: &mut Parser) {
50} 50}
51 51
52fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool { 52fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool {
53 match kind { 53 let forbid = matches!(kind, BIN_EXPR | RANGE_EXPR);
54 BIN_EXPR | RANGE_EXPR | IF_EXPR => false, 54 !forbid
55 _ => true,
56 }
57} 55}
58 56
59pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) { 57pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) {
diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs
index 67a924de5..97642bc24 100644
--- a/crates/ra_parser/src/grammar/items.rs
+++ b/crates/ra_parser/src/grammar/items.rs
@@ -118,7 +118,22 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul
118 && p.at_contextual_kw("default") 118 && p.at_contextual_kw("default")
119 && (match p.nth(1) { 119 && (match p.nth(1) {
120 T![impl] => true, 120 T![impl] => true,
121 T![fn] | T![type] => { 121 T![unsafe] => {
122 // test default_unsafe_impl
123 // default unsafe impl Foo {}
124
125 // test default_unsafe_fn
126 // impl T for Foo {
127 // default unsafe fn foo() {}
128 // }
129 if p.nth(2) == T![impl] || p.nth(2) == T![fn] {
130 p.bump_remap(T![default]);
131 p.bump(T![unsafe]);
132 has_mods = true;
133 }
134 false
135 }
136 T![fn] | T![type] | T![const] => {
122 if let ItemFlavor::Mod = flavor { 137 if let ItemFlavor::Mod = flavor {
123 true 138 true
124 } else { 139 } else {
@@ -198,6 +213,9 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul
198 // default type T = Bar; 213 // default type T = Bar;
199 // default fn foo() {} 214 // default fn foo() {}
200 // } 215 // }
216 T![const] => {
217 consts::const_def(p, m);
218 }
201 219
202 // test unsafe_default_impl 220 // test unsafe_default_impl
203 // unsafe default impl Foo {} 221 // unsafe default impl Foo {}
diff --git a/crates/ra_parser/src/grammar/paths.rs b/crates/ra_parser/src/grammar/paths.rs
index 332acc1a0..b503af1dc 100644
--- a/crates/ra_parser/src/grammar/paths.rs
+++ b/crates/ra_parser/src/grammar/paths.rs
@@ -3,7 +3,7 @@
3use super::*; 3use super::*;
4 4
5pub(super) const PATH_FIRST: TokenSet = 5pub(super) const PATH_FIRST: TokenSet =
6 token_set![IDENT, SELF_KW, SUPER_KW, CRATE_KW, COLON, L_ANGLE]; 6 token_set![IDENT, T![self], T![super], T![crate], T![:], T![<]];
7 7
8pub(super) fn is_path_start(p: &Parser) -> bool { 8pub(super) fn is_path_start(p: &Parser) -> bool {
9 is_use_path_start(p) || p.at(T![<]) 9 is_use_path_start(p) || p.at(T![<])
@@ -41,10 +41,7 @@ fn path(p: &mut Parser, mode: Mode) {
41 path_segment(p, mode, true); 41 path_segment(p, mode, true);
42 let mut qual = path.complete(p, PATH); 42 let mut qual = path.complete(p, PATH);
43 loop { 43 loop {
44 let use_tree = match p.nth(2) { 44 let use_tree = matches!(p.nth(2), T![*] | T!['{']);
45 T![*] | T!['{'] => true,
46 _ => false,
47 };
48 if p.at(T![::]) && !use_tree { 45 if p.at(T![::]) && !use_tree {
49 let path = qual.precede(p); 46 let path = qual.precede(p);
50 p.bump(T![::]); 47 p.bump(T![::]);
@@ -73,8 +70,10 @@ fn path_segment(p: &mut Parser, mode: Mode, first: bool) {
73 } 70 }
74 p.expect(T![>]); 71 p.expect(T![>]);
75 } else { 72 } else {
73 let mut empty = true;
76 if first { 74 if first {
77 p.eat(T![::]); 75 p.eat(T![::]);
76 empty = false;
78 } 77 }
79 match p.current() { 78 match p.current() {
80 IDENT => { 79 IDENT => {
@@ -86,6 +85,12 @@ fn path_segment(p: &mut Parser, mode: Mode, first: bool) {
86 T![self] | T![super] | T![crate] => p.bump_any(), 85 T![self] | T![super] | T![crate] => p.bump_any(),
87 _ => { 86 _ => {
88 p.err_recover("expected identifier", items::ITEM_RECOVERY_SET); 87 p.err_recover("expected identifier", items::ITEM_RECOVERY_SET);
88 if empty {
89 // test_err empty_segment
90 // use crate::;
91 m.abandon(p);
92 return;
93 }
89 } 94 }
90 }; 95 };
91 } 96 }
diff --git a/crates/ra_parser/src/grammar/patterns.rs b/crates/ra_parser/src/grammar/patterns.rs
index 68fb2fc73..427c0eb49 100644
--- a/crates/ra_parser/src/grammar/patterns.rs
+++ b/crates/ra_parser/src/grammar/patterns.rs
@@ -4,7 +4,7 @@ use super::*;
4 4
5pub(super) const PATTERN_FIRST: TokenSet = expressions::LITERAL_FIRST 5pub(super) const PATTERN_FIRST: TokenSet = expressions::LITERAL_FIRST
6 .union(paths::PATH_FIRST) 6 .union(paths::PATH_FIRST)
7 .union(token_set![BOX_KW, REF_KW, MUT_KW, L_PAREN, L_BRACK, AMP, UNDERSCORE, MINUS, DOT]); 7 .union(token_set![T![box], T![ref], T![mut], T!['('], T!['['], T![&], T![_], T![-], T![.]]);
8 8
9pub(crate) fn pattern(p: &mut Parser) { 9pub(crate) fn pattern(p: &mut Parser) {
10 pattern_r(p, PAT_RECOVERY_SET); 10 pattern_r(p, PAT_RECOVERY_SET);
@@ -88,7 +88,9 @@ fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
88 _ => bind_pat(p, true), 88 _ => bind_pat(p, true),
89 }, 89 },
90 90
91 _ if paths::is_use_path_start(p) => path_or_macro_pat(p), 91 // test type_path_in_pattern
92 // fn main() { let <_>::Foo = (); }
93 _ if paths::is_path_start(p) => path_or_macro_pat(p),
92 _ if is_literal_pat_start(p) => literal_pat(p), 94 _ if is_literal_pat_start(p) => literal_pat(p),
93 95
94 T![.] if p.at(T![..]) => dot_dot_pat(p), 96 T![.] if p.at(T![..]) => dot_dot_pat(p),
@@ -138,7 +140,7 @@ fn literal_pat(p: &mut Parser) -> CompletedMarker {
138// let Bar(..) = (); 140// let Bar(..) = ();
139// } 141// }
140fn path_or_macro_pat(p: &mut Parser) -> CompletedMarker { 142fn path_or_macro_pat(p: &mut Parser) -> CompletedMarker {
141 assert!(paths::is_use_path_start(p)); 143 assert!(paths::is_path_start(p));
142 let m = p.start(); 144 let m = p.start();
143 paths::expr_path(p); 145 paths::expr_path(p);
144 let kind = match p.current() { 146 let kind = match p.current() {
diff --git a/crates/ra_parser/src/grammar/type_params.rs b/crates/ra_parser/src/grammar/type_params.rs
index 50e4900c3..d1330d4b9 100644
--- a/crates/ra_parser/src/grammar/type_params.rs
+++ b/crates/ra_parser/src/grammar/type_params.rs
@@ -169,10 +169,7 @@ fn is_where_predicate(p: &mut Parser) -> bool {
169} 169}
170 170
171fn is_where_clause_end(p: &mut Parser) -> bool { 171fn is_where_clause_end(p: &mut Parser) -> bool {
172 match p.current() { 172 matches!(p.current(), T!['{'] | T![;] | T![=])
173 T!['{'] | T![;] | T![=] => true,
174 _ => false,
175 }
176} 173}
177 174
178fn where_predicate(p: &mut Parser) { 175fn where_predicate(p: &mut Parser) {
@@ -191,10 +188,14 @@ fn where_predicate(p: &mut Parser) {
191 } 188 }
192 _ => { 189 _ => {
193 // test where_pred_for 190 // test where_pred_for
194 // fn test<F>() 191 // fn for_trait<F>()
195 // where 192 // where
196 // for<'a> F: Fn(&'a str) 193 // for<'a> F: Fn(&'a str)
197 // { } 194 // { }
195 if p.at(T![for]) {
196 types::for_binder(p);
197 }
198
198 types::type_(p); 199 types::type_(p);
199 200
200 if p.at(T![:]) { 201 if p.at(T![:]) {
diff --git a/crates/ra_parser/src/grammar/types.rs b/crates/ra_parser/src/grammar/types.rs
index fe1a039cb..9e8e3bd97 100644
--- a/crates/ra_parser/src/grammar/types.rs
+++ b/crates/ra_parser/src/grammar/types.rs
@@ -216,19 +216,21 @@ pub(super) fn for_binder(p: &mut Parser) {
216 216
217// test for_type 217// test for_type
218// type A = for<'a> fn() -> (); 218// type A = for<'a> fn() -> ();
219// fn foo<T>(_t: &T) where for<'a> &'a T: Iterator {} 219// type B = for<'a> unsafe extern "C" fn(&'a ()) -> ();
220// fn bar<T>(_t: &T) where for<'a> &'a mut T: Iterator {} 220// type Obj = for<'a> PartialEq<&'a i32>;
221// fn baz<T>(_t: &T) where for<'a> <&'a T as Baz>::Foo: Iterator {}
222pub(super) fn for_type(p: &mut Parser) { 221pub(super) fn for_type(p: &mut Parser) {
223 assert!(p.at(T![for])); 222 assert!(p.at(T![for]));
224 let m = p.start(); 223 let m = p.start();
225 for_binder(p); 224 for_binder(p);
226 match p.current() { 225 match p.current() {
227 T![fn] | T![unsafe] | T![extern] => fn_pointer_type(p), 226 T![fn] | T![unsafe] | T![extern] => {}
228 T![&] => reference_type(p), 227 // OK: legacy trait object format
229 _ if paths::is_path_start(p) => path_type_(p, false), 228 _ if paths::is_use_path_start(p) => {}
230 _ => p.error("expected a path"), 229 _ => {
230 p.error("expected a function pointer or path");
231 }
231 } 232 }
233 type_no_bounds(p);
232 m.complete(p, FOR_TYPE); 234 m.complete(p, FOR_TYPE);
233} 235}
234 236
diff --git a/crates/ra_parser/src/parser.rs b/crates/ra_parser/src/parser.rs
index 4f59b0a23..d797f2cc9 100644
--- a/crates/ra_parser/src/parser.rs
+++ b/crates/ra_parser/src/parser.rs
@@ -127,17 +127,24 @@ impl<'t> Parser<'t> {
127 127
128 fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool { 128 fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
129 let t1 = self.token_source.lookahead_nth(n); 129 let t1 = self.token_source.lookahead_nth(n);
130 if t1.kind != k1 || !t1.is_jointed_to_next {
131 return false;
132 }
130 let t2 = self.token_source.lookahead_nth(n + 1); 133 let t2 = self.token_source.lookahead_nth(n + 1);
131 t1.kind == k1 && t1.is_jointed_to_next && t2.kind == k2 134 t2.kind == k2
132 } 135 }
133 136
134 fn at_composite3(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind, k3: SyntaxKind) -> bool { 137 fn at_composite3(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind, k3: SyntaxKind) -> bool {
135 let t1 = self.token_source.lookahead_nth(n); 138 let t1 = self.token_source.lookahead_nth(n);
139 if t1.kind != k1 || !t1.is_jointed_to_next {
140 return false;
141 }
136 let t2 = self.token_source.lookahead_nth(n + 1); 142 let t2 = self.token_source.lookahead_nth(n + 1);
143 if t2.kind != k2 || !t2.is_jointed_to_next {
144 return false;
145 }
137 let t3 = self.token_source.lookahead_nth(n + 2); 146 let t3 = self.token_source.lookahead_nth(n + 2);
138 (t1.kind == k1 && t1.is_jointed_to_next) 147 t3.kind == k3
139 && (t2.kind == k2 && t2.is_jointed_to_next)
140 && t3.kind == k3
141 } 148 }
142 149
143 /// Checks if the current token is in `kinds`. 150 /// Checks if the current token is in `kinds`.
diff --git a/crates/ra_parser/src/syntax_kind.rs b/crates/ra_parser/src/syntax_kind.rs
index 8d6bd057b..63204436c 100644
--- a/crates/ra_parser/src/syntax_kind.rs
+++ b/crates/ra_parser/src/syntax_kind.rs
@@ -20,9 +20,6 @@ impl From<SyntaxKind> for u16 {
20 20
21impl SyntaxKind { 21impl SyntaxKind {
22 pub fn is_trivia(self) -> bool { 22 pub fn is_trivia(self) -> bool {
23 match self { 23 matches!(self, SyntaxKind::WHITESPACE | SyntaxKind::COMMENT)
24 SyntaxKind::WHITESPACE | SyntaxKind::COMMENT => true,
25 _ => false,
26 }
27 } 24 }
28} 25}
diff --git a/crates/ra_proc_macro/Cargo.toml b/crates/ra_proc_macro/Cargo.toml
index d009ceb82..c4b6e9e7b 100644
--- a/crates/ra_proc_macro/Cargo.toml
+++ b/crates/ra_proc_macro/Cargo.toml
@@ -4,6 +4,7 @@ name = "ra_proc_macro"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = false
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml
index bb3003278..12ce497f8 100644
--- a/crates/ra_proc_macro_srv/Cargo.toml
+++ b/crates/ra_proc_macro_srv/Cargo.toml
@@ -4,6 +4,7 @@ name = "ra_proc_macro_srv"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = false
@@ -22,3 +23,4 @@ cargo_metadata = "0.10.0"
22difference = "2.0.0" 23difference = "2.0.0"
23# used as proc macro test target 24# used as proc macro test target
24serde_derive = "1.0.106" 25serde_derive = "1.0.106"
26ra_toolchain = { path = "../ra_toolchain" }
diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs
index aa84e951c..1addbbd54 100644
--- a/crates/ra_proc_macro_srv/src/dylib.rs
+++ b/crates/ra_proc_macro_srv/src/dylib.rs
@@ -45,7 +45,7 @@ fn find_registrar_symbol(file: &Path) -> io::Result<Option<String>> {
45 // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html 45 // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
46 // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be 46 // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
47 // prepended with an underscore. 47 // prepended with an underscore.
48 if s.name.starts_with("_") { 48 if s.name.starts_with('_') {
49 &s.name[1..] 49 &s.name[1..]
50 } else { 50 } else {
51 &s.name 51 &s.name
diff --git a/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt b/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt
index bc010cfe9..e6fd21610 100644
--- a/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt
+++ b/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt
@@ -14,7 +14,7 @@ SUBTREE $
14 PUNCH , [alone] 4294967295 14 PUNCH , [alone] 4294967295
15 IDENT unused_qualifications 4294967295 15 IDENT unused_qualifications 4294967295
16 IDENT const 4294967295 16 IDENT const 4294967295
17 IDENT _IMPL_SERIALIZE_FOR_Foo 4294967295 17 IDENT _ 4294967295
18 PUNCH : [alone] 4294967295 18 PUNCH : [alone] 4294967295
19 SUBTREE () 4294967295 19 SUBTREE () 4294967295
20 PUNCH = [alone] 4294967295 20 PUNCH = [alone] 4294967295
diff --git a/crates/ra_proc_macro_srv/src/tests/mod.rs b/crates/ra_proc_macro_srv/src/tests/mod.rs
index 82cefbb29..8e6f28abd 100644
--- a/crates/ra_proc_macro_srv/src/tests/mod.rs
+++ b/crates/ra_proc_macro_srv/src/tests/mod.rs
@@ -11,7 +11,7 @@ fn test_derive_serialize_proc_macro() {
11 "serde_derive", 11 "serde_derive",
12 "Serialize", 12 "Serialize",
13 "1.0", 13 "1.0",
14 r##"struct Foo {}"##, 14 r"struct Foo {}",
15 include_str!("fixtures/test_serialize_proc_macro.txt"), 15 include_str!("fixtures/test_serialize_proc_macro.txt"),
16 ); 16 );
17} 17}
@@ -22,9 +22,7 @@ fn test_derive_serialize_proc_macro_failed() {
22 "serde_derive", 22 "serde_derive",
23 "Serialize", 23 "Serialize",
24 "1.0", 24 "1.0",
25 r##" 25 r"struct {}",
26 struct {}
27"##,
28 r##" 26 r##"
29SUBTREE $ 27SUBTREE $
30 IDENT compile_error 4294967295 28 IDENT compile_error 4294967295
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs
index 84348b5de..dcb00671f 100644
--- a/crates/ra_proc_macro_srv/src/tests/utils.rs
+++ b/crates/ra_proc_macro_srv/src/tests/utils.rs
@@ -2,7 +2,6 @@
2 2
3use crate::dylib; 3use crate::dylib;
4use crate::ProcMacroSrv; 4use crate::ProcMacroSrv;
5pub use difference::Changeset as __Changeset;
6use ra_proc_macro::ListMacrosTask; 5use ra_proc_macro::ListMacrosTask;
7use std::str::FromStr; 6use std::str::FromStr;
8use test_utils::assert_eq_text; 7use test_utils::assert_eq_text;
@@ -13,7 +12,7 @@ mod fixtures {
13 12
14 // Use current project metadata to get the proc-macro dylib path 13 // Use current project metadata to get the proc-macro dylib path
15 pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf { 14 pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf {
16 let command = Command::new("cargo") 15 let command = Command::new(ra_toolchain::cargo())
17 .args(&["check", "--message-format", "json"]) 16 .args(&["check", "--message-format", "json"])
18 .output() 17 .output()
19 .unwrap() 18 .unwrap()
@@ -45,12 +44,12 @@ pub fn assert_expand(
45 crate_name: &str, 44 crate_name: &str,
46 macro_name: &str, 45 macro_name: &str,
47 version: &str, 46 version: &str,
48 fixture: &str, 47 ra_fixture: &str,
49 expect: &str, 48 expect: &str,
50) { 49) {
51 let path = fixtures::dylib_path(crate_name, version); 50 let path = fixtures::dylib_path(crate_name, version);
52 let expander = dylib::Expander::new(&path).unwrap(); 51 let expander = dylib::Expander::new(&path).unwrap();
53 let fixture = parse_string(fixture).unwrap(); 52 let fixture = parse_string(ra_fixture).unwrap();
54 53
55 let res = expander.expand(macro_name, &fixture.subtree, None).unwrap(); 54 let res = expander.expand(macro_name, &fixture.subtree, None).unwrap();
56 assert_eq_text!(&format!("{:?}", res), &expect.trim()); 55 assert_eq_text!(&format!("{:?}", res), &expect.trim());
diff --git a/crates/ra_prof/Cargo.toml b/crates/ra_prof/Cargo.toml
index c33b5121a..b3d52985a 100644
--- a/crates/ra_prof/Cargo.toml
+++ b/crates/ra_prof/Cargo.toml
@@ -4,6 +4,7 @@ name = "ra_prof"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = false
@@ -12,6 +13,7 @@ doctest = false
12ra_arena = { path = "../ra_arena" } 13ra_arena = { path = "../ra_arena" }
13once_cell = "1.3.1" 14once_cell = "1.3.1"
14backtrace = { version = "0.3.44", optional = true } 15backtrace = { version = "0.3.44", optional = true }
16mimalloc = { version = "0.1.19", default-features = false, optional = true }
15 17
16[target.'cfg(not(target_env = "msvc"))'.dependencies] 18[target.'cfg(not(target_env = "msvc"))'.dependencies]
17jemallocator = { version = "0.3.2", optional = true } 19jemallocator = { version = "0.3.2", optional = true }
@@ -20,3 +22,9 @@ jemalloc-ctl = { version = "0.3.3", optional = true }
20[features] 22[features]
21jemalloc = [ "jemallocator", "jemalloc-ctl" ] 23jemalloc = [ "jemallocator", "jemalloc-ctl" ]
22cpu_profiler = [] 24cpu_profiler = []
25
26# Uncomment to enable for the whole crate graph
27# default = [ "backtrace" ]
28# default = [ "jemalloc" ]
29# default = [ "mimalloc" ]
30# default = [ "cpu_profiler" ]
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs
index 89df7f04b..b54531b4e 100644
--- a/crates/ra_prof/src/lib.rs
+++ b/crates/ra_prof/src/lib.rs
@@ -19,6 +19,10 @@ pub use crate::{
19#[global_allocator] 19#[global_allocator]
20static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; 20static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
21 21
22#[cfg(all(feature = "mimalloc"))]
23#[global_allocator]
24static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;
25
22/// Prints backtrace to stderr, useful for debugging. 26/// Prints backtrace to stderr, useful for debugging.
23#[cfg(feature = "backtrace")] 27#[cfg(feature = "backtrace")]
24pub fn print_backtrace() { 28pub fn print_backtrace() {
@@ -43,6 +47,7 @@ pub struct Scope {
43} 47}
44 48
45impl Scope { 49impl Scope {
50 #[must_use]
46 pub fn enter() -> Scope { 51 pub fn enter() -> Scope {
47 let prev = IN_SCOPE.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), true)); 52 let prev = IN_SCOPE.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), true));
48 Scope { prev } 53 Scope { prev }
@@ -65,7 +70,8 @@ impl Drop for Scope {
65/// 2. Build with `cpu_profiler` feature. 70/// 2. Build with `cpu_profiler` feature.
66/// 3. Tun the code, the *raw* output would be in the `./out.profile` file. 71/// 3. Tun the code, the *raw* output would be in the `./out.profile` file.
67/// 4. Install pprof for visualization (https://github.com/google/pprof). 72/// 4. Install pprof for visualization (https://github.com/google/pprof).
68/// 5. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results. 73/// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000`
74/// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results.
69/// 75///
70/// For example, here's how I run profiling on NixOS: 76/// For example, here's how I run profiling on NixOS:
71/// 77///
@@ -73,11 +79,16 @@ impl Drop for Scope {
73/// $ nix-shell -p gperftools --run \ 79/// $ nix-shell -p gperftools --run \
74/// 'cargo run --release -p rust-analyzer -- parse < ~/projects/rustbench/parser.rs > /dev/null' 80/// 'cargo run --release -p rust-analyzer -- parse < ~/projects/rustbench/parser.rs > /dev/null'
75/// ``` 81/// ```
82///
83/// See this diff for how to profile completions:
84///
85/// https://github.com/rust-analyzer/rust-analyzer/pull/5306
76#[derive(Debug)] 86#[derive(Debug)]
77pub struct CpuProfiler { 87pub struct CpuProfiler {
78 _private: (), 88 _private: (),
79} 89}
80 90
91#[must_use]
81pub fn cpu_profiler() -> CpuProfiler { 92pub fn cpu_profiler() -> CpuProfiler {
82 #[cfg(feature = "cpu_profiler")] 93 #[cfg(feature = "cpu_profiler")]
83 { 94 {
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml
index e4a60f4c0..827eb7e28 100644
--- a/crates/ra_project_model/Cargo.toml
+++ b/crates/ra_project_model/Cargo.toml
@@ -3,6 +3,7 @@ edition = "2018"
3name = "ra_project_model" 3name = "ra_project_model"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
@@ -18,6 +19,8 @@ ra_cfg = { path = "../ra_cfg" }
18ra_db = { path = "../ra_db" } 19ra_db = { path = "../ra_db" }
19ra_toolchain = { path = "../ra_toolchain" } 20ra_toolchain = { path = "../ra_toolchain" }
20ra_proc_macro = { path = "../ra_proc_macro" } 21ra_proc_macro = { path = "../ra_proc_macro" }
22paths = { path = "../paths" }
23stdx = { path = "../stdx" }
21 24
22serde = { version = "1.0.106", features = ["derive"] } 25serde = { version = "1.0.106", features = ["derive"] }
23serde_json = "1.0.48" 26serde_json = "1.0.48"
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index a306ce95f..4182ca156 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -9,6 +9,7 @@ use std::{
9 9
10use anyhow::{Context, Result}; 10use anyhow::{Context, Result};
11use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; 11use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
12use paths::{AbsPath, AbsPathBuf};
12use ra_arena::{Arena, Idx}; 13use ra_arena::{Arena, Idx};
13use ra_db::Edition; 14use ra_db::Edition;
14use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
@@ -20,11 +21,14 @@ use rustc_hash::FxHashMap;
20/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates, 21/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
21/// while this knows about `Packages` & `Targets`: purely cargo-related 22/// while this knows about `Packages` & `Targets`: purely cargo-related
22/// concepts. 23/// concepts.
23#[derive(Debug, Clone)] 24///
25/// We use absolute paths here, `cargo metadata` guarantees to always produce
26/// abs paths.
27#[derive(Debug, Clone, Eq, PartialEq)]
24pub struct CargoWorkspace { 28pub struct CargoWorkspace {
25 packages: Arena<PackageData>, 29 packages: Arena<PackageData>,
26 targets: Arena<TargetData>, 30 targets: Arena<TargetData>,
27 workspace_root: PathBuf, 31 workspace_root: AbsPathBuf,
28} 32}
29 33
30impl ops::Index<Package> for CargoWorkspace { 34impl ops::Index<Package> for CargoWorkspace {
@@ -41,7 +45,7 @@ impl ops::Index<Target> for CargoWorkspace {
41 } 45 }
42} 46}
43 47
44#[derive(Clone, Debug, PartialEq, Eq)] 48#[derive(Default, Clone, Debug, PartialEq, Eq)]
45pub struct CargoConfig { 49pub struct CargoConfig {
46 /// Do not activate the `default` feature. 50 /// Do not activate the `default` feature.
47 pub no_default_features: bool, 51 pub no_default_features: bool,
@@ -60,48 +64,36 @@ pub struct CargoConfig {
60 pub target: Option<String>, 64 pub target: Option<String>,
61} 65}
62 66
63impl Default for CargoConfig {
64 fn default() -> Self {
65 CargoConfig {
66 no_default_features: false,
67 all_features: true,
68 features: Vec::new(),
69 load_out_dirs_from_check: false,
70 target: None,
71 }
72 }
73}
74
75pub type Package = Idx<PackageData>; 67pub type Package = Idx<PackageData>;
76 68
77pub type Target = Idx<TargetData>; 69pub type Target = Idx<TargetData>;
78 70
79#[derive(Debug, Clone)] 71#[derive(Debug, Clone, Eq, PartialEq)]
80pub struct PackageData { 72pub struct PackageData {
81 pub version: String, 73 pub version: String,
82 pub name: String, 74 pub name: String,
83 pub manifest: PathBuf, 75 pub manifest: AbsPathBuf,
84 pub targets: Vec<Target>, 76 pub targets: Vec<Target>,
85 pub is_member: bool, 77 pub is_member: bool,
86 pub dependencies: Vec<PackageDependency>, 78 pub dependencies: Vec<PackageDependency>,
87 pub edition: Edition, 79 pub edition: Edition,
88 pub features: Vec<String>, 80 pub features: Vec<String>,
89 pub cfgs: Vec<String>, 81 pub cfgs: Vec<String>,
90 pub out_dir: Option<PathBuf>, 82 pub out_dir: Option<AbsPathBuf>,
91 pub proc_macro_dylib_path: Option<PathBuf>, 83 pub proc_macro_dylib_path: Option<AbsPathBuf>,
92} 84}
93 85
94#[derive(Debug, Clone)] 86#[derive(Debug, Clone, Eq, PartialEq)]
95pub struct PackageDependency { 87pub struct PackageDependency {
96 pub pkg: Package, 88 pub pkg: Package,
97 pub name: String, 89 pub name: String,
98} 90}
99 91
100#[derive(Debug, Clone)] 92#[derive(Debug, Clone, Eq, PartialEq)]
101pub struct TargetData { 93pub struct TargetData {
102 pub package: Package, 94 pub package: Package,
103 pub name: String, 95 pub name: String,
104 pub root: PathBuf, 96 pub root: AbsPathBuf,
105 pub kind: TargetKind, 97 pub kind: TargetKind,
106 pub is_proc_macro: bool, 98 pub is_proc_macro: bool,
107} 99}
@@ -135,19 +127,19 @@ impl TargetKind {
135} 127}
136 128
137impl PackageData { 129impl PackageData {
138 pub fn root(&self) -> &Path { 130 pub fn root(&self) -> &AbsPath {
139 self.manifest.parent().unwrap() 131 self.manifest.parent().unwrap()
140 } 132 }
141} 133}
142 134
143impl CargoWorkspace { 135impl CargoWorkspace {
144 pub fn from_cargo_metadata( 136 pub fn from_cargo_metadata(
145 cargo_toml: &Path, 137 cargo_toml: &AbsPath,
146 cargo_features: &CargoConfig, 138 cargo_features: &CargoConfig,
147 ) -> Result<CargoWorkspace> { 139 ) -> Result<CargoWorkspace> {
148 let mut meta = MetadataCommand::new(); 140 let mut meta = MetadataCommand::new();
149 meta.cargo_path(ra_toolchain::cargo()); 141 meta.cargo_path(ra_toolchain::cargo());
150 meta.manifest_path(cargo_toml); 142 meta.manifest_path(cargo_toml.to_path_buf());
151 if cargo_features.all_features { 143 if cargo_features.all_features {
152 meta.features(CargoOpt::AllFeatures); 144 meta.features(CargoOpt::AllFeatures);
153 } else if cargo_features.no_default_features { 145 } else if cargo_features.no_default_features {
@@ -158,12 +150,12 @@ impl CargoWorkspace {
158 meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone())); 150 meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone()));
159 } 151 }
160 if let Some(parent) = cargo_toml.parent() { 152 if let Some(parent) = cargo_toml.parent() {
161 meta.current_dir(parent); 153 meta.current_dir(parent.to_path_buf());
162 } 154 }
163 if let Some(target) = cargo_features.target.as_ref() { 155 if let Some(target) = cargo_features.target.as_ref() {
164 meta.other_options(vec![String::from("--filter-platform"), target.clone()]); 156 meta.other_options(vec![String::from("--filter-platform"), target.clone()]);
165 } 157 }
166 let meta = meta.exec().with_context(|| { 158 let mut meta = meta.exec().with_context(|| {
167 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) 159 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display())
168 })?; 160 })?;
169 161
@@ -183,6 +175,7 @@ impl CargoWorkspace {
183 175
184 let ws_members = &meta.workspace_members; 176 let ws_members = &meta.workspace_members;
185 177
178 meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
186 for meta_pkg in meta.packages { 179 for meta_pkg in meta.packages {
187 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = 180 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
188 meta_pkg; 181 meta_pkg;
@@ -193,7 +186,7 @@ impl CargoWorkspace {
193 let pkg = packages.alloc(PackageData { 186 let pkg = packages.alloc(PackageData {
194 name, 187 name,
195 version: version.to_string(), 188 version: version.to_string(),
196 manifest: manifest_path, 189 manifest: AbsPathBuf::assert(manifest_path),
197 targets: Vec::new(), 190 targets: Vec::new(),
198 is_member, 191 is_member,
199 edition, 192 edition,
@@ -210,7 +203,7 @@ impl CargoWorkspace {
210 let tgt = targets.alloc(TargetData { 203 let tgt = targets.alloc(TargetData {
211 package: pkg, 204 package: pkg,
212 name: meta_tgt.name, 205 name: meta_tgt.name,
213 root: meta_tgt.src_path.clone(), 206 root: AbsPathBuf::assert(meta_tgt.src_path.clone()),
214 kind: TargetKind::new(meta_tgt.kind.as_slice()), 207 kind: TargetKind::new(meta_tgt.kind.as_slice()),
215 is_proc_macro, 208 is_proc_macro,
216 }); 209 });
@@ -218,7 +211,7 @@ impl CargoWorkspace {
218 } 211 }
219 } 212 }
220 let resolve = meta.resolve.expect("metadata executed with deps"); 213 let resolve = meta.resolve.expect("metadata executed with deps");
221 for node in resolve.nodes { 214 for mut node in resolve.nodes {
222 let source = match pkg_by_id.get(&node.id) { 215 let source = match pkg_by_id.get(&node.id) {
223 Some(&src) => src, 216 Some(&src) => src,
224 // FIXME: replace this and a similar branch below with `.unwrap`, once 217 // FIXME: replace this and a similar branch below with `.unwrap`, once
@@ -229,6 +222,7 @@ impl CargoWorkspace {
229 continue; 222 continue;
230 } 223 }
231 }; 224 };
225 node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg));
232 for dep_node in node.deps { 226 for dep_node in node.deps {
233 let pkg = match pkg_by_id.get(&dep_node.pkg) { 227 let pkg = match pkg_by_id.get(&dep_node.pkg) {
234 Some(&pkg) => pkg, 228 Some(&pkg) => pkg,
@@ -246,21 +240,22 @@ impl CargoWorkspace {
246 packages[source].features.extend(node.features); 240 packages[source].features.extend(node.features);
247 } 241 }
248 242
249 Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root }) 243 let workspace_root = AbsPathBuf::assert(meta.workspace_root);
244 Ok(CargoWorkspace { packages, targets, workspace_root: workspace_root })
250 } 245 }
251 246
252 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a { 247 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
253 self.packages.iter().map(|(id, _pkg)| id) 248 self.packages.iter().map(|(id, _pkg)| id)
254 } 249 }
255 250
256 pub fn target_by_root(&self, root: &Path) -> Option<Target> { 251 pub fn target_by_root(&self, root: &AbsPath) -> Option<Target> {
257 self.packages() 252 self.packages()
258 .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| self[it].root == root)) 253 .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root))
259 .next() 254 .next()
260 .copied() 255 .copied()
261 } 256 }
262 257
263 pub fn workspace_root(&self) -> &Path { 258 pub fn workspace_root(&self) -> &AbsPath {
264 &self.workspace_root 259 &self.workspace_root
265 } 260 }
266 261
@@ -279,8 +274,8 @@ impl CargoWorkspace {
279 274
280#[derive(Debug, Clone, Default)] 275#[derive(Debug, Clone, Default)]
281pub struct ExternResources { 276pub struct ExternResources {
282 out_dirs: FxHashMap<PackageId, PathBuf>, 277 out_dirs: FxHashMap<PackageId, AbsPathBuf>,
283 proc_dylib_paths: FxHashMap<PackageId, PathBuf>, 278 proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>,
284 cfgs: FxHashMap<PackageId, Vec<String>>, 279 cfgs: FxHashMap<PackageId, Vec<String>>,
285} 280}
286 281
@@ -308,8 +303,13 @@ pub fn load_extern_resources(
308 if let Ok(message) = message { 303 if let Ok(message) = message {
309 match message { 304 match message {
310 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { 305 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => {
311 res.out_dirs.insert(package_id.clone(), out_dir); 306 // cargo_metadata crate returns default (empty) path for
312 res.cfgs.insert(package_id, cfgs); 307 // older cargos, which is not absolute, so work around that.
308 if out_dir != PathBuf::default() {
309 let out_dir = AbsPathBuf::assert(out_dir);
310 res.out_dirs.insert(package_id.clone(), out_dir);
311 res.cfgs.insert(package_id, cfgs);
312 }
313 } 313 }
314 Message::CompilerArtifact(message) => { 314 Message::CompilerArtifact(message) => {
315 if message.target.kind.contains(&"proc-macro".to_string()) { 315 if message.target.kind.contains(&"proc-macro".to_string()) {
@@ -317,7 +317,8 @@ pub fn load_extern_resources(
317 // Skip rmeta file 317 // Skip rmeta file
318 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name)) 318 if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
319 { 319 {
320 res.proc_dylib_paths.insert(package_id, filename.clone()); 320 let filename = AbsPathBuf::assert(filename.clone());
321 res.proc_dylib_paths.insert(package_id, filename);
321 } 322 }
322 } 323 }
323 } 324 }
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs
deleted file mode 100644
index b030c8a6a..000000000
--- a/crates/ra_project_model/src/json_project.rs
+++ /dev/null
@@ -1,56 +0,0 @@
1//! FIXME: write short doc here
2
3use std::path::PathBuf;
4
5use rustc_hash::{FxHashMap, FxHashSet};
6use serde::Deserialize;
7
8/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in
9/// all roots. Roots might be nested.
10#[derive(Clone, Debug, Deserialize)]
11#[serde(transparent)]
12pub struct Root {
13 pub(crate) path: PathBuf,
14}
15
16/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
17/// useful in creating the crate graph.
18#[derive(Clone, Debug, Deserialize)]
19pub struct Crate {
20 pub(crate) root_module: PathBuf,
21 pub(crate) edition: Edition,
22 pub(crate) deps: Vec<Dep>,
23 pub(crate) atom_cfgs: FxHashSet<String>,
24 pub(crate) key_value_cfgs: FxHashMap<String, String>,
25 pub(crate) out_dir: Option<PathBuf>,
26 pub(crate) proc_macro_dylib_path: Option<PathBuf>,
27}
28
29#[derive(Clone, Copy, Debug, Deserialize)]
30#[serde(rename = "edition")]
31pub enum Edition {
32 #[serde(rename = "2015")]
33 Edition2015,
34 #[serde(rename = "2018")]
35 Edition2018,
36}
37
38/// Identifies a crate by position in the crates array.
39#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
40#[serde(transparent)]
41pub struct CrateId(pub usize);
42
43/// A dependency of a crate, identified by its id in the crates array and name.
44#[derive(Clone, Debug, Deserialize)]
45pub struct Dep {
46 #[serde(rename = "crate")]
47 pub(crate) krate: CrateId,
48 pub(crate) name: String,
49}
50
51/// Roots and crates that compose this Rust project.
52#[derive(Clone, Debug, Deserialize)]
53pub struct JsonProject {
54 pub(crate) roots: Vec<Root>,
55 pub(crate) crates: Vec<Crate>,
56}
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index a2e9f65ef..b9c5424bf 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -1,35 +1,35 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3mod cargo_workspace; 3mod cargo_workspace;
4mod json_project; 4mod project_json;
5mod sysroot; 5mod sysroot;
6 6
7use std::{ 7use std::{
8 fs::{read_dir, File, ReadDir}, 8 fs::{self, read_dir, ReadDir},
9 io::{self, BufReader}, 9 io,
10 path::{Path, PathBuf}, 10 path::Path,
11 process::{Command, Output}, 11 process::{Command, Output},
12}; 12};
13 13
14use anyhow::{bail, Context, Result}; 14use anyhow::{bail, Context, Result};
15use paths::{AbsPath, AbsPathBuf};
15use ra_cfg::CfgOptions; 16use ra_cfg::CfgOptions;
16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; 17use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId};
17use rustc_hash::FxHashMap; 18use rustc_hash::{FxHashMap, FxHashSet};
18use serde_json::from_reader;
19 19
20pub use crate::{ 20pub use crate::{
21 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, 21 cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind},
22 json_project::JsonProject, 22 project_json::{ProjectJson, ProjectJsonData},
23 sysroot::Sysroot, 23 sysroot::Sysroot,
24}; 24};
25pub use ra_proc_macro::ProcMacroClient; 25pub use ra_proc_macro::ProcMacroClient;
26 26
27#[derive(Debug, Clone)] 27#[derive(Debug, Clone, Eq, PartialEq)]
28pub enum ProjectWorkspace { 28pub enum ProjectWorkspace {
29 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 29 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
30 Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, 30 Cargo { cargo: CargoWorkspace, sysroot: Sysroot },
31 /// Project workspace was manually specified using a `rust-project.json` file. 31 /// Project workspace was manually specified using a `rust-project.json` file.
32 Json { project: JsonProject }, 32 Json { project: ProjectJson },
33} 33}
34 34
35/// `PackageRoot` describes a package root folder. 35/// `PackageRoot` describes a package root folder.
@@ -38,44 +38,48 @@ pub enum ProjectWorkspace {
38#[derive(Debug, Clone)] 38#[derive(Debug, Clone)]
39pub struct PackageRoot { 39pub struct PackageRoot {
40 /// Path to the root folder 40 /// Path to the root folder
41 path: PathBuf, 41 path: AbsPathBuf,
42 /// Is a member of the current workspace 42 /// Is a member of the current workspace
43 is_member: bool, 43 is_member: bool,
44 out_dir: Option<AbsPathBuf>,
44} 45}
45impl PackageRoot { 46impl PackageRoot {
46 pub fn new_member(path: PathBuf) -> PackageRoot { 47 pub fn new_member(path: AbsPathBuf) -> PackageRoot {
47 Self { path, is_member: true } 48 Self { path, is_member: true, out_dir: None }
48 } 49 }
49 pub fn new_non_member(path: PathBuf) -> PackageRoot { 50 pub fn new_non_member(path: AbsPathBuf) -> PackageRoot {
50 Self { path, is_member: false } 51 Self { path, is_member: false, out_dir: None }
51 } 52 }
52 pub fn path(&self) -> &Path { 53 pub fn path(&self) -> &AbsPath {
53 &self.path 54 &self.path
54 } 55 }
56 pub fn out_dir(&self) -> Option<&AbsPath> {
57 self.out_dir.as_deref()
58 }
55 pub fn is_member(&self) -> bool { 59 pub fn is_member(&self) -> bool {
56 self.is_member 60 self.is_member
57 } 61 }
58} 62}
59 63
60#[derive(Debug, Clone, PartialEq, Eq, Hash)] 64#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
61pub enum ProjectRoot { 65pub enum ProjectManifest {
62 ProjectJson(PathBuf), 66 ProjectJson(AbsPathBuf),
63 CargoToml(PathBuf), 67 CargoToml(AbsPathBuf),
64} 68}
65 69
66impl ProjectRoot { 70impl ProjectManifest {
67 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> { 71 pub fn from_manifest_file(path: AbsPathBuf) -> Result<ProjectManifest> {
68 if path.ends_with("rust-project.json") { 72 if path.ends_with("rust-project.json") {
69 return Ok(ProjectRoot::ProjectJson(path)); 73 return Ok(ProjectManifest::ProjectJson(path));
70 } 74 }
71 if path.ends_with("Cargo.toml") { 75 if path.ends_with("Cargo.toml") {
72 return Ok(ProjectRoot::CargoToml(path)); 76 return Ok(ProjectManifest::CargoToml(path));
73 } 77 }
74 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) 78 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
75 } 79 }
76 80
77 pub fn discover_single(path: &Path) -> Result<ProjectRoot> { 81 pub fn discover_single(path: &AbsPath) -> Result<ProjectManifest> {
78 let mut candidates = ProjectRoot::discover(path)?; 82 let mut candidates = ProjectManifest::discover(path)?;
79 let res = match candidates.pop() { 83 let res = match candidates.pop() {
80 None => bail!("no projects"), 84 None => bail!("no projects"),
81 Some(it) => it, 85 Some(it) => it,
@@ -87,23 +91,23 @@ impl ProjectRoot {
87 Ok(res) 91 Ok(res)
88 } 92 }
89 93
90 pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { 94 pub fn discover(path: &AbsPath) -> io::Result<Vec<ProjectManifest>> {
91 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { 95 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
92 return Ok(vec![ProjectRoot::ProjectJson(project_json)]); 96 return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
93 } 97 }
94 return find_cargo_toml(path) 98 return find_cargo_toml(path)
95 .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); 99 .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());
96 100
97 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { 101 fn find_cargo_toml(path: &AbsPath) -> io::Result<Vec<AbsPathBuf>> {
98 match find_in_parent_dirs(path, "Cargo.toml") { 102 match find_in_parent_dirs(path, "Cargo.toml") {
99 Some(it) => Ok(vec![it]), 103 Some(it) => Ok(vec![it]),
100 None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)), 104 None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)),
101 } 105 }
102 } 106 }
103 107
104 fn find_in_parent_dirs(path: &Path, target_file_name: &str) -> Option<PathBuf> { 108 fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option<AbsPathBuf> {
105 if path.ends_with(target_file_name) { 109 if path.ends_with(target_file_name) {
106 return Some(path.to_owned()); 110 return Some(path.to_path_buf());
107 } 111 }
108 112
109 let mut curr = Some(path); 113 let mut curr = Some(path);
@@ -119,37 +123,50 @@ impl ProjectRoot {
119 None 123 None
120 } 124 }
121 125
122 fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> { 126 fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<AbsPathBuf> {
123 // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects 127 // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
124 entities 128 entities
125 .filter_map(Result::ok) 129 .filter_map(Result::ok)
126 .map(|it| it.path().join("Cargo.toml")) 130 .map(|it| it.path().join("Cargo.toml"))
127 .filter(|it| it.exists()) 131 .filter(|it| it.exists())
132 .map(AbsPathBuf::assert)
128 .collect() 133 .collect()
129 } 134 }
130 } 135 }
136
137 pub fn discover_all(paths: &[impl AsRef<AbsPath>]) -> Vec<ProjectManifest> {
138 let mut res = paths
139 .iter()
140 .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
141 .flatten()
142 .collect::<FxHashSet<_>>()
143 .into_iter()
144 .collect::<Vec<_>>();
145 res.sort();
146 res
147 }
131} 148}
132 149
133impl ProjectWorkspace { 150impl ProjectWorkspace {
134 pub fn load( 151 pub fn load(
135 root: ProjectRoot, 152 manifest: ProjectManifest,
136 cargo_features: &CargoConfig, 153 cargo_config: &CargoConfig,
137 with_sysroot: bool, 154 with_sysroot: bool,
138 ) -> Result<ProjectWorkspace> { 155 ) -> Result<ProjectWorkspace> {
139 let res = match root { 156 let res = match manifest {
140 ProjectRoot::ProjectJson(project_json) => { 157 ProjectManifest::ProjectJson(project_json) => {
141 let file = File::open(&project_json).with_context(|| { 158 let file = fs::read_to_string(&project_json).with_context(|| {
142 format!("Failed to open json file {}", project_json.display()) 159 format!("Failed to read json file {}", project_json.display())
143 })?; 160 })?;
144 let reader = BufReader::new(file); 161 let data = serde_json::from_str(&file).with_context(|| {
145 ProjectWorkspace::Json { 162 format!("Failed to deserialize json file {}", project_json.display())
146 project: from_reader(reader).with_context(|| { 163 })?;
147 format!("Failed to deserialize json file {}", project_json.display()) 164 let project_location = project_json.parent().unwrap().to_path_buf();
148 })?, 165 let project = ProjectJson::new(&project_location, data);
149 } 166 ProjectWorkspace::Json { project }
150 } 167 }
151 ProjectRoot::CargoToml(cargo_toml) => { 168 ProjectManifest::CargoToml(cargo_toml) => {
152 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) 169 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_config)
153 .with_context(|| { 170 .with_context(|| {
154 format!( 171 format!(
155 "Failed to read Cargo metadata from Cargo.toml file {}", 172 "Failed to read Cargo metadata from Cargo.toml file {}",
@@ -186,6 +203,7 @@ impl ProjectWorkspace {
186 .map(|pkg| PackageRoot { 203 .map(|pkg| PackageRoot {
187 path: cargo[pkg].root().to_path_buf(), 204 path: cargo[pkg].root().to_path_buf(),
188 is_member: cargo[pkg].is_member, 205 is_member: cargo[pkg].is_member,
206 out_dir: cargo[pkg].out_dir.clone(),
189 }) 207 })
190 .chain(sysroot.crates().map(|krate| { 208 .chain(sysroot.crates().map(|krate| {
191 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) 209 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf())
@@ -194,18 +212,7 @@ impl ProjectWorkspace {
194 } 212 }
195 } 213 }
196 214
197 pub fn out_dirs(&self) -> Vec<PathBuf> { 215 pub fn proc_macro_dylib_paths(&self) -> Vec<AbsPathBuf> {
198 match self {
199 ProjectWorkspace::Json { project } => {
200 project.crates.iter().filter_map(|krate| krate.out_dir.as_ref()).cloned().collect()
201 }
202 ProjectWorkspace::Cargo { cargo, sysroot: _ } => {
203 cargo.packages().filter_map(|pkg| cargo[pkg].out_dir.as_ref()).cloned().collect()
204 }
205 }
206 }
207
208 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> {
209 match self { 216 match self {
210 ProjectWorkspace::Json { project } => project 217 ProjectWorkspace::Json { project } => project
211 .crates 218 .crates
@@ -223,7 +230,7 @@ impl ProjectWorkspace {
223 230
224 pub fn n_packages(&self) -> usize { 231 pub fn n_packages(&self) -> usize {
225 match self { 232 match self {
226 ProjectWorkspace::Json { project } => project.crates.len(), 233 ProjectWorkspace::Json { project, .. } => project.crates.len(),
227 ProjectWorkspace::Cargo { cargo, sysroot } => { 234 ProjectWorkspace::Cargo { cargo, sysroot } => {
228 cargo.packages().len() + sysroot.crates().len() 235 cargo.packages().len() + sysroot.crates().len()
229 } 236 }
@@ -232,61 +239,51 @@ impl ProjectWorkspace {
232 239
233 pub fn to_crate_graph( 240 pub fn to_crate_graph(
234 &self, 241 &self,
235 default_cfg_options: &CfgOptions, 242 target: Option<&str>,
236 extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>,
237 proc_macro_client: &ProcMacroClient, 243 proc_macro_client: &ProcMacroClient,
238 load: &mut dyn FnMut(&Path) -> Option<FileId>, 244 load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
239 ) -> CrateGraph { 245 ) -> CrateGraph {
240 let mut crate_graph = CrateGraph::default(); 246 let mut crate_graph = CrateGraph::default();
241 match self { 247 match self {
242 ProjectWorkspace::Json { project } => { 248 ProjectWorkspace::Json { project } => {
249 let mut target_cfg_map = FxHashMap::<Option<&str>, CfgOptions>::default();
243 let crates: FxHashMap<_, _> = project 250 let crates: FxHashMap<_, _> = project
244 .crates 251 .crates
245 .iter() 252 .iter()
246 .enumerate() 253 .enumerate()
247 .filter_map(|(seq_index, krate)| { 254 .filter_map(|(seq_index, krate)| {
248 let file_id = load(&krate.root_module)?; 255 let file_path = &krate.root_module;
249 let edition = match krate.edition { 256 let file_id = load(&file_path)?;
250 json_project::Edition::Edition2015 => Edition::Edition2015,
251 json_project::Edition::Edition2018 => Edition::Edition2018,
252 };
253 let cfg_options = {
254 let mut opts = default_cfg_options.clone();
255 for name in &krate.atom_cfgs {
256 opts.insert_atom(name.into());
257 }
258 for (key, value) in &krate.key_value_cfgs {
259 opts.insert_key_value(key.into(), value.into());
260 }
261 opts
262 };
263 257
264 let mut env = Env::default(); 258 let mut env = Env::default();
265 let mut extern_source = ExternSource::default();
266 if let Some(out_dir) = &krate.out_dir { 259 if let Some(out_dir) = &krate.out_dir {
267 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() 260 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
268 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { 261 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
269 env.set("OUT_DIR", out_dir); 262 env.set("OUT_DIR", out_dir);
270 } 263 }
271 if let Some(&extern_source_id) = extern_source_roots.get(out_dir) {
272 extern_source.set_extern_path(&out_dir, extern_source_id);
273 }
274 } 264 }
275 let proc_macro = krate 265 let proc_macro = krate
276 .proc_macro_dylib_path 266 .proc_macro_dylib_path
277 .clone() 267 .clone()
278 .map(|it| proc_macro_client.by_dylib_path(&it)); 268 .map(|it| proc_macro_client.by_dylib_path(&it));
269
270 let target = krate.target.as_deref().or(target);
271 let target_cfgs = target_cfg_map
272 .entry(target.clone())
273 .or_insert_with(|| get_rustc_cfg_options(target.as_deref()));
274 let mut cfg_options = krate.cfg.clone();
275 cfg_options.append(target_cfgs);
276
279 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env 277 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env
280 Some(( 278 Some((
281 json_project::CrateId(seq_index), 279 CrateId(seq_index as u32),
282 crate_graph.add_crate_root( 280 crate_graph.add_crate_root(
283 file_id, 281 file_id,
284 edition, 282 krate.edition,
285 // FIXME json definitions can store the crate name 283 // FIXME json definitions can store the crate name
286 None, 284 None,
287 cfg_options, 285 cfg_options,
288 env, 286 env,
289 extern_source,
290 proc_macro.unwrap_or_default(), 287 proc_macro.unwrap_or_default(),
291 ), 288 ),
292 )) 289 ))
@@ -295,15 +292,12 @@ impl ProjectWorkspace {
295 292
296 for (id, krate) in project.crates.iter().enumerate() { 293 for (id, krate) in project.crates.iter().enumerate() {
297 for dep in &krate.deps { 294 for dep in &krate.deps {
298 let from_crate_id = json_project::CrateId(id); 295 let from_crate_id = CrateId(id as u32);
299 let to_crate_id = dep.krate; 296 let to_crate_id = dep.crate_id;
300 if let (Some(&from), Some(&to)) = 297 if let (Some(&from), Some(&to)) =
301 (crates.get(&from_crate_id), crates.get(&to_crate_id)) 298 (crates.get(&from_crate_id), crates.get(&to_crate_id))
302 { 299 {
303 if crate_graph 300 if crate_graph.add_dep(from, dep.name.clone(), to).is_err() {
304 .add_dep(from, CrateName::new(&dep.name).unwrap(), to)
305 .is_err()
306 {
307 log::error!( 301 log::error!(
308 "cyclic dependency {:?} -> {:?}", 302 "cyclic dependency {:?} -> {:?}",
309 from_crate_id, 303 from_crate_id,
@@ -315,31 +309,22 @@ impl ProjectWorkspace {
315 } 309 }
316 } 310 }
317 ProjectWorkspace::Cargo { cargo, sysroot } => { 311 ProjectWorkspace::Cargo { cargo, sysroot } => {
312 let mut cfg_options = get_rustc_cfg_options(target);
313
318 let sysroot_crates: FxHashMap<_, _> = sysroot 314 let sysroot_crates: FxHashMap<_, _> = sysroot
319 .crates() 315 .crates()
320 .filter_map(|krate| { 316 .filter_map(|krate| {
321 let file_id = load(&sysroot[krate].root)?; 317 let file_id = load(&sysroot[krate].root)?;
322 318
323 // Crates from sysroot have `cfg(test)` disabled
324 let cfg_options = {
325 let mut opts = default_cfg_options.clone();
326 opts.remove_atom("test");
327 opts
328 };
329
330 let env = Env::default(); 319 let env = Env::default();
331 let extern_source = ExternSource::default();
332 let proc_macro = vec![]; 320 let proc_macro = vec![];
333 let crate_name = CrateName::new(&sysroot[krate].name) 321 let name = sysroot[krate].name.clone();
334 .expect("Sysroot crate names should not contain dashes");
335
336 let crate_id = crate_graph.add_crate_root( 322 let crate_id = crate_graph.add_crate_root(
337 file_id, 323 file_id,
338 Edition::Edition2018, 324 Edition::Edition2018,
339 Some(crate_name), 325 Some(name),
340 cfg_options, 326 cfg_options.clone(),
341 env, 327 env,
342 extern_source,
343 proc_macro, 328 proc_macro,
344 ); 329 );
345 Some((krate, crate_id)) 330 Some((krate, crate_id))
@@ -368,6 +353,10 @@ impl ProjectWorkspace {
368 353
369 let mut pkg_to_lib_crate = FxHashMap::default(); 354 let mut pkg_to_lib_crate = FxHashMap::default();
370 let mut pkg_crates = FxHashMap::default(); 355 let mut pkg_crates = FxHashMap::default();
356
357 // Add test cfg for non-sysroot crates
358 cfg_options.insert_atom("test".into());
359
371 // Next, create crates for each package, target pair 360 // Next, create crates for each package, target pair
372 for pkg in cargo.packages() { 361 for pkg in cargo.packages() {
373 let mut lib_tgt = None; 362 let mut lib_tgt = None;
@@ -376,7 +365,7 @@ impl ProjectWorkspace {
376 if let Some(file_id) = load(root) { 365 if let Some(file_id) = load(root) {
377 let edition = cargo[pkg].edition; 366 let edition = cargo[pkg].edition;
378 let cfg_options = { 367 let cfg_options = {
379 let mut opts = default_cfg_options.clone(); 368 let mut opts = cfg_options.clone();
380 for feature in cargo[pkg].features.iter() { 369 for feature in cargo[pkg].features.iter() {
381 opts.insert_key_value("feature".into(), feature.into()); 370 opts.insert_key_value("feature".into(), feature.into());
382 } 371 }
@@ -392,15 +381,11 @@ impl ProjectWorkspace {
392 opts 381 opts
393 }; 382 };
394 let mut env = Env::default(); 383 let mut env = Env::default();
395 let mut extern_source = ExternSource::default();
396 if let Some(out_dir) = &cargo[pkg].out_dir { 384 if let Some(out_dir) = &cargo[pkg].out_dir {
397 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!() 385 // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
398 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) { 386 if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
399 env.set("OUT_DIR", out_dir); 387 env.set("OUT_DIR", out_dir);
400 } 388 }
401 if let Some(&extern_source_id) = extern_source_roots.get(out_dir) {
402 extern_source.set_extern_path(&out_dir, extern_source_id);
403 }
404 } 389 }
405 let proc_macro = cargo[pkg] 390 let proc_macro = cargo[pkg]
406 .proc_macro_dylib_path 391 .proc_macro_dylib_path
@@ -411,10 +396,9 @@ impl ProjectWorkspace {
411 let crate_id = crate_graph.add_crate_root( 396 let crate_id = crate_graph.add_crate_root(
412 file_id, 397 file_id,
413 edition, 398 edition,
414 Some(CrateName::normalize_dashes(&cargo[pkg].name)), 399 Some(cargo[pkg].name.clone()),
415 cfg_options, 400 cfg_options,
416 env, 401 env,
417 extern_source,
418 proc_macro.clone(), 402 proc_macro.clone(),
419 ); 403 );
420 if cargo[tgt].kind == TargetKind::Lib { 404 if cargo[tgt].kind == TargetKind::Lib {
@@ -520,20 +504,20 @@ impl ProjectWorkspace {
520 crate_graph 504 crate_graph
521 } 505 }
522 506
523 pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> { 507 pub fn workspace_root_for(&self, path: &Path) -> Option<&AbsPath> {
524 match self { 508 match self {
525 ProjectWorkspace::Cargo { cargo, .. } => { 509 ProjectWorkspace::Cargo { cargo, .. } => {
526 Some(cargo.workspace_root()).filter(|root| path.starts_with(root)) 510 Some(cargo.workspace_root()).filter(|root| path.starts_with(root))
527 } 511 }
528 ProjectWorkspace::Json { project: JsonProject { roots, .. } } => roots 512 ProjectWorkspace::Json { project: ProjectJson { roots, .. }, .. } => roots
529 .iter() 513 .iter()
530 .find(|root| path.starts_with(&root.path)) 514 .find(|root| path.starts_with(&root.path))
531 .map(|root| root.path.as_ref()), 515 .map(|root| root.path.as_path()),
532 } 516 }
533 } 517 }
534} 518}
535 519
536pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { 520fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions {
537 let mut cfg_options = CfgOptions::default(); 521 let mut cfg_options = CfgOptions::default();
538 522
539 // Some nightly-only cfgs, which are required for stdlib 523 // Some nightly-only cfgs, which are required for stdlib
@@ -551,7 +535,7 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions {
551 let mut cmd = Command::new(ra_toolchain::rustc()); 535 let mut cmd = Command::new(ra_toolchain::rustc());
552 cmd.args(&["--print", "cfg", "-O"]); 536 cmd.args(&["--print", "cfg", "-O"]);
553 if let Some(target) = target { 537 if let Some(target) = target {
554 cmd.args(&["--target", target.as_str()]); 538 cmd.args(&["--target", target]);
555 } 539 }
556 let output = output(cmd)?; 540 let output = output(cmd)?;
557 Ok(String::from_utf8(output.stdout)?) 541 Ok(String::from_utf8(output.stdout)?)
@@ -573,6 +557,8 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions {
573 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), 557 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
574 } 558 }
575 559
560 cfg_options.insert_atom("debug_assertions".into());
561
576 cfg_options 562 cfg_options
577} 563}
578 564
diff --git a/crates/ra_project_model/src/project_json.rs b/crates/ra_project_model/src/project_json.rs
new file mode 100644
index 000000000..b96227949
--- /dev/null
+++ b/crates/ra_project_model/src/project_json.rs
@@ -0,0 +1,129 @@
1//! FIXME: write short doc here
2
3use std::path::PathBuf;
4
5use paths::{AbsPath, AbsPathBuf};
6use ra_cfg::CfgOptions;
7use ra_db::{CrateId, CrateName, Dependency, Edition};
8use rustc_hash::FxHashSet;
9use serde::{de, Deserialize};
10use stdx::split_delim;
11
12/// Roots and crates that compose this Rust project.
13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct ProjectJson {
15 pub(crate) roots: Vec<Root>,
16 pub(crate) crates: Vec<Crate>,
17}
18
19/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in
20/// all roots. Roots might be nested.
21#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct Root {
23 pub(crate) path: AbsPathBuf,
24}
25
26/// A crate points to the root module of a crate and lists the dependencies of the crate. This is
27/// useful in creating the crate graph.
28#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct Crate {
30 pub(crate) root_module: AbsPathBuf,
31 pub(crate) edition: Edition,
32 pub(crate) deps: Vec<Dependency>,
33 pub(crate) cfg: CfgOptions,
34 pub(crate) target: Option<String>,
35 pub(crate) out_dir: Option<AbsPathBuf>,
36 pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
37}
38
39impl ProjectJson {
40 pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson {
41 ProjectJson {
42 roots: data.roots.into_iter().map(|path| Root { path: base.join(path) }).collect(),
43 crates: data
44 .crates
45 .into_iter()
46 .map(|crate_data| Crate {
47 root_module: base.join(crate_data.root_module),
48 edition: crate_data.edition.into(),
49 deps: crate_data
50 .deps
51 .into_iter()
52 .map(|dep_data| Dependency {
53 crate_id: CrateId(dep_data.krate as u32),
54 name: dep_data.name,
55 })
56 .collect::<Vec<_>>(),
57 cfg: {
58 let mut cfg = CfgOptions::default();
59 for entry in &crate_data.cfg {
60 match split_delim(entry, '=') {
61 Some((key, value)) => {
62 cfg.insert_key_value(key.into(), value.into());
63 }
64 None => cfg.insert_atom(entry.into()),
65 }
66 }
67 cfg
68 },
69 target: crate_data.target,
70 out_dir: crate_data.out_dir.map(|it| base.join(it)),
71 proc_macro_dylib_path: crate_data.proc_macro_dylib_path.map(|it| base.join(it)),
72 })
73 .collect::<Vec<_>>(),
74 }
75 }
76}
77
78#[derive(Deserialize)]
79pub struct ProjectJsonData {
80 roots: Vec<PathBuf>,
81 crates: Vec<CrateData>,
82}
83
84#[derive(Deserialize)]
85struct CrateData {
86 root_module: PathBuf,
87 edition: EditionData,
88 deps: Vec<DepData>,
89 #[serde(default)]
90 cfg: FxHashSet<String>,
91 target: Option<String>,
92 out_dir: Option<PathBuf>,
93 proc_macro_dylib_path: Option<PathBuf>,
94}
95
96#[derive(Deserialize)]
97#[serde(rename = "edition")]
98enum EditionData {
99 #[serde(rename = "2015")]
100 Edition2015,
101 #[serde(rename = "2018")]
102 Edition2018,
103}
104
105impl From<EditionData> for Edition {
106 fn from(data: EditionData) -> Self {
107 match data {
108 EditionData::Edition2015 => Edition::Edition2015,
109 EditionData::Edition2018 => Edition::Edition2018,
110 }
111 }
112}
113
114#[derive(Deserialize)]
115struct DepData {
116 /// Identifies a crate by position in the crates array.
117 #[serde(rename = "crate")]
118 krate: usize,
119 #[serde(deserialize_with = "deserialize_crate_name")]
120 name: CrateName,
121}
122
123fn deserialize_crate_name<'de, D>(de: D) -> Result<CrateName, D::Error>
124where
125 D: de::Deserializer<'de>,
126{
127 let name = String::deserialize(de)?;
128 CrateName::new(&name).map_err(|err| de::Error::custom(format!("invalid crate name: {:?}", err)))
129}
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs
index a8a196e64..68d134da4 100644
--- a/crates/ra_project_model/src/sysroot.rs
+++ b/crates/ra_project_model/src/sysroot.rs
@@ -1,27 +1,24 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{ 3use std::{convert::TryFrom, env, ops, path::Path, process::Command};
4 env, ops,
5 path::{Path, PathBuf},
6 process::Command,
7};
8 4
9use anyhow::{bail, Result}; 5use anyhow::{bail, format_err, Result};
6use paths::{AbsPath, AbsPathBuf};
10use ra_arena::{Arena, Idx}; 7use ra_arena::{Arena, Idx};
11 8
12use crate::output; 9use crate::output;
13 10
14#[derive(Default, Debug, Clone)] 11#[derive(Default, Debug, Clone, Eq, PartialEq)]
15pub struct Sysroot { 12pub struct Sysroot {
16 crates: Arena<SysrootCrateData>, 13 crates: Arena<SysrootCrateData>,
17} 14}
18 15
19pub type SysrootCrate = Idx<SysrootCrateData>; 16pub type SysrootCrate = Idx<SysrootCrateData>;
20 17
21#[derive(Debug, Clone)] 18#[derive(Debug, Clone, Eq, PartialEq)]
22pub struct SysrootCrateData { 19pub struct SysrootCrateData {
23 pub name: String, 20 pub name: String,
24 pub root: PathBuf, 21 pub root: AbsPathBuf,
25 pub deps: Vec<SysrootCrate>, 22 pub deps: Vec<SysrootCrate>,
26} 23}
27 24
@@ -53,7 +50,7 @@ impl Sysroot {
53 self.crates.iter().map(|(id, _data)| id) 50 self.crates.iter().map(|(id, _data)| id)
54 } 51 }
55 52
56 pub fn discover(cargo_toml: &Path) -> Result<Sysroot> { 53 pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
57 let src = get_or_install_rust_src(cargo_toml)?; 54 let src = get_or_install_rust_src(cargo_toml)?;
58 let mut sysroot = Sysroot { crates: Arena::default() }; 55 let mut sysroot = Sysroot { crates: Arena::default() };
59 for name in SYSROOT_CRATES.trim().lines() { 56 for name in SYSROOT_CRATES.trim().lines() {
@@ -86,16 +83,18 @@ impl Sysroot {
86 } 83 }
87} 84}
88 85
89fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> { 86fn get_or_install_rust_src(cargo_toml: &AbsPath) -> Result<AbsPathBuf> {
90 if let Ok(path) = env::var("RUST_SRC_PATH") { 87 if let Ok(path) = env::var("RUST_SRC_PATH") {
91 return Ok(path.into()); 88 let path = AbsPathBuf::try_from(path.as_str())
89 .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?;
90 return Ok(path);
92 } 91 }
93 let current_dir = cargo_toml.parent().unwrap(); 92 let current_dir = cargo_toml.parent().unwrap();
94 let mut rustc = Command::new(ra_toolchain::rustc()); 93 let mut rustc = Command::new(ra_toolchain::rustc());
95 rustc.current_dir(current_dir).args(&["--print", "sysroot"]); 94 rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
96 let rustc_output = output(rustc)?; 95 let rustc_output = output(rustc)?;
97 let stdout = String::from_utf8(rustc_output.stdout)?; 96 let stdout = String::from_utf8(rustc_output.stdout)?;
98 let sysroot_path = Path::new(stdout.trim()); 97 let sysroot_path = AbsPath::assert(Path::new(stdout.trim()));
99 let src_path = sysroot_path.join("lib/rustlib/src/rust/src"); 98 let src_path = sysroot_path.join("lib/rustlib/src/rust/src");
100 99
101 if !src_path.exists() { 100 if !src_path.exists() {
@@ -116,7 +115,7 @@ fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> {
116} 115}
117 116
118impl SysrootCrateData { 117impl SysrootCrateData {
119 pub fn root_dir(&self) -> &Path { 118 pub fn root_dir(&self) -> &AbsPath {
120 self.root.parent().unwrap() 119 self.root.parent().unwrap()
121 } 120 }
122} 121}
@@ -127,7 +126,6 @@ core
127alloc 126alloc
128collections 127collections
129libc 128libc
130panic_unwind
131proc_macro 129proc_macro
132rustc_unicode 130rustc_unicode
133std_unicode 131std_unicode
diff --git a/crates/ra_ssr/Cargo.toml b/crates/ra_ssr/Cargo.toml
new file mode 100644
index 000000000..fe098aaee
--- /dev/null
+++ b/crates/ra_ssr/Cargo.toml
@@ -0,0 +1,20 @@
1[package]
2edition = "2018"
3name = "ra_ssr"
4version = "0.1.0"
5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
7description = "Structural search and replace of Rust code"
8repository = "https://github.com/rust-analyzer/rust-analyzer"
9
10[lib]
11doctest = false
12
13[dependencies]
14ra_text_edit = { path = "../ra_text_edit" }
15ra_syntax = { path = "../ra_syntax" }
16ra_db = { path = "../ra_db" }
17ra_ide_db = { path = "../ra_ide_db" }
18hir = { path = "../ra_hir", package = "ra_hir" }
19rustc-hash = "1.1.0"
20test_utils = { path = "../test_utils" }
diff --git a/crates/ra_ssr/src/errors.rs b/crates/ra_ssr/src/errors.rs
new file mode 100644
index 000000000..c02bacae6
--- /dev/null
+++ b/crates/ra_ssr/src/errors.rs
@@ -0,0 +1,29 @@
1//! Code relating to errors produced by SSR.
2
3/// Constructs an SsrError taking arguments like the format macro.
4macro_rules! _error {
5 ($fmt:expr) => {$crate::SsrError::new(format!($fmt))};
6 ($fmt:expr, $($arg:tt)+) => {$crate::SsrError::new(format!($fmt, $($arg)+))}
7}
8pub(crate) use _error as error;
9
10/// Returns from the current function with an error, supplied by arguments as for format!
11macro_rules! _bail {
12 ($($tokens:tt)*) => {return Err(crate::errors::error!($($tokens)*))}
13}
14pub(crate) use _bail as bail;
15
16#[derive(Debug, PartialEq)]
17pub struct SsrError(pub(crate) String);
18
19impl std::fmt::Display for SsrError {
20 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
21 write!(f, "Parse error: {}", self.0)
22 }
23}
24
25impl SsrError {
26 pub(crate) fn new(message: impl Into<String>) -> SsrError {
27 SsrError(message.into())
28 }
29}
diff --git a/crates/ra_ssr/src/lib.rs b/crates/ra_ssr/src/lib.rs
new file mode 100644
index 000000000..cca4576ce
--- /dev/null
+++ b/crates/ra_ssr/src/lib.rs
@@ -0,0 +1,285 @@
1//! Structural Search Replace
2//!
3//! Allows searching the AST for code that matches one or more patterns and then replacing that code
4//! based on a template.
5
6mod matching;
7mod parsing;
8mod replacing;
9#[macro_use]
10mod errors;
11#[cfg(test)]
12mod tests;
13
14pub use crate::errors::SsrError;
15pub use crate::matching::Match;
16use crate::matching::{record_match_fails_reasons_scope, MatchFailureReason};
17use hir::Semantics;
18use ra_db::{FileId, FileRange};
19use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, TextRange};
20use ra_text_edit::TextEdit;
21use rustc_hash::FxHashMap;
22
23// A structured search replace rule. Create by calling `parse` on a str.
24#[derive(Debug)]
25pub struct SsrRule {
26 /// A structured pattern that we're searching for.
27 pattern: SsrPattern,
28 /// What we'll replace it with.
29 template: parsing::SsrTemplate,
30}
31
32#[derive(Debug)]
33pub struct SsrPattern {
34 raw: parsing::RawSearchPattern,
35 /// Placeholders keyed by the stand-in ident that we use in Rust source code.
36 placeholders_by_stand_in: FxHashMap<SmolStr, parsing::Placeholder>,
37 // We store our search pattern, parsed as each different kind of thing we can look for. As we
38 // traverse the AST, we get the appropriate one of these for the type of node we're on. For many
39 // search patterns, only some of these will be present.
40 expr: Option<SyntaxNode>,
41 type_ref: Option<SyntaxNode>,
42 item: Option<SyntaxNode>,
43 path: Option<SyntaxNode>,
44 pattern: Option<SyntaxNode>,
45}
46
47#[derive(Debug, Default)]
48pub struct SsrMatches {
49 pub matches: Vec<Match>,
50}
51
52/// Searches a crate for pattern matches and possibly replaces them with something else.
53pub struct MatchFinder<'db> {
54 /// Our source of information about the user's code.
55 sema: Semantics<'db, ra_ide_db::RootDatabase>,
56 rules: Vec<SsrRule>,
57}
58
59impl<'db> MatchFinder<'db> {
60 pub fn new(db: &'db ra_ide_db::RootDatabase) -> MatchFinder<'db> {
61 MatchFinder { sema: Semantics::new(db), rules: Vec::new() }
62 }
63
64 pub fn add_rule(&mut self, rule: SsrRule) {
65 self.rules.push(rule);
66 }
67
68 /// Adds a search pattern. For use if you intend to only call `find_matches_in_file`. If you
69 /// intend to do replacement, use `add_rule` instead.
70 pub fn add_search_pattern(&mut self, pattern: SsrPattern) {
71 self.add_rule(SsrRule { pattern, template: "()".parse().unwrap() })
72 }
73
74 pub fn edits_for_file(&self, file_id: FileId) -> Option<TextEdit> {
75 let matches = self.find_matches_in_file(file_id);
76 if matches.matches.is_empty() {
77 None
78 } else {
79 use ra_db::SourceDatabaseExt;
80 Some(replacing::matches_to_edit(&matches, &self.sema.db.file_text(file_id)))
81 }
82 }
83
84 pub fn find_matches_in_file(&self, file_id: FileId) -> SsrMatches {
85 let file = self.sema.parse(file_id);
86 let code = file.syntax();
87 let mut matches = SsrMatches::default();
88 self.find_matches(code, &None, &mut matches);
89 matches
90 }
91
92 /// Finds all nodes in `file_id` whose text is exactly equal to `snippet` and attempts to match
93 /// them, while recording reasons why they don't match. This API is useful for command
94 /// line-based debugging where providing a range is difficult.
95 pub fn debug_where_text_equal(&self, file_id: FileId, snippet: &str) -> Vec<MatchDebugInfo> {
96 use ra_db::SourceDatabaseExt;
97 let file = self.sema.parse(file_id);
98 let mut res = Vec::new();
99 let file_text = self.sema.db.file_text(file_id);
100 let mut remaining_text = file_text.as_str();
101 let mut base = 0;
102 let len = snippet.len() as u32;
103 while let Some(offset) = remaining_text.find(snippet) {
104 let start = base + offset as u32;
105 let end = start + len;
106 self.output_debug_for_nodes_at_range(
107 file.syntax(),
108 FileRange { file_id, range: TextRange::new(start.into(), end.into()) },
109 &None,
110 &mut res,
111 );
112 remaining_text = &remaining_text[offset + snippet.len()..];
113 base = end;
114 }
115 res
116 }
117
118 fn find_matches(
119 &self,
120 code: &SyntaxNode,
121 restrict_range: &Option<FileRange>,
122 matches_out: &mut SsrMatches,
123 ) {
124 for rule in &self.rules {
125 if let Ok(mut m) = matching::get_match(false, rule, &code, restrict_range, &self.sema) {
126 // Continue searching in each of our placeholders.
127 for placeholder_value in m.placeholder_values.values_mut() {
128 if let Some(placeholder_node) = &placeholder_value.node {
129 // Don't search our placeholder if it's the entire matched node, otherwise we'd
130 // find the same match over and over until we got a stack overflow.
131 if placeholder_node != code {
132 self.find_matches(
133 placeholder_node,
134 restrict_range,
135 &mut placeholder_value.inner_matches,
136 );
137 }
138 }
139 }
140 matches_out.matches.push(m);
141 return;
142 }
143 }
144 // If we've got a macro call, we already tried matching it pre-expansion, which is the only
145 // way to match the whole macro, now try expanding it and matching the expansion.
146 if let Some(macro_call) = ast::MacroCall::cast(code.clone()) {
147 if let Some(expanded) = self.sema.expand(&macro_call) {
148 if let Some(tt) = macro_call.token_tree() {
149 // When matching within a macro expansion, we only want to allow matches of
150 // nodes that originated entirely from within the token tree of the macro call.
151 // i.e. we don't want to match something that came from the macro itself.
152 self.find_matches(
153 &expanded,
154 &Some(self.sema.original_range(tt.syntax())),
155 matches_out,
156 );
157 }
158 }
159 }
160 for child in code.children() {
161 self.find_matches(&child, restrict_range, matches_out);
162 }
163 }
164
165 fn output_debug_for_nodes_at_range(
166 &self,
167 node: &SyntaxNode,
168 range: FileRange,
169 restrict_range: &Option<FileRange>,
170 out: &mut Vec<MatchDebugInfo>,
171 ) {
172 for node in node.children() {
173 let node_range = self.sema.original_range(&node);
174 if node_range.file_id != range.file_id || !node_range.range.contains_range(range.range)
175 {
176 continue;
177 }
178 if node_range.range == range.range {
179 for rule in &self.rules {
180 let pattern =
181 rule.pattern.tree_for_kind_with_reason(node.kind()).map(|p| p.clone());
182 out.push(MatchDebugInfo {
183 matched: matching::get_match(true, rule, &node, restrict_range, &self.sema)
184 .map_err(|e| MatchFailureReason {
185 reason: e.reason.unwrap_or_else(|| {
186 "Match failed, but no reason was given".to_owned()
187 }),
188 }),
189 pattern,
190 node: node.clone(),
191 });
192 }
193 } else if let Some(macro_call) = ast::MacroCall::cast(node.clone()) {
194 if let Some(expanded) = self.sema.expand(&macro_call) {
195 if let Some(tt) = macro_call.token_tree() {
196 self.output_debug_for_nodes_at_range(
197 &expanded,
198 range,
199 &Some(self.sema.original_range(tt.syntax())),
200 out,
201 );
202 }
203 }
204 }
205 self.output_debug_for_nodes_at_range(&node, range, restrict_range, out);
206 }
207 }
208}
209
210pub struct MatchDebugInfo {
211 node: SyntaxNode,
212 /// Our search pattern parsed as the same kind of syntax node as `node`. e.g. expression, item,
213 /// etc. Will be absent if the pattern can't be parsed as that kind.
214 pattern: Result<SyntaxNode, MatchFailureReason>,
215 matched: Result<Match, MatchFailureReason>,
216}
217
218impl std::fmt::Debug for MatchDebugInfo {
219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220 match &self.matched {
221 Ok(_) => writeln!(f, "Node matched")?,
222 Err(reason) => writeln!(f, "Node failed to match because: {}", reason.reason)?,
223 }
224 writeln!(
225 f,
226 "============ AST ===========\n\
227 {:#?}",
228 self.node
229 )?;
230 writeln!(f, "========= PATTERN ==========")?;
231 match &self.pattern {
232 Ok(pattern) => {
233 writeln!(f, "{:#?}", pattern)?;
234 }
235 Err(err) => {
236 writeln!(f, "{}", err.reason)?;
237 }
238 }
239 writeln!(f, "============================")?;
240 Ok(())
241 }
242}
243
244impl SsrPattern {
245 fn tree_for_kind_with_reason(
246 &self,
247 kind: SyntaxKind,
248 ) -> Result<&SyntaxNode, MatchFailureReason> {
249 record_match_fails_reasons_scope(true, || self.tree_for_kind(kind))
250 .map_err(|e| MatchFailureReason { reason: e.reason.unwrap() })
251 }
252}
253
254impl SsrMatches {
255 /// Returns `self` with any nested matches removed and made into top-level matches.
256 pub fn flattened(self) -> SsrMatches {
257 let mut out = SsrMatches::default();
258 self.flatten_into(&mut out);
259 out
260 }
261
262 fn flatten_into(self, out: &mut SsrMatches) {
263 for mut m in self.matches {
264 for p in m.placeholder_values.values_mut() {
265 std::mem::replace(&mut p.inner_matches, SsrMatches::default()).flatten_into(out);
266 }
267 out.matches.push(m);
268 }
269 }
270}
271
272impl Match {
273 pub fn matched_text(&self) -> String {
274 self.matched_node.text().to_string()
275 }
276}
277
278impl std::error::Error for SsrError {}
279
280#[cfg(test)]
281impl MatchDebugInfo {
282 pub(crate) fn match_failure_reason(&self) -> Option<&str> {
283 self.matched.as_ref().err().map(|r| r.reason.as_str())
284 }
285}
diff --git a/crates/ra_ssr/src/matching.rs b/crates/ra_ssr/src/matching.rs
new file mode 100644
index 000000000..50b29eab2
--- /dev/null
+++ b/crates/ra_ssr/src/matching.rs
@@ -0,0 +1,623 @@
1//! This module is responsible for matching a search pattern against a node in the AST. In the
2//! process of matching, placeholder values are recorded.
3
4use crate::{
5 parsing::{Constraint, NodeKind, Placeholder, SsrTemplate},
6 SsrMatches, SsrPattern, SsrRule,
7};
8use hir::Semantics;
9use ra_db::FileRange;
10use ra_syntax::ast::{AstNode, AstToken};
11use ra_syntax::{ast, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken};
12use rustc_hash::FxHashMap;
13use std::{cell::Cell, iter::Peekable};
14use test_utils::mark;
15
16// Creates a match error. If we're currently attempting to match some code that we thought we were
17// going to match, as indicated by the --debug-snippet flag, then populate the reason field.
18macro_rules! match_error {
19 ($e:expr) => {{
20 MatchFailed {
21 reason: if recording_match_fail_reasons() {
22 Some(format!("{}", $e))
23 } else {
24 None
25 }
26 }
27 }};
28 ($fmt:expr, $($arg:tt)+) => {{
29 MatchFailed {
30 reason: if recording_match_fail_reasons() {
31 Some(format!($fmt, $($arg)+))
32 } else {
33 None
34 }
35 }
36 }};
37}
38
39// Fails the current match attempt, recording the supplied reason if we're recording match fail reasons.
40macro_rules! fail_match {
41 ($($args:tt)*) => {return Err(match_error!($($args)*))};
42}
43
44/// Information about a match that was found.
45#[derive(Debug)]
46pub struct Match {
47 pub(crate) range: FileRange,
48 pub(crate) matched_node: SyntaxNode,
49 pub(crate) placeholder_values: FxHashMap<Var, PlaceholderMatch>,
50 pub(crate) ignored_comments: Vec<ast::Comment>,
51 // A copy of the template for the rule that produced this match. We store this on the match for
52 // if/when we do replacement.
53 pub(crate) template: SsrTemplate,
54}
55
56/// Represents a `$var` in an SSR query.
57#[derive(Debug, Clone, PartialEq, Eq, Hash)]
58pub(crate) struct Var(pub String);
59
60/// Information about a placeholder bound in a match.
61#[derive(Debug)]
62pub(crate) struct PlaceholderMatch {
63 /// The node that the placeholder matched to. If set, then we'll search for further matches
64 /// within this node. It isn't set when we match tokens within a macro call's token tree.
65 pub(crate) node: Option<SyntaxNode>,
66 pub(crate) range: FileRange,
67 /// More matches, found within `node`.
68 pub(crate) inner_matches: SsrMatches,
69}
70
71#[derive(Debug)]
72pub(crate) struct MatchFailureReason {
73 pub(crate) reason: String,
74}
75
76/// An "error" indicating that matching failed. Use the fail_match! macro to create and return this.
77#[derive(Clone)]
78pub(crate) struct MatchFailed {
79 /// The reason why we failed to match. Only present when debug_active true in call to
80 /// `get_match`.
81 pub(crate) reason: Option<String>,
82}
83
84/// Checks if `code` matches the search pattern found in `search_scope`, returning information about
85/// the match, if it does. Since we only do matching in this module and searching is done by the
86/// parent module, we don't populate nested matches.
87pub(crate) fn get_match(
88 debug_active: bool,
89 rule: &SsrRule,
90 code: &SyntaxNode,
91 restrict_range: &Option<FileRange>,
92 sema: &Semantics<ra_ide_db::RootDatabase>,
93) -> Result<Match, MatchFailed> {
94 record_match_fails_reasons_scope(debug_active, || {
95 Matcher::try_match(rule, code, restrict_range, sema)
96 })
97}
98
99/// Checks if our search pattern matches a particular node of the AST.
100struct Matcher<'db, 'sema> {
101 sema: &'sema Semantics<'db, ra_ide_db::RootDatabase>,
102 /// If any placeholders come from anywhere outside of this range, then the match will be
103 /// rejected.
104 restrict_range: Option<FileRange>,
105 rule: &'sema SsrRule,
106}
107
108/// Which phase of matching we're currently performing. We do two phases because most attempted
109/// matches will fail and it means we can defer more expensive checks to the second phase.
110enum Phase<'a> {
111 /// On the first phase, we perform cheap checks. No state is mutated and nothing is recorded.
112 First,
113 /// On the second phase, we construct the `Match`. Things like what placeholders bind to is
114 /// recorded.
115 Second(&'a mut Match),
116}
117
118impl<'db, 'sema> Matcher<'db, 'sema> {
119 fn try_match(
120 rule: &'sema SsrRule,
121 code: &SyntaxNode,
122 restrict_range: &Option<FileRange>,
123 sema: &'sema Semantics<'db, ra_ide_db::RootDatabase>,
124 ) -> Result<Match, MatchFailed> {
125 let match_state = Matcher { sema, restrict_range: restrict_range.clone(), rule };
126 let pattern_tree = rule.pattern.tree_for_kind(code.kind())?;
127 // First pass at matching, where we check that node types and idents match.
128 match_state.attempt_match_node(&mut Phase::First, &pattern_tree, code)?;
129 match_state.validate_range(&sema.original_range(code))?;
130 let mut the_match = Match {
131 range: sema.original_range(code),
132 matched_node: code.clone(),
133 placeholder_values: FxHashMap::default(),
134 ignored_comments: Vec::new(),
135 template: rule.template.clone(),
136 };
137 // Second matching pass, where we record placeholder matches, ignored comments and maybe do
138 // any other more expensive checks that we didn't want to do on the first pass.
139 match_state.attempt_match_node(&mut Phase::Second(&mut the_match), &pattern_tree, code)?;
140 Ok(the_match)
141 }
142
143 /// Checks that `range` is within the permitted range if any. This is applicable when we're
144 /// processing a macro expansion and we want to fail the match if we're working with a node that
145 /// didn't originate from the token tree of the macro call.
146 fn validate_range(&self, range: &FileRange) -> Result<(), MatchFailed> {
147 if let Some(restrict_range) = &self.restrict_range {
148 if restrict_range.file_id != range.file_id
149 || !restrict_range.range.contains_range(range.range)
150 {
151 fail_match!("Node originated from a macro");
152 }
153 }
154 Ok(())
155 }
156
157 fn attempt_match_node(
158 &self,
159 phase: &mut Phase,
160 pattern: &SyntaxNode,
161 code: &SyntaxNode,
162 ) -> Result<(), MatchFailed> {
163 // Handle placeholders.
164 if let Some(placeholder) = self.get_placeholder(&SyntaxElement::Node(pattern.clone())) {
165 for constraint in &placeholder.constraints {
166 self.check_constraint(constraint, code)?;
167 }
168 if let Phase::Second(matches_out) = phase {
169 let original_range = self.sema.original_range(code);
170 // We validated the range for the node when we started the match, so the placeholder
171 // probably can't fail range validation, but just to be safe...
172 self.validate_range(&original_range)?;
173 matches_out.placeholder_values.insert(
174 Var(placeholder.ident.to_string()),
175 PlaceholderMatch::new(code, original_range),
176 );
177 }
178 return Ok(());
179 }
180 // Non-placeholders.
181 if pattern.kind() != code.kind() {
182 fail_match!(
183 "Pattern had a `{}` ({:?}), code had `{}` ({:?})",
184 pattern.text(),
185 pattern.kind(),
186 code.text(),
187 code.kind()
188 );
189 }
190 // Some kinds of nodes have special handling. For everything else, we fall back to default
191 // matching.
192 match code.kind() {
193 SyntaxKind::RECORD_FIELD_LIST => {
194 self.attempt_match_record_field_list(phase, pattern, code)
195 }
196 SyntaxKind::TOKEN_TREE => self.attempt_match_token_tree(phase, pattern, code),
197 _ => self.attempt_match_node_children(phase, pattern, code),
198 }
199 }
200
201 fn attempt_match_node_children(
202 &self,
203 phase: &mut Phase,
204 pattern: &SyntaxNode,
205 code: &SyntaxNode,
206 ) -> Result<(), MatchFailed> {
207 self.attempt_match_sequences(
208 phase,
209 PatternIterator::new(pattern),
210 code.children_with_tokens(),
211 )
212 }
213
214 fn attempt_match_sequences(
215 &self,
216 phase: &mut Phase,
217 pattern_it: PatternIterator,
218 mut code_it: SyntaxElementChildren,
219 ) -> Result<(), MatchFailed> {
220 let mut pattern_it = pattern_it.peekable();
221 loop {
222 match phase.next_non_trivial(&mut code_it) {
223 None => {
224 if let Some(p) = pattern_it.next() {
225 fail_match!("Part of the pattern was unmatched: {:?}", p);
226 }
227 return Ok(());
228 }
229 Some(SyntaxElement::Token(c)) => {
230 self.attempt_match_token(phase, &mut pattern_it, &c)?;
231 }
232 Some(SyntaxElement::Node(c)) => match pattern_it.next() {
233 Some(SyntaxElement::Node(p)) => {
234 self.attempt_match_node(phase, &p, &c)?;
235 }
236 Some(p) => fail_match!("Pattern wanted '{}', code has {}", p, c.text()),
237 None => fail_match!("Pattern reached end, code has {}", c.text()),
238 },
239 }
240 }
241 }
242
243 fn attempt_match_token(
244 &self,
245 phase: &mut Phase,
246 pattern: &mut Peekable<PatternIterator>,
247 code: &ra_syntax::SyntaxToken,
248 ) -> Result<(), MatchFailed> {
249 phase.record_ignored_comments(code);
250 // Ignore whitespace and comments.
251 if code.kind().is_trivia() {
252 return Ok(());
253 }
254 if let Some(SyntaxElement::Token(p)) = pattern.peek() {
255 // If the code has a comma and the pattern is about to close something, then accept the
256 // comma without advancing the pattern. i.e. ignore trailing commas.
257 if code.kind() == SyntaxKind::COMMA && is_closing_token(p.kind()) {
258 return Ok(());
259 }
260 // Conversely, if the pattern has a comma and the code doesn't, skip that part of the
261 // pattern and continue to match the code.
262 if p.kind() == SyntaxKind::COMMA && is_closing_token(code.kind()) {
263 pattern.next();
264 }
265 }
266 // Consume an element from the pattern and make sure it matches.
267 match pattern.next() {
268 Some(SyntaxElement::Token(p)) => {
269 if p.kind() != code.kind() || p.text() != code.text() {
270 fail_match!(
271 "Pattern wanted token '{}' ({:?}), but code had token '{}' ({:?})",
272 p.text(),
273 p.kind(),
274 code.text(),
275 code.kind()
276 )
277 }
278 }
279 Some(SyntaxElement::Node(p)) => {
280 // Not sure if this is actually reachable.
281 fail_match!(
282 "Pattern wanted {:?}, but code had token '{}' ({:?})",
283 p,
284 code.text(),
285 code.kind()
286 );
287 }
288 None => {
289 fail_match!("Pattern exhausted, while code remains: `{}`", code.text());
290 }
291 }
292 Ok(())
293 }
294
295 fn check_constraint(
296 &self,
297 constraint: &Constraint,
298 code: &SyntaxNode,
299 ) -> Result<(), MatchFailed> {
300 match constraint {
301 Constraint::Kind(kind) => {
302 kind.matches(code)?;
303 }
304 Constraint::Not(sub) => {
305 if self.check_constraint(&*sub, code).is_ok() {
306 fail_match!("Constraint {:?} failed for '{}'", constraint, code.text());
307 }
308 }
309 }
310 Ok(())
311 }
312
313 /// We want to allow the records to match in any order, so we have special matching logic for
314 /// them.
315 fn attempt_match_record_field_list(
316 &self,
317 phase: &mut Phase,
318 pattern: &SyntaxNode,
319 code: &SyntaxNode,
320 ) -> Result<(), MatchFailed> {
321 // Build a map keyed by field name.
322 let mut fields_by_name = FxHashMap::default();
323 for child in code.children() {
324 if let Some(record) = ast::RecordField::cast(child.clone()) {
325 if let Some(name) = record.field_name() {
326 fields_by_name.insert(name.text().clone(), child.clone());
327 }
328 }
329 }
330 for p in pattern.children_with_tokens() {
331 if let SyntaxElement::Node(p) = p {
332 if let Some(name_element) = p.first_child_or_token() {
333 if self.get_placeholder(&name_element).is_some() {
334 // If the pattern is using placeholders for field names then order
335 // independence doesn't make sense. Fall back to regular ordered
336 // matching.
337 return self.attempt_match_node_children(phase, pattern, code);
338 }
339 if let Some(ident) = only_ident(name_element) {
340 let code_record = fields_by_name.remove(ident.text()).ok_or_else(|| {
341 match_error!(
342 "Placeholder has record field '{}', but code doesn't",
343 ident
344 )
345 })?;
346 self.attempt_match_node(phase, &p, &code_record)?;
347 }
348 }
349 }
350 }
351 if let Some(unmatched_fields) = fields_by_name.keys().next() {
352 fail_match!(
353 "{} field(s) of a record literal failed to match, starting with {}",
354 fields_by_name.len(),
355 unmatched_fields
356 );
357 }
358 Ok(())
359 }
360
361 /// Outside of token trees, a placeholder can only match a single AST node, whereas in a token
362 /// tree it can match a sequence of tokens. Note, that this code will only be used when the
363 /// pattern matches the macro invocation. For matches within the macro call, we'll already have
364 /// expanded the macro.
365 fn attempt_match_token_tree(
366 &self,
367 phase: &mut Phase,
368 pattern: &SyntaxNode,
369 code: &ra_syntax::SyntaxNode,
370 ) -> Result<(), MatchFailed> {
371 let mut pattern = PatternIterator::new(pattern).peekable();
372 let mut children = code.children_with_tokens();
373 while let Some(child) = children.next() {
374 if let Some(placeholder) = pattern.peek().and_then(|p| self.get_placeholder(p)) {
375 pattern.next();
376 let next_pattern_token = pattern
377 .peek()
378 .and_then(|p| match p {
379 SyntaxElement::Token(t) => Some(t.clone()),
380 SyntaxElement::Node(n) => n.first_token(),
381 })
382 .map(|p| p.text().to_string());
383 let first_matched_token = child.clone();
384 let mut last_matched_token = child;
385 // Read code tokens util we reach one equal to the next token from our pattern
386 // or we reach the end of the token tree.
387 while let Some(next) = children.next() {
388 match &next {
389 SyntaxElement::Token(t) => {
390 if Some(t.to_string()) == next_pattern_token {
391 pattern.next();
392 break;
393 }
394 }
395 SyntaxElement::Node(n) => {
396 if let Some(first_token) = n.first_token() {
397 if Some(first_token.to_string()) == next_pattern_token {
398 if let Some(SyntaxElement::Node(p)) = pattern.next() {
399 // We have a subtree that starts with the next token in our pattern.
400 self.attempt_match_token_tree(phase, &p, &n)?;
401 break;
402 }
403 }
404 }
405 }
406 };
407 last_matched_token = next;
408 }
409 if let Phase::Second(match_out) = phase {
410 match_out.placeholder_values.insert(
411 Var(placeholder.ident.to_string()),
412 PlaceholderMatch::from_range(FileRange {
413 file_id: self.sema.original_range(code).file_id,
414 range: first_matched_token
415 .text_range()
416 .cover(last_matched_token.text_range()),
417 }),
418 );
419 }
420 continue;
421 }
422 // Match literal (non-placeholder) tokens.
423 match child {
424 SyntaxElement::Token(token) => {
425 self.attempt_match_token(phase, &mut pattern, &token)?;
426 }
427 SyntaxElement::Node(node) => match pattern.next() {
428 Some(SyntaxElement::Node(p)) => {
429 self.attempt_match_token_tree(phase, &p, &node)?;
430 }
431 Some(SyntaxElement::Token(p)) => fail_match!(
432 "Pattern has token '{}', code has subtree '{}'",
433 p.text(),
434 node.text()
435 ),
436 None => fail_match!("Pattern has nothing, code has '{}'", node.text()),
437 },
438 }
439 }
440 if let Some(p) = pattern.next() {
441 fail_match!("Reached end of token tree in code, but pattern still has {:?}", p);
442 }
443 Ok(())
444 }
445
446 fn get_placeholder(&self, element: &SyntaxElement) -> Option<&Placeholder> {
447 only_ident(element.clone())
448 .and_then(|ident| self.rule.pattern.placeholders_by_stand_in.get(ident.text()))
449 }
450}
451
452impl Phase<'_> {
453 fn next_non_trivial(&mut self, code_it: &mut SyntaxElementChildren) -> Option<SyntaxElement> {
454 loop {
455 let c = code_it.next();
456 if let Some(SyntaxElement::Token(t)) = &c {
457 self.record_ignored_comments(t);
458 if t.kind().is_trivia() {
459 continue;
460 }
461 }
462 return c;
463 }
464 }
465
466 fn record_ignored_comments(&mut self, token: &SyntaxToken) {
467 if token.kind() == SyntaxKind::COMMENT {
468 if let Phase::Second(match_out) = self {
469 if let Some(comment) = ast::Comment::cast(token.clone()) {
470 match_out.ignored_comments.push(comment);
471 }
472 }
473 }
474 }
475}
476
477fn is_closing_token(kind: SyntaxKind) -> bool {
478 kind == SyntaxKind::R_PAREN || kind == SyntaxKind::R_CURLY || kind == SyntaxKind::R_BRACK
479}
480
481pub(crate) fn record_match_fails_reasons_scope<F, T>(debug_active: bool, f: F) -> T
482where
483 F: Fn() -> T,
484{
485 RECORDING_MATCH_FAIL_REASONS.with(|c| c.set(debug_active));
486 let res = f();
487 RECORDING_MATCH_FAIL_REASONS.with(|c| c.set(false));
488 res
489}
490
491// For performance reasons, we don't want to record the reason why every match fails, only the bit
492// of code that the user indicated they thought would match. We use a thread local to indicate when
493// we are trying to match that bit of code. This saves us having to pass a boolean into all the bits
494// of code that can make the decision to not match.
495thread_local! {
496 pub static RECORDING_MATCH_FAIL_REASONS: Cell<bool> = Cell::new(false);
497}
498
499fn recording_match_fail_reasons() -> bool {
500 RECORDING_MATCH_FAIL_REASONS.with(|c| c.get())
501}
502
503impl PlaceholderMatch {
504 fn new(node: &SyntaxNode, range: FileRange) -> Self {
505 Self { node: Some(node.clone()), range, inner_matches: SsrMatches::default() }
506 }
507
508 fn from_range(range: FileRange) -> Self {
509 Self { node: None, range, inner_matches: SsrMatches::default() }
510 }
511}
512
513impl SsrPattern {
514 pub(crate) fn tree_for_kind(&self, kind: SyntaxKind) -> Result<&SyntaxNode, MatchFailed> {
515 let (tree, kind_name) = if ast::Expr::can_cast(kind) {
516 (&self.expr, "expression")
517 } else if ast::TypeRef::can_cast(kind) {
518 (&self.type_ref, "type reference")
519 } else if ast::ModuleItem::can_cast(kind) {
520 (&self.item, "item")
521 } else if ast::Path::can_cast(kind) {
522 (&self.path, "path")
523 } else if ast::Pat::can_cast(kind) {
524 (&self.pattern, "pattern")
525 } else {
526 fail_match!("Matching nodes of kind {:?} is not supported", kind);
527 };
528 match tree {
529 Some(tree) => Ok(tree),
530 None => fail_match!("Pattern cannot be parsed as a {}", kind_name),
531 }
532 }
533}
534
535impl NodeKind {
536 fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> {
537 let ok = match self {
538 Self::Literal => {
539 mark::hit!(literal_constraint);
540 ast::Literal::can_cast(node.kind())
541 }
542 };
543 if !ok {
544 fail_match!("Code '{}' isn't of kind {:?}", node.text(), self);
545 }
546 Ok(())
547 }
548}
549
550// If `node` contains nothing but an ident then return it, otherwise return None.
551fn only_ident(element: SyntaxElement) -> Option<SyntaxToken> {
552 match element {
553 SyntaxElement::Token(t) => {
554 if t.kind() == SyntaxKind::IDENT {
555 return Some(t);
556 }
557 }
558 SyntaxElement::Node(n) => {
559 let mut children = n.children_with_tokens();
560 if let (Some(only_child), None) = (children.next(), children.next()) {
561 return only_ident(only_child);
562 }
563 }
564 }
565 None
566}
567
568struct PatternIterator {
569 iter: SyntaxElementChildren,
570}
571
572impl Iterator for PatternIterator {
573 type Item = SyntaxElement;
574
575 fn next(&mut self) -> Option<SyntaxElement> {
576 while let Some(element) = self.iter.next() {
577 if !element.kind().is_trivia() {
578 return Some(element);
579 }
580 }
581 None
582 }
583}
584
585impl PatternIterator {
586 fn new(parent: &SyntaxNode) -> Self {
587 Self { iter: parent.children_with_tokens() }
588 }
589}
590
591#[cfg(test)]
592mod tests {
593 use super::*;
594 use crate::{MatchFinder, SsrRule};
595
596 #[test]
597 fn parse_match_replace() {
598 let rule: SsrRule = "foo($x) ==>> bar($x)".parse().unwrap();
599 let input = "fn foo() {} fn main() { foo(1+2); }";
600
601 use ra_db::fixture::WithFixture;
602 let (db, file_id) = ra_ide_db::RootDatabase::with_single_file(input);
603 let mut match_finder = MatchFinder::new(&db);
604 match_finder.add_rule(rule);
605 let matches = match_finder.find_matches_in_file(file_id);
606 assert_eq!(matches.matches.len(), 1);
607 assert_eq!(matches.matches[0].matched_node.text(), "foo(1+2)");
608 assert_eq!(matches.matches[0].placeholder_values.len(), 1);
609 assert_eq!(
610 matches.matches[0].placeholder_values[&Var("x".to_string())]
611 .node
612 .as_ref()
613 .unwrap()
614 .text(),
615 "1+2"
616 );
617
618 let edit = crate::replacing::matches_to_edit(&matches, input);
619 let mut after = input.to_string();
620 edit.apply(&mut after);
621 assert_eq!(after, "fn foo() {} fn main() { bar(1+2); }");
622 }
623}
diff --git a/crates/ra_ssr/src/parsing.rs b/crates/ra_ssr/src/parsing.rs
new file mode 100644
index 000000000..4aee97bb2
--- /dev/null
+++ b/crates/ra_ssr/src/parsing.rs
@@ -0,0 +1,343 @@
1//! This file contains code for parsing SSR rules, which look something like `foo($a) ==>> bar($b)`.
2//! We first split everything before and after the separator `==>>`. Next, both the search pattern
3//! and the replacement template get tokenized by the Rust tokenizer. Tokens are then searched for
4//! placeholders, which start with `$`. For replacement templates, this is the final form. For
5//! search patterns, we go further and parse the pattern as each kind of thing that we can match.
6//! e.g. expressions, type references etc.
7
8use crate::errors::bail;
9use crate::{SsrError, SsrPattern, SsrRule};
10use ra_syntax::{ast, AstNode, SmolStr, SyntaxKind, T};
11use rustc_hash::{FxHashMap, FxHashSet};
12use std::str::FromStr;
13
14#[derive(Clone, Debug)]
15pub(crate) struct SsrTemplate {
16 pub(crate) tokens: Vec<PatternElement>,
17}
18
19#[derive(Debug)]
20pub(crate) struct RawSearchPattern {
21 tokens: Vec<PatternElement>,
22}
23
24// Part of a search or replace pattern.
25#[derive(Clone, Debug, PartialEq, Eq)]
26pub(crate) enum PatternElement {
27 Token(Token),
28 Placeholder(Placeholder),
29}
30
31#[derive(Clone, Debug, PartialEq, Eq)]
32pub(crate) struct Placeholder {
33 /// The name of this placeholder. e.g. for "$a", this would be "a"
34 pub(crate) ident: SmolStr,
35 /// A unique name used in place of this placeholder when we parse the pattern as Rust code.
36 stand_in_name: String,
37 pub(crate) constraints: Vec<Constraint>,
38}
39
40#[derive(Clone, Debug, PartialEq, Eq)]
41pub(crate) enum Constraint {
42 Kind(NodeKind),
43 Not(Box<Constraint>),
44}
45
46#[derive(Clone, Debug, PartialEq, Eq)]
47pub(crate) enum NodeKind {
48 Literal,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub(crate) struct Token {
53 kind: SyntaxKind,
54 pub(crate) text: SmolStr,
55}
56
57impl FromStr for SsrRule {
58 type Err = SsrError;
59
60 fn from_str(query: &str) -> Result<SsrRule, SsrError> {
61 let mut it = query.split("==>>");
62 let pattern = it.next().expect("at least empty string").trim();
63 let template = it
64 .next()
65 .ok_or_else(|| SsrError("Cannot find delimiter `==>>`".into()))?
66 .trim()
67 .to_string();
68 if it.next().is_some() {
69 return Err(SsrError("More than one delimiter found".into()));
70 }
71 let rule = SsrRule { pattern: pattern.parse()?, template: template.parse()? };
72 validate_rule(&rule)?;
73 Ok(rule)
74 }
75}
76
77impl FromStr for RawSearchPattern {
78 type Err = SsrError;
79
80 fn from_str(pattern_str: &str) -> Result<RawSearchPattern, SsrError> {
81 Ok(RawSearchPattern { tokens: parse_pattern(pattern_str)? })
82 }
83}
84
85impl RawSearchPattern {
86 /// Returns this search pattern as Rust source code that we can feed to the Rust parser.
87 fn as_rust_code(&self) -> String {
88 let mut res = String::new();
89 for t in &self.tokens {
90 res.push_str(match t {
91 PatternElement::Token(token) => token.text.as_str(),
92 PatternElement::Placeholder(placeholder) => placeholder.stand_in_name.as_str(),
93 });
94 }
95 res
96 }
97
98 fn placeholders_by_stand_in(&self) -> FxHashMap<SmolStr, Placeholder> {
99 let mut res = FxHashMap::default();
100 for t in &self.tokens {
101 if let PatternElement::Placeholder(placeholder) = t {
102 res.insert(SmolStr::new(placeholder.stand_in_name.clone()), placeholder.clone());
103 }
104 }
105 res
106 }
107}
108
109impl FromStr for SsrPattern {
110 type Err = SsrError;
111
112 fn from_str(pattern_str: &str) -> Result<SsrPattern, SsrError> {
113 let raw: RawSearchPattern = pattern_str.parse()?;
114 let raw_str = raw.as_rust_code();
115 let res = SsrPattern {
116 expr: ast::Expr::parse(&raw_str).ok().map(|n| n.syntax().clone()),
117 type_ref: ast::TypeRef::parse(&raw_str).ok().map(|n| n.syntax().clone()),
118 item: ast::ModuleItem::parse(&raw_str).ok().map(|n| n.syntax().clone()),
119 path: ast::Path::parse(&raw_str).ok().map(|n| n.syntax().clone()),
120 pattern: ast::Pat::parse(&raw_str).ok().map(|n| n.syntax().clone()),
121 placeholders_by_stand_in: raw.placeholders_by_stand_in(),
122 raw,
123 };
124 if res.expr.is_none()
125 && res.type_ref.is_none()
126 && res.item.is_none()
127 && res.path.is_none()
128 && res.pattern.is_none()
129 {
130 bail!("Pattern is not a valid Rust expression, type, item, path or pattern");
131 }
132 Ok(res)
133 }
134}
135
136impl FromStr for SsrTemplate {
137 type Err = SsrError;
138
139 fn from_str(pattern_str: &str) -> Result<SsrTemplate, SsrError> {
140 let tokens = parse_pattern(pattern_str)?;
141 // Validate that the template is a valid fragment of Rust code. We reuse the validation
142 // logic for search patterns since the only thing that differs is the error message.
143 if SsrPattern::from_str(pattern_str).is_err() {
144 bail!("Replacement is not a valid Rust expression, type, item, path or pattern");
145 }
146 // Our actual template needs to preserve whitespace, so we can't reuse `tokens`.
147 Ok(SsrTemplate { tokens })
148 }
149}
150
151/// Returns `pattern_str`, parsed as a search or replace pattern. If `remove_whitespace` is true,
152/// then any whitespace tokens will be removed, which we do for the search pattern, but not for the
153/// replace pattern.
154fn parse_pattern(pattern_str: &str) -> Result<Vec<PatternElement>, SsrError> {
155 let mut res = Vec::new();
156 let mut placeholder_names = FxHashSet::default();
157 let mut tokens = tokenize(pattern_str)?.into_iter();
158 while let Some(token) = tokens.next() {
159 if token.kind == T![$] {
160 let placeholder = parse_placeholder(&mut tokens)?;
161 if !placeholder_names.insert(placeholder.ident.clone()) {
162 bail!("Name `{}` repeats more than once", placeholder.ident);
163 }
164 res.push(PatternElement::Placeholder(placeholder));
165 } else {
166 res.push(PatternElement::Token(token));
167 }
168 }
169 Ok(res)
170}
171
172/// Checks for errors in a rule. e.g. the replace pattern referencing placeholders that the search
173/// pattern didn't define.
174fn validate_rule(rule: &SsrRule) -> Result<(), SsrError> {
175 let mut defined_placeholders = FxHashSet::default();
176 for p in &rule.pattern.raw.tokens {
177 if let PatternElement::Placeholder(placeholder) = p {
178 defined_placeholders.insert(&placeholder.ident);
179 }
180 }
181 let mut undefined = Vec::new();
182 for p in &rule.template.tokens {
183 if let PatternElement::Placeholder(placeholder) = p {
184 if !defined_placeholders.contains(&placeholder.ident) {
185 undefined.push(format!("${}", placeholder.ident));
186 }
187 if !placeholder.constraints.is_empty() {
188 bail!("Replacement placeholders cannot have constraints");
189 }
190 }
191 }
192 if !undefined.is_empty() {
193 bail!("Replacement contains undefined placeholders: {}", undefined.join(", "));
194 }
195 Ok(())
196}
197
198fn tokenize(source: &str) -> Result<Vec<Token>, SsrError> {
199 let mut start = 0;
200 let (raw_tokens, errors) = ra_syntax::tokenize(source);
201 if let Some(first_error) = errors.first() {
202 bail!("Failed to parse pattern: {}", first_error);
203 }
204 let mut tokens: Vec<Token> = Vec::new();
205 for raw_token in raw_tokens {
206 let token_len = usize::from(raw_token.len);
207 tokens.push(Token {
208 kind: raw_token.kind,
209 text: SmolStr::new(&source[start..start + token_len]),
210 });
211 start += token_len;
212 }
213 Ok(tokens)
214}
215
216fn parse_placeholder(tokens: &mut std::vec::IntoIter<Token>) -> Result<Placeholder, SsrError> {
217 let mut name = None;
218 let mut constraints = Vec::new();
219 if let Some(token) = tokens.next() {
220 match token.kind {
221 SyntaxKind::IDENT => {
222 name = Some(token.text);
223 }
224 T!['{'] => {
225 let token =
226 tokens.next().ok_or_else(|| SsrError::new("Unexpected end of placeholder"))?;
227 if token.kind == SyntaxKind::IDENT {
228 name = Some(token.text);
229 }
230 loop {
231 let token = tokens
232 .next()
233 .ok_or_else(|| SsrError::new("Placeholder is missing closing brace '}'"))?;
234 match token.kind {
235 T![:] => {
236 constraints.push(parse_constraint(tokens)?);
237 }
238 T!['}'] => break,
239 _ => bail!("Unexpected token while parsing placeholder: '{}'", token.text),
240 }
241 }
242 }
243 _ => {
244 bail!("Placeholders should either be $name or ${{name:constraints}}");
245 }
246 }
247 }
248 let name = name.ok_or_else(|| SsrError::new("Placeholder ($) with no name"))?;
249 Ok(Placeholder::new(name, constraints))
250}
251
252fn parse_constraint(tokens: &mut std::vec::IntoIter<Token>) -> Result<Constraint, SsrError> {
253 let constraint_type = tokens
254 .next()
255 .ok_or_else(|| SsrError::new("Found end of placeholder while looking for a constraint"))?
256 .text
257 .to_string();
258 match constraint_type.as_str() {
259 "kind" => {
260 expect_token(tokens, "(")?;
261 let t = tokens.next().ok_or_else(|| {
262 SsrError::new("Unexpected end of constraint while looking for kind")
263 })?;
264 if t.kind != SyntaxKind::IDENT {
265 bail!("Expected ident, found {:?} while parsing kind constraint", t.kind);
266 }
267 expect_token(tokens, ")")?;
268 Ok(Constraint::Kind(NodeKind::from(&t.text)?))
269 }
270 "not" => {
271 expect_token(tokens, "(")?;
272 let sub = parse_constraint(tokens)?;
273 expect_token(tokens, ")")?;
274 Ok(Constraint::Not(Box::new(sub)))
275 }
276 x => bail!("Unsupported constraint type '{}'", x),
277 }
278}
279
280fn expect_token(tokens: &mut std::vec::IntoIter<Token>, expected: &str) -> Result<(), SsrError> {
281 if let Some(t) = tokens.next() {
282 if t.text == expected {
283 return Ok(());
284 }
285 bail!("Expected {} found {}", expected, t.text);
286 }
287 bail!("Expected {} found end of stream", expected);
288}
289
290impl NodeKind {
291 fn from(name: &SmolStr) -> Result<NodeKind, SsrError> {
292 Ok(match name.as_str() {
293 "literal" => NodeKind::Literal,
294 _ => bail!("Unknown node kind '{}'", name),
295 })
296 }
297}
298
299impl Placeholder {
300 fn new(name: SmolStr, constraints: Vec<Constraint>) -> Self {
301 Self { stand_in_name: format!("__placeholder_{}", name), constraints, ident: name }
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 #[test]
310 fn parser_happy_case() {
311 fn token(kind: SyntaxKind, text: &str) -> PatternElement {
312 PatternElement::Token(Token { kind, text: SmolStr::new(text) })
313 }
314 fn placeholder(name: &str) -> PatternElement {
315 PatternElement::Placeholder(Placeholder::new(SmolStr::new(name), Vec::new()))
316 }
317 let result: SsrRule = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap();
318 assert_eq!(
319 result.pattern.raw.tokens,
320 vec![
321 token(SyntaxKind::IDENT, "foo"),
322 token(T!['('], "("),
323 placeholder("a"),
324 token(T![,], ","),
325 token(SyntaxKind::WHITESPACE, " "),
326 placeholder("b"),
327 token(T![')'], ")"),
328 ]
329 );
330 assert_eq!(
331 result.template.tokens,
332 vec![
333 token(SyntaxKind::IDENT, "bar"),
334 token(T!['('], "("),
335 placeholder("b"),
336 token(T![,], ","),
337 token(SyntaxKind::WHITESPACE, " "),
338 placeholder("a"),
339 token(T![')'], ")"),
340 ]
341 );
342 }
343}
diff --git a/crates/ra_ssr/src/replacing.rs b/crates/ra_ssr/src/replacing.rs
new file mode 100644
index 000000000..e43cc5167
--- /dev/null
+++ b/crates/ra_ssr/src/replacing.rs
@@ -0,0 +1,66 @@
1//! Code for applying replacement templates for matches that have previously been found.
2
3use crate::matching::Var;
4use crate::parsing::PatternElement;
5use crate::{Match, SsrMatches};
6use ra_syntax::ast::AstToken;
7use ra_syntax::TextSize;
8use ra_text_edit::TextEdit;
9
10/// Returns a text edit that will replace each match in `matches` with its corresponding replacement
11/// template. Placeholders in the template will have been substituted with whatever they matched to
12/// in the original code.
13pub(crate) fn matches_to_edit(matches: &SsrMatches, file_src: &str) -> TextEdit {
14 matches_to_edit_at_offset(matches, file_src, 0.into())
15}
16
17fn matches_to_edit_at_offset(
18 matches: &SsrMatches,
19 file_src: &str,
20 relative_start: TextSize,
21) -> TextEdit {
22 let mut edit_builder = ra_text_edit::TextEditBuilder::default();
23 for m in &matches.matches {
24 edit_builder.replace(
25 m.range.range.checked_sub(relative_start).unwrap(),
26 render_replace(m, file_src),
27 );
28 }
29 edit_builder.finish()
30}
31
32fn render_replace(match_info: &Match, file_src: &str) -> String {
33 let mut out = String::new();
34 for r in &match_info.template.tokens {
35 match r {
36 PatternElement::Token(t) => out.push_str(t.text.as_str()),
37 PatternElement::Placeholder(p) => {
38 if let Some(placeholder_value) =
39 match_info.placeholder_values.get(&Var(p.ident.to_string()))
40 {
41 let range = &placeholder_value.range.range;
42 let mut matched_text =
43 file_src[usize::from(range.start())..usize::from(range.end())].to_owned();
44 let edit = matches_to_edit_at_offset(
45 &placeholder_value.inner_matches,
46 file_src,
47 range.start(),
48 );
49 edit.apply(&mut matched_text);
50 out.push_str(&matched_text);
51 } else {
52 // We validated that all placeholder references were valid before we
53 // started, so this shouldn't happen.
54 panic!(
55 "Internal error: replacement referenced unknown placeholder {}",
56 p.ident
57 );
58 }
59 }
60 }
61 }
62 for comment in &match_info.ignored_comments {
63 out.push_str(&comment.syntax().to_string());
64 }
65 out
66}
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs
new file mode 100644
index 000000000..f20ae2cdf
--- /dev/null
+++ b/crates/ra_ssr/src/tests.rs
@@ -0,0 +1,582 @@
1use crate::{MatchFinder, SsrRule};
2use ra_db::{FileId, SourceDatabaseExt};
3use test_utils::mark;
4
5fn parse_error_text(query: &str) -> String {
6 format!("{}", query.parse::<SsrRule>().unwrap_err())
7}
8
9#[test]
10fn parser_empty_query() {
11 assert_eq!(parse_error_text(""), "Parse error: Cannot find delimiter `==>>`");
12}
13
14#[test]
15fn parser_no_delimiter() {
16 assert_eq!(parse_error_text("foo()"), "Parse error: Cannot find delimiter `==>>`");
17}
18
19#[test]
20fn parser_two_delimiters() {
21 assert_eq!(
22 parse_error_text("foo() ==>> a ==>> b "),
23 "Parse error: More than one delimiter found"
24 );
25}
26
27#[test]
28fn parser_repeated_name() {
29 assert_eq!(
30 parse_error_text("foo($a, $a) ==>>"),
31 "Parse error: Name `a` repeats more than once"
32 );
33}
34
35#[test]
36fn parser_invalid_pattern() {
37 assert_eq!(
38 parse_error_text(" ==>> ()"),
39 "Parse error: Pattern is not a valid Rust expression, type, item, path or pattern"
40 );
41}
42
43#[test]
44fn parser_invalid_template() {
45 assert_eq!(
46 parse_error_text("() ==>> )"),
47 "Parse error: Replacement is not a valid Rust expression, type, item, path or pattern"
48 );
49}
50
51#[test]
52fn parser_undefined_placeholder_in_replacement() {
53 assert_eq!(
54 parse_error_text("42 ==>> $a"),
55 "Parse error: Replacement contains undefined placeholders: $a"
56 );
57}
58
59fn single_file(code: &str) -> (ra_ide_db::RootDatabase, FileId) {
60 use ra_db::fixture::WithFixture;
61 ra_ide_db::RootDatabase::with_single_file(code)
62}
63
64fn assert_ssr_transform(rule: &str, input: &str, result: &str) {
65 assert_ssr_transforms(&[rule], input, result);
66}
67
68fn normalize_code(code: &str) -> String {
69 let (db, file_id) = single_file(code);
70 db.file_text(file_id).to_string()
71}
72
73fn assert_ssr_transforms(rules: &[&str], input: &str, result: &str) {
74 let (db, file_id) = single_file(input);
75 let mut match_finder = MatchFinder::new(&db);
76 for rule in rules {
77 let rule: SsrRule = rule.parse().unwrap();
78 match_finder.add_rule(rule);
79 }
80 if let Some(edits) = match_finder.edits_for_file(file_id) {
81 // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters
82 // stuff.
83 let mut after = db.file_text(file_id).to_string();
84 edits.apply(&mut after);
85 // Likewise, we need to make sure that whatever transformations fixture parsing applies,
86 // also get applied to our expected result.
87 let result = normalize_code(result);
88 assert_eq!(after, result);
89 } else {
90 panic!("No edits were made");
91 }
92}
93
94fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet: &str) {
95 let debug_info = match_finder.debug_where_text_equal(file_id, snippet);
96 println!(
97 "Match debug info: {} nodes had text exactly equal to '{}'",
98 debug_info.len(),
99 snippet
100 );
101 for (index, d) in debug_info.iter().enumerate() {
102 println!("Node #{}\n{:#?}\n", index, d);
103 }
104}
105
106fn assert_matches(pattern: &str, code: &str, expected: &[&str]) {
107 let (db, file_id) = single_file(code);
108 let mut match_finder = MatchFinder::new(&db);
109 match_finder.add_search_pattern(pattern.parse().unwrap());
110 let matched_strings: Vec<String> = match_finder
111 .find_matches_in_file(file_id)
112 .flattened()
113 .matches
114 .iter()
115 .map(|m| m.matched_text())
116 .collect();
117 if matched_strings != expected && !expected.is_empty() {
118 print_match_debug_info(&match_finder, file_id, &expected[0]);
119 }
120 assert_eq!(matched_strings, expected);
121}
122
123fn assert_no_match(pattern: &str, code: &str) {
124 let (db, file_id) = single_file(code);
125 let mut match_finder = MatchFinder::new(&db);
126 match_finder.add_search_pattern(pattern.parse().unwrap());
127 let matches = match_finder.find_matches_in_file(file_id).flattened().matches;
128 if !matches.is_empty() {
129 print_match_debug_info(&match_finder, file_id, &matches[0].matched_text());
130 panic!("Got {} matches when we expected none: {:#?}", matches.len(), matches);
131 }
132}
133
134fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) {
135 let (db, file_id) = single_file(code);
136 let mut match_finder = MatchFinder::new(&db);
137 match_finder.add_search_pattern(pattern.parse().unwrap());
138 let mut reasons = Vec::new();
139 for d in match_finder.debug_where_text_equal(file_id, snippet) {
140 if let Some(reason) = d.match_failure_reason() {
141 reasons.push(reason.to_owned());
142 }
143 }
144 assert_eq!(reasons, vec![expected_reason]);
145}
146
147#[test]
148fn ssr_function_to_method() {
149 assert_ssr_transform(
150 "my_function($a, $b) ==>> ($a).my_method($b)",
151 "fn my_function() {} fn main() { loop { my_function( other_func(x, y), z + w) } }",
152 "fn my_function() {} fn main() { loop { (other_func(x, y)).my_method(z + w) } }",
153 )
154}
155
156#[test]
157fn ssr_nested_function() {
158 assert_ssr_transform(
159 "foo($a, $b, $c) ==>> bar($c, baz($a, $b))",
160 "fn foo() {} fn main { foo (x + value.method(b), x+y-z, true && false) }",
161 "fn foo() {} fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }",
162 )
163}
164
165#[test]
166fn ssr_expected_spacing() {
167 assert_ssr_transform(
168 "foo($x) + bar() ==>> bar($x)",
169 "fn foo() {} fn bar() {} fn main() { foo(5) + bar() }",
170 "fn foo() {} fn bar() {} fn main() { bar(5) }",
171 );
172}
173
174#[test]
175fn ssr_with_extra_space() {
176 assert_ssr_transform(
177 "foo($x ) + bar() ==>> bar($x)",
178 "fn foo() {} fn bar() {} fn main() { foo( 5 ) +bar( ) }",
179 "fn foo() {} fn bar() {} fn main() { bar(5) }",
180 );
181}
182
183#[test]
184fn ssr_keeps_nested_comment() {
185 assert_ssr_transform(
186 "foo($x) ==>> bar($x)",
187 "fn foo() {} fn main() { foo(other(5 /* using 5 */)) }",
188 "fn foo() {} fn main() { bar(other(5 /* using 5 */)) }",
189 )
190}
191
192#[test]
193fn ssr_keeps_comment() {
194 assert_ssr_transform(
195 "foo($x) ==>> bar($x)",
196 "fn foo() {} fn main() { foo(5 /* using 5 */) }",
197 "fn foo() {} fn main() { bar(5)/* using 5 */ }",
198 )
199}
200
201#[test]
202fn ssr_struct_lit() {
203 assert_ssr_transform(
204 "foo{a: $a, b: $b} ==>> foo::new($a, $b)",
205 "fn foo() {} fn main() { foo{b:2, a:1} }",
206 "fn foo() {} fn main() { foo::new(1, 2) }",
207 )
208}
209
210#[test]
211fn ignores_whitespace() {
212 assert_matches("1+2", "fn f() -> i32 {1 + 2}", &["1 + 2"]);
213 assert_matches("1 + 2", "fn f() -> i32 {1+2}", &["1+2"]);
214}
215
216#[test]
217fn no_match() {
218 assert_no_match("1 + 3", "fn f() -> i32 {1 + 2}");
219}
220
221#[test]
222fn match_fn_definition() {
223 assert_matches("fn $a($b: $t) {$c}", "fn f(a: i32) {bar()}", &["fn f(a: i32) {bar()}"]);
224}
225
226#[test]
227fn match_struct_definition() {
228 let code = r#"
229 struct Option<T> {}
230 struct Bar {}
231 struct Foo {name: Option<String>}"#;
232 assert_matches("struct $n {$f: Option<String>}", code, &["struct Foo {name: Option<String>}"]);
233}
234
235#[test]
236fn match_expr() {
237 let code = r#"
238 fn foo() {}
239 fn f() -> i32 {foo(40 + 2, 42)}"#;
240 assert_matches("foo($a, $b)", code, &["foo(40 + 2, 42)"]);
241 assert_no_match("foo($a, $b, $c)", code);
242 assert_no_match("foo($a)", code);
243}
244
245#[test]
246fn match_nested_method_calls() {
247 assert_matches(
248 "$a.z().z().z()",
249 "fn f() {h().i().j().z().z().z().d().e()}",
250 &["h().i().j().z().z().z()"],
251 );
252}
253
254// Make sure that our node matching semantics don't differ within macro calls.
255#[test]
256fn match_nested_method_calls_with_macro_call() {
257 assert_matches(
258 "$a.z().z().z()",
259 r#"
260 macro_rules! m1 { ($a:expr) => {$a}; }
261 fn f() {m1!(h().i().j().z().z().z().d().e())}"#,
262 &["h().i().j().z().z().z()"],
263 );
264}
265
266#[test]
267fn match_complex_expr() {
268 let code = r#"
269 fn foo() {} fn bar() {}
270 fn f() -> i32 {foo(bar(40, 2), 42)}"#;
271 assert_matches("foo($a, $b)", code, &["foo(bar(40, 2), 42)"]);
272 assert_no_match("foo($a, $b, $c)", code);
273 assert_no_match("foo($a)", code);
274 assert_matches("bar($a, $b)", code, &["bar(40, 2)"]);
275}
276
277// Trailing commas in the code should be ignored.
278#[test]
279fn match_with_trailing_commas() {
280 // Code has comma, pattern doesn't.
281 assert_matches("foo($a, $b)", "fn foo() {} fn f() {foo(1, 2,);}", &["foo(1, 2,)"]);
282 assert_matches("Foo{$a, $b}", "struct Foo {} fn f() {Foo{1, 2,};}", &["Foo{1, 2,}"]);
283
284 // Pattern has comma, code doesn't.
285 assert_matches("foo($a, $b,)", "fn foo() {} fn f() {foo(1, 2);}", &["foo(1, 2)"]);
286 assert_matches("Foo{$a, $b,}", "struct Foo {} fn f() {Foo{1, 2};}", &["Foo{1, 2}"]);
287}
288
289#[test]
290fn match_type() {
291 assert_matches("i32", "fn f() -> i32 {1 + 2}", &["i32"]);
292 assert_matches(
293 "Option<$a>",
294 "struct Option<T> {} fn f() -> Option<i32> {42}",
295 &["Option<i32>"],
296 );
297 assert_no_match(
298 "Option<$a>",
299 "struct Option<T> {} struct Result<T, E> {} fn f() -> Result<i32, ()> {42}",
300 );
301}
302
303#[test]
304fn match_struct_instantiation() {
305 let code = r#"
306 struct Foo {bar: i32, baz: i32}
307 fn f() {Foo {bar: 1, baz: 2}}"#;
308 assert_matches("Foo {bar: 1, baz: 2}", code, &["Foo {bar: 1, baz: 2}"]);
309 // Now with placeholders for all parts of the struct.
310 assert_matches("Foo {$a: $b, $c: $d}", code, &["Foo {bar: 1, baz: 2}"]);
311 assert_matches("Foo {}", "struct Foo {} fn f() {Foo {}}", &["Foo {}"]);
312}
313
314#[test]
315fn match_path() {
316 let code = r#"
317 mod foo {
318 fn bar() {}
319 }
320 fn f() {foo::bar(42)}"#;
321 assert_matches("foo::bar", code, &["foo::bar"]);
322 assert_matches("$a::bar", code, &["foo::bar"]);
323 assert_matches("foo::$b", code, &["foo::bar"]);
324}
325
326#[test]
327fn match_pattern() {
328 assert_matches("Some($a)", "struct Some(); fn f() {if let Some(x) = foo() {}}", &["Some(x)"]);
329}
330
331#[test]
332fn literal_constraint() {
333 mark::check!(literal_constraint);
334 let code = r#"
335 enum Option<T> { Some(T), None }
336 use Option::Some;
337 fn f1() {
338 let x1 = Some(42);
339 let x2 = Some("foo");
340 let x3 = Some(x1);
341 let x4 = Some(40 + 2);
342 let x5 = Some(true);
343 }
344 "#;
345 assert_matches("Some(${a:kind(literal)})", code, &["Some(42)", "Some(\"foo\")", "Some(true)"]);
346 assert_matches("Some(${a:not(kind(literal))})", code, &["Some(x1)", "Some(40 + 2)"]);
347}
348
349#[test]
350fn match_reordered_struct_instantiation() {
351 assert_matches(
352 "Foo {aa: 1, b: 2, ccc: 3}",
353 "struct Foo {} fn f() {Foo {b: 2, ccc: 3, aa: 1}}",
354 &["Foo {b: 2, ccc: 3, aa: 1}"],
355 );
356 assert_no_match("Foo {a: 1}", "struct Foo {} fn f() {Foo {b: 1}}");
357 assert_no_match("Foo {a: 1}", "struct Foo {} fn f() {Foo {a: 2}}");
358 assert_no_match("Foo {a: 1, b: 2}", "struct Foo {} fn f() {Foo {a: 1}}");
359 assert_no_match("Foo {a: 1, b: 2}", "struct Foo {} fn f() {Foo {b: 2}}");
360 assert_no_match("Foo {a: 1, }", "struct Foo {} fn f() {Foo {a: 1, b: 2}}");
361 assert_no_match("Foo {a: 1, z: 9}", "struct Foo {} fn f() {Foo {a: 1}}");
362}
363
364#[test]
365fn match_macro_invocation() {
366 assert_matches(
367 "foo!($a)",
368 "macro_rules! foo {() => {}} fn() {foo(foo!(foo()))}",
369 &["foo!(foo())"],
370 );
371 assert_matches(
372 "foo!(41, $a, 43)",
373 "macro_rules! foo {() => {}} fn() {foo!(41, 42, 43)}",
374 &["foo!(41, 42, 43)"],
375 );
376 assert_no_match("foo!(50, $a, 43)", "macro_rules! foo {() => {}} fn() {foo!(41, 42, 43}");
377 assert_no_match("foo!(41, $a, 50)", "macro_rules! foo {() => {}} fn() {foo!(41, 42, 43}");
378 assert_matches(
379 "foo!($a())",
380 "macro_rules! foo {() => {}} fn() {foo!(bar())}",
381 &["foo!(bar())"],
382 );
383}
384
385// When matching within a macro expansion, we only allow matches of nodes that originated from
386// the macro call, not from the macro definition.
387#[test]
388fn no_match_expression_from_macro() {
389 assert_no_match(
390 "$a.clone()",
391 r#"
392 macro_rules! m1 {
393 () => {42.clone()}
394 }
395 fn f1() {m1!()}
396 "#,
397 );
398}
399
400// We definitely don't want to allow matching of an expression that part originates from the
401// macro call `42` and part from the macro definition `.clone()`.
402#[test]
403fn no_match_split_expression() {
404 assert_no_match(
405 "$a.clone()",
406 r#"
407 macro_rules! m1 {
408 ($x:expr) => {$x.clone()}
409 }
410 fn f1() {m1!(42)}
411 "#,
412 );
413}
414
415#[test]
416fn replace_function_call() {
417 assert_ssr_transform(
418 "foo() ==>> bar()",
419 "fn foo() {} fn f1() {foo(); foo();}",
420 "fn foo() {} fn f1() {bar(); bar();}",
421 );
422}
423
424#[test]
425fn replace_function_call_with_placeholders() {
426 assert_ssr_transform(
427 "foo($a, $b) ==>> bar($b, $a)",
428 "fn foo() {} fn f1() {foo(5, 42)}",
429 "fn foo() {} fn f1() {bar(42, 5)}",
430 );
431}
432
433#[test]
434fn replace_nested_function_calls() {
435 assert_ssr_transform(
436 "foo($a) ==>> bar($a)",
437 "fn foo() {} fn f1() {foo(foo(42))}",
438 "fn foo() {} fn f1() {bar(bar(42))}",
439 );
440}
441
442#[test]
443fn replace_type() {
444 assert_ssr_transform(
445 "Result<(), $a> ==>> Option<$a>",
446 "struct Result<T, E> {} fn f1() -> Result<(), Vec<Error>> {foo()}",
447 "struct Result<T, E> {} fn f1() -> Option<Vec<Error>> {foo()}",
448 );
449}
450
451#[test]
452fn replace_struct_init() {
453 assert_ssr_transform(
454 "Foo {a: $a, b: $b} ==>> Foo::new($a, $b)",
455 "struct Foo {} fn f1() {Foo{b: 1, a: 2}}",
456 "struct Foo {} fn f1() {Foo::new(2, 1)}",
457 );
458}
459
460#[test]
461fn replace_macro_invocations() {
462 assert_ssr_transform(
463 "try!($a) ==>> $a?",
464 "macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(try!(foo()));}",
465 "macro_rules! try {() => {}} fn f1() -> Result<(), E> {bar(foo()?);}",
466 );
467 assert_ssr_transform(
468 "foo!($a($b)) ==>> foo($b, $a)",
469 "macro_rules! foo {() => {}} fn f1() {foo!(abc(def() + 2));}",
470 "macro_rules! foo {() => {}} fn f1() {foo(def() + 2, abc);}",
471 );
472}
473
474#[test]
475fn replace_binary_op() {
476 assert_ssr_transform(
477 "$a + $b ==>> $b + $a",
478 "fn f() {2 * 3 + 4 * 5}",
479 "fn f() {4 * 5 + 2 * 3}",
480 );
481 assert_ssr_transform(
482 "$a + $b ==>> $b + $a",
483 "fn f() {1 + 2 + 3 + 4}",
484 "fn f() {4 + 3 + 2 + 1}",
485 );
486}
487
488#[test]
489fn match_binary_op() {
490 assert_matches("$a + $b", "fn f() {1 + 2 + 3 + 4}", &["1 + 2", "1 + 2 + 3", "1 + 2 + 3 + 4"]);
491}
492
493#[test]
494fn multiple_rules() {
495 assert_ssr_transforms(
496 &["$a + 1 ==>> add_one($a)", "$a + $b ==>> add($a, $b)"],
497 "fn f() -> i32 {3 + 2 + 1}",
498 "fn f() -> i32 {add_one(add(3, 2))}",
499 )
500}
501
502#[test]
503fn match_within_macro_invocation() {
504 let code = r#"
505 macro_rules! foo {
506 ($a:stmt; $b:expr) => {
507 $b
508 };
509 }
510 struct A {}
511 impl A {
512 fn bar() {}
513 }
514 fn f1() {
515 let aaa = A {};
516 foo!(macro_ignores_this(); aaa.bar());
517 }
518 "#;
519 assert_matches("$a.bar()", code, &["aaa.bar()"]);
520}
521
522#[test]
523fn replace_within_macro_expansion() {
524 assert_ssr_transform(
525 "$a.foo() ==>> bar($a)",
526 r#"
527 macro_rules! macro1 {
528 ($a:expr) => {$a}
529 }
530 fn f() {macro1!(5.x().foo().o2())}"#,
531 r#"
532 macro_rules! macro1 {
533 ($a:expr) => {$a}
534 }
535 fn f() {macro1!(bar(5.x()).o2())}"#,
536 )
537}
538
539#[test]
540fn preserves_whitespace_within_macro_expansion() {
541 assert_ssr_transform(
542 "$a + $b ==>> $b - $a",
543 r#"
544 macro_rules! macro1 {
545 ($a:expr) => {$a}
546 }
547 fn f() {macro1!(1 * 2 + 3 + 4}"#,
548 r#"
549 macro_rules! macro1 {
550 ($a:expr) => {$a}
551 }
552 fn f() {macro1!(4 - 3 - 1 * 2}"#,
553 )
554}
555
556#[test]
557fn match_failure_reasons() {
558 let code = r#"
559 fn bar() {}
560 macro_rules! foo {
561 ($a:expr) => {
562 1 + $a + 2
563 };
564 }
565 fn f1() {
566 bar(1, 2);
567 foo!(5 + 43.to_string() + 5);
568 }
569 "#;
570 assert_match_failure_reason(
571 "bar($a, 3)",
572 code,
573 "bar(1, 2)",
574 r#"Pattern wanted token '3' (INT_NUMBER), but code had token '2' (INT_NUMBER)"#,
575 );
576 assert_match_failure_reason(
577 "42.to_string()",
578 code,
579 "43.to_string()",
580 r#"Pattern wanted token '42' (INT_NUMBER), but code had token '43' (INT_NUMBER)"#,
581 );
582}
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index a8ff2e74f..57cc09854 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
13[dependencies] 13[dependencies]
14itertools = "0.9.0" 14itertools = "0.9.0"
15rowan = "0.10.0" 15rowan = "0.10.0"
16rustc_lexer = { version = "661.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "666.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"
@@ -31,4 +31,6 @@ serde = { version = "1.0.106", features = ["derive"] }
31 31
32[dev-dependencies] 32[dev-dependencies]
33test_utils = { path = "../test_utils" } 33test_utils = { path = "../test_utils" }
34expect = { path = "../expect" }
34walkdir = "2.3.1" 35walkdir = "2.3.1"
36rayon = "1"
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs
index 664894d1f..26b3c813a 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -41,6 +41,10 @@ pub fn find_node_at_offset<N: AstNode>(syntax: &SyntaxNode, offset: TextSize) ->
41 ancestors_at_offset(syntax, offset).find_map(N::cast) 41 ancestors_at_offset(syntax, offset).find_map(N::cast)
42} 42}
43 43
44pub fn find_node_at_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
45 find_covering_element(syntax, range).ancestors().find_map(N::cast)
46}
47
44/// Skip to next non `trivia` token 48/// Skip to next non `trivia` token
45pub fn skip_trivia_token(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> { 49pub fn skip_trivia_token(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> {
46 while token.kind().is_trivia() { 50 while token.kind().is_trivia() {
@@ -290,6 +294,11 @@ impl<'a> SyntaxRewriter<'a> {
290 N::cast(self.rewrite(node.syntax())).unwrap() 294 N::cast(self.rewrite(node.syntax())).unwrap()
291 } 295 }
292 296
297 /// Returns a node that encompasses all replacements to be done by this rewriter.
298 ///
299 /// Passing the returned node to `rewrite` will apply all replacements queued up in `self`.
300 ///
301 /// Returns `None` when there are no replacements.
293 pub fn rewrite_root(&self) -> Option<SyntaxNode> { 302 pub fn rewrite_root(&self) -> Option<SyntaxNode> {
294 assert!(self.f.is_none()); 303 assert!(self.f.is_none());
295 self.replacements 304 self.replacements
@@ -298,6 +307,9 @@ impl<'a> SyntaxRewriter<'a> {
298 SyntaxElement::Node(it) => it.clone(), 307 SyntaxElement::Node(it) => it.clone(),
299 SyntaxElement::Token(it) => it.parent(), 308 SyntaxElement::Token(it) => it.parent(),
300 }) 309 })
310 // If we only have one replacement, we must return its parent node, since `rewrite` does
311 // not replace the node passed to it.
312 .map(|it| it.parent().unwrap_or(it))
301 .fold1(|a, b| least_common_ancestor(&a, &b).unwrap()) 313 .fold1(|a, b| least_common_ancestor(&a, &b).unwrap())
302 } 314 }
303 315
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 1876afe95..9d02aeef3 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -75,7 +75,7 @@ impl<N> AstChildren<N> {
75impl<N: AstNode> Iterator for AstChildren<N> { 75impl<N: AstNode> Iterator for AstChildren<N> {
76 type Item = N; 76 type Item = N;
77 fn next(&mut self) -> Option<N> { 77 fn next(&mut self) -> Option<N> {
78 self.inner.by_ref().find_map(N::cast) 78 self.inner.find_map(N::cast)
79 } 79 }
80} 80}
81 81
@@ -285,6 +285,8 @@ where
285 let pred = predicates.next().unwrap(); 285 let pred = predicates.next().unwrap();
286 let mut bounds = pred.type_bound_list().unwrap().bounds(); 286 let mut bounds = pred.type_bound_list().unwrap().bounds();
287 287
288 assert!(pred.for_token().is_none());
289 assert!(pred.type_param_list().is_none());
288 assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string()); 290 assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string());
289 assert_bound("Clone", bounds.next()); 291 assert_bound("Clone", bounds.next());
290 assert_bound("Copy", bounds.next()); 292 assert_bound("Copy", bounds.next());
@@ -322,6 +324,8 @@ where
322 let pred = predicates.next().unwrap(); 324 let pred = predicates.next().unwrap();
323 let mut bounds = pred.type_bound_list().unwrap().bounds(); 325 let mut bounds = pred.type_bound_list().unwrap().bounds();
324 326
325 assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string()); 327 assert!(pred.for_token().is_some());
328 assert_eq!("<'a>", pred.type_param_list().unwrap().syntax().text().to_string());
329 assert_eq!("F", pred.type_ref().unwrap().syntax().text().to_string());
326 assert_bound("Fn(&'a str)", bounds.next()); 330 assert_bound("Fn(&'a str)", bounds.next());
327} 331}
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 29eb3fcb9..abc7a646c 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -189,6 +189,21 @@ impl ast::RecordFieldList {
189 } 189 }
190} 190}
191 191
192impl ast::TypeAliasDef {
193 #[must_use]
194 pub fn remove_bounds(&self) -> ast::TypeAliasDef {
195 let colon = match self.colon_token() {
196 Some(it) => it,
197 None => return self.clone(),
198 };
199 let end = match self.type_bound_list() {
200 Some(it) => it.syntax().clone().into(),
201 None => colon.clone().into(),
202 };
203 self.replace_children(colon.into()..=end, iter::empty())
204 }
205}
206
192impl ast::TypeParam { 207impl ast::TypeParam {
193 #[must_use] 208 #[must_use]
194 pub fn remove_bounds(&self) -> ast::TypeParam { 209 pub fn remove_bounds(&self) -> ast::TypeParam {
@@ -299,12 +314,8 @@ impl ast::UseTree {
299 Some(it) => it, 314 Some(it) => it,
300 None => return self.clone(), 315 None => return self.clone(),
301 }; 316 };
302 let use_tree = make::use_tree( 317 let use_tree =
303 suffix.clone(), 318 make::use_tree(suffix, self.use_tree_list(), self.alias(), self.star_token().is_some());
304 self.use_tree_list(),
305 self.alias(),
306 self.star_token().is_some(),
307 );
308 let nested = make::use_tree_list(iter::once(use_tree)); 319 let nested = make::use_tree_list(iter::once(use_tree));
309 return make::use_tree(prefix.clone(), Some(nested), None, false); 320 return make::use_tree(prefix.clone(), Some(nested), None, false);
310 321
@@ -579,12 +590,17 @@ pub trait AstNodeEdit: AstNode + Clone + Sized {
579 rewriter.rewrite_ast(self) 590 rewriter.rewrite_ast(self)
580 } 591 }
581 #[must_use] 592 #[must_use]
582 fn indent(&self, indent: IndentLevel) -> Self { 593 fn indent(&self, level: IndentLevel) -> Self {
583 Self::cast(indent.increase_indent(self.syntax().clone())).unwrap() 594 Self::cast(level.increase_indent(self.syntax().clone())).unwrap()
595 }
596 #[must_use]
597 fn dedent(&self, level: IndentLevel) -> Self {
598 Self::cast(level.decrease_indent(self.syntax().clone())).unwrap()
584 } 599 }
585 #[must_use] 600 #[must_use]
586 fn dedent(&self, indent: IndentLevel) -> Self { 601 fn reset_indent(&self) -> Self {
587 Self::cast(indent.decrease_indent(self.syntax().clone())).unwrap() 602 let level = IndentLevel::from_node(self.syntax());
603 self.dedent(level)
588 } 604 }
589} 605}
590 606
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs
index 7771d6759..db5438d68 100644
--- a/crates/ra_syntax/src/ast/expr_extensions.rs
+++ b/crates/ra_syntax/src/ast/expr_extensions.rs
@@ -399,10 +399,7 @@ impl ast::BlockExpr {
399 Some(it) => it, 399 Some(it) => it,
400 None => return true, 400 None => return true,
401 }; 401 };
402 match parent.kind() { 402 !matches!(parent.kind(), FN_DEF | IF_EXPR | WHILE_EXPR | LOOP_EXPR | EFFECT_EXPR)
403 FN_DEF | IF_EXPR | WHILE_EXPR | LOOP_EXPR | EFFECT_EXPR => false,
404 _ => true,
405 }
406 } 403 }
407} 404}
408 405
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs
index 98c38d009..662c6f73e 100644
--- a/crates/ra_syntax/src/ast/extensions.rs
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -459,16 +459,16 @@ impl ast::RangePat {
459 459
460impl ast::TokenTree { 460impl ast::TokenTree {
461 pub fn left_delimiter_token(&self) -> Option<SyntaxToken> { 461 pub fn left_delimiter_token(&self) -> Option<SyntaxToken> {
462 self.syntax().first_child_or_token()?.into_token().filter(|it| match it.kind() { 462 self.syntax()
463 T!['{'] | T!['('] | T!['['] => true, 463 .first_child_or_token()?
464 _ => false, 464 .into_token()
465 }) 465 .filter(|it| matches!(it.kind(), T!['{'] | T!['('] | T!['[']))
466 } 466 }
467 467
468 pub fn right_delimiter_token(&self) -> Option<SyntaxToken> { 468 pub fn right_delimiter_token(&self) -> Option<SyntaxToken> {
469 self.syntax().last_child_or_token()?.into_token().filter(|it| match it.kind() { 469 self.syntax()
470 T!['}'] | T![')'] | T![']'] => true, 470 .last_child_or_token()?
471 _ => false, 471 .into_token()
472 }) 472 .filter(|it| matches!(it.kind(), T!['}'] | T![')'] | T![']']))
473 } 473 }
474} 474}
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index cb430ca01..58141da11 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -2052,6 +2052,8 @@ pub struct WherePred {
2052} 2052}
2053impl ast::TypeBoundsOwner for WherePred {} 2053impl ast::TypeBoundsOwner for WherePred {}
2054impl WherePred { 2054impl WherePred {
2055 pub fn for_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![for]) }
2056 pub fn type_param_list(&self) -> Option<TypeParamList> { support::child(&self.syntax) }
2055 pub fn lifetime_token(&self) -> Option<SyntaxToken> { 2057 pub fn lifetime_token(&self) -> Option<SyntaxToken> {
2056 support::token(&self.syntax, T![lifetime]) 2058 support::token(&self.syntax, T![lifetime])
2057 } 2059 }
@@ -4849,687 +4851,687 @@ impl AstNode for FieldDefList {
4849 } 4851 }
4850} 4852}
4851impl std::fmt::Display for NominalDef { 4853impl std::fmt::Display for NominalDef {
4852 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4854 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4853 std::fmt::Display::fmt(self.syntax(), f) 4855 std::fmt::Display::fmt(self.syntax(), f)
4854 } 4856 }
4855} 4857}
4856impl std::fmt::Display for GenericParam { 4858impl std::fmt::Display for GenericParam {
4857 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4859 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4858 std::fmt::Display::fmt(self.syntax(), f) 4860 std::fmt::Display::fmt(self.syntax(), f)
4859 } 4861 }
4860} 4862}
4861impl std::fmt::Display for GenericArg { 4863impl std::fmt::Display for GenericArg {
4862 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4864 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4863 std::fmt::Display::fmt(self.syntax(), f) 4865 std::fmt::Display::fmt(self.syntax(), f)
4864 } 4866 }
4865} 4867}
4866impl std::fmt::Display for TypeRef { 4868impl std::fmt::Display for TypeRef {
4867 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4869 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4868 std::fmt::Display::fmt(self.syntax(), f) 4870 std::fmt::Display::fmt(self.syntax(), f)
4869 } 4871 }
4870} 4872}
4871impl std::fmt::Display for ModuleItem { 4873impl std::fmt::Display for ModuleItem {
4872 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4874 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4873 std::fmt::Display::fmt(self.syntax(), f) 4875 std::fmt::Display::fmt(self.syntax(), f)
4874 } 4876 }
4875} 4877}
4876impl std::fmt::Display for AssocItem { 4878impl std::fmt::Display for AssocItem {
4877 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4879 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4878 std::fmt::Display::fmt(self.syntax(), f) 4880 std::fmt::Display::fmt(self.syntax(), f)
4879 } 4881 }
4880} 4882}
4881impl std::fmt::Display for ExternItem { 4883impl std::fmt::Display for ExternItem {
4882 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4884 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4883 std::fmt::Display::fmt(self.syntax(), f) 4885 std::fmt::Display::fmt(self.syntax(), f)
4884 } 4886 }
4885} 4887}
4886impl std::fmt::Display for Expr { 4888impl std::fmt::Display for Expr {
4887 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4889 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4888 std::fmt::Display::fmt(self.syntax(), f) 4890 std::fmt::Display::fmt(self.syntax(), f)
4889 } 4891 }
4890} 4892}
4891impl std::fmt::Display for Pat { 4893impl std::fmt::Display for Pat {
4892 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4894 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4893 std::fmt::Display::fmt(self.syntax(), f) 4895 std::fmt::Display::fmt(self.syntax(), f)
4894 } 4896 }
4895} 4897}
4896impl std::fmt::Display for RecordInnerPat { 4898impl std::fmt::Display for RecordInnerPat {
4897 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4899 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4898 std::fmt::Display::fmt(self.syntax(), f) 4900 std::fmt::Display::fmt(self.syntax(), f)
4899 } 4901 }
4900} 4902}
4901impl std::fmt::Display for AttrInput { 4903impl std::fmt::Display for AttrInput {
4902 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4904 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4903 std::fmt::Display::fmt(self.syntax(), f) 4905 std::fmt::Display::fmt(self.syntax(), f)
4904 } 4906 }
4905} 4907}
4906impl std::fmt::Display for Stmt { 4908impl std::fmt::Display for Stmt {
4907 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4909 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4908 std::fmt::Display::fmt(self.syntax(), f) 4910 std::fmt::Display::fmt(self.syntax(), f)
4909 } 4911 }
4910} 4912}
4911impl std::fmt::Display for FieldDefList { 4913impl std::fmt::Display for FieldDefList {
4912 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4914 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4913 std::fmt::Display::fmt(self.syntax(), f) 4915 std::fmt::Display::fmt(self.syntax(), f)
4914 } 4916 }
4915} 4917}
4916impl std::fmt::Display for SourceFile { 4918impl std::fmt::Display for SourceFile {
4917 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4919 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4918 std::fmt::Display::fmt(self.syntax(), f) 4920 std::fmt::Display::fmt(self.syntax(), f)
4919 } 4921 }
4920} 4922}
4921impl std::fmt::Display for FnDef { 4923impl std::fmt::Display for FnDef {
4922 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4924 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4923 std::fmt::Display::fmt(self.syntax(), f) 4925 std::fmt::Display::fmt(self.syntax(), f)
4924 } 4926 }
4925} 4927}
4926impl std::fmt::Display for RetType { 4928impl std::fmt::Display for RetType {
4927 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4929 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4928 std::fmt::Display::fmt(self.syntax(), f) 4930 std::fmt::Display::fmt(self.syntax(), f)
4929 } 4931 }
4930} 4932}
4931impl std::fmt::Display for StructDef { 4933impl std::fmt::Display for StructDef {
4932 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4934 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4933 std::fmt::Display::fmt(self.syntax(), f) 4935 std::fmt::Display::fmt(self.syntax(), f)
4934 } 4936 }
4935} 4937}
4936impl std::fmt::Display for UnionDef { 4938impl std::fmt::Display for UnionDef {
4937 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4939 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4938 std::fmt::Display::fmt(self.syntax(), f) 4940 std::fmt::Display::fmt(self.syntax(), f)
4939 } 4941 }
4940} 4942}
4941impl std::fmt::Display for RecordFieldDefList { 4943impl std::fmt::Display for RecordFieldDefList {
4942 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4944 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4943 std::fmt::Display::fmt(self.syntax(), f) 4945 std::fmt::Display::fmt(self.syntax(), f)
4944 } 4946 }
4945} 4947}
4946impl std::fmt::Display for RecordFieldDef { 4948impl std::fmt::Display for RecordFieldDef {
4947 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4949 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4948 std::fmt::Display::fmt(self.syntax(), f) 4950 std::fmt::Display::fmt(self.syntax(), f)
4949 } 4951 }
4950} 4952}
4951impl std::fmt::Display for TupleFieldDefList { 4953impl std::fmt::Display for TupleFieldDefList {
4952 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4954 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4953 std::fmt::Display::fmt(self.syntax(), f) 4955 std::fmt::Display::fmt(self.syntax(), f)
4954 } 4956 }
4955} 4957}
4956impl std::fmt::Display for TupleFieldDef { 4958impl std::fmt::Display for TupleFieldDef {
4957 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4959 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4958 std::fmt::Display::fmt(self.syntax(), f) 4960 std::fmt::Display::fmt(self.syntax(), f)
4959 } 4961 }
4960} 4962}
4961impl std::fmt::Display for EnumDef { 4963impl std::fmt::Display for EnumDef {
4962 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4964 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4963 std::fmt::Display::fmt(self.syntax(), f) 4965 std::fmt::Display::fmt(self.syntax(), f)
4964 } 4966 }
4965} 4967}
4966impl std::fmt::Display for EnumVariantList { 4968impl std::fmt::Display for EnumVariantList {
4967 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4969 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4968 std::fmt::Display::fmt(self.syntax(), f) 4970 std::fmt::Display::fmt(self.syntax(), f)
4969 } 4971 }
4970} 4972}
4971impl std::fmt::Display for EnumVariant { 4973impl std::fmt::Display for EnumVariant {
4972 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4974 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4973 std::fmt::Display::fmt(self.syntax(), f) 4975 std::fmt::Display::fmt(self.syntax(), f)
4974 } 4976 }
4975} 4977}
4976impl std::fmt::Display for TraitDef { 4978impl std::fmt::Display for TraitDef {
4977 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4979 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4978 std::fmt::Display::fmt(self.syntax(), f) 4980 std::fmt::Display::fmt(self.syntax(), f)
4979 } 4981 }
4980} 4982}
4981impl std::fmt::Display for Module { 4983impl std::fmt::Display for Module {
4982 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4984 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4983 std::fmt::Display::fmt(self.syntax(), f) 4985 std::fmt::Display::fmt(self.syntax(), f)
4984 } 4986 }
4985} 4987}
4986impl std::fmt::Display for ItemList { 4988impl std::fmt::Display for ItemList {
4987 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4989 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4988 std::fmt::Display::fmt(self.syntax(), f) 4990 std::fmt::Display::fmt(self.syntax(), f)
4989 } 4991 }
4990} 4992}
4991impl std::fmt::Display for ConstDef { 4993impl std::fmt::Display for ConstDef {
4992 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4994 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4993 std::fmt::Display::fmt(self.syntax(), f) 4995 std::fmt::Display::fmt(self.syntax(), f)
4994 } 4996 }
4995} 4997}
4996impl std::fmt::Display for StaticDef { 4998impl std::fmt::Display for StaticDef {
4997 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4999 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4998 std::fmt::Display::fmt(self.syntax(), f) 5000 std::fmt::Display::fmt(self.syntax(), f)
4999 } 5001 }
5000} 5002}
5001impl std::fmt::Display for TypeAliasDef { 5003impl std::fmt::Display for TypeAliasDef {
5002 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5004 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5003 std::fmt::Display::fmt(self.syntax(), f) 5005 std::fmt::Display::fmt(self.syntax(), f)
5004 } 5006 }
5005} 5007}
5006impl std::fmt::Display for ImplDef { 5008impl std::fmt::Display for ImplDef {
5007 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5009 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5008 std::fmt::Display::fmt(self.syntax(), f) 5010 std::fmt::Display::fmt(self.syntax(), f)
5009 } 5011 }
5010} 5012}
5011impl std::fmt::Display for ParenType { 5013impl std::fmt::Display for ParenType {
5012 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5014 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5013 std::fmt::Display::fmt(self.syntax(), f) 5015 std::fmt::Display::fmt(self.syntax(), f)
5014 } 5016 }
5015} 5017}
5016impl std::fmt::Display for TupleType { 5018impl std::fmt::Display for TupleType {
5017 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5019 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5018 std::fmt::Display::fmt(self.syntax(), f) 5020 std::fmt::Display::fmt(self.syntax(), f)
5019 } 5021 }
5020} 5022}
5021impl std::fmt::Display for NeverType { 5023impl std::fmt::Display for NeverType {
5022 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5024 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5023 std::fmt::Display::fmt(self.syntax(), f) 5025 std::fmt::Display::fmt(self.syntax(), f)
5024 } 5026 }
5025} 5027}
5026impl std::fmt::Display for PathType { 5028impl std::fmt::Display for PathType {
5027 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5029 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5028 std::fmt::Display::fmt(self.syntax(), f) 5030 std::fmt::Display::fmt(self.syntax(), f)
5029 } 5031 }
5030} 5032}
5031impl std::fmt::Display for PointerType { 5033impl std::fmt::Display for PointerType {
5032 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5034 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5033 std::fmt::Display::fmt(self.syntax(), f) 5035 std::fmt::Display::fmt(self.syntax(), f)
5034 } 5036 }
5035} 5037}
5036impl std::fmt::Display for ArrayType { 5038impl std::fmt::Display for ArrayType {
5037 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5039 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5038 std::fmt::Display::fmt(self.syntax(), f) 5040 std::fmt::Display::fmt(self.syntax(), f)
5039 } 5041 }
5040} 5042}
5041impl std::fmt::Display for SliceType { 5043impl std::fmt::Display for SliceType {
5042 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5044 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5043 std::fmt::Display::fmt(self.syntax(), f) 5045 std::fmt::Display::fmt(self.syntax(), f)
5044 } 5046 }
5045} 5047}
5046impl std::fmt::Display for ReferenceType { 5048impl std::fmt::Display for ReferenceType {
5047 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5049 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5048 std::fmt::Display::fmt(self.syntax(), f) 5050 std::fmt::Display::fmt(self.syntax(), f)
5049 } 5051 }
5050} 5052}
5051impl std::fmt::Display for PlaceholderType { 5053impl std::fmt::Display for PlaceholderType {
5052 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5054 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5053 std::fmt::Display::fmt(self.syntax(), f) 5055 std::fmt::Display::fmt(self.syntax(), f)
5054 } 5056 }
5055} 5057}
5056impl std::fmt::Display for FnPointerType { 5058impl std::fmt::Display for FnPointerType {
5057 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5059 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5058 std::fmt::Display::fmt(self.syntax(), f) 5060 std::fmt::Display::fmt(self.syntax(), f)
5059 } 5061 }
5060} 5062}
5061impl std::fmt::Display for ForType { 5063impl std::fmt::Display for ForType {
5062 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5064 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5063 std::fmt::Display::fmt(self.syntax(), f) 5065 std::fmt::Display::fmt(self.syntax(), f)
5064 } 5066 }
5065} 5067}
5066impl std::fmt::Display for ImplTraitType { 5068impl std::fmt::Display for ImplTraitType {
5067 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5069 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5068 std::fmt::Display::fmt(self.syntax(), f) 5070 std::fmt::Display::fmt(self.syntax(), f)
5069 } 5071 }
5070} 5072}
5071impl std::fmt::Display for DynTraitType { 5073impl std::fmt::Display for DynTraitType {
5072 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5074 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5073 std::fmt::Display::fmt(self.syntax(), f) 5075 std::fmt::Display::fmt(self.syntax(), f)
5074 } 5076 }
5075} 5077}
5076impl std::fmt::Display for TupleExpr { 5078impl std::fmt::Display for TupleExpr {
5077 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5079 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5078 std::fmt::Display::fmt(self.syntax(), f) 5080 std::fmt::Display::fmt(self.syntax(), f)
5079 } 5081 }
5080} 5082}
5081impl std::fmt::Display for ArrayExpr { 5083impl std::fmt::Display for ArrayExpr {
5082 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5084 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5083 std::fmt::Display::fmt(self.syntax(), f) 5085 std::fmt::Display::fmt(self.syntax(), f)
5084 } 5086 }
5085} 5087}
5086impl std::fmt::Display for ParenExpr { 5088impl std::fmt::Display for ParenExpr {
5087 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5089 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5088 std::fmt::Display::fmt(self.syntax(), f) 5090 std::fmt::Display::fmt(self.syntax(), f)
5089 } 5091 }
5090} 5092}
5091impl std::fmt::Display for PathExpr { 5093impl std::fmt::Display for PathExpr {
5092 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5094 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5093 std::fmt::Display::fmt(self.syntax(), f) 5095 std::fmt::Display::fmt(self.syntax(), f)
5094 } 5096 }
5095} 5097}
5096impl std::fmt::Display for LambdaExpr { 5098impl std::fmt::Display for LambdaExpr {
5097 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5099 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5098 std::fmt::Display::fmt(self.syntax(), f) 5100 std::fmt::Display::fmt(self.syntax(), f)
5099 } 5101 }
5100} 5102}
5101impl std::fmt::Display for IfExpr { 5103impl std::fmt::Display for IfExpr {
5102 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5103 std::fmt::Display::fmt(self.syntax(), f) 5105 std::fmt::Display::fmt(self.syntax(), f)
5104 } 5106 }
5105} 5107}
5106impl std::fmt::Display for LoopExpr { 5108impl std::fmt::Display for LoopExpr {
5107 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5108 std::fmt::Display::fmt(self.syntax(), f) 5110 std::fmt::Display::fmt(self.syntax(), f)
5109 } 5111 }
5110} 5112}
5111impl std::fmt::Display for EffectExpr { 5113impl std::fmt::Display for EffectExpr {
5112 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5113 std::fmt::Display::fmt(self.syntax(), f) 5115 std::fmt::Display::fmt(self.syntax(), f)
5114 } 5116 }
5115} 5117}
5116impl std::fmt::Display for ForExpr { 5118impl std::fmt::Display for ForExpr {
5117 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5118 std::fmt::Display::fmt(self.syntax(), f) 5120 std::fmt::Display::fmt(self.syntax(), f)
5119 } 5121 }
5120} 5122}
5121impl std::fmt::Display for WhileExpr { 5123impl std::fmt::Display for WhileExpr {
5122 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5123 std::fmt::Display::fmt(self.syntax(), f) 5125 std::fmt::Display::fmt(self.syntax(), f)
5124 } 5126 }
5125} 5127}
5126impl std::fmt::Display for ContinueExpr { 5128impl std::fmt::Display for ContinueExpr {
5127 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5128 std::fmt::Display::fmt(self.syntax(), f) 5130 std::fmt::Display::fmt(self.syntax(), f)
5129 } 5131 }
5130} 5132}
5131impl std::fmt::Display for BreakExpr { 5133impl std::fmt::Display for BreakExpr {
5132 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5133 std::fmt::Display::fmt(self.syntax(), f) 5135 std::fmt::Display::fmt(self.syntax(), f)
5134 } 5136 }
5135} 5137}
5136impl std::fmt::Display for Label { 5138impl std::fmt::Display for Label {
5137 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5138 std::fmt::Display::fmt(self.syntax(), f) 5140 std::fmt::Display::fmt(self.syntax(), f)
5139 } 5141 }
5140} 5142}
5141impl std::fmt::Display for BlockExpr { 5143impl std::fmt::Display for BlockExpr {
5142 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5143 std::fmt::Display::fmt(self.syntax(), f) 5145 std::fmt::Display::fmt(self.syntax(), f)
5144 } 5146 }
5145} 5147}
5146impl std::fmt::Display for ReturnExpr { 5148impl std::fmt::Display for ReturnExpr {
5147 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5148 std::fmt::Display::fmt(self.syntax(), f) 5150 std::fmt::Display::fmt(self.syntax(), f)
5149 } 5151 }
5150} 5152}
5151impl std::fmt::Display for CallExpr { 5153impl std::fmt::Display for CallExpr {
5152 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5153 std::fmt::Display::fmt(self.syntax(), f) 5155 std::fmt::Display::fmt(self.syntax(), f)
5154 } 5156 }
5155} 5157}
5156impl std::fmt::Display for MethodCallExpr { 5158impl std::fmt::Display for MethodCallExpr {
5157 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5158 std::fmt::Display::fmt(self.syntax(), f) 5160 std::fmt::Display::fmt(self.syntax(), f)
5159 } 5161 }
5160} 5162}
5161impl std::fmt::Display for IndexExpr { 5163impl std::fmt::Display for IndexExpr {
5162 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5163 std::fmt::Display::fmt(self.syntax(), f) 5165 std::fmt::Display::fmt(self.syntax(), f)
5164 } 5166 }
5165} 5167}
5166impl std::fmt::Display for FieldExpr { 5168impl std::fmt::Display for FieldExpr {
5167 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5168 std::fmt::Display::fmt(self.syntax(), f) 5170 std::fmt::Display::fmt(self.syntax(), f)
5169 } 5171 }
5170} 5172}
5171impl std::fmt::Display for AwaitExpr { 5173impl std::fmt::Display for AwaitExpr {
5172 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5173 std::fmt::Display::fmt(self.syntax(), f) 5175 std::fmt::Display::fmt(self.syntax(), f)
5174 } 5176 }
5175} 5177}
5176impl std::fmt::Display for TryExpr { 5178impl std::fmt::Display for TryExpr {
5177 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5178 std::fmt::Display::fmt(self.syntax(), f) 5180 std::fmt::Display::fmt(self.syntax(), f)
5179 } 5181 }
5180} 5182}
5181impl std::fmt::Display for CastExpr { 5183impl std::fmt::Display for CastExpr {
5182 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5183 std::fmt::Display::fmt(self.syntax(), f) 5185 std::fmt::Display::fmt(self.syntax(), f)
5184 } 5186 }
5185} 5187}
5186impl std::fmt::Display for RefExpr { 5188impl std::fmt::Display for RefExpr {
5187 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5188 std::fmt::Display::fmt(self.syntax(), f) 5190 std::fmt::Display::fmt(self.syntax(), f)
5189 } 5191 }
5190} 5192}
5191impl std::fmt::Display for PrefixExpr { 5193impl std::fmt::Display for PrefixExpr {
5192 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5193 std::fmt::Display::fmt(self.syntax(), f) 5195 std::fmt::Display::fmt(self.syntax(), f)
5194 } 5196 }
5195} 5197}
5196impl std::fmt::Display for BoxExpr { 5198impl std::fmt::Display for BoxExpr {
5197 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5199 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5198 std::fmt::Display::fmt(self.syntax(), f) 5200 std::fmt::Display::fmt(self.syntax(), f)
5199 } 5201 }
5200} 5202}
5201impl std::fmt::Display for RangeExpr { 5203impl std::fmt::Display for RangeExpr {
5202 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5203 std::fmt::Display::fmt(self.syntax(), f) 5205 std::fmt::Display::fmt(self.syntax(), f)
5204 } 5206 }
5205} 5207}
5206impl std::fmt::Display for BinExpr { 5208impl std::fmt::Display for BinExpr {
5207 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5208 std::fmt::Display::fmt(self.syntax(), f) 5210 std::fmt::Display::fmt(self.syntax(), f)
5209 } 5211 }
5210} 5212}
5211impl std::fmt::Display for Literal { 5213impl std::fmt::Display for Literal {
5212 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5213 std::fmt::Display::fmt(self.syntax(), f) 5215 std::fmt::Display::fmt(self.syntax(), f)
5214 } 5216 }
5215} 5217}
5216impl std::fmt::Display for MatchExpr { 5218impl std::fmt::Display for MatchExpr {
5217 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5218 std::fmt::Display::fmt(self.syntax(), f) 5220 std::fmt::Display::fmt(self.syntax(), f)
5219 } 5221 }
5220} 5222}
5221impl std::fmt::Display for MatchArmList { 5223impl std::fmt::Display for MatchArmList {
5222 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5223 std::fmt::Display::fmt(self.syntax(), f) 5225 std::fmt::Display::fmt(self.syntax(), f)
5224 } 5226 }
5225} 5227}
5226impl std::fmt::Display for MatchArm { 5228impl std::fmt::Display for MatchArm {
5227 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5228 std::fmt::Display::fmt(self.syntax(), f) 5230 std::fmt::Display::fmt(self.syntax(), f)
5229 } 5231 }
5230} 5232}
5231impl std::fmt::Display for MatchGuard { 5233impl std::fmt::Display for MatchGuard {
5232 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5233 std::fmt::Display::fmt(self.syntax(), f) 5235 std::fmt::Display::fmt(self.syntax(), f)
5234 } 5236 }
5235} 5237}
5236impl std::fmt::Display for RecordLit { 5238impl std::fmt::Display for RecordLit {
5237 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5238 std::fmt::Display::fmt(self.syntax(), f) 5240 std::fmt::Display::fmt(self.syntax(), f)
5239 } 5241 }
5240} 5242}
5241impl std::fmt::Display for RecordFieldList { 5243impl std::fmt::Display for RecordFieldList {
5242 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5243 std::fmt::Display::fmt(self.syntax(), f) 5245 std::fmt::Display::fmt(self.syntax(), f)
5244 } 5246 }
5245} 5247}
5246impl std::fmt::Display for RecordField { 5248impl std::fmt::Display for RecordField {
5247 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5248 std::fmt::Display::fmt(self.syntax(), f) 5250 std::fmt::Display::fmt(self.syntax(), f)
5249 } 5251 }
5250} 5252}
5251impl std::fmt::Display for OrPat { 5253impl std::fmt::Display for OrPat {
5252 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5254 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5253 std::fmt::Display::fmt(self.syntax(), f) 5255 std::fmt::Display::fmt(self.syntax(), f)
5254 } 5256 }
5255} 5257}
5256impl std::fmt::Display for ParenPat { 5258impl std::fmt::Display for ParenPat {
5257 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5258 std::fmt::Display::fmt(self.syntax(), f) 5260 std::fmt::Display::fmt(self.syntax(), f)
5259 } 5261 }
5260} 5262}
5261impl std::fmt::Display for RefPat { 5263impl std::fmt::Display for RefPat {
5262 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5264 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5263 std::fmt::Display::fmt(self.syntax(), f) 5265 std::fmt::Display::fmt(self.syntax(), f)
5264 } 5266 }
5265} 5267}
5266impl std::fmt::Display for BoxPat { 5268impl std::fmt::Display for BoxPat {
5267 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5269 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5268 std::fmt::Display::fmt(self.syntax(), f) 5270 std::fmt::Display::fmt(self.syntax(), f)
5269 } 5271 }
5270} 5272}
5271impl std::fmt::Display for BindPat { 5273impl std::fmt::Display for BindPat {
5272 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5273 std::fmt::Display::fmt(self.syntax(), f) 5275 std::fmt::Display::fmt(self.syntax(), f)
5274 } 5276 }
5275} 5277}
5276impl std::fmt::Display for PlaceholderPat { 5278impl std::fmt::Display for PlaceholderPat {
5277 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5278 std::fmt::Display::fmt(self.syntax(), f) 5280 std::fmt::Display::fmt(self.syntax(), f)
5279 } 5281 }
5280} 5282}
5281impl std::fmt::Display for DotDotPat { 5283impl std::fmt::Display for DotDotPat {
5282 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5284 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5283 std::fmt::Display::fmt(self.syntax(), f) 5285 std::fmt::Display::fmt(self.syntax(), f)
5284 } 5286 }
5285} 5287}
5286impl std::fmt::Display for PathPat { 5288impl std::fmt::Display for PathPat {
5287 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5288 std::fmt::Display::fmt(self.syntax(), f) 5290 std::fmt::Display::fmt(self.syntax(), f)
5289 } 5291 }
5290} 5292}
5291impl std::fmt::Display for SlicePat { 5293impl std::fmt::Display for SlicePat {
5292 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5294 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5293 std::fmt::Display::fmt(self.syntax(), f) 5295 std::fmt::Display::fmt(self.syntax(), f)
5294 } 5296 }
5295} 5297}
5296impl std::fmt::Display for RangePat { 5298impl std::fmt::Display for RangePat {
5297 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5298 std::fmt::Display::fmt(self.syntax(), f) 5300 std::fmt::Display::fmt(self.syntax(), f)
5299 } 5301 }
5300} 5302}
5301impl std::fmt::Display for LiteralPat { 5303impl std::fmt::Display for LiteralPat {
5302 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5304 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5303 std::fmt::Display::fmt(self.syntax(), f) 5305 std::fmt::Display::fmt(self.syntax(), f)
5304 } 5306 }
5305} 5307}
5306impl std::fmt::Display for MacroPat { 5308impl std::fmt::Display for MacroPat {
5307 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5309 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5308 std::fmt::Display::fmt(self.syntax(), f) 5310 std::fmt::Display::fmt(self.syntax(), f)
5309 } 5311 }
5310} 5312}
5311impl std::fmt::Display for RecordPat { 5313impl std::fmt::Display for RecordPat {
5312 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5313 std::fmt::Display::fmt(self.syntax(), f) 5315 std::fmt::Display::fmt(self.syntax(), f)
5314 } 5316 }
5315} 5317}
5316impl std::fmt::Display for RecordFieldPatList { 5318impl std::fmt::Display for RecordFieldPatList {
5317 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5318 std::fmt::Display::fmt(self.syntax(), f) 5320 std::fmt::Display::fmt(self.syntax(), f)
5319 } 5321 }
5320} 5322}
5321impl std::fmt::Display for RecordFieldPat { 5323impl std::fmt::Display for RecordFieldPat {
5322 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5324 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5323 std::fmt::Display::fmt(self.syntax(), f) 5325 std::fmt::Display::fmt(self.syntax(), f)
5324 } 5326 }
5325} 5327}
5326impl std::fmt::Display for TupleStructPat { 5328impl std::fmt::Display for TupleStructPat {
5327 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5329 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5328 std::fmt::Display::fmt(self.syntax(), f) 5330 std::fmt::Display::fmt(self.syntax(), f)
5329 } 5331 }
5330} 5332}
5331impl std::fmt::Display for TuplePat { 5333impl std::fmt::Display for TuplePat {
5332 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5333 std::fmt::Display::fmt(self.syntax(), f) 5335 std::fmt::Display::fmt(self.syntax(), f)
5334 } 5336 }
5335} 5337}
5336impl std::fmt::Display for Visibility { 5338impl std::fmt::Display for Visibility {
5337 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5339 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5338 std::fmt::Display::fmt(self.syntax(), f) 5340 std::fmt::Display::fmt(self.syntax(), f)
5339 } 5341 }
5340} 5342}
5341impl std::fmt::Display for Name { 5343impl std::fmt::Display for Name {
5342 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5344 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5343 std::fmt::Display::fmt(self.syntax(), f) 5345 std::fmt::Display::fmt(self.syntax(), f)
5344 } 5346 }
5345} 5347}
5346impl std::fmt::Display for NameRef { 5348impl std::fmt::Display for NameRef {
5347 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5348 std::fmt::Display::fmt(self.syntax(), f) 5350 std::fmt::Display::fmt(self.syntax(), f)
5349 } 5351 }
5350} 5352}
5351impl std::fmt::Display for MacroCall { 5353impl std::fmt::Display for MacroCall {
5352 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5354 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5353 std::fmt::Display::fmt(self.syntax(), f) 5355 std::fmt::Display::fmt(self.syntax(), f)
5354 } 5356 }
5355} 5357}
5356impl std::fmt::Display for Attr { 5358impl std::fmt::Display for Attr {
5357 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5359 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5358 std::fmt::Display::fmt(self.syntax(), f) 5360 std::fmt::Display::fmt(self.syntax(), f)
5359 } 5361 }
5360} 5362}
5361impl std::fmt::Display for TokenTree { 5363impl std::fmt::Display for TokenTree {
5362 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5364 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5363 std::fmt::Display::fmt(self.syntax(), f) 5365 std::fmt::Display::fmt(self.syntax(), f)
5364 } 5366 }
5365} 5367}
5366impl std::fmt::Display for TypeParamList { 5368impl std::fmt::Display for TypeParamList {
5367 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5369 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5368 std::fmt::Display::fmt(self.syntax(), f) 5370 std::fmt::Display::fmt(self.syntax(), f)
5369 } 5371 }
5370} 5372}
5371impl std::fmt::Display for TypeParam { 5373impl std::fmt::Display for TypeParam {
5372 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5374 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5373 std::fmt::Display::fmt(self.syntax(), f) 5375 std::fmt::Display::fmt(self.syntax(), f)
5374 } 5376 }
5375} 5377}
5376impl std::fmt::Display for ConstParam { 5378impl std::fmt::Display for ConstParam {
5377 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5379 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5378 std::fmt::Display::fmt(self.syntax(), f) 5380 std::fmt::Display::fmt(self.syntax(), f)
5379 } 5381 }
5380} 5382}
5381impl std::fmt::Display for LifetimeParam { 5383impl std::fmt::Display for LifetimeParam {
5382 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5383 std::fmt::Display::fmt(self.syntax(), f) 5385 std::fmt::Display::fmt(self.syntax(), f)
5384 } 5386 }
5385} 5387}
5386impl std::fmt::Display for TypeBound { 5388impl std::fmt::Display for TypeBound {
5387 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5389 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5388 std::fmt::Display::fmt(self.syntax(), f) 5390 std::fmt::Display::fmt(self.syntax(), f)
5389 } 5391 }
5390} 5392}
5391impl std::fmt::Display for TypeBoundList { 5393impl std::fmt::Display for TypeBoundList {
5392 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5394 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5393 std::fmt::Display::fmt(self.syntax(), f) 5395 std::fmt::Display::fmt(self.syntax(), f)
5394 } 5396 }
5395} 5397}
5396impl std::fmt::Display for WherePred { 5398impl std::fmt::Display for WherePred {
5397 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5399 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5398 std::fmt::Display::fmt(self.syntax(), f) 5400 std::fmt::Display::fmt(self.syntax(), f)
5399 } 5401 }
5400} 5402}
5401impl std::fmt::Display for WhereClause { 5403impl std::fmt::Display for WhereClause {
5402 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5404 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5403 std::fmt::Display::fmt(self.syntax(), f) 5405 std::fmt::Display::fmt(self.syntax(), f)
5404 } 5406 }
5405} 5407}
5406impl std::fmt::Display for Abi { 5408impl std::fmt::Display for Abi {
5407 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5409 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5408 std::fmt::Display::fmt(self.syntax(), f) 5410 std::fmt::Display::fmt(self.syntax(), f)
5409 } 5411 }
5410} 5412}
5411impl std::fmt::Display for ExprStmt { 5413impl std::fmt::Display for ExprStmt {
5412 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5413 std::fmt::Display::fmt(self.syntax(), f) 5415 std::fmt::Display::fmt(self.syntax(), f)
5414 } 5416 }
5415} 5417}
5416impl std::fmt::Display for LetStmt { 5418impl std::fmt::Display for LetStmt {
5417 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5419 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5418 std::fmt::Display::fmt(self.syntax(), f) 5420 std::fmt::Display::fmt(self.syntax(), f)
5419 } 5421 }
5420} 5422}
5421impl std::fmt::Display for Condition { 5423impl std::fmt::Display for Condition {
5422 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5424 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5423 std::fmt::Display::fmt(self.syntax(), f) 5425 std::fmt::Display::fmt(self.syntax(), f)
5424 } 5426 }
5425} 5427}
5426impl std::fmt::Display for ParamList { 5428impl std::fmt::Display for ParamList {
5427 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5429 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5428 std::fmt::Display::fmt(self.syntax(), f) 5430 std::fmt::Display::fmt(self.syntax(), f)
5429 } 5431 }
5430} 5432}
5431impl std::fmt::Display for SelfParam { 5433impl std::fmt::Display for SelfParam {
5432 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5434 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5433 std::fmt::Display::fmt(self.syntax(), f) 5435 std::fmt::Display::fmt(self.syntax(), f)
5434 } 5436 }
5435} 5437}
5436impl std::fmt::Display for Param { 5438impl std::fmt::Display for Param {
5437 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5439 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5438 std::fmt::Display::fmt(self.syntax(), f) 5440 std::fmt::Display::fmt(self.syntax(), f)
5439 } 5441 }
5440} 5442}
5441impl std::fmt::Display for UseItem { 5443impl std::fmt::Display for UseItem {
5442 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5444 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5443 std::fmt::Display::fmt(self.syntax(), f) 5445 std::fmt::Display::fmt(self.syntax(), f)
5444 } 5446 }
5445} 5447}
5446impl std::fmt::Display for UseTree { 5448impl std::fmt::Display for UseTree {
5447 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5449 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5448 std::fmt::Display::fmt(self.syntax(), f) 5450 std::fmt::Display::fmt(self.syntax(), f)
5449 } 5451 }
5450} 5452}
5451impl std::fmt::Display for Alias { 5453impl std::fmt::Display for Alias {
5452 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5454 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5453 std::fmt::Display::fmt(self.syntax(), f) 5455 std::fmt::Display::fmt(self.syntax(), f)
5454 } 5456 }
5455} 5457}
5456impl std::fmt::Display for UseTreeList { 5458impl std::fmt::Display for UseTreeList {
5457 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5458 std::fmt::Display::fmt(self.syntax(), f) 5460 std::fmt::Display::fmt(self.syntax(), f)
5459 } 5461 }
5460} 5462}
5461impl std::fmt::Display for ExternCrateItem { 5463impl std::fmt::Display for ExternCrateItem {
5462 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5464 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5463 std::fmt::Display::fmt(self.syntax(), f) 5465 std::fmt::Display::fmt(self.syntax(), f)
5464 } 5466 }
5465} 5467}
5466impl std::fmt::Display for ArgList { 5468impl std::fmt::Display for ArgList {
5467 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5469 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5468 std::fmt::Display::fmt(self.syntax(), f) 5470 std::fmt::Display::fmt(self.syntax(), f)
5469 } 5471 }
5470} 5472}
5471impl std::fmt::Display for Path { 5473impl std::fmt::Display for Path {
5472 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5474 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5473 std::fmt::Display::fmt(self.syntax(), f) 5475 std::fmt::Display::fmt(self.syntax(), f)
5474 } 5476 }
5475} 5477}
5476impl std::fmt::Display for PathSegment { 5478impl std::fmt::Display for PathSegment {
5477 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5479 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5478 std::fmt::Display::fmt(self.syntax(), f) 5480 std::fmt::Display::fmt(self.syntax(), f)
5479 } 5481 }
5480} 5482}
5481impl std::fmt::Display for TypeArgList { 5483impl std::fmt::Display for TypeArgList {
5482 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5484 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5483 std::fmt::Display::fmt(self.syntax(), f) 5485 std::fmt::Display::fmt(self.syntax(), f)
5484 } 5486 }
5485} 5487}
5486impl std::fmt::Display for TypeArg { 5488impl std::fmt::Display for TypeArg {
5487 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5489 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5488 std::fmt::Display::fmt(self.syntax(), f) 5490 std::fmt::Display::fmt(self.syntax(), f)
5489 } 5491 }
5490} 5492}
5491impl std::fmt::Display for AssocTypeArg { 5493impl std::fmt::Display for AssocTypeArg {
5492 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5494 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5493 std::fmt::Display::fmt(self.syntax(), f) 5495 std::fmt::Display::fmt(self.syntax(), f)
5494 } 5496 }
5495} 5497}
5496impl std::fmt::Display for LifetimeArg { 5498impl std::fmt::Display for LifetimeArg {
5497 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5499 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5498 std::fmt::Display::fmt(self.syntax(), f) 5500 std::fmt::Display::fmt(self.syntax(), f)
5499 } 5501 }
5500} 5502}
5501impl std::fmt::Display for ConstArg { 5503impl std::fmt::Display for ConstArg {
5502 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5504 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5503 std::fmt::Display::fmt(self.syntax(), f) 5505 std::fmt::Display::fmt(self.syntax(), f)
5504 } 5506 }
5505} 5507}
5506impl std::fmt::Display for MacroItems { 5508impl std::fmt::Display for MacroItems {
5507 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5508 std::fmt::Display::fmt(self.syntax(), f) 5510 std::fmt::Display::fmt(self.syntax(), f)
5509 } 5511 }
5510} 5512}
5511impl std::fmt::Display for MacroStmts { 5513impl std::fmt::Display for MacroStmts {
5512 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5514 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5513 std::fmt::Display::fmt(self.syntax(), f) 5515 std::fmt::Display::fmt(self.syntax(), f)
5514 } 5516 }
5515} 5517}
5516impl std::fmt::Display for ExternItemList { 5518impl std::fmt::Display for ExternItemList {
5517 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5519 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5518 std::fmt::Display::fmt(self.syntax(), f) 5520 std::fmt::Display::fmt(self.syntax(), f)
5519 } 5521 }
5520} 5522}
5521impl std::fmt::Display for ExternBlock { 5523impl std::fmt::Display for ExternBlock {
5522 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5524 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5523 std::fmt::Display::fmt(self.syntax(), f) 5525 std::fmt::Display::fmt(self.syntax(), f)
5524 } 5526 }
5525} 5527}
5526impl std::fmt::Display for MetaItem { 5528impl std::fmt::Display for MetaItem {
5527 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5529 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5528 std::fmt::Display::fmt(self.syntax(), f) 5530 std::fmt::Display::fmt(self.syntax(), f)
5529 } 5531 }
5530} 5532}
5531impl std::fmt::Display for MacroDef { 5533impl std::fmt::Display for MacroDef {
5532 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5534 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5533 std::fmt::Display::fmt(self.syntax(), f) 5535 std::fmt::Display::fmt(self.syntax(), f)
5534 } 5536 }
5535} 5537}
diff --git a/crates/ra_syntax/src/ast/generated/tokens.rs b/crates/ra_syntax/src/ast/generated/tokens.rs
index f91befaac..abadd0b61 100644
--- a/crates/ra_syntax/src/ast/generated/tokens.rs
+++ b/crates/ra_syntax/src/ast/generated/tokens.rs
@@ -11,7 +11,7 @@ pub struct Whitespace {
11 pub(crate) syntax: SyntaxToken, 11 pub(crate) syntax: SyntaxToken,
12} 12}
13impl std::fmt::Display for Whitespace { 13impl std::fmt::Display for Whitespace {
14 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 std::fmt::Display::fmt(&self.syntax, f) 15 std::fmt::Display::fmt(&self.syntax, f)
16 } 16 }
17} 17}
@@ -32,7 +32,7 @@ pub struct Comment {
32 pub(crate) syntax: SyntaxToken, 32 pub(crate) syntax: SyntaxToken,
33} 33}
34impl std::fmt::Display for Comment { 34impl std::fmt::Display for Comment {
35 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 std::fmt::Display::fmt(&self.syntax, f) 36 std::fmt::Display::fmt(&self.syntax, f)
37 } 37 }
38} 38}
@@ -53,7 +53,7 @@ pub struct String {
53 pub(crate) syntax: SyntaxToken, 53 pub(crate) syntax: SyntaxToken,
54} 54}
55impl std::fmt::Display for String { 55impl std::fmt::Display for String {
56 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 std::fmt::Display::fmt(&self.syntax, f) 57 std::fmt::Display::fmt(&self.syntax, f)
58 } 58 }
59} 59}
@@ -74,7 +74,7 @@ pub struct RawString {
74 pub(crate) syntax: SyntaxToken, 74 pub(crate) syntax: SyntaxToken,
75} 75}
76impl std::fmt::Display for RawString { 76impl std::fmt::Display for RawString {
77 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 std::fmt::Display::fmt(&self.syntax, f) 78 std::fmt::Display::fmt(&self.syntax, f)
79 } 79 }
80} 80}
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index da0eb0926..192c610f1 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -75,6 +75,10 @@ pub fn record_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordF
75 } 75 }
76} 76}
77 77
78pub fn record_field_def(name: ast::NameRef, ty: ast::TypeRef) -> ast::RecordFieldDef {
79 ast_from_text(&format!("struct S {{ {}: {}, }}", name, ty))
80}
81
78pub fn block_expr( 82pub fn block_expr(
79 stmts: impl IntoIterator<Item = ast::Stmt>, 83 stmts: impl IntoIterator<Item = ast::Stmt>,
80 tail_expr: Option<ast::Expr>, 84 tail_expr: Option<ast::Expr>,
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index 3cd6d99c3..045f69133 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -1,6 +1,9 @@
1//! There are many AstNodes, but only a few tokens, so we hand-write them here. 1//! There are many AstNodes, but only a few tokens, so we hand-write them here.
2 2
3use std::convert::{TryFrom, TryInto}; 3use std::{
4 borrow::Cow,
5 convert::{TryFrom, TryInto},
6};
4 7
5use crate::{ 8use crate::{
6 ast::{AstToken, Comment, RawString, String, Whitespace}, 9 ast::{AstToken, Comment, RawString, String, Whitespace},
@@ -84,7 +87,7 @@ impl Whitespace {
84} 87}
85 88
86pub struct QuoteOffsets { 89pub struct QuoteOffsets {
87 pub quotes: [TextRange; 2], 90 pub quotes: (TextRange, TextRange),
88 pub contents: TextRange, 91 pub contents: TextRange,
89} 92}
90 93
@@ -103,7 +106,7 @@ impl QuoteOffsets {
103 let end = TextSize::of(literal); 106 let end = TextSize::of(literal);
104 107
105 let res = QuoteOffsets { 108 let res = QuoteOffsets {
106 quotes: [TextRange::new(start, left_quote), TextRange::new(right_quote, end)], 109 quotes: (TextRange::new(start, left_quote), TextRange::new(right_quote, end)),
107 contents: TextRange::new(left_quote, right_quote), 110 contents: TextRange::new(left_quote, right_quote),
108 }; 111 };
109 Some(res) 112 Some(res)
@@ -116,17 +119,17 @@ pub trait HasQuotes: AstToken {
116 let offsets = QuoteOffsets::new(text)?; 119 let offsets = QuoteOffsets::new(text)?;
117 let o = self.syntax().text_range().start(); 120 let o = self.syntax().text_range().start();
118 let offsets = QuoteOffsets { 121 let offsets = QuoteOffsets {
119 quotes: [offsets.quotes[0] + o, offsets.quotes[1] + o], 122 quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o),
120 contents: offsets.contents + o, 123 contents: offsets.contents + o,
121 }; 124 };
122 Some(offsets) 125 Some(offsets)
123 } 126 }
124 fn open_quote_text_range(&self) -> Option<TextRange> { 127 fn open_quote_text_range(&self) -> Option<TextRange> {
125 self.quote_offsets().map(|it| it.quotes[0]) 128 self.quote_offsets().map(|it| it.quotes.0)
126 } 129 }
127 130
128 fn close_quote_text_range(&self) -> Option<TextRange> { 131 fn close_quote_text_range(&self) -> Option<TextRange> {
129 self.quote_offsets().map(|it| it.quotes[1]) 132 self.quote_offsets().map(|it| it.quotes.1)
130 } 133 }
131 134
132 fn text_range_between_quotes(&self) -> Option<TextRange> { 135 fn text_range_between_quotes(&self) -> Option<TextRange> {
@@ -138,11 +141,11 @@ impl HasQuotes for String {}
138impl HasQuotes for RawString {} 141impl HasQuotes for RawString {}
139 142
140pub trait HasStringValue: HasQuotes { 143pub trait HasStringValue: HasQuotes {
141 fn value(&self) -> Option<std::string::String>; 144 fn value(&self) -> Option<Cow<'_, str>>;
142} 145}
143 146
144impl HasStringValue for String { 147impl HasStringValue for String {
145 fn value(&self) -> Option<std::string::String> { 148 fn value(&self) -> Option<Cow<'_, str>> {
146 let text = self.text().as_str(); 149 let text = self.text().as_str();
147 let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; 150 let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
148 151
@@ -156,15 +159,17 @@ impl HasStringValue for String {
156 if has_error { 159 if has_error {
157 return None; 160 return None;
158 } 161 }
159 Some(buf) 162 // FIXME: don't actually allocate for borrowed case
163 let res = if buf == text { Cow::Borrowed(text) } else { Cow::Owned(buf) };
164 Some(res)
160 } 165 }
161} 166}
162 167
163impl HasStringValue for RawString { 168impl HasStringValue for RawString {
164 fn value(&self) -> Option<std::string::String> { 169 fn value(&self) -> Option<Cow<'_, str>> {
165 let text = self.text().as_str(); 170 let text = self.text().as_str();
166 let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; 171 let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
167 Some(text.to_string()) 172 Some(Cow::Borrowed(text))
168 } 173 }
169} 174}
170 175
@@ -335,16 +340,26 @@ pub trait HasFormatSpecifier: AstToken {
335 } 340 }
336 c if c == '_' || c.is_alphabetic() => { 341 c if c == '_' || c.is_alphabetic() => {
337 read_identifier(&mut chars, &mut callback); 342 read_identifier(&mut chars, &mut callback);
338 if chars.peek().and_then(|next| next.1.as_ref().ok()).copied() 343 // can be either width (indicated by dollar sign, or type in which case
339 != Some('$') 344 // the next sign has to be `}`)
340 { 345 let next =
341 continue; 346 chars.peek().and_then(|next| next.1.as_ref().ok()).copied();
342 } 347 match next {
343 skip_char_and_emit( 348 Some('$') => skip_char_and_emit(
344 &mut chars, 349 &mut chars,
345 FormatSpecifier::DollarSign, 350 FormatSpecifier::DollarSign,
346 &mut callback, 351 &mut callback,
347 ); 352 ),
353 Some('}') => {
354 skip_char_and_emit(
355 &mut chars,
356 FormatSpecifier::Close,
357 &mut callback,
358 );
359 continue;
360 }
361 _ => continue,
362 };
348 } 363 }
349 _ => {} 364 _ => {}
350 } 365 }
@@ -416,17 +431,11 @@ pub trait HasFormatSpecifier: AstToken {
416 } 431 }
417 } 432 }
418 433
419 let mut cloned = chars.clone().take(2); 434 if let Some((_, Ok('}'))) = chars.peek() {
420 let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); 435 skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback);
421 let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); 436 } else {
422 if first != Some('}') {
423 continue;
424 }
425 if second == Some('}') {
426 // Escaped format end specifier, `}}`
427 continue; 437 continue;
428 } 438 }
429 skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback);
430 } 439 }
431 _ => { 440 _ => {
432 while let Some((_, Ok(next_char))) = chars.peek() { 441 while let Some((_, Ok(next_char))) = chars.peek() {
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs
index bfc05e08b..a8f2454fd 100644
--- a/crates/ra_syntax/src/ast/traits.rs
+++ b/crates/ra_syntax/src/ast/traits.rs
@@ -83,13 +83,22 @@ pub trait DocCommentsOwner: AstNode {
83 CommentIter { iter: self.syntax().children_with_tokens() } 83 CommentIter { iter: self.syntax().children_with_tokens() }
84 } 84 }
85 85
86 fn doc_comment_text(&self) -> Option<String> {
87 self.doc_comments().doc_comment_text()
88 }
89}
90
91impl CommentIter {
92 pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> CommentIter {
93 CommentIter { iter: syntax_node.children_with_tokens() }
94 }
95
86 /// Returns the textual content of a doc comment block as a single string. 96 /// Returns the textual content of a doc comment block as a single string.
87 /// That is, strips leading `///` (+ optional 1 character of whitespace), 97 /// That is, strips leading `///` (+ optional 1 character of whitespace),
88 /// trailing `*/`, trailing whitespace and then joins the lines. 98 /// trailing `*/`, trailing whitespace and then joins the lines.
89 fn doc_comment_text(&self) -> Option<String> { 99 pub fn doc_comment_text(self) -> Option<String> {
90 let mut has_comments = false; 100 let mut has_comments = false;
91 let docs = self 101 let docs = self
92 .doc_comments()
93 .filter(|comment| comment.kind().doc.is_some()) 102 .filter(|comment| comment.kind().doc.is_some())
94 .map(|comment| { 103 .map(|comment| {
95 has_comments = true; 104 has_comments = true;
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index 61e686da5..9b7664576 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -51,7 +51,8 @@ pub use crate::{
51 ptr::{AstPtr, SyntaxNodePtr}, 51 ptr::{AstPtr, SyntaxNodePtr},
52 syntax_error::SyntaxError, 52 syntax_error::SyntaxError,
53 syntax_node::{ 53 syntax_node::{
54 Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, 54 Direction, NodeOrToken, SyntaxElement, SyntaxElementChildren, SyntaxNode,
55 SyntaxNodeChildren, SyntaxToken, SyntaxTreeBuilder,
55 }, 56 },
56}; 57};
57pub use ra_parser::{SyntaxKind, T}; 58pub use ra_parser::{SyntaxKind, T};
@@ -167,6 +168,41 @@ impl SourceFile {
167 } 168 }
168} 169}
169 170
171impl ast::Path {
172 /// Returns `text`, parsed as a path, but only if it has no errors.
173 pub fn parse(text: &str) -> Result<Self, ()> {
174 parsing::parse_text_fragment(text, ra_parser::FragmentKind::Path)
175 }
176}
177
178impl ast::Pat {
179 /// Returns `text`, parsed as a pattern, but only if it has no errors.
180 pub fn parse(text: &str) -> Result<Self, ()> {
181 parsing::parse_text_fragment(text, ra_parser::FragmentKind::Pattern)
182 }
183}
184
185impl ast::Expr {
186 /// Returns `text`, parsed as an expression, but only if it has no errors.
187 pub fn parse(text: &str) -> Result<Self, ()> {
188 parsing::parse_text_fragment(text, ra_parser::FragmentKind::Expr)
189 }
190}
191
192impl ast::ModuleItem {
193 /// Returns `text`, parsed as an item, but only if it has no errors.
194 pub fn parse(text: &str) -> Result<Self, ()> {
195 parsing::parse_text_fragment(text, ra_parser::FragmentKind::Item)
196 }
197}
198
199impl ast::TypeRef {
200 /// Returns `text`, parsed as an type reference, but only if it has no errors.
201 pub fn parse(text: &str) -> Result<Self, ()> {
202 parsing::parse_text_fragment(text, ra_parser::FragmentKind::Type)
203 }
204}
205
170/// Matches a `SyntaxNode` against an `ast` type. 206/// Matches a `SyntaxNode` against an `ast` type.
171/// 207///
172/// # Example: 208/// # Example:
diff --git a/crates/ra_syntax/src/parsing.rs b/crates/ra_syntax/src/parsing.rs
index e5eb80850..0ed3c20ef 100644
--- a/crates/ra_syntax/src/parsing.rs
+++ b/crates/ra_syntax/src/parsing.rs
@@ -6,13 +6,14 @@ mod text_token_source;
6mod text_tree_sink; 6mod text_tree_sink;
7mod reparsing; 7mod reparsing;
8 8
9use crate::{syntax_node::GreenNode, SyntaxError}; 9use crate::{syntax_node::GreenNode, AstNode, SyntaxError, SyntaxNode};
10use text_token_source::TextTokenSource; 10use text_token_source::TextTokenSource;
11use text_tree_sink::TextTreeSink; 11use text_tree_sink::TextTreeSink;
12 12
13pub use lexer::*; 13pub use lexer::*;
14 14
15pub(crate) use self::reparsing::incremental_reparse; 15pub(crate) use self::reparsing::incremental_reparse;
16use ra_parser::SyntaxKind;
16 17
17pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) { 18pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
18 let (tokens, lexer_errors) = tokenize(&text); 19 let (tokens, lexer_errors) = tokenize(&text);
@@ -27,3 +28,32 @@ pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec<SyntaxError>) {
27 28
28 (tree, parser_errors) 29 (tree, parser_errors)
29} 30}
31
32/// Returns `text` parsed as a `T` provided there are no parse errors.
33pub(crate) fn parse_text_fragment<T: AstNode>(
34 text: &str,
35 fragment_kind: ra_parser::FragmentKind,
36) -> Result<T, ()> {
37 let (tokens, lexer_errors) = tokenize(&text);
38 if !lexer_errors.is_empty() {
39 return Err(());
40 }
41
42 let mut token_source = TextTokenSource::new(text, &tokens);
43 let mut tree_sink = TextTreeSink::new(text, &tokens);
44
45 // TextTreeSink assumes that there's at least some root node to which it can attach errors and
46 // tokens. We arbitrarily give it a SourceFile.
47 use ra_parser::TreeSink;
48 tree_sink.start_node(SyntaxKind::SOURCE_FILE);
49 ra_parser::parse_fragment(&mut token_source, &mut tree_sink, fragment_kind);
50 tree_sink.finish_node();
51
52 let (tree, parser_errors) = tree_sink.finish();
53 use ra_parser::TokenSource;
54 if !parser_errors.is_empty() || token_source.current().kind != SyntaxKind::EOF {
55 return Err(());
56 }
57
58 SyntaxNode::new_root(tree).first_child().and_then(T::cast).ok_or(())
59}
diff --git a/crates/ra_syntax/src/parsing/lexer.rs b/crates/ra_syntax/src/parsing/lexer.rs
index 1a5a6dc06..fa3be1016 100644
--- a/crates/ra_syntax/src/parsing/lexer.rs
+++ b/crates/ra_syntax/src/parsing/lexer.rs
@@ -1,6 +1,8 @@
1//! Lexer analyzes raw input string and produces lexemes (tokens). 1//! Lexer analyzes raw input string and produces lexemes (tokens).
2//! It is just a bridge to `rustc_lexer`. 2//! It is just a bridge to `rustc_lexer`.
3 3
4use rustc_lexer::{LiteralKind as LK, RawStrError};
5
4use std::convert::TryInto; 6use std::convert::TryInto;
5 7
6use crate::{ 8use crate::{
@@ -180,8 +182,6 @@ fn rustc_token_kind_to_syntax_kind(
180 return (syntax_kind, None); 182 return (syntax_kind, None);
181 183
182 fn match_literal_kind(kind: &rustc_lexer::LiteralKind) -> (SyntaxKind, Option<&'static str>) { 184 fn match_literal_kind(kind: &rustc_lexer::LiteralKind) -> (SyntaxKind, Option<&'static str>) {
183 use rustc_lexer::{LexRawStrError, LiteralKind as LK};
184
185 #[rustfmt::skip] 185 #[rustfmt::skip]
186 let syntax_kind = match *kind { 186 let syntax_kind = match *kind {
187 LK::Int { empty_int: false, .. } => INT_NUMBER, 187 LK::Int { empty_int: false, .. } => INT_NUMBER,
@@ -215,27 +215,27 @@ fn rustc_token_kind_to_syntax_kind(
215 return (BYTE_STRING, Some("Missing trailing `\"` symbol to terminate the byte string literal")) 215 return (BYTE_STRING, Some("Missing trailing `\"` symbol to terminate the byte string literal"))
216 } 216 }
217 217
218 LK::RawStr(str) => match str.validate() { 218 LK::RawStr { err, .. } => match err {
219 Ok(_) => RAW_STRING, 219 None => RAW_STRING,
220 Err(LexRawStrError::InvalidStarter) => return (RAW_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw string literal")), 220 Some(RawStrError::InvalidStarter { .. }) => return (RAW_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw string literal")),
221 Err(LexRawStrError::NoTerminator { expected, found, .. }) => if expected == found { 221 Some(RawStrError::NoTerminator { expected, found, .. }) => if expected == found {
222 return (RAW_STRING, Some("Missing trailing `\"` to terminate the raw string literal")) 222 return (RAW_STRING, Some("Missing trailing `\"` to terminate the raw string literal"))
223 } else { 223 } else {
224 return (RAW_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw string literal")) 224 return (RAW_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw string literal"))
225 225
226 }, 226 },
227 Err(LexRawStrError::TooManyDelimiters { .. }) => return (RAW_STRING, Some("Too many `#` symbols: raw strings may be delimited by up to 65535 `#` symbols")), 227 Some(RawStrError::TooManyDelimiters { .. }) => return (RAW_STRING, Some("Too many `#` symbols: raw strings may be delimited by up to 65535 `#` symbols")),
228 }, 228 },
229 LK::RawByteStr(str) => match str.validate() { 229 LK::RawByteStr { err, .. } => match err {
230 Ok(_) => RAW_BYTE_STRING, 230 None => RAW_BYTE_STRING,
231 Err(LexRawStrError::InvalidStarter) => return (RAW_BYTE_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw byte string literal")), 231 Some(RawStrError::InvalidStarter { .. }) => return (RAW_BYTE_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw byte string literal")),
232 Err(LexRawStrError::NoTerminator { expected, found, .. }) => if expected == found { 232 Some(RawStrError::NoTerminator { expected, found, .. }) => if expected == found {
233 return (RAW_BYTE_STRING, Some("Missing trailing `\"` to terminate the raw byte string literal")) 233 return (RAW_BYTE_STRING, Some("Missing trailing `\"` to terminate the raw byte string literal"))
234 } else { 234 } else {
235 return (RAW_BYTE_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw byte string literal")) 235 return (RAW_BYTE_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw byte string literal"))
236 236
237 }, 237 },
238 Err(LexRawStrError::TooManyDelimiters { .. }) => return (RAW_BYTE_STRING, Some("Too many `#` symbols: raw byte strings may be delimited by up to 65535 `#` symbols")), 238 Some(RawStrError::TooManyDelimiters { .. }) => return (RAW_BYTE_STRING, Some("Too many `#` symbols: raw byte strings may be delimited by up to 65535 `#` symbols")),
239 }, 239 },
240 }; 240 };
241 241
diff --git a/crates/ra_syntax/src/parsing/reparsing.rs b/crates/ra_syntax/src/parsing/reparsing.rs
index edbc190f8..ed5a42ea3 100644
--- a/crates/ra_syntax/src/parsing/reparsing.rs
+++ b/crates/ra_syntax/src/parsing/reparsing.rs
@@ -120,10 +120,7 @@ fn get_text_after_edit(element: SyntaxElement, edit: &Indel) -> String {
120} 120}
121 121
122fn is_contextual_kw(text: &str) -> bool { 122fn is_contextual_kw(text: &str) -> bool {
123 match text { 123 matches!(text, "auto" | "default" | "union")
124 "auto" | "default" | "union" => true,
125 _ => false,
126 }
127} 124}
128 125
129fn find_reparsable_node(node: &SyntaxNode, range: TextRange) -> Option<(SyntaxNode, Reparser)> { 126fn find_reparsable_node(node: &SyntaxNode, range: TextRange) -> Option<(SyntaxNode, Reparser)> {
diff --git a/crates/ra_syntax/src/parsing/text_token_source.rs b/crates/ra_syntax/src/parsing/text_token_source.rs
index 7ddc2c2c3..97aa3e795 100644
--- a/crates/ra_syntax/src/parsing/text_token_source.rs
+++ b/crates/ra_syntax/src/parsing/text_token_source.rs
@@ -1,40 +1,35 @@
1//! FIXME: write short doc here 1//! See `TextTokenSource` docs.
2 2
3use ra_parser::Token as PToken;
4use ra_parser::TokenSource; 3use ra_parser::TokenSource;
5 4
6use crate::{parsing::lexer::Token, SyntaxKind::EOF, TextRange, TextSize}; 5use crate::{parsing::lexer::Token, SyntaxKind::EOF, TextRange, TextSize};
7 6
7/// Implementation of `ra_parser::TokenSource` that takes tokens from source code text.
8pub(crate) struct TextTokenSource<'t> { 8pub(crate) struct TextTokenSource<'t> {
9 text: &'t str, 9 text: &'t str,
10 /// start position of each token(expect whitespace and comment) 10 /// token and its start position (non-whitespace/comment tokens)
11 /// ```non-rust 11 /// ```non-rust
12 /// struct Foo; 12 /// struct Foo;
13 /// ^------^--- 13 /// ^------^--^-
14 /// | | ^- 14 /// | | \________
15 /// 0 7 10 15 /// | \____ \
16 /// | \ |
17 /// (struct, 0) (Foo, 7) (;, 10)
16 /// ``` 18 /// ```
17 /// (token, start_offset): `[(struct, 0), (Foo, 7), (;, 10)]` 19 /// `[(struct, 0), (Foo, 7), (;, 10)]`
18 start_offsets: Vec<TextSize>, 20 token_offset_pairs: Vec<(Token, TextSize)>,
19 /// non-whitespace/comment tokens
20 /// ```non-rust
21 /// struct Foo {}
22 /// ^^^^^^ ^^^ ^^
23 /// ```
24 /// tokens: `[struct, Foo, {, }]`
25 tokens: Vec<Token>,
26 21
27 /// Current token and position 22 /// Current token and position
28 curr: (PToken, usize), 23 curr: (ra_parser::Token, usize),
29} 24}
30 25
31impl<'t> TokenSource for TextTokenSource<'t> { 26impl<'t> TokenSource for TextTokenSource<'t> {
32 fn current(&self) -> PToken { 27 fn current(&self) -> ra_parser::Token {
33 self.curr.0 28 self.curr.0
34 } 29 }
35 30
36 fn lookahead_nth(&self, n: usize) -> PToken { 31 fn lookahead_nth(&self, n: usize) -> ra_parser::Token {
37 mk_token(self.curr.1 + n, &self.start_offsets, &self.tokens) 32 mk_token(self.curr.1 + n, &self.token_offset_pairs)
38 } 33 }
39 34
40 fn bump(&mut self) { 35 fn bump(&mut self) {
@@ -43,45 +38,47 @@ impl<'t> TokenSource for TextTokenSource<'t> {
43 } 38 }
44 39
45 let pos = self.curr.1 + 1; 40 let pos = self.curr.1 + 1;
46 self.curr = (mk_token(pos, &self.start_offsets, &self.tokens), pos); 41 self.curr = (mk_token(pos, &self.token_offset_pairs), pos);
47 } 42 }
48 43
49 fn is_keyword(&self, kw: &str) -> bool { 44 fn is_keyword(&self, kw: &str) -> bool {
50 let pos = self.curr.1; 45 self.token_offset_pairs
51 if pos >= self.tokens.len() { 46 .get(self.curr.1)
52 return false; 47 .map(|(token, offset)| &self.text[TextRange::at(*offset, token.len)] == kw)
53 } 48 .unwrap_or(false)
54 let range = TextRange::at(self.start_offsets[pos], self.tokens[pos].len);
55 self.text[range] == *kw
56 } 49 }
57} 50}
58 51
59fn mk_token(pos: usize, start_offsets: &[TextSize], tokens: &[Token]) -> PToken { 52fn mk_token(pos: usize, token_offset_pairs: &[(Token, TextSize)]) -> ra_parser::Token {
60 let kind = tokens.get(pos).map(|t| t.kind).unwrap_or(EOF); 53 let (kind, is_jointed_to_next) = match token_offset_pairs.get(pos) {
61 let is_jointed_to_next = if pos + 1 < start_offsets.len() { 54 Some((token, offset)) => (
62 start_offsets[pos] + tokens[pos].len == start_offsets[pos + 1] 55 token.kind,
63 } else { 56 token_offset_pairs
64 false 57 .get(pos + 1)
58 .map(|(_, next_offset)| offset + token.len == *next_offset)
59 .unwrap_or(false),
60 ),
61 None => (EOF, false),
65 }; 62 };
66 63 ra_parser::Token { kind, is_jointed_to_next }
67 PToken { kind, is_jointed_to_next }
68} 64}
69 65
70impl<'t> TextTokenSource<'t> { 66impl<'t> TextTokenSource<'t> {
71 /// Generate input from tokens(expect comment and whitespace). 67 /// Generate input from tokens(expect comment and whitespace).
72 pub fn new(text: &'t str, raw_tokens: &'t [Token]) -> TextTokenSource<'t> { 68 pub fn new(text: &'t str, raw_tokens: &'t [Token]) -> TextTokenSource<'t> {
73 let mut tokens = Vec::new(); 69 let token_offset_pairs: Vec<_> = raw_tokens
74 let mut start_offsets = Vec::new(); 70 .iter()
75 let mut len = 0.into(); 71 .filter_map({
76 for &token in raw_tokens.iter() { 72 let mut len = 0.into();
77 if !token.kind.is_trivia() { 73 move |token| {
78 tokens.push(token); 74 let pair = if token.kind.is_trivia() { None } else { Some((*token, len)) };
79 start_offsets.push(len); 75 len += token.len;
80 } 76 pair
81 len += token.len; 77 }
82 } 78 })
79 .collect();
83 80
84 let first = mk_token(0, &start_offsets, &tokens); 81 let first = mk_token(0, &token_offset_pairs);
85 TextTokenSource { text, start_offsets, tokens, curr: (first, 0) } 82 TextTokenSource { text, token_offset_pairs, curr: (first, 0) }
86 } 83 }
87} 84}
diff --git a/crates/ra_syntax/src/parsing/text_tree_sink.rs b/crates/ra_syntax/src/parsing/text_tree_sink.rs
index 22aed1db1..c6b30a02a 100644
--- a/crates/ra_syntax/src/parsing/text_tree_sink.rs
+++ b/crates/ra_syntax/src/parsing/text_tree_sink.rs
@@ -160,7 +160,10 @@ fn n_attached_trivias<'a>(
160 if let Some((peek_kind, peek_text)) = 160 if let Some((peek_kind, peek_text)) =
161 trivias.peek().map(|(_, pair)| pair) 161 trivias.peek().map(|(_, pair)| pair)
162 { 162 {
163 if *peek_kind == COMMENT && peek_text.starts_with("///") { 163 if *peek_kind == COMMENT
164 && peek_text.starts_with("///")
165 && !peek_text.starts_with("////")
166 {
164 continue; 167 continue;
165 } 168 }
166 } 169 }
diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs
index e566af7e8..9650b8781 100644
--- a/crates/ra_syntax/src/syntax_node.rs
+++ b/crates/ra_syntax/src/syntax_node.rs
@@ -48,11 +48,11 @@ impl SyntaxTreeBuilder {
48 48
49 pub fn finish(self) -> Parse<SyntaxNode> { 49 pub fn finish(self) -> Parse<SyntaxNode> {
50 let (green, errors) = self.finish_raw(); 50 let (green, errors) = self.finish_raw();
51 let node = SyntaxNode::new_root(green);
52 if cfg!(debug_assertions) { 51 if cfg!(debug_assertions) {
52 let node = SyntaxNode::new_root(green.clone());
53 crate::validation::validate_block_structure(&node); 53 crate::validation::validate_block_structure(&node);
54 } 54 }
55 Parse::new(node.green().clone(), errors) 55 Parse::new(green, errors)
56 } 56 }
57 57
58 pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) { 58 pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) {
diff --git a/crates/ra_syntax/src/tests.rs b/crates/ra_syntax/src/tests.rs
index aee57db62..8447dcad7 100644
--- a/crates/ra_syntax/src/tests.rs
+++ b/crates/ra_syntax/src/tests.rs
@@ -1,9 +1,12 @@
1use std::{ 1use std::{
2 fmt::Write, 2 fmt::Write,
3 path::{Component, Path, PathBuf}, 3 fs,
4 path::{Path, PathBuf},
4}; 5};
5 6
6use test_utils::{collect_rust_files, dir_tests, project_dir, read_text}; 7use expect::expect_file;
8use rayon::prelude::*;
9use test_utils::project_dir;
7 10
8use crate::{fuzz, tokenize, SourceFile, SyntaxError, TextRange, TextSize, Token}; 11use crate::{fuzz, tokenize, SourceFile, SyntaxError, TextRange, TextSize, Token};
9 12
@@ -55,6 +58,51 @@ fn parser_tests() {
55} 58}
56 59
57#[test] 60#[test]
61fn expr_parser_tests() {
62 fragment_parser_dir_test(
63 &["parser/fragments/expr/ok"],
64 &["parser/fragments/expr/err"],
65 crate::ast::Expr::parse,
66 );
67}
68
69#[test]
70fn path_parser_tests() {
71 fragment_parser_dir_test(
72 &["parser/fragments/path/ok"],
73 &["parser/fragments/path/err"],
74 crate::ast::Path::parse,
75 );
76}
77
78#[test]
79fn pattern_parser_tests() {
80 fragment_parser_dir_test(
81 &["parser/fragments/pattern/ok"],
82 &["parser/fragments/pattern/err"],
83 crate::ast::Pat::parse,
84 );
85}
86
87#[test]
88fn item_parser_tests() {
89 fragment_parser_dir_test(
90 &["parser/fragments/item/ok"],
91 &["parser/fragments/item/err"],
92 crate::ast::ModuleItem::parse,
93 );
94}
95
96#[test]
97fn type_parser_tests() {
98 fragment_parser_dir_test(
99 &["parser/fragments/type/ok"],
100 &["parser/fragments/type/err"],
101 crate::ast::TypeRef::parse,
102 );
103}
104
105#[test]
58fn parser_fuzz_tests() { 106fn parser_fuzz_tests() {
59 for (_, text) in collect_rust_files(&test_data_dir(), &["parser/fuzz-failures"]) { 107 for (_, text) in collect_rust_files(&test_data_dir(), &["parser/fuzz-failures"]) {
60 fuzz::check_parser(&text) 108 fuzz::check_parser(&text)
@@ -74,33 +122,43 @@ fn reparse_fuzz_tests() {
74/// FIXME: Use this as a benchmark 122/// FIXME: Use this as a benchmark
75#[test] 123#[test]
76fn self_hosting_parsing() { 124fn self_hosting_parsing() {
77 use std::ffi::OsStr;
78 let dir = project_dir().join("crates"); 125 let dir = project_dir().join("crates");
79 let mut count = 0; 126 let files = walkdir::WalkDir::new(dir)
80 for entry in walkdir::WalkDir::new(dir)
81 .into_iter() 127 .into_iter()
82 .filter_entry(|entry| { 128 .filter_entry(|entry| {
83 !entry.path().components().any(|component| { 129 // Get all files which are not in the crates/ra_syntax/test_data folder
84 // Get all files which are not in the crates/ra_syntax/test_data folder 130 !entry.path().components().any(|component| component.as_os_str() == "test_data")
85 component == Component::Normal(OsStr::new("test_data"))
86 })
87 }) 131 })
88 .map(|e| e.unwrap()) 132 .map(|e| e.unwrap())
89 .filter(|entry| { 133 .filter(|entry| {
90 // Get all `.rs ` files 134 // Get all `.rs ` files
91 !entry.path().is_dir() && (entry.path().extension() == Some(OsStr::new("rs"))) 135 !entry.path().is_dir() && (entry.path().extension().unwrap_or_default() == "rs")
92 }) 136 })
93 { 137 .map(|entry| entry.into_path())
94 count += 1; 138 .collect::<Vec<_>>();
95 let text = read_text(entry.path());
96 if let Err(errors) = SourceFile::parse(&text).ok() {
97 panic!("Parsing errors:\n{:?}\n{}\n", errors, entry.path().display());
98 }
99 }
100 assert!( 139 assert!(
101 count > 30, 140 files.len() > 100,
102 "self_hosting_parsing found too few files - is it running in the right directory?" 141 "self_hosting_parsing found too few files - is it running in the right directory?"
103 ) 142 );
143
144 let errors = files
145 .into_par_iter()
146 .filter_map(|file| {
147 let text = read_text(&file);
148 match SourceFile::parse(&text).ok() {
149 Ok(_) => None,
150 Err(err) => Some((file, err)),
151 }
152 })
153 .collect::<Vec<_>>();
154
155 if !errors.is_empty() {
156 let errors = errors
157 .into_iter()
158 .map(|(path, err)| format!("{}: {:?}\n", path.display(), err))
159 .collect::<String>();
160 panic!("Parsing errors:\n{}\n", errors);
161 }
104} 162}
105 163
106fn test_data_dir() -> PathBuf { 164fn test_data_dir() -> PathBuf {
@@ -134,3 +192,89 @@ fn dump_tokens_and_errors(tokens: &[Token], errors: &[SyntaxError], text: &str)
134 } 192 }
135 acc 193 acc
136} 194}
195
196fn fragment_parser_dir_test<T, F>(ok_paths: &[&str], err_paths: &[&str], f: F)
197where
198 T: crate::AstNode,
199 F: Fn(&str) -> Result<T, ()>,
200{
201 dir_tests(&test_data_dir(), ok_paths, "rast", |text, path| {
202 if let Ok(node) = f(text) {
203 format!("{:#?}", crate::ast::AstNode::syntax(&node))
204 } else {
205 panic!("Failed to parse '{:?}'", path);
206 }
207 });
208 dir_tests(&test_data_dir(), err_paths, "rast", |text, path| {
209 if let Ok(_) = f(text) {
210 panic!("'{:?}' successfully parsed when it should have errored", path);
211 } else {
212 "ERROR\n".to_owned()
213 }
214 });
215}
216
217/// Calls callback `f` with input code and file paths for each `.rs` file in `test_data_dir`
218/// subdirectories defined by `paths`.
219///
220/// If the content of the matching output file differs from the output of `f()`
221/// the test will fail.
222///
223/// If there is no matching output file it will be created and filled with the
224/// output of `f()`, but the test will fail.
225fn dir_tests<F>(test_data_dir: &Path, paths: &[&str], outfile_extension: &str, f: F)
226where
227 F: Fn(&str, &Path) -> String,
228{
229 for (path, input_code) in collect_rust_files(test_data_dir, paths) {
230 let actual = f(&input_code, &path);
231 let path = path.with_extension(outfile_extension);
232 expect_file![path].assert_eq(&actual)
233 }
234}
235
236/// Collects all `.rs` files from `dir` subdirectories defined by `paths`.
237fn collect_rust_files(root_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, String)> {
238 paths
239 .iter()
240 .flat_map(|path| {
241 let path = root_dir.to_owned().join(path);
242 rust_files_in_dir(&path).into_iter()
243 })
244 .map(|path| {
245 let text = read_text(&path);
246 (path, text)
247 })
248 .collect()
249}
250
251/// Collects paths to all `.rs` files from `dir` in a sorted `Vec<PathBuf>`.
252fn rust_files_in_dir(dir: &Path) -> Vec<PathBuf> {
253 let mut acc = Vec::new();
254 for file in fs::read_dir(&dir).unwrap() {
255 let file = file.unwrap();
256 let path = file.path();
257 if path.extension().unwrap_or_default() == "rs" {
258 acc.push(path);
259 }
260 }
261 acc.sort();
262 acc
263}
264
265/// Read file and normalize newlines.
266///
267/// `rustc` seems to always normalize `\r\n` newlines to `\n`:
268///
269/// ```
270/// let s = "
271/// ";
272/// assert_eq!(s.as_bytes(), &[10]);
273/// ```
274///
275/// so this should always be correct.
276fn read_text(path: &Path) -> String {
277 fs::read_to_string(path)
278 .unwrap_or_else(|_| panic!("File at {:?} should be valid", path))
279 .replace("\r\n", "\n")
280}
diff --git a/crates/ra_syntax/test_data/parser/err/0004_use_path_bad_segment.rast b/crates/ra_syntax/test_data/parser/err/0004_use_path_bad_segment.rast
index 8c6b89dc2..b3bcf472a 100644
--- a/crates/ra_syntax/test_data/parser/err/0004_use_path_bad_segment.rast
+++ b/crates/ra_syntax/test_data/parser/err/0004_use_path_bad_segment.rast
@@ -9,8 +9,7 @@ [email protected]
9 [email protected] 9 [email protected]
10 [email protected] "foo" 10 [email protected] "foo"
11 [email protected] "::" 11 [email protected] "::"
12 [email protected] 12 [email protected]
13 [email protected] 13 [email protected] "92"
14 [email protected] "92"
15 [email protected] ";" 14 [email protected] ";"
16error 9..9: expected identifier 15error 9..9: expected identifier
diff --git a/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast b/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast
index 7c957fdde..48610a5eb 100644
--- a/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast
+++ b/crates/ra_syntax/test_data/parser/err/0024_many_type_parens.rast
@@ -180,44 +180,45 @@ [email protected]
180 [email protected] 180 [email protected]
181 [email protected] 181 [email protected]
182 [email protected] "(" 182 [email protected] "("
183 [email protected] 183 [email protected]
184 [email protected] 184 [email protected] "for"
185 [email protected] 185 [email protected]
186 [email protected] 186 [email protected]
187 [email protected] 187 [email protected]
188 [email protected] 188 [email protected] "<"
189 [email protected] "for" 189 [email protected]
190 [email protected] 190 [email protected] "\'a"
191 [email protected] "<" 191 [email protected] ">"
192 [email protected] 192 [email protected] " "
193 [email protected] "\'a" 193 [email protected]
194 [email protected] ">" 194 [email protected]
195 [email protected] " " 195 [email protected]
196 [email protected]
196 [email protected] 197 [email protected]
197 [email protected] 198 [email protected]
198 [email protected] 199 [email protected]
199 [email protected] 200 [email protected]
200 [email protected] "Trait" 201 [email protected] "Trait"
201 [email protected] "<" 202 [email protected] "<"
202 [email protected] 203 [email protected]
203 [email protected] "\'a" 204 [email protected] "\'a"
204 [email protected] ">" 205 [email protected] ">"
205 [email protected] 206 [email protected]
206 [email protected] ")" 207 [email protected] ")"
207 [email protected] " " 208 [email protected] " "
208 [email protected] "+" 209 [email protected] "+"
209 [email protected] " " 210 [email protected] " "
210 [email protected] 211 [email protected]
211 [email protected] "(" 212 [email protected] "("
212 [email protected] 213 [email protected]
213 [email protected] 214 [email protected]
214 [email protected] 215 [email protected]
215 [email protected] 216 [email protected]
216 [email protected] "Copy" 217 [email protected] "Copy"
217 [email protected] ")" 218 [email protected] ")"
218 [email protected] ">" 219 [email protected] ">"
219 [email protected] 220 [email protected]
220 [email protected] ";" 221 [email protected] ";"
221 [email protected] "\n " 222 [email protected] "\n "
222 [email protected] 223 [email protected]
223 [email protected] "let" 224 [email protected] "let"
@@ -302,13 +303,12 @@ error 146..146: expected expression
302error 147..147: expected SEMICOLON 303error 147..147: expected SEMICOLON
303error 148..148: expected expression 304error 148..148: expected expression
304error 149..149: expected SEMICOLON 305error 149..149: expected SEMICOLON
305error 154..154: expected pattern 306error 155..155: expected type
306error 155..155: expected IN_KW 307error 158..158: expected IN_KW
307error 155..155: expected expression
308error 157..157: expected a block
309error 165..165: expected expression 308error 165..165: expected expression
310error 168..168: expected expression 309error 168..168: expected expression
311error 179..179: expected expression 310error 179..179: expected expression
311error 180..180: expected a block
312error 180..180: expected COMMA 312error 180..180: expected COMMA
313error 180..180: expected expression 313error 180..180: expected expression
314error 180..180: expected R_PAREN 314error 180..180: expected R_PAREN
diff --git a/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast b/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast
index 568a4cc02..4d6461d1e 100644
--- a/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast
+++ b/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast
@@ -12,17 +12,16 @@ [email protected]
12 [email protected] "where" 12 [email protected] "where"
13 [email protected] " " 13 [email protected] " "
14 [email protected] 14 [email protected]
15 [email protected] 15 [email protected] "for"
16 [email protected] "for" 16 [email protected]
17 [email protected] 17 [email protected] "<"
18 [email protected] "<" 18 [email protected]
19 [email protected] 19 [email protected] "\'a"
20 [email protected] "\'a" 20 [email protected] ">"
21 [email protected] ">"
22 [email protected] "\n" 21 [email protected] "\n"
23 [email protected] 22 [email protected]
24 [email protected] "{" 23 [email protected] "{"
25 [email protected] "}" 24 [email protected] "}"
26 [email protected] "\n" 25 [email protected] "\n"
27error 26..26: expected a path 26error 26..26: expected type
28error 26..26: expected colon 27error 26..26: expected colon
diff --git a/crates/ra_syntax/test_data/parser/err/0043_default_const.rast b/crates/ra_syntax/test_data/parser/err/0043_default_const.rast
new file mode 100644
index 000000000..8eb583ef8
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0043_default_const.rast
@@ -0,0 +1,40 @@
1[email protected]
2 [email protected]
3 [email protected] "trait"
4 [email protected] " "
5 [email protected]
6 [email protected] "T"
7 [email protected] " "
8 [email protected]
9 [email protected] "{"
10 [email protected] "\n "
11 [email protected]
12 [email protected]
13 [email protected]
14 [email protected]
15 [email protected] "default"
16 [email protected] " "
17 [email protected]
18 [email protected] "const"
19 [email protected] " "
20 [email protected]
21 [email protected] "f"
22 [email protected] ":"
23 [email protected] " "
24 [email protected]
25 [email protected]
26 [email protected]
27 [email protected]
28 [email protected] "u8"
29 [email protected] " "
30 [email protected] "="
31 [email protected] " "
32 [email protected]
33 [email protected] "0"
34 [email protected] ";"
35 [email protected] "\n"
36 [email protected] "}"
37 [email protected] "\n"
38error 19..19: expected BANG
39error 19..19: expected `{`, `[`, `(`
40error 19..19: expected SEMICOLON
diff --git a/crates/ra_syntax/test_data/parser/err/0043_default_const.rs b/crates/ra_syntax/test_data/parser/err/0043_default_const.rs
new file mode 100644
index 000000000..80f15474a
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0043_default_const.rs
@@ -0,0 +1,3 @@
1trait T {
2 default const f: u8 = 0;
3}
diff --git a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast
new file mode 100644
index 000000000..cb90f28bc
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast
@@ -0,0 +1,240 @@
1[email protected]
2 [email protected]
3 [email protected] "type"
4 [email protected] " "
5 [email protected]
6 [email protected] "ForRef"
7 [email protected] " "
8 [email protected] "="
9 [email protected] " "
10 [email protected]
11 [email protected] "for"
12 [email protected]
13 [email protected] "<"
14 [email protected]
15 [email protected] "\'a"
16 [email protected] ">"
17 [email protected] " "
18 [email protected]
19 [email protected] "&"
20 [email protected] "\'a"
21 [email protected] " "
22 [email protected]
23 [email protected]
24 [email protected]
25 [email protected]
26 [email protected] "u32"
27 [email protected] ";"
28 [email protected] "\n"
29 [email protected]
30 [email protected] "type"
31 [email protected] " "
32 [email protected]
33 [email protected] "ForTup"
34 [email protected] " "
35 [email protected] "="
36 [email protected] " "
37 [email protected]
38 [email protected] "for"
39 [email protected]
40 [email protected] "<"
41 [email protected]
42 [email protected] "\'a"
43 [email protected] ">"
44 [email protected] " "
45 [email protected]
46 [email protected] "("
47 [email protected]
48 [email protected] "&"
49 [email protected] "\'a"
50 [email protected] " "
51 [email protected]
52 [email protected]
53 [email protected]
54 [email protected]
55 [email protected] "u32"
56 [email protected] ","
57 [email protected] ")"
58 [email protected] ";"
59 [email protected] "\n"
60 [email protected]
61 [email protected] "type"
62 [email protected] " "
63 [email protected]
64 [email protected] "ForSlice"
65 [email protected] " "
66 [email protected] "="
67 [email protected] " "
68 [email protected]
69 [email protected] "for"
70 [email protected]
71 [email protected] "<"
72 [email protected]
73 [email protected] "\'a"
74 [email protected] ">"
75 [email protected] " "
76 [email protected]
77 [email protected] "["
78 [email protected]
79 [email protected]
80 [email protected]
81 [email protected]
82 [email protected] "u32"
83 [email protected] "]"
84 [email protected] ";"
85 [email protected] "\n"
86 [email protected]
87 [email protected] "type"
88 [email protected] " "
89 [email protected]
90 [email protected] "ForForFn"
91 [email protected] " "
92 [email protected] "="
93 [email protected] " "
94 [email protected]
95 [email protected] "for"
96 [email protected]
97 [email protected] "<"
98 [email protected]
99 [email protected] "\'a"
100 [email protected] ">"
101 [email protected] " "
102 [email protected]
103 [email protected] "for"
104 [email protected]
105 [email protected] "<"
106 [email protected]
107 [email protected] "\'b"
108 [email protected] ">"
109 [email protected] " "
110 [email protected]
111 [email protected] "fn"
112 [email protected]
113 [email protected] "("
114 [email protected]
115 [email protected]
116 [email protected] "&"
117 [email protected] "\'a"
118 [email protected] " "
119 [email protected]
120 [email protected]
121 [email protected]
122 [email protected]
123 [email protected] "i32"
124 [email protected] ","
125 [email protected] " "
126 [email protected]
127 [email protected]
128 [email protected] "&"
129 [email protected] "\'b"
130 [email protected] " "
131 [email protected]
132 [email protected]
133 [email protected]
134 [email protected]
135 [email protected] "i32"
136 [email protected] ")"
137 [email protected] ";"
138 [email protected] "\n"
139 [email protected]
140 [email protected] "fn"
141 [email protected] " "
142 [email protected]
143 [email protected] "for_for_for"
144 [email protected]
145 [email protected] "<"
146 [email protected]
147 [email protected]
148 [email protected] "T"
149 [email protected] ">"
150 [email protected]
151 [email protected] "("
152 [email protected] ")"
153 [email protected] "\n"
154 [email protected]
155 [email protected] "where"
156 [email protected] "\n "
157 [email protected]
158 [email protected] "for"
159 [email protected]
160 [email protected] "<"
161 [email protected]
162 [email protected] "\'a"
163 [email protected] ">"
164 [email protected] " "
165 [email protected]
166 [email protected] "for"
167 [email protected]
168 [email protected] "<"
169 [email protected]
170 [email protected] "\'b"
171 [email protected] ">"
172 [email protected] " "
173 [email protected]
174 [email protected] "for"
175 [email protected]
176 [email protected] "<"
177 [email protected]
178 [email protected] "\'c"
179 [email protected] ">"
180 [email protected] " "
181 [email protected]
182 [email protected] "fn"
183 [email protected]
184 [email protected] "("
185 [email protected]
186 [email protected]
187 [email protected] "&"
188 [email protected] "\'a"
189 [email protected] " "
190 [email protected]
191 [email protected]
192 [email protected]
193 [email protected]
194 [email protected] "T"
195 [email protected] ","
196 [email protected] " "
197 [email protected]
198 [email protected]
199 [email protected] "&"
200 [email protected] "\'b"
201 [email protected] " "
202 [email protected]
203 [email protected]
204 [email protected]
205 [email protected]
206 [email protected] "T"
207 [email protected] ","
208 [email protected] " "
209 [email protected]
210 [email protected]
211 [email protected] "&"
212 [email protected] "\'c"
213 [email protected] " "
214 [email protected]
215 [email protected]
216 [email protected]
217 [email protected]
218 [email protected] "T"
219 [email protected] ")"
220 [email protected] ":"
221 [email protected] " "
222 [email protected]
223 [email protected]
224 [email protected]
225 [email protected]
226 [email protected]
227 [email protected]
228 [email protected] "Copy"
229 [email protected] ","
230 [email protected] "\n"
231 [email protected]
232 [email protected] "{"
233 [email protected] "\n"
234 [email protected] "}"
235 [email protected] "\n"
236error 21..21: expected a function pointer or path
237error 52..52: expected a function pointer or path
238error 88..88: expected a function pointer or path
239error 119..119: expected a function pointer or path
240error 195..195: expected a function pointer or path
diff --git a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs
new file mode 100644
index 000000000..0e9f8ccb4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs
@@ -0,0 +1,9 @@
1type ForRef = for<'a> &'a u32;
2type ForTup = for<'a> (&'a u32,);
3type ForSlice = for<'a> [u32];
4type ForForFn = for<'a> for<'b> fn(&'a i32, &'b i32);
5fn for_for_for<T>()
6where
7 for<'a> for<'b> for<'c> fn(&'a T, &'b T, &'c T): Copy,
8{
9}
diff --git a/crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rast b/crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rast
new file mode 100644
index 000000000..5df7507e2
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rast
@@ -0,0 +1 @@
ERROR
diff --git a/crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rs b/crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rs
new file mode 100644
index 000000000..ca49acb07
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/expr/err/0000_truncated_add.rs
@@ -0,0 +1 @@
1 +
diff --git a/crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rast b/crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rast
new file mode 100644
index 000000000..fa78a02a6
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rast
@@ -0,0 +1,8 @@
1[email protected]
2 [email protected]
3 [email protected] "1"
4 [email protected] " "
5 [email protected] "+"
6 [email protected] " "
7 [email protected]
8 [email protected] "2"
diff --git a/crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rs b/crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rs
new file mode 100644
index 000000000..e0ef58402
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/expr/ok/0000_add.rs
@@ -0,0 +1 @@
1 + 2
diff --git a/crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rast b/crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rast
new file mode 100644
index 000000000..5df7507e2
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rast
@@ -0,0 +1 @@
ERROR
diff --git a/crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rs b/crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rs
new file mode 100644
index 000000000..dc32389bb
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/item/err/0000_extra_keyword.rs
@@ -0,0 +1 @@
fn fn foo() {}
diff --git a/crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rast b/crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rast
new file mode 100644
index 000000000..f1e78f388
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rast
@@ -0,0 +1,12 @@
1[email protected]
2 [email protected] "fn"
3 [email protected] " "
4 [email protected]
5 [email protected] "foo"
6 [email protected]
7 [email protected] "("
8 [email protected] ")"
9 [email protected] " "
10 [email protected]
11 [email protected] "{"
12 [email protected] "}"
diff --git a/crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rs b/crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rs
new file mode 100644
index 000000000..8f3b7ef11
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/item/ok/0000_fn.rs
@@ -0,0 +1 @@
fn foo() {}
diff --git a/crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rast b/crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rast
new file mode 100644
index 000000000..5df7507e2
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rast
@@ -0,0 +1 @@
ERROR
diff --git a/crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rs b/crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rs
new file mode 100644
index 000000000..2046de049
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/path/err/0000_reserved_word.rs
@@ -0,0 +1 @@
struct
diff --git a/crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rast b/crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rast
new file mode 100644
index 000000000..5df7507e2
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rast
@@ -0,0 +1 @@
ERROR
diff --git a/crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rs b/crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rs
new file mode 100644
index 000000000..745e8d376
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/path/err/0001_expression.rs
@@ -0,0 +1 @@
a + b
diff --git a/crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rast b/crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rast
new file mode 100644
index 000000000..0c5d4360f
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rast
@@ -0,0 +1,4 @@
1[email protected]
2 [email protected]
3 [email protected]
4 [email protected] "foo"
diff --git a/crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rs b/crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rs
new file mode 100644
index 000000000..257cc5642
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/path/ok/0000_single_ident.rs
@@ -0,0 +1 @@
foo
diff --git a/crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rast b/crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rast
new file mode 100644
index 000000000..4a2b45e6a
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rast
@@ -0,0 +1,14 @@
1[email protected]
2 [email protected]
3 [email protected]
4 [email protected]
5 [email protected]
6 [email protected] "foo"
7 [email protected] "::"
8 [email protected]
9 [email protected]
10 [email protected] "bar"
11 [email protected] "::"
12 [email protected]
13 [email protected]
14 [email protected] "baz"
diff --git a/crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rs b/crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rs
new file mode 100644
index 000000000..81e0b21cd
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/path/ok/0001_multipart.rs
@@ -0,0 +1 @@
foo::bar::baz
diff --git a/crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rast b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rast
new file mode 100644
index 000000000..5df7507e2
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rast
@@ -0,0 +1 @@
ERROR
diff --git a/crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rs b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rs
new file mode 100644
index 000000000..ae26fc455
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0000_reserved_word.rs
@@ -0,0 +1 @@
fn
diff --git a/crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rast b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rast
new file mode 100644
index 000000000..5df7507e2
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rast
@@ -0,0 +1 @@
ERROR
diff --git a/crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rs b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rs
new file mode 100644
index 000000000..61a391d08
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/pattern/err/0001_missing_paren.rs
@@ -0,0 +1 @@
Some(x
diff --git a/crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rast b/crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rast
new file mode 100644
index 000000000..15eb7f9c6
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rast
@@ -0,0 +1,10 @@
1[email protected]
2 [email protected]
3 [email protected]
4 [email protected]
5 [email protected] "Some"
6 [email protected] "("
7 [email protected]
8 [email protected]
9 [email protected] "x"
10 [email protected] ")"
diff --git a/crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rs b/crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rs
new file mode 100644
index 000000000..87114dd78
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/pattern/ok/0000_enum.rs
@@ -0,0 +1 @@
Some(x)
diff --git a/crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rast b/crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rast
new file mode 100644
index 000000000..5df7507e2
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rast
@@ -0,0 +1 @@
ERROR
diff --git a/crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rs b/crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rs
new file mode 100644
index 000000000..caa4d7c09
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/type/err/0000_missing_close.rs
@@ -0,0 +1 @@
Result<Foo, Bar
diff --git a/crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rast b/crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rast
new file mode 100644
index 000000000..8831cfa6c
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rast
@@ -0,0 +1,22 @@
1[email protected]
2 [email protected]
3 [email protected]
4 [email protected]
5 [email protected] "Result"
6 [email protected]
7 [email protected] "<"
8 [email protected]
9 [email protected]
10 [email protected]
11 [email protected]
12 [email protected]
13 [email protected] "Foo"
14 [email protected] ","
15 [email protected] " "
16 [email protected]
17 [email protected]
18 [email protected]
19 [email protected]
20 [email protected]
21 [email protected] "Bar"
22 [email protected] ">"
diff --git a/crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rs b/crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rs
new file mode 100644
index 000000000..b50b3bb3b
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/fragments/type/ok/0000_result.rs
@@ -0,0 +1 @@
Result<Foo, Bar>
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast b/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
index 0656fdf73..4e3fa704e 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
+++ b/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
@@ -56,4 +56,3 @@ [email protected]
56 [email protected] "}" 56 [email protected] "}"
57 [email protected] "\n" 57 [email protected] "\n"
58error 24..24: attributes are not allowed on BIN_EXPR 58error 24..24: attributes are not allowed on BIN_EXPR
59error 44..44: attributes are not allowed on IF_EXPR
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast b/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast
index 9be441110..53f7ebaf9 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast
+++ b/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast
@@ -17,23 +17,29 @@ [email protected]
17 [email protected] "{" 17 [email protected] "{"
18 [email protected] "}" 18 [email protected] "}"
19 [email protected] "\n" 19 [email protected] "\n"
20 ERROR@25..31 20 CONST_DEF@25..46
21 [email protected] "unsafe" 21 [email protected] "unsafe"
22 [email protected] " " 22 [email protected] " "
23 [email protected]
24 [email protected] "const" 23 [email protected] "const"
25 [email protected] " " 24 [email protected] " "
26 [email protected] "fn" 25 [email protected]
26 [email protected] "fn"
27 [email protected] " " 27 [email protected] " "
28 [email protected] 28 [email protected]
29 [email protected] "bar" 29 [email protected]
30 [email protected] 30 [email protected]
31 [email protected] "(" 31 [email protected]
32 [email protected] ")" 32 [email protected] "bar"
33 [email protected] " " 33 [email protected]
34 [email protected] 34 [email protected] "("
35 [email protected] "{" 35 [email protected] ")"
36 [email protected] "}" 36 [email protected] " "
37 [email protected]
38 [email protected] "{"
39 [email protected] "}"
37 [email protected] "\n" 40 [email protected] "\n"
38error 6..6: expected existential, fn, trait or impl 41error 6..6: expected existential, fn, trait or impl
39error 31..31: expected existential, fn, trait or impl 42error 38..38: expected a name
43error 40..40: expected COLON
44error 46..46: expected SEMICOLON
45error 47..47: expected an item
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0015_empty_segment.rast b/crates/ra_syntax/test_data/parser/inline/err/0015_empty_segment.rast
new file mode 100644
index 000000000..da8505607
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/err/0015_empty_segment.rast
@@ -0,0 +1,15 @@
1[email protected]
2 [email protected]
3 [email protected] "use"
4 [email protected] " "
5 [email protected]
6 [email protected]
7 [email protected]
8 [email protected]
9 [email protected] "crate"
10 [email protected] "::"
11 [email protected]
12 [email protected] ";"
13 [email protected] "\n"
14error 11..11: expected identifier
15error 12..12: expected SEMICOLON
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0015_empty_segment.rs b/crates/ra_syntax/test_data/parser/inline/err/0015_empty_segment.rs
new file mode 100644
index 000000000..7510664e1
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/err/0015_empty_segment.rs
@@ -0,0 +1 @@
use crate::;
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
index 9dc473e43..cd0892451 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
@@ -1,61 +1,60 @@
1[email protected]9 1SOURCE_FILE@0..54
2 FN_DEF@0..48 2 FN_DEF@0..53
3 [email protected] "fn" 3 [email protected] "fn"
4 [email protected] " " 4 [email protected] " "
5 [email protected] 5 [email protected]
6 [email protected] "test" 6 [email protected] "for_trait"
7 [email protected] 7 [email protected]
8 [email protected] "<" 8 [email protected] "<"
9 [email protected] 9 [email protected]
10 [email protected] 10 [email protected]
11 [email protected] "F" 11 [email protected] "F"
12 [email protected] ">" 12 [email protected] ">"
13 [email protected] 13 [email protected]
14 [email protected] "(" 14 [email protected] "("
15 [email protected] ")" 15 [email protected] ")"
16 [email protected] "\n" 16 [email protected] "\n"
17 [email protected] 17 [email protected]
18 [email protected] "where" 18 [email protected] "where"
19 [email protected] "\n " 19 [email protected] "\n "
20 [email protected] 20 [email protected]
21 [email protected] 21 [email protected] "for"
22 [email protected] "for" 22 [email protected]
23 [email protected] 23 [email protected] "<"
24 [email protected] "<" 24 [email protected]
25 [email protected] 25 [email protected] "\'a"
26 [email protected] "\'a" 26 [email protected] ">"
27 [email protected] ">" 27 [email protected] " "
28 [email protected] " " 28 [email protected]
29 [email protected] 29 [email protected]
30 [email protected] 30 [email protected]
31 [email protected] 31 [email protected]
32 [email protected] 32 [email protected] "F"
33 [email protected] "F" 33 [email protected] ":"
34 [email protected] ":" 34 [email protected] " "
35 [email protected] " " 35 [email protected]
36 [email protected] 36 [email protected]
37 [email protected] 37 [email protected]
38 [email protected] 38 [email protected]
39 [email protected] 39 [email protected]
40 [email protected] 40 [email protected]
41 [email protected] 41 [email protected] "Fn"
42 [email protected] "Fn" 42 [email protected]
43 [email protected] 43 [email protected] "("
44 [email protected] "(" 44 [email protected]
45 [email protected] 45 [email protected]
46 [email protected] 46 [email protected] "&"
47 [email protected] "&" 47 [email protected] "\'a"
48 [email protected] "\'a" 48 [email protected] " "
49 [email protected] " " 49 [email protected]
50 [email protected] 50 [email protected]
51 [email protected] 51 [email protected]
52 [email protected] 52 [email protected]
53 [email protected] 53 [email protected] "str"
54 [email protected] "str" 54 [email protected] ")"
55 [email protected] ")" 55 [email protected] "\n"
56 [email protected] "\n" 56 [email protected]
57 [email protected] 57 [email protected] "{"
58 [email protected] "{" 58 [email protected] " "
59 [email protected] " " 59 [email protected] "}"
60 [email protected] "}" 60 [email protected] "\n"
61 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs
index b448c6178..423bc105b 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs
@@ -1,4 +1,4 @@
1fn test<F>() 1fn for_trait<F>()
2where 2where
3 for<'a> F: Fn(&'a str) 3 for<'a> F: Fn(&'a str)
4{ } 4{ }
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast
index dfb8d57ad..b26ac2d36 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast
@@ -1,4 +1,4 @@
1[email protected]00 1SOURCE_FILE@0..121
2 [email protected] 2 [email protected]
3 [email protected] "type" 3 [email protected] "type"
4 [email protected] " " 4 [email protected] " "
@@ -29,212 +29,84 @@ [email protected]
29 [email protected] ")" 29 [email protected] ")"
30 [email protected] ";" 30 [email protected] ";"
31 [email protected] "\n" 31 [email protected] "\n"
32 [email protected] 32 [email protected]
33 [email protected] "fn" 33 [email protected] "type"
34 [email protected] " " 34 [email protected] " "
35 [email protected] 35 [email protected]
36 [email protected] "foo" 36 [email protected] "B"
37 [email protected] 37 [email protected] " "
38 [email protected] "<" 38 [email protected] "="
39 [email protected] 39 [email protected] " "
40 [email protected] 40 [email protected]
41 [email protected] "T" 41 [email protected] "for"
42 [email protected] ">" 42 [email protected]
43 [email protected] 43 [email protected] "<"
44 [email protected] "(" 44 [email protected]
45 [email protected] 45 [email protected] "\'a"
46 [email protected] 46 [email protected] ">"
47 [email protected] 47 [email protected] " "
48 [email protected] "_t" 48 [email protected]
49 [email protected] ":" 49 [email protected] "unsafe"
50 [email protected] " " 50 [email protected] " "
51 [email protected] 51 [email protected]
52 [email protected] "&" 52 [email protected] "extern"
53 [email protected] 53 [email protected] " "
54 [email protected] 54 [email protected] "\"C\""
55 [email protected] 55 [email protected] " "
56 [email protected] 56 [email protected] "fn"
57 [email protected] "T" 57 [email protected]
58 [email protected] ")" 58 [email protected] "("
59 [email protected] " " 59 [email protected]
60 [email protected] 60 [email protected]
61 [email protected] "where" 61 [email protected] "&"
62 [email protected] " " 62 [email protected] "\'a"
63 [email protected] 63 [email protected] " "
64 [email protected] 64 [email protected]
65 [email protected] "for" 65 [email protected] "("
66 [email protected] 66 [email protected] ")"
67 [email protected] "<" 67 [email protected] ")"
68 [email protected] 68 [email protected] " "
69 [email protected] "\'a" 69 [email protected]
70 [email protected] ">" 70 [email protected] "->"
71 [email protected] " " 71 [email protected] " "
72 [email protected] 72 [email protected]
73 [email protected] "&" 73 [email protected] "("
74 [email protected] "\'a" 74 [email protected] ")"
75 [email protected] " " 75 [email protected] ";"
76 [email protected] 76 [email protected] "\n"
77 [email protected] 77 [email protected]
78 [email protected] 78 [email protected] "type"
79 [email protected] 79 [email protected] " "
80 [email protected] "T" 80 [email protected]
81 [email protected] ":" 81 [email protected] "Obj"
82 [email protected] " " 82 [email protected] " "
83 [email protected] 83 [email protected] "="
84 [email protected] 84 [email protected] " "
85 [email protected] 85 [email protected]
86 [email protected] 86 [email protected] "for"
87 [email protected] 87 [email protected]
88 [email protected] 88 [email protected] "<"
89 [email protected] "Iterator" 89 [email protected]
90 [email protected] " " 90 [email protected] "\'a"
91 [email protected] 91 [email protected] ">"
92 [email protected] "{" 92 [email protected] " "
93 [email protected] "}" 93 [email protected]
94 [email protected] "\n" 94 [email protected]
95 [email protected] 95 [email protected]
96 [email protected] "fn" 96 [email protected]
97 [email protected] " " 97 [email protected] "PartialEq"
98 [email protected] 98 [email protected]
99 [email protected] "bar" 99 [email protected] "<"
100 [email protected] 100 [email protected]
101 [email protected] "<" 101 [email protected]
102 [email protected] 102 [email protected] "&"
103 [email protected] 103 [email protected] "\'a"
104 [email protected] "T" 104 [email protected] " "
105 [email protected] ">" 105 [email protected]
106 [email protected] 106 [email protected]
107 [email protected] "(" 107 [email protected]
108 [email protected] 108 [email protected]
109 [email protected] 109 [email protected] "i32"
110 [email protected] 110 [email protected] ">"
111 [email protected] "_t" 111 [email protected] ";"
112 [email protected] ":" 112 [email protected] "\n"
113 [email protected] " "
114 [email protected]
115 [email protected] "&"
116 [email protected]
117 [email protected]
118 [email protected]
119 [email protected]
120 [email protected] "T"
121 [email protected] ")"
122 [email protected] " "
123 [email protected]
124 [email protected] "where"
125 [email protected] " "
126 [email protected]
127 [email protected]
128 [email protected] "for"
129 [email protected]
130 [email protected] "<"
131 [email protected]
132 [email protected] "\'a"
133 [email protected] ">"
134 [email protected] " "
135 [email protected]
136 [email protected] "&"
137 [email protected] "\'a"
138 [email protected] " "
139 [email protected] "mut"
140 [email protected] " "
141 [email protected]
142 [email protected]
143 [email protected]
144 [email protected]
145 [email protected] "T"
146 [email protected] ":"
147 [email protected] " "
148 [email protected]
149 [email protected]
150 [email protected]
151 [email protected]
152 [email protected]
153 [email protected]
154 [email protected] "Iterator"
155 [email protected] " "
156 [email protected]
157 [email protected] "{"
158 [email protected] "}"
159 [email protected] "\n"
160 [email protected]
161 [email protected] "fn"
162 [email protected] " "
163 [email protected]
164 [email protected] "baz"
165 [email protected]
166 [email protected] "<"
167 [email protected]
168 [email protected]
169 [email protected] "T"
170 [email protected] ">"
171 [email protected]
172 [email protected] "("
173 [email protected]
174 [email protected]
175 [email protected]
176 [email protected] "_t"
177 [email protected] ":"
178 [email protected] " "
179 [email protected]
180 [email protected] "&"
181 [email protected]
182 [email protected]
183 [email protected]
184 [email protected]
185 [email protected] "T"
186 [email protected] ")"
187 [email protected] " "
188 [email protected]
189 [email protected] "where"
190 [email protected] " "
191 [email protected]
192 [email protected]
193 [email protected] "for"
194 [email protected]
195 [email protected] "<"
196 [email protected]
197 [email protected] "\'a"
198 [email protected] ">"
199 [email protected] " "
200 [email protected]
201 [email protected]
202 [email protected]
203 [email protected]
204 [email protected] "<"
205 [email protected]
206 [email protected] "&"
207 [email protected] "\'a"
208 [email protected] " "
209 [email protected]
210 [email protected]
211 [email protected]
212 [email protected]
213 [email protected] "T"
214 [email protected] " "
215 [email protected] "as"
216 [email protected] " "
217 [email protected]
218 [email protected]
219 [email protected]
220 [email protected]
221 [email protected] "Baz"
222 [email protected] ">"
223 [email protected] "::"
224 [email protected]
225 [email protected]
226 [email protected] "Foo"
227 [email protected] ":"
228 [email protected] " "
229 [email protected]
230 [email protected]
231 [email protected]
232 [email protected]
233 [email protected]
234 [email protected]
235 [email protected] "Iterator"
236 [email protected] " "
237 [email protected]
238 [email protected] "{"
239 [email protected] "}"
240 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs
index d6774d438..8ac7b9e10 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs
@@ -1,4 +1,3 @@
1type A = for<'a> fn() -> (); 1type A = for<'a> fn() -> ();
2fn foo<T>(_t: &T) where for<'a> &'a T: Iterator {} 2type B = for<'a> unsafe extern "C" fn(&'a ()) -> ();
3fn bar<T>(_t: &T) where for<'a> &'a mut T: Iterator {} 3type Obj = for<'a> PartialEq<&'a i32>;
4fn baz<T>(_t: &T) where for<'a> <&'a T as Baz>::Foo: Iterator {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast
new file mode 100644
index 000000000..adb6159f4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast
@@ -0,0 +1,40 @@
1[email protected]
2 [email protected]
3 [email protected] "impl"
4 [email protected] " "
5 [email protected]
6 [email protected]
7 [email protected]
8 [email protected]
9 [email protected] "T"
10 [email protected] " "
11 [email protected] "for"
12 [email protected] " "
13 [email protected]
14 [email protected]
15 [email protected]
16 [email protected]
17 [email protected] "Foo"
18 [email protected] " "
19 [email protected]
20 [email protected] "{"
21 [email protected] "\n "
22 [email protected]
23 [email protected] "default"
24 [email protected] " "
25 [email protected] "unsafe"
26 [email protected] " "
27 [email protected] "fn"
28 [email protected] " "
29 [email protected]
30 [email protected] "foo"
31 [email protected]
32 [email protected] "("
33 [email protected] ")"
34 [email protected] " "
35 [email protected]
36 [email protected] "{"
37 [email protected] "}"
38 [email protected] "\n"
39 [email protected] "}"
40 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs
new file mode 100644
index 000000000..12926cd8a
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs
@@ -0,0 +1,3 @@
1impl T for Foo {
2 default unsafe fn foo() {}
3}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast
new file mode 100644
index 000000000..a9eda5668
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast
@@ -0,0 +1,18 @@
1[email protected]
2 [email protected]
3 [email protected] "default"
4 [email protected] " "
5 [email protected] "unsafe"
6 [email protected] " "
7 [email protected] "impl"
8 [email protected] " "
9 [email protected]
10 [email protected]
11 [email protected]
12 [email protected]
13 [email protected] "Foo"
14 [email protected] " "
15 [email protected]
16 [email protected] "{"
17 [email protected] "}"
18 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs
new file mode 100644
index 000000000..ba0998ff4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs
@@ -0,0 +1 @@
default unsafe impl Foo {}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast
new file mode 100644
index 000000000..868899275
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rast
@@ -0,0 +1,38 @@
1[email protected]
2 [email protected]
3 [email protected] "fn"
4 [email protected] " "
5 [email protected]
6 [email protected] "main"
7 [email protected]
8 [email protected] "("
9 [email protected] ")"
10 [email protected] " "
11 [email protected]
12 [email protected] "{"
13 [email protected] " "
14 [email protected]
15 [email protected] "let"
16 [email protected] " "
17 [email protected]
18 [email protected]
19 [email protected]
20 [email protected]
21 [email protected] "<"
22 [email protected]
23 [email protected] "_"
24 [email protected] ">"
25 [email protected] "::"
26 [email protected]
27 [email protected]
28 [email protected] "Foo"
29 [email protected] " "
30 [email protected] "="
31 [email protected] " "
32 [email protected]
33 [email protected] "("
34 [email protected] ")"
35 [email protected] ";"
36 [email protected] " "
37 [email protected] "}"
38 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs
new file mode 100644
index 000000000..ebe26834d
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0164_type_path_in_pattern.rs
@@ -0,0 +1 @@
fn main() { let <_>::Foo = (); }
diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast
new file mode 100644
index 000000000..dab0247ee
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast
@@ -0,0 +1,44 @@
1[email protected]
2 [email protected]
3 [email protected] "impl"
4 [email protected] " "
5 [email protected]
6 [email protected]
7 [email protected]
8 [email protected]
9 [email protected] "T"
10 [email protected] " "
11 [email protected] "for"
12 [email protected] " "
13 [email protected]
14 [email protected]
15 [email protected]
16 [email protected]
17 [email protected] "Foo"
18 [email protected] " "
19 [email protected]
20 [email protected] "{"
21 [email protected] "\n "
22 [email protected]
23 [email protected] "default"
24 [email protected] " "
25 [email protected] "const"
26 [email protected] " "
27 [email protected]
28 [email protected] "f"
29 [email protected] ":"
30 [email protected] " "
31 [email protected]
32 [email protected]
33 [email protected]
34 [email protected]
35 [email protected] "u8"
36 [email protected] " "
37 [email protected] "="
38 [email protected] " "
39 [email protected]
40 [email protected] "0"
41 [email protected] ";"
42 [email protected] "\n"
43 [email protected] "}"
44 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs
new file mode 100644
index 000000000..dfb3b92dc
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs
@@ -0,0 +1,3 @@
1impl T for Foo {
2 default const f: u8 = 0;
3}
diff --git a/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast
new file mode 100644
index 000000000..503585103
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast
@@ -0,0 +1,392 @@
1[email protected]
2 [email protected]
3 [email protected] "fn"
4 [email protected] " "
5 [email protected]
6 [email protected] "for_trait"
7 [email protected]
8 [email protected] "<"
9 [email protected]
10 [email protected]
11 [email protected] "F"
12 [email protected] ">"
13 [email protected]
14 [email protected] "("
15 [email protected] ")"
16 [email protected] "\n"
17 [email protected]
18 [email protected] "where"
19 [email protected] "\n "
20 [email protected]
21 [email protected] "for"
22 [email protected]
23 [email protected] "<"
24 [email protected]
25 [email protected] "\'a"
26 [email protected] ">"
27 [email protected] " "
28 [email protected]
29 [email protected]
30 [email protected]
31 [email protected]
32 [email protected] "F"
33 [email protected] ":"
34 [email protected] " "
35 [email protected]
36 [email protected]
37 [email protected]
38 [email protected]
39 [email protected]
40 [email protected]
41 [email protected] "Fn"
42 [email protected]
43 [email protected] "("
44 [email protected]
45 [email protected]
46 [email protected] "&"
47 [email protected] "\'a"
48 [email protected] " "
49 [email protected]
50 [email protected]
51 [email protected]
52 [email protected]
53 [email protected] "str"
54 [email protected] ")"
55 [email protected] ","
56 [email protected] "\n"
57 [email protected]
58 [email protected] "{"
59 [email protected] "\n"
60 [email protected] "}"
61 [email protected] "\n"
62 [email protected]
63 [email protected] "fn"
64 [email protected] " "
65 [email protected]
66 [email protected] "for_ref"
67 [email protected]
68 [email protected] "<"
69 [email protected]
70 [email protected]
71 [email protected] "F"
72 [email protected] ">"
73 [email protected]
74 [email protected] "("
75 [email protected] ")"
76 [email protected] "\n"
77 [email protected]
78 [email protected] "where"
79 [email protected] "\n "
80 [email protected]
81 [email protected] "for"
82 [email protected]
83 [email protected] "<"
84 [email protected]
85 [email protected] "\'a"
86 [email protected] ">"
87 [email protected] " "
88 [email protected]
89 [email protected] "&"
90 [email protected] "\'a"
91 [email protected] " "
92 [email protected]
93 [email protected]
94 [email protected]
95 [email protected]
96 [email protected] "F"
97 [email protected] ":"
98 [email protected] " "
99 [email protected]
100 [email protected]
101 [email protected]
102 [email protected]
103 [email protected]
104 [email protected]
105 [email protected] "Debug"
106 [email protected] ","
107 [email protected] "\n"
108 [email protected]
109 [email protected] "{"
110 [email protected] "\n"
111 [email protected] "}"
112 [email protected] "\n"
113 [email protected]
114 [email protected] "fn"
115 [email protected] " "
116 [email protected]
117 [email protected] "for_parens"
118 [email protected]
119 [email protected] "<"
120 [email protected]
121 [email protected]
122 [email protected] "F"
123 [email protected] ">"
124 [email protected]
125 [email protected] "("
126 [email protected] ")"
127 [email protected] "\n"
128 [email protected]
129 [email protected] "where"
130 [email protected] "\n "
131 [email protected]
132 [email protected] "for"
133 [email protected]
134 [email protected] "<"
135 [email protected]
136 [email protected] "\'a"
137 [email protected] ">"
138 [email protected] " "
139 [email protected]
140 [email protected] "("
141 [email protected]
142 [email protected] "&"
143 [email protected] "\'a"
144 [email protected] " "
145 [email protected]
146 [email protected]
147 [email protected]
148 [email protected]
149 [email protected] "F"
150 [email protected] ")"
151 [email protected] ":"
152 [email protected] " "
153 [email protected]
154 [email protected]
155 [email protected]
156 [email protected]
157 [email protected]
158 [email protected]
159 [email protected] "Fn"
160 [email protected]
161 [email protected] "("
162 [email protected]
163 [email protected]
164 [email protected] "&"
165 [email protected] "\'a"
166 [email protected] " "
167 [email protected]
168 [email protected]
169 [email protected]
170 [email protected]
171 [email protected] "str"
172 [email protected] ")"
173 [email protected] ","
174 [email protected] "\n"
175 [email protected]
176 [email protected] "{"
177 [email protected] "\n"
178 [email protected] "}"
179 [email protected] "\n"
180 [email protected]
181 [email protected] "fn"
182 [email protected] " "
183 [email protected]
184 [email protected] "for_slice"
185 [email protected]
186 [email protected] "<"
187 [email protected]
188 [email protected]
189 [email protected] "F"
190 [email protected] ">"
191 [email protected]
192 [email protected] "("
193 [email protected] ")"
194 [email protected] "\n"
195 [email protected]
196 [email protected] "where"
197 [email protected] "\n "
198 [email protected]
199 [email protected] "for"
200 [email protected]
201 [email protected] "<"
202 [email protected]
203 [email protected] "\'a"
204 [email protected] ">"
205 [email protected] " "
206 [email protected]
207 [email protected] "["
208 [email protected]
209 [email protected] "&"
210 [email protected] "\'a"
211 [email protected] " "
212 [email protected]
213 [email protected]
214 [email protected]
215 [email protected]
216 [email protected] "F"
217 [email protected] "]"
218 [email protected] ":"
219 [email protected] " "
220 [email protected]
221 [email protected]
222 [email protected]
223 [email protected]
224 [email protected]
225 [email protected]
226 [email protected] "Eq"
227 [email protected] ","
228 [email protected] "\n"
229 [email protected]
230 [email protected] "{"
231 [email protected] "\n"
232 [email protected] "}"
233 [email protected] "\n"
234 [email protected]
235 [email protected] "fn"
236 [email protected] " "
237 [email protected]
238 [email protected] "for_qpath"
239 [email protected]
240 [email protected] "<"
241 [email protected]
242 [email protected]
243 [email protected] "T"
244 [email protected] ">"
245 [email protected]
246 [email protected] "("
247 [email protected]
248 [email protected]
249 [email protected]
250 [email protected] "_t"
251 [email protected] ":"
252 [email protected] " "
253 [email protected]
254 [email protected] "&"
255 [email protected]
256 [email protected]
257 [email protected]
258 [email protected]
259 [email protected] "T"
260 [email protected] ")"
261 [email protected] "\n"
262 [email protected]
263 [email protected] "where"
264 [email protected] "\n "
265 [email protected]
266 [email protected] "for"
267 [email protected]
268 [email protected] "<"
269 [email protected]
270 [email protected] "\'a"
271 [email protected] ">"
272 [email protected] " "
273 [email protected]
274 [email protected]
275 [email protected]
276 [email protected]
277 [email protected] "<"
278 [email protected]
279 [email protected] "&"
280 [email protected] "\'a"
281 [email protected] " "
282 [email protected]
283 [email protected]
284 [email protected]
285 [email protected]
286 [email protected] "T"
287 [email protected] " "
288 [email protected] "as"
289 [email protected] " "
290 [email protected]
291 [email protected]
292 [email protected]
293 [email protected]
294 [email protected] "Baz"
295 [email protected] ">"
296 [email protected] "::"
297 [email protected]
298 [email protected]
299 [email protected] "Foo"
300 [email protected] ":"
301 [email protected] " "
302 [email protected]
303 [email protected]
304 [email protected]
305 [email protected]
306 [email protected]
307 [email protected]
308 [email protected] "Iterator"
309 [email protected] ","
310 [email protected] "\n"
311 [email protected]
312 [email protected] "{"
313 [email protected] "\n"
314 [email protected] "}"
315 [email protected] "\n"
316 [email protected]
317 [email protected] "fn"
318 [email protected] " "
319 [email protected]
320 [email protected] "for_for_fn"
321 [email protected]
322 [email protected] "<"
323 [email protected]
324 [email protected]
325 [email protected] "T"
326 [email protected] ">"
327 [email protected]
328 [email protected] "("
329 [email protected] ")"
330 [email protected] "\n"
331 [email protected]
332 [email protected] "where"
333 [email protected] "\n "
334 [email protected]
335 [email protected] "for"
336 [email protected]
337 [email protected] "<"
338 [email protected]
339 [email protected] "\'a"
340 [email protected] ">"
341 [email protected] " "
342 [email protected]
343 [email protected] "for"
344 [email protected]
345 [email protected] "<"
346 [email protected]
347 [email protected] "\'b"
348 [email protected] ">"
349 [email protected] " "
350 [email protected]
351 [email protected] "fn"
352 [email protected]
353 [email protected] "("
354 [email protected]
355 [email protected]
356 [email protected] "&"
357 [email protected] "\'a"
358 [email protected] " "
359 [email protected]
360 [email protected]
361 [email protected]
362 [email protected]
363 [email protected] "T"
364 [email protected] ","
365 [email protected] " "
366 [email protected]
367 [email protected]
368 [email protected] "&"
369 [email protected] "\'b"
370 [email protected] " "
371 [email protected]
372 [email protected]
373 [email protected]
374 [email protected]
375 [email protected] "T"
376 [email protected] ")"
377 [email protected] ":"
378 [email protected] " "
379 [email protected]
380 [email protected]
381 [email protected]
382 [email protected]
383 [email protected]
384 [email protected]
385 [email protected] "Copy"
386 [email protected] ","
387 [email protected] "\n"
388 [email protected]
389 [email protected] "{"
390 [email protected] "\n"
391 [email protected] "}"
392 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs
new file mode 100644
index 000000000..9058c4619
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs
@@ -0,0 +1,30 @@
1fn for_trait<F>()
2where
3 for<'a> F: Fn(&'a str),
4{
5}
6fn for_ref<F>()
7where
8 for<'a> &'a F: Debug,
9{
10}
11fn for_parens<F>()
12where
13 for<'a> (&'a F): Fn(&'a str),
14{
15}
16fn for_slice<F>()
17where
18 for<'a> [&'a F]: Eq,
19{
20}
21fn for_qpath<T>(_t: &T)
22where
23 for<'a> <&'a T as Baz>::Foo: Iterator,
24{
25}
26fn for_for_fn<T>()
27where
28 for<'a> for<'b> fn(&'a T, &'b T): Copy,
29{
30}
diff --git a/crates/ra_text_edit/Cargo.toml b/crates/ra_text_edit/Cargo.toml
index 46a2ab68f..dbb223350 100644
--- a/crates/ra_text_edit/Cargo.toml
+++ b/crates/ra_text_edit/Cargo.toml
@@ -4,6 +4,7 @@ name = "ra_text_edit"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6publish = false 6publish = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = false
diff --git a/crates/ra_toolchain/Cargo.toml b/crates/ra_toolchain/Cargo.toml
index 1873fbe16..84b748c0a 100644
--- a/crates/ra_toolchain/Cargo.toml
+++ b/crates/ra_toolchain/Cargo.toml
@@ -3,6 +3,10 @@ edition = "2018"
3name = "ra_toolchain" 3name = "ra_toolchain"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
7
8[lib]
9doctest = false
6 10
7[dependencies] 11[dependencies]
8home = "0.5.3" 12home = "0.5.3"
diff --git a/crates/ra_toolchain/src/lib.rs b/crates/ra_toolchain/src/lib.rs
index 3d2865e09..9916e52c4 100644
--- a/crates/ra_toolchain/src/lib.rs
+++ b/crates/ra_toolchain/src/lib.rs
@@ -15,6 +15,10 @@ pub fn rustup() -> PathBuf {
15 get_path_for_executable("rustup") 15 get_path_for_executable("rustup")
16} 16}
17 17
18pub fn rustfmt() -> PathBuf {
19 get_path_for_executable("rustfmt")
20}
21
18/// Return a `PathBuf` to use for the given executable. 22/// Return a `PathBuf` to use for the given executable.
19/// 23///
20/// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that 24/// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that
@@ -42,22 +46,23 @@ fn get_path_for_executable(executable_name: &'static str) -> PathBuf {
42 path.push(".cargo"); 46 path.push(".cargo");
43 path.push("bin"); 47 path.push("bin");
44 path.push(executable_name); 48 path.push(executable_name);
45 if path.is_file() { 49 if let Some(path) = probe(path) {
46 return path; 50 return path;
47 } 51 }
48 } 52 }
53
49 executable_name.into() 54 executable_name.into()
50} 55}
51 56
52fn lookup_in_path(exec: &str) -> bool { 57fn lookup_in_path(exec: &str) -> bool {
53 let paths = env::var_os("PATH").unwrap_or_default(); 58 let paths = env::var_os("PATH").unwrap_or_default();
54 let mut candidates = env::split_paths(&paths).flat_map(|path| { 59 env::split_paths(&paths).map(|path| path.join(exec)).find_map(probe).is_some()
55 let candidate = path.join(&exec); 60}
56 let with_exe = match env::consts::EXE_EXTENSION { 61
57 "" => None, 62fn probe(path: PathBuf) -> Option<PathBuf> {
58 it => Some(candidate.with_extension(it)), 63 let with_extension = match env::consts::EXE_EXTENSION {
59 }; 64 "" => None,
60 iter::once(candidate).chain(with_exe) 65 it => Some(path.with_extension(it)),
61 }); 66 };
62 candidates.any(|it| it.is_file()) 67 iter::once(path).chain(with_extension).find(|it| it.is_file())
63} 68}
diff --git a/crates/ra_tt/Cargo.toml b/crates/ra_tt/Cargo.toml
index f7230a9ca..3c45248c3 100644
--- a/crates/ra_tt/Cargo.toml
+++ b/crates/ra_tt/Cargo.toml
@@ -3,11 +3,13 @@ edition = "2018"
3name = "ra_tt" 3name = "ra_tt"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
9 10
10[dependencies] 11[dependencies]
12stdx = { path = "../stdx" }
11# ideally, `serde` should be enabled by `rust-analyzer`, but we enable it here 13# ideally, `serde` should be enabled by `rust-analyzer`, but we enable it here
12# to reduce number of compilations 14# to reduce number of compilations
13smol_str = { version = "0.1.15", features = ["serde"] } 15smol_str = { version = "0.1.15", features = ["serde"] }
diff --git a/crates/ra_tt/src/buffer.rs b/crates/ra_tt/src/buffer.rs
index 5967f44cd..02c771f70 100644
--- a/crates/ra_tt/src/buffer.rs
+++ b/crates/ra_tt/src/buffer.rs
@@ -105,10 +105,7 @@ impl<'a> Eq for Cursor<'a> {}
105impl<'a> Cursor<'a> { 105impl<'a> Cursor<'a> {
106 /// Check whether it is eof 106 /// Check whether it is eof
107 pub fn eof(self) -> bool { 107 pub fn eof(self) -> bool {
108 match self.buffer.entry(&self.ptr) { 108 matches!(self.buffer.entry(&self.ptr), None | Some(Entry::End(None)))
109 None | Some(Entry::End(None)) => true,
110 _ => false,
111 }
112 } 109 }
113 110
114 /// If the cursor is pointing at the end of a subtree, returns 111 /// If the cursor is pointing at the end of a subtree, returns
diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs
index 342ddbe32..8faf1cc67 100644
--- a/crates/ra_tt/src/lib.rs
+++ b/crates/ra_tt/src/lib.rs
@@ -1,24 +1,13 @@
1//! `tt` crate defines a `TokenTree` data structure: this is the interface (both 1//! `tt` crate defines a `TokenTree` data structure: this is the interface (both
2//! input and output) of macros. It closely mirrors `proc_macro` crate's 2//! input and output) of macros. It closely mirrors `proc_macro` crate's
3//! `TokenTree`. 3//! `TokenTree`.
4
5macro_rules! impl_froms {
6 ($e:ident: $($v:ident), *) => {
7 $(
8 impl From<$v> for $e {
9 fn from(it: $v) -> $e {
10 $e::$v(it)
11 }
12 }
13 )*
14 }
15}
16
17use std::{ 4use std::{
18 fmt::{self, Debug}, 5 fmt::{self, Debug},
19 panic::RefUnwindSafe, 6 panic::RefUnwindSafe,
20}; 7};
21 8
9use stdx::impl_from;
10
22pub use smol_str::SmolStr; 11pub use smol_str::SmolStr;
23 12
24/// Represents identity of the token. 13/// Represents identity of the token.
@@ -41,7 +30,7 @@ pub enum TokenTree {
41 Leaf(Leaf), 30 Leaf(Leaf),
42 Subtree(Subtree), 31 Subtree(Subtree),
43} 32}
44impl_froms!(TokenTree: Leaf, Subtree); 33impl_from!(Leaf, Subtree for TokenTree);
45 34
46impl TokenTree { 35impl TokenTree {
47 pub fn empty() -> Self { 36 pub fn empty() -> Self {
@@ -55,7 +44,7 @@ pub enum Leaf {
55 Punct(Punct), 44 Punct(Punct),
56 Ident(Ident), 45 Ident(Ident),
57} 46}
58impl_froms!(Leaf: Literal, Punct, Ident); 47impl_from!(Literal, Punct, Ident for Leaf);
59 48
60#[derive(Clone, PartialEq, Eq, Hash, Default)] 49#[derive(Clone, PartialEq, Eq, Hash, Default)]
61pub struct Subtree { 50pub struct Subtree {
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 65b487db3..3ed2a689e 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -4,13 +4,14 @@ name = "rust-analyzer"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6autobins = false 6autobins = false
7license = "MIT OR Apache-2.0"
7 8
8[lib] 9[lib]
9doctest = false 10doctest = false
10 11
11[[bin]] 12[[bin]]
12name = "rust-analyzer" 13name = "rust-analyzer"
13path = "./src/bin/main.rs" 14path = "src/bin/main.rs"
14 15
15[dependencies] 16[dependencies]
16anyhow = "1.0.26" 17anyhow = "1.0.26"
@@ -20,30 +21,34 @@ globset = "0.4.4"
20itertools = "0.9.0" 21itertools = "0.9.0"
21jod-thread = "0.1.0" 22jod-thread = "0.1.0"
22log = "0.4.8" 23log = "0.4.8"
23lsp-types = { version = "0.74.0", features = ["proposed"] } 24lsp-types = { version = "0.77.0", features = ["proposed"] }
24parking_lot = "0.10.0" 25parking_lot = "0.11.0"
25pico-args = "0.3.1" 26pico-args = "0.3.1"
26rand = { version = "0.7.3", features = ["small_rng"] } 27rand = { version = "0.7.3", features = ["small_rng"] }
27relative-path = "1.0.0"
28rustc-hash = "1.1.0" 28rustc-hash = "1.1.0"
29serde = { version = "1.0.106", features = ["derive"] } 29serde = { version = "1.0.106", features = ["derive"] }
30serde_json = "1.0.48" 30serde_json = "1.0.48"
31threadpool = "1.7.1" 31threadpool = "1.7.1"
32rayon = "1.3.1"
32 33
33stdx = { path = "../stdx" } 34stdx = { path = "../stdx" }
34 35
35lsp-server = "0.3.1" 36lsp-server = "0.3.3"
36ra_flycheck = { path = "../ra_flycheck" } 37flycheck = { path = "../flycheck" }
37ra_ide = { path = "../ra_ide" } 38ra_ide = { path = "../ra_ide" }
38ra_prof = { path = "../ra_prof" } 39ra_prof = { path = "../ra_prof" }
39ra_project_model = { path = "../ra_project_model" } 40ra_project_model = { path = "../ra_project_model" }
40ra_syntax = { path = "../ra_syntax" } 41ra_syntax = { path = "../ra_syntax" }
41ra_text_edit = { path = "../ra_text_edit" } 42ra_text_edit = { path = "../ra_text_edit" }
42ra_vfs = "0.6.0" 43vfs = { path = "../vfs" }
44vfs-notify = { path = "../vfs-notify" }
43ra_cfg = { path = "../ra_cfg"} 45ra_cfg = { path = "../ra_cfg"}
46ra_toolchain = { path = "../ra_toolchain" }
44 47
45# This should only be used in CLI 48# This should only be used in CLI
46ra_db = { path = "../ra_db" } 49ra_db = { path = "../ra_db" }
50ra_ide_db = { path = "../ra_ide_db" }
51ra_ssr = { path = "../ra_ssr" }
47hir = { path = "../ra_hir", package = "ra_hir" } 52hir = { path = "../ra_hir", package = "ra_hir" }
48hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } 53hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
49hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" } 54hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" }
@@ -54,10 +59,11 @@ winapi = "0.3.8"
54 59
55[dev-dependencies] 60[dev-dependencies]
56tempfile = "3.1.0" 61tempfile = "3.1.0"
57insta = "0.16.0" 62expect = { path = "../expect" }
58test_utils = { path = "../test_utils" } 63test_utils = { path = "../test_utils" }
59mbe = { path = "../ra_mbe", package = "ra_mbe" } 64mbe = { path = "../ra_mbe", package = "ra_mbe" }
60tt = { path = "../ra_tt", package = "ra_tt" } 65tt = { path = "../ra_tt", package = "ra_tt" }
61 66
62[features] 67[features]
63jemalloc = [ "ra_prof/jemalloc" ] 68jemalloc = [ "ra_prof/jemalloc" ]
69mimalloc = [ "ra_prof/mimalloc" ]
diff --git a/crates/rust-analyzer/build.rs b/crates/rust-analyzer/build.rs
index d4b010c04..5ae76ba30 100644
--- a/crates/rust-analyzer/build.rs
+++ b/crates/rust-analyzer/build.rs
@@ -5,11 +5,14 @@ use std::{env, path::PathBuf, process::Command};
5fn main() { 5fn main() {
6 set_rerun(); 6 set_rerun();
7 7
8 let rev = rev().unwrap_or_else(|| "???????".to_string()); 8 let rev =
9 env::var("RUST_ANALYZER_REV").ok().or_else(rev).unwrap_or_else(|| "???????".to_string());
9 println!("cargo:rustc-env=REV={}", rev) 10 println!("cargo:rustc-env=REV={}", rev)
10} 11}
11 12
12fn set_rerun() { 13fn set_rerun() {
14 println!("cargo:rerun-if-env-changed=RUST_ANALYZER_REV");
15
13 let mut manifest_dir = PathBuf::from( 16 let mut manifest_dir = PathBuf::from(
14 env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is always set by cargo."), 17 env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is always set by cargo."),
15 ); 18 );
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs
index 8e3ca9343..3210416ee 100644
--- a/crates/rust-analyzer/src/bin/args.rs
+++ b/crates/rust-analyzer/src/bin/args.rs
@@ -3,11 +3,13 @@
3//! If run started args, we run the LSP server loop. With a subcommand, we do a 3//! If run started args, we run the LSP server loop. With a subcommand, we do a
4//! one-time batch processing. 4//! one-time batch processing.
5 5
6use std::{env, fmt::Write, path::PathBuf};
7
6use anyhow::{bail, Result}; 8use anyhow::{bail, Result};
7use pico_args::Arguments; 9use pico_args::Arguments;
10use ra_ssr::{SsrPattern, SsrRule};
8use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; 11use rust_analyzer::cli::{BenchWhat, Position, Verbosity};
9 12use vfs::AbsPathBuf;
10use std::{fmt::Write, path::PathBuf};
11 13
12pub(crate) struct Args { 14pub(crate) struct Args {
13 pub(crate) verbosity: Verbosity, 15 pub(crate) verbosity: Verbosity,
@@ -24,6 +26,7 @@ pub(crate) enum Command {
24 }, 26 },
25 Stats { 27 Stats {
26 randomize: bool, 28 randomize: bool,
29 parallel: bool,
27 memory_usage: bool, 30 memory_usage: bool,
28 only: Option<String>, 31 only: Option<String>,
29 with_deps: bool, 32 with_deps: bool,
@@ -32,6 +35,7 @@ pub(crate) enum Command {
32 with_proc_macro: bool, 35 with_proc_macro: bool,
33 }, 36 },
34 Bench { 37 Bench {
38 memory_usage: bool,
35 path: PathBuf, 39 path: PathBuf,
36 what: BenchWhat, 40 what: BenchWhat,
37 load_output_dirs: bool, 41 load_output_dirs: bool,
@@ -45,6 +49,13 @@ pub(crate) enum Command {
45 /// this would include the parser test files. 49 /// this would include the parser test files.
46 all: bool, 50 all: bool,
47 }, 51 },
52 Ssr {
53 rules: Vec<SsrRule>,
54 },
55 StructuredSearch {
56 debug_snippet: Option<String>,
57 patterns: Vec<SsrPattern>,
58 },
48 ProcMacro, 59 ProcMacro,
49 RunServer, 60 RunServer,
50 Version, 61 Version,
@@ -94,7 +105,7 @@ USAGE:
94 rust-analyzer parse [FLAGS] 105 rust-analyzer parse [FLAGS]
95 106
96FLAGS: 107FLAGS:
97 -h, --help Prints help inforamtion 108 -h, --help Prints help information
98 --no-dump" 109 --no-dump"
99 ); 110 );
100 return Ok(Err(HelpPrinted)); 111 return Ok(Err(HelpPrinted));
@@ -153,10 +164,14 @@ USAGE:
153 rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH] 164 rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH]
154 165
155FLAGS: 166FLAGS:
167 -o, --only Only analyze items matching this path
156 -h, --help Prints help information 168 -h, --help Prints help information
157 --memory-usage 169 --memory-usage Collect memory usage statistics (requires `--features jemalloc`)
170 --randomize Randomize order in which crates, modules, and items are processed
171 --parallel Run type inference in parallel
158 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis 172 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
159 --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding 173 --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding
174 --with-deps Also analyze all dependencies
160 -v, --verbose 175 -v, --verbose
161 -q, --quiet 176 -q, --quiet
162 177
@@ -170,6 +185,7 @@ ARGS:
170 } 185 }
171 186
172 let randomize = matches.contains("--randomize"); 187 let randomize = matches.contains("--randomize");
188 let parallel = matches.contains("--parallel");
173 let memory_usage = matches.contains("--memory-usage"); 189 let memory_usage = matches.contains("--memory-usage");
174 let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?; 190 let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
175 let with_deps: bool = matches.contains("--with-deps"); 191 let with_deps: bool = matches.contains("--with-deps");
@@ -185,6 +201,7 @@ ARGS:
185 201
186 Command::Stats { 202 Command::Stats {
187 randomize, 203 randomize,
204 parallel,
188 memory_usage, 205 memory_usage,
189 only, 206 only,
190 with_deps, 207 with_deps,
@@ -204,8 +221,9 @@ USAGE:
204 221
205FLAGS: 222FLAGS:
206 -h, --help Prints help information 223 -h, --help Prints help information
224 --memory-usage Collect memory usage statistics (requires `--features jemalloc`)
207 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis 225 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
208 --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding 226 --with-proc-macro Use ra-proc-macro-srv for proc-macro expanding
209 -v, --verbose 227 -v, --verbose
210 228
211OPTIONS: 229OPTIONS:
@@ -225,16 +243,20 @@ ARGS:
225 let complete_path: Option<Position> = matches.opt_value_from_str("--complete")?; 243 let complete_path: Option<Position> = matches.opt_value_from_str("--complete")?;
226 let goto_def_path: Option<Position> = matches.opt_value_from_str("--goto-def")?; 244 let goto_def_path: Option<Position> = matches.opt_value_from_str("--goto-def")?;
227 let what = match (highlight_path, complete_path, goto_def_path) { 245 let what = match (highlight_path, complete_path, goto_def_path) {
228 (Some(path), None, None) => BenchWhat::Highlight { path: path.into() }, 246 (Some(path), None, None) => {
247 let path = env::current_dir().unwrap().join(path);
248 BenchWhat::Highlight { path: AbsPathBuf::assert(path) }
249 }
229 (None, Some(position), None) => BenchWhat::Complete(position), 250 (None, Some(position), None) => BenchWhat::Complete(position),
230 (None, None, Some(position)) => BenchWhat::GotoDef(position), 251 (None, None, Some(position)) => BenchWhat::GotoDef(position),
231 _ => panic!( 252 _ => panic!(
232 "exactly one of `--highlight`, `--complete` or `--goto-def` must be set" 253 "exactly one of `--highlight`, `--complete` or `--goto-def` must be set"
233 ), 254 ),
234 }; 255 };
256 let memory_usage = matches.contains("--memory-usage");
235 let load_output_dirs = matches.contains("--load-output-dirs"); 257 let load_output_dirs = matches.contains("--load-output-dirs");
236 let with_proc_macro = matches.contains("--with-proc-macro"); 258 let with_proc_macro = matches.contains("--with-proc-macro");
237 Command::Bench { path, what, load_output_dirs, with_proc_macro } 259 Command::Bench { memory_usage, path, what, load_output_dirs, with_proc_macro }
238 } 260 }
239 "diagnostics" => { 261 "diagnostics" => {
240 if matches.contains(["-h", "--help"]) { 262 if matches.contains(["-h", "--help"]) {
@@ -270,6 +292,61 @@ ARGS:
270 Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } 292 Command::Diagnostics { path, load_output_dirs, with_proc_macro, all }
271 } 293 }
272 "proc-macro" => Command::ProcMacro, 294 "proc-macro" => Command::ProcMacro,
295 "ssr" => {
296 if matches.contains(["-h", "--help"]) {
297 eprintln!(
298 "\
299rust-analyzer ssr
300
301USAGE:
302 rust-analyzer ssr [FLAGS] [RULE...]
303
304EXAMPLE:
305 rust-analyzer ssr '$a.foo($b) ==> bar($a, $b)'
306
307FLAGS:
308 --debug <snippet> Prints debug information for any nodes with source exactly equal to <snippet>
309 -h, --help Prints help information
310
311ARGS:
312 <RULE> A structured search replace rule"
313 );
314 return Ok(Err(HelpPrinted));
315 }
316 let mut rules = Vec::new();
317 while let Some(rule) = matches.free_from_str()? {
318 rules.push(rule);
319 }
320 Command::Ssr { rules }
321 }
322 "search" => {
323 if matches.contains(["-h", "--help"]) {
324 eprintln!(
325 "\
326rust-analyzer search
327
328USAGE:
329 rust-analyzer search [FLAGS] [PATTERN...]
330
331EXAMPLE:
332 rust-analyzer search '$a.foo($b)'
333
334FLAGS:
335 --debug <snippet> Prints debug information for any nodes with source exactly equal to <snippet>
336 -h, --help Prints help information
337
338ARGS:
339 <PATTERN> A structured search pattern"
340 );
341 return Ok(Err(HelpPrinted));
342 }
343 let debug_snippet = matches.opt_value_from_str("--debug")?;
344 let mut patterns = Vec::new();
345 while let Some(rule) = matches.free_from_str()? {
346 patterns.push(rule);
347 }
348 Command::StructuredSearch { patterns, debug_snippet }
349 }
273 _ => { 350 _ => {
274 print_subcommands(); 351 print_subcommands();
275 return Ok(Err(HelpPrinted)); 352 return Ok(Err(HelpPrinted));
@@ -297,6 +374,8 @@ SUBCOMMANDS:
297 diagnostics 374 diagnostics
298 proc-macro 375 proc-macro
299 parse 376 parse
377 search
378 ssr
300 symbols" 379 symbols"
301 ) 380 )
302} 381}
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index e82fd57de..408892eab 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -3,8 +3,16 @@
3//! Based on cli flags, either spawns an LSP server, or runs a batch analysis 3//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
4mod args; 4mod args;
5 5
6use std::convert::TryFrom;
7
6use lsp_server::Connection; 8use lsp_server::Connection;
7use rust_analyzer::{cli, config::Config, from_json, Result}; 9use ra_project_model::ProjectManifest;
10use rust_analyzer::{
11 cli,
12 config::{Config, LinkedProject},
13 from_json, Result,
14};
15use vfs::AbsPathBuf;
8 16
9use crate::args::HelpPrinted; 17use crate::args::HelpPrinted;
10 18
@@ -15,11 +23,15 @@ fn main() -> Result<()> {
15 Err(HelpPrinted) => return Ok(()), 23 Err(HelpPrinted) => return Ok(()),
16 }; 24 };
17 match args.command { 25 match args.command {
26 args::Command::RunServer => run_server()?,
27 args::Command::ProcMacro => ra_proc_macro_srv::cli::run()?,
28
18 args::Command::Parse { no_dump } => cli::parse(no_dump)?, 29 args::Command::Parse { no_dump } => cli::parse(no_dump)?,
19 args::Command::Symbols => cli::symbols()?, 30 args::Command::Symbols => cli::symbols()?,
20 args::Command::Highlight { rainbow } => cli::highlight(rainbow)?, 31 args::Command::Highlight { rainbow } => cli::highlight(rainbow)?,
21 args::Command::Stats { 32 args::Command::Stats {
22 randomize, 33 randomize,
34 parallel,
23 memory_usage, 35 memory_usage,
24 only, 36 only,
25 with_deps, 37 with_deps,
@@ -33,26 +45,29 @@ fn main() -> Result<()> {
33 only.as_ref().map(String::as_ref), 45 only.as_ref().map(String::as_ref),
34 with_deps, 46 with_deps,
35 randomize, 47 randomize,
48 parallel,
36 load_output_dirs, 49 load_output_dirs,
37 with_proc_macro, 50 with_proc_macro,
38 )?, 51 )?,
39 52 args::Command::Bench { memory_usage, path, what, load_output_dirs, with_proc_macro } => {
40 args::Command::Bench { path, what, load_output_dirs, with_proc_macro } => {
41 cli::analysis_bench( 53 cli::analysis_bench(
42 args.verbosity, 54 args.verbosity,
43 path.as_ref(), 55 path.as_ref(),
44 what, 56 what,
57 memory_usage,
45 load_output_dirs, 58 load_output_dirs,
46 with_proc_macro, 59 with_proc_macro,
47 )? 60 )?
48 } 61 }
49
50 args::Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } => { 62 args::Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } => {
51 cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)? 63 cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)?
52 } 64 }
53 65 args::Command::Ssr { rules } => {
54 args::Command::ProcMacro => run_proc_macro_srv()?, 66 cli::apply_ssr_rules(rules)?;
55 args::Command::RunServer => run_server()?, 67 }
68 args::Command::StructuredSearch { patterns, debug_snippet } => {
69 cli::search_for_patterns(patterns, debug_snippet)?;
70 }
56 args::Command::Version => println!("rust-analyzer {}", env!("REV")), 71 args::Command::Version => println!("rust-analyzer {}", env!("REV")),
57 } 72 }
58 Ok(()) 73 Ok(())
@@ -65,11 +80,6 @@ fn setup_logging() -> Result<()> {
65 Ok(()) 80 Ok(())
66} 81}
67 82
68fn run_proc_macro_srv() -> Result<()> {
69 ra_proc_macro_srv::cli::run()?;
70 Ok(())
71}
72
73fn run_server() -> Result<()> { 83fn run_server() -> Result<()> {
74 log::info!("lifecycle: server started"); 84 log::info!("lifecycle: server started");
75 85
@@ -97,28 +107,48 @@ fn run_server() -> Result<()> {
97 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); 107 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
98 } 108 }
99 109
100 let cwd = std::env::current_dir()?;
101 let root = initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
102
103 let workspace_roots = initialize_params
104 .workspace_folders
105 .map(|workspaces| {
106 workspaces.into_iter().filter_map(|it| it.uri.to_file_path().ok()).collect::<Vec<_>>()
107 })
108 .filter(|workspaces| !workspaces.is_empty())
109 .unwrap_or_else(|| vec![root]);
110
111 let config = { 110 let config = {
112 let mut config = Config::default(); 111 let root_path = match initialize_params
113 if let Some(value) = &initialize_params.initialization_options { 112 .root_uri
114 config.update(value); 113 .and_then(|it| it.to_file_path().ok())
114 .and_then(|it| AbsPathBuf::try_from(it).ok())
115 {
116 Some(it) => it,
117 None => {
118 let cwd = std::env::current_dir()?;
119 AbsPathBuf::assert(cwd)
120 }
121 };
122
123 let mut config = Config::new(root_path);
124 if let Some(json) = initialize_params.initialization_options {
125 config.update(json);
115 } 126 }
116 config.update_caps(&initialize_params.capabilities); 127 config.update_caps(&initialize_params.capabilities);
117 128
129 if config.linked_projects.is_empty() {
130 let workspace_roots = initialize_params
131 .workspace_folders
132 .map(|workspaces| {
133 workspaces
134 .into_iter()
135 .filter_map(|it| it.uri.to_file_path().ok())
136 .filter_map(|it| AbsPathBuf::try_from(it).ok())
137 .collect::<Vec<_>>()
138 })
139 .filter(|workspaces| !workspaces.is_empty())
140 .unwrap_or_else(|| vec![config.root_path.clone()]);
141
142 config.linked_projects = ProjectManifest::discover_all(&workspace_roots)
143 .into_iter()
144 .map(LinkedProject::from)
145 .collect();
146 }
147
118 config 148 config
119 }; 149 };
120 150
121 rust_analyzer::main_loop(workspace_roots, config, connection)?; 151 rust_analyzer::main_loop(config, connection)?;
122 152
123 log::info!("shutting down IO..."); 153 log::info!("shutting down IO...");
124 io_threads.join()?; 154 io_threads.join()?;
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 345693524..37d695448 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -2,9 +2,9 @@
2use std::env; 2use std::env;
3 3
4use lsp_types::{ 4use lsp_types::{
5 CallHierarchyServerCapability, ClientCapabilities, CodeActionOptions, 5 CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
6 CodeActionProviderCapability, CodeLensOptions, CompletionOptions, 6 CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
7 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, 7 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, HoverProviderCapability,
8 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, 8 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
9 SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend, 9 SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend,
10 SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, 10 SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
@@ -28,9 +28,9 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
28 }), 28 }),
29 will_save: None, 29 will_save: None,
30 will_save_wait_until: None, 30 will_save_wait_until: None,
31 save: Some(SaveOptions::default()), 31 save: Some(SaveOptions::default().into()),
32 })), 32 })),
33 hover_provider: Some(true), 33 hover_provider: Some(HoverProviderCapability::Simple(true)),
34 completion_provider: Some(CompletionOptions { 34 completion_provider: Some(CompletionOptions {
35 resolve_provider: None, 35 resolve_provider: None,
36 trigger_characters: Some(vec![":".to_string(), ".".to_string()]), 36 trigger_characters: Some(vec![":".to_string(), ".".to_string()]),
@@ -87,6 +87,9 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
87 "ssr": true, 87 "ssr": true,
88 "onEnter": true, 88 "onEnter": true,
89 "parentModule": true, 89 "parentModule": true,
90 "runnables": {
91 "kinds": [ "cargo" ],
92 },
90 })), 93 })),
91 } 94 }
92} 95}
@@ -103,14 +106,12 @@ fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProvi
103 // Ideally we would base this off of the client capabilities 106 // Ideally we would base this off of the client capabilities
104 // but the client is supposed to fall back gracefully for unknown values. 107 // but the client is supposed to fall back gracefully for unknown values.
105 code_action_kinds: Some(vec![ 108 code_action_kinds: Some(vec![
106 lsp_types::code_action_kind::EMPTY.to_string(), 109 CodeActionKind::EMPTY,
107 lsp_types::code_action_kind::QUICKFIX.to_string(), 110 CodeActionKind::QUICKFIX,
108 lsp_types::code_action_kind::REFACTOR.to_string(), 111 CodeActionKind::REFACTOR,
109 lsp_types::code_action_kind::REFACTOR_EXTRACT.to_string(), 112 CodeActionKind::REFACTOR_EXTRACT,
110 lsp_types::code_action_kind::REFACTOR_INLINE.to_string(), 113 CodeActionKind::REFACTOR_INLINE,
111 lsp_types::code_action_kind::REFACTOR_REWRITE.to_string(), 114 CodeActionKind::REFACTOR_REWRITE,
112 lsp_types::code_action_kind::SOURCE.to_string(),
113 lsp_types::code_action_kind::SOURCE_ORGANIZE_IMPORTS.to_string(),
114 ]), 115 ]),
115 work_done_progress_options: Default::default(), 116 work_done_progress_options: Default::default(),
116 }) 117 })
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 441fb61df..318399624 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -1,10 +1,11 @@
1//! See `CargoTargetSpec` 1//! See `CargoTargetSpec`
2 2
3use ra_cfg::CfgExpr;
3use ra_ide::{FileId, RunnableKind, TestId}; 4use ra_ide::{FileId, RunnableKind, TestId};
4use ra_project_model::{self, ProjectWorkspace, TargetKind}; 5use ra_project_model::{self, TargetKind};
6use vfs::AbsPathBuf;
5 7
6use crate::{world::WorldSnapshot, Result}; 8use crate::{global_state::GlobalStateSnapshot, Result};
7use ra_syntax::SmolStr;
8 9
9/// Abstract representation of Cargo target. 10/// Abstract representation of Cargo target.
10/// 11///
@@ -12,6 +13,7 @@ use ra_syntax::SmolStr;
12/// build/test/run the target. 13/// build/test/run the target.
13#[derive(Clone)] 14#[derive(Clone)]
14pub(crate) struct CargoTargetSpec { 15pub(crate) struct CargoTargetSpec {
16 pub(crate) workspace_root: AbsPathBuf,
15 pub(crate) package: String, 17 pub(crate) package: String,
16 pub(crate) target: String, 18 pub(crate) target: String,
17 pub(crate) target_kind: TargetKind, 19 pub(crate) target_kind: TargetKind,
@@ -19,9 +21,10 @@ pub(crate) struct CargoTargetSpec {
19 21
20impl CargoTargetSpec { 22impl CargoTargetSpec {
21 pub(crate) fn runnable_args( 23 pub(crate) fn runnable_args(
24 snap: &GlobalStateSnapshot,
22 spec: Option<CargoTargetSpec>, 25 spec: Option<CargoTargetSpec>,
23 kind: &RunnableKind, 26 kind: &RunnableKind,
24 features_needed: &Vec<SmolStr>, 27 cfgs: &[CfgExpr],
25 ) -> Result<(Vec<String>, Vec<String>)> { 28 ) -> Result<(Vec<String>, Vec<String>)> {
26 let mut args = Vec::new(); 29 let mut args = Vec::new();
27 let mut extra_args = Vec::new(); 30 let mut extra_args = Vec::new();
@@ -76,36 +79,45 @@ impl CargoTargetSpec {
76 } 79 }
77 } 80 }
78 81
79 features_needed.iter().for_each(|feature| { 82 if snap.config.cargo.all_features {
80 args.push("--features".to_string()); 83 args.push("--all-features".to_string());
81 args.push(feature.to_string()); 84 } else {
82 }); 85 let mut features = Vec::new();
86 for cfg in cfgs {
87 required_features(cfg, &mut features);
88 }
89 for feature in &snap.config.cargo.features {
90 features.push(feature.clone());
91 }
92 features.dedup();
93 for feature in features {
94 args.push("--features".to_string());
95 args.push(feature);
96 }
97 }
83 98
84 Ok((args, extra_args)) 99 Ok((args, extra_args))
85 } 100 }
86 101
87 pub(crate) fn for_file( 102 pub(crate) fn for_file(
88 world: &WorldSnapshot, 103 global_state_snapshot: &GlobalStateSnapshot,
89 file_id: FileId, 104 file_id: FileId,
90 ) -> Result<Option<CargoTargetSpec>> { 105 ) -> Result<Option<CargoTargetSpec>> {
91 let &crate_id = match world.analysis().crate_for(file_id)?.first() { 106 let crate_id = match global_state_snapshot.analysis.crate_for(file_id)?.first() {
92 Some(crate_id) => crate_id, 107 Some(crate_id) => *crate_id,
93 None => return Ok(None), 108 None => return Ok(None),
94 }; 109 };
95 let file_id = world.analysis().crate_root(crate_id)?; 110 let (cargo_ws, target) = match global_state_snapshot.cargo_target_for_crate_root(crate_id) {
96 let path = world.file_id_to_path(file_id); 111 Some(it) => it,
97 let res = world.workspaces.iter().find_map(|ws| match ws { 112 None => return Ok(None),
98 ProjectWorkspace::Cargo { cargo, .. } => { 113 };
99 let tgt = cargo.target_by_root(&path)?; 114 let res = CargoTargetSpec {
100 Some(CargoTargetSpec { 115 workspace_root: cargo_ws.workspace_root().to_path_buf(),
101 package: cargo.package_flag(&cargo[cargo[tgt].package]), 116 package: cargo_ws.package_flag(&cargo_ws[cargo_ws[target].package]),
102 target: cargo[tgt].name.clone(), 117 target: cargo_ws[target].name.clone(),
103 target_kind: cargo[tgt].kind, 118 target_kind: cargo_ws[target].kind,
104 }) 119 };
105 } 120 Ok(Some(res))
106 ProjectWorkspace::Json { .. } => None,
107 });
108 Ok(res)
109 } 121 }
110 122
111 pub(crate) fn push_to(self, buf: &mut Vec<String>, kind: &RunnableKind) { 123 pub(crate) fn push_to(self, buf: &mut Vec<String>, kind: &RunnableKind) {
@@ -140,3 +152,74 @@ impl CargoTargetSpec {
140 } 152 }
141 } 153 }
142} 154}
155
156/// Fill minimal features needed
157fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) {
158 match cfg_expr {
159 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.to_string()),
160 CfgExpr::All(preds) => {
161 preds.iter().for_each(|cfg| required_features(cfg, features));
162 }
163 CfgExpr::Any(preds) => {
164 for cfg in preds {
165 let len_features = features.len();
166 required_features(cfg, features);
167 if len_features != features.len() {
168 break;
169 }
170 }
171 }
172 _ => {}
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 use mbe::{ast_to_token_tree, TokenMap};
181 use ra_cfg::parse_cfg;
182 use ra_syntax::{
183 ast::{self, AstNode},
184 SmolStr,
185 };
186
187 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
188 let source_file = ast::SourceFile::parse(input).ok().unwrap();
189 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
190 ast_to_token_tree(&tt).unwrap()
191 }
192
193 #[test]
194 fn test_cfg_expr_minimal_features_needed() {
195 let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
196 let cfg_expr = parse_cfg(&subtree);
197 let mut min_features = vec![];
198 required_features(&cfg_expr, &mut min_features);
199
200 assert_eq!(min_features, vec![SmolStr::new("baz")]);
201
202 let (subtree, _) =
203 get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
204 let cfg_expr = parse_cfg(&subtree);
205
206 let mut min_features = vec![];
207 required_features(&cfg_expr, &mut min_features);
208 assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
209
210 let (subtree, _) =
211 get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
212 let cfg_expr = parse_cfg(&subtree);
213
214 let mut min_features = vec![];
215 required_features(&cfg_expr, &mut min_features);
216 assert_eq!(min_features, vec![SmolStr::new("baz")]);
217
218 let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
219 let cfg_expr = parse_cfg(&subtree);
220
221 let mut min_features = vec![];
222 required_features(&cfg_expr, &mut min_features);
223 assert!(min_features.is_empty());
224 }
225}
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs
index 39ce77900..6863f100b 100644
--- a/crates/rust-analyzer/src/cli.rs
+++ b/crates/rust-analyzer/src/cli.rs
@@ -5,6 +5,7 @@ mod analysis_stats;
5mod analysis_bench; 5mod analysis_bench;
6mod diagnostics; 6mod diagnostics;
7mod progress_report; 7mod progress_report;
8mod ssr;
8 9
9use std::io::Read; 10use std::io::Read;
10 11
@@ -17,6 +18,7 @@ pub use analysis_bench::{analysis_bench, BenchWhat, Position};
17pub use analysis_stats::analysis_stats; 18pub use analysis_stats::analysis_stats;
18pub use diagnostics::diagnostics; 19pub use diagnostics::diagnostics;
19pub use load_cargo::load_cargo; 20pub use load_cargo::load_cargo;
21pub use ssr::{apply_ssr_rules, search_for_patterns};
20 22
21#[derive(Clone, Copy)] 23#[derive(Clone, Copy)]
22pub enum Verbosity { 24pub enum Verbosity {
@@ -28,16 +30,10 @@ pub enum Verbosity {
28 30
29impl Verbosity { 31impl Verbosity {
30 pub fn is_verbose(self) -> bool { 32 pub fn is_verbose(self) -> bool {
31 match self { 33 matches!(self, Verbosity::Verbose | Verbosity::Spammy)
32 Verbosity::Verbose | Verbosity::Spammy => true,
33 _ => false,
34 }
35 } 34 }
36 pub fn is_spammy(self) -> bool { 35 pub fn is_spammy(self) -> bool {
37 match self { 36 matches!(self, Verbosity::Spammy)
38 Verbosity::Spammy => true,
39 _ => false,
40 }
41 } 37 }
42} 38}
43 39
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index b20efe98d..9299879b7 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -1,29 +1,28 @@
1//! Benchmark operations like highlighting or goto definition. 1//! Benchmark operations like highlighting or goto definition.
2 2
3use std::{ 3use std::{env, path::Path, str::FromStr, sync::Arc, time::Instant};
4 path::{Path, PathBuf},
5 str::FromStr,
6 sync::Arc,
7 time::Instant,
8};
9 4
10use anyhow::{format_err, Result}; 5use anyhow::{format_err, Result};
11use ra_db::{ 6use ra_db::{
12 salsa::{Database, Durability}, 7 salsa::{Database, Durability},
13 FileId, SourceDatabaseExt, 8 FileId,
14}; 9};
15use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionConfig, FilePosition, LineCol}; 10use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionConfig, FilePosition, LineCol};
11use vfs::AbsPathBuf;
16 12
17use crate::cli::{load_cargo::load_cargo, Verbosity}; 13use crate::{
14 cli::{load_cargo::load_cargo, Verbosity},
15 print_memory_usage,
16};
18 17
19pub enum BenchWhat { 18pub enum BenchWhat {
20 Highlight { path: PathBuf }, 19 Highlight { path: AbsPathBuf },
21 Complete(Position), 20 Complete(Position),
22 GotoDef(Position), 21 GotoDef(Position),
23} 22}
24 23
25pub struct Position { 24pub struct Position {
26 pub path: PathBuf, 25 pub path: AbsPathBuf,
27 pub line: u32, 26 pub line: u32,
28 pub column: u32, 27 pub column: u32,
29} 28}
@@ -33,7 +32,9 @@ impl FromStr for Position {
33 fn from_str(s: &str) -> Result<Self> { 32 fn from_str(s: &str) -> Result<Self> {
34 let (path_line, column) = rsplit_at_char(s, ':')?; 33 let (path_line, column) = rsplit_at_char(s, ':')?;
35 let (path, line) = rsplit_at_char(path_line, ':')?; 34 let (path, line) = rsplit_at_char(path_line, ':')?;
36 Ok(Position { path: path.into(), line: line.parse()?, column: column.parse()? }) 35 let path = env::current_dir().unwrap().join(path);
36 let path = AbsPathBuf::assert(path);
37 Ok(Position { path, line: line.parse()?, column: column.parse()? })
37 } 38 }
38} 39}
39 40
@@ -46,6 +47,7 @@ pub fn analysis_bench(
46 verbosity: Verbosity, 47 verbosity: Verbosity,
47 path: &Path, 48 path: &Path,
48 what: BenchWhat, 49 what: BenchWhat,
50 memory_usage: bool,
49 load_output_dirs: bool, 51 load_output_dirs: bool,
50 with_proc_macro: bool, 52 with_proc_macro: bool,
51) -> Result<()> { 53) -> Result<()> {
@@ -53,8 +55,7 @@ pub fn analysis_bench(
53 55
54 let start = Instant::now(); 56 let start = Instant::now();
55 eprint!("loading: "); 57 eprint!("loading: ");
56 let (mut host, roots) = load_cargo(path, load_output_dirs, with_proc_macro)?; 58 let (mut host, vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?;
57 let db = host.raw_database();
58 eprintln!("{:?}\n", start.elapsed()); 59 eprintln!("{:?}\n", start.elapsed());
59 60
60 let file_id = { 61 let file_id = {
@@ -62,22 +63,8 @@ pub fn analysis_bench(
62 BenchWhat::Highlight { path } => path, 63 BenchWhat::Highlight { path } => path,
63 BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => &pos.path, 64 BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => &pos.path,
64 }; 65 };
65 let path = std::env::current_dir()?.join(path).canonicalize()?; 66 let path = path.clone().into();
66 roots 67 vfs.file_id(&path).ok_or_else(|| format_err!("Can't find {}", path))?
67 .iter()
68 .find_map(|(source_root_id, project_root)| {
69 if project_root.is_member() {
70 for file_id in db.source_root(*source_root_id).walk() {
71 let rel_path = db.file_relative_path(file_id);
72 let abs_path = rel_path.to_path(project_root.path());
73 if abs_path == path {
74 return Some(file_id);
75 }
76 }
77 }
78 None
79 })
80 .ok_or_else(|| format_err!("Can't find {}", path.display()))?
81 }; 68 };
82 69
83 match &what { 70 match &what {
@@ -91,10 +78,7 @@ pub fn analysis_bench(
91 } 78 }
92 } 79 }
93 BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => { 80 BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => {
94 let is_completion = match what { 81 let is_completion = matches!(what, BenchWhat::Complete(..));
95 BenchWhat::Complete(..) => true,
96 _ => false,
97 };
98 82
99 let offset = host 83 let offset = host
100 .analysis() 84 .analysis()
@@ -119,6 +103,11 @@ pub fn analysis_bench(
119 } 103 }
120 } 104 }
121 } 105 }
106
107 if memory_usage {
108 print_memory_usage(host, vfs);
109 }
110
122 Ok(()) 111 Ok(())
123} 112}
124 113
@@ -149,7 +138,20 @@ fn do_work<F: Fn(&Analysis) -> T, T>(host: &mut AnalysisHost, file_id: FileId, w
149 let mut text = host.analysis().file_text(file_id).unwrap().to_string(); 138 let mut text = host.analysis().file_text(file_id).unwrap().to_string();
150 text.push_str("\n/* Hello world */\n"); 139 text.push_str("\n/* Hello world */\n");
151 let mut change = AnalysisChange::new(); 140 let mut change = AnalysisChange::new();
152 change.change_file(file_id, Arc::new(text)); 141 change.change_file(file_id, Some(Arc::new(text)));
142 host.apply_change(change);
143 }
144 work(&host.analysis());
145 eprintln!("{:?}", start.elapsed());
146 }
147 {
148 let start = Instant::now();
149 eprint!("item change: ");
150 {
151 let mut text = host.analysis().file_text(file_id).unwrap().to_string();
152 text.push_str("\npub fn _dummy() {}\n");
153 let mut change = AnalysisChange::new();
154 change.change_file(file_id, Some(Arc::new(text)));
153 host.apply_change(change); 155 host.apply_change(change);
154 } 156 }
155 work(&host.analysis()); 157 work(&host.analysis());
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 72183da15..ddb3db6c3 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -1,7 +1,12 @@
1//! Fully type-check project and print various stats, like the number of type 1//! Fully type-check project and print various stats, like the number of type
2//! errors. 2//! errors.
3 3
4use std::{collections::HashSet, path::Path, time::Instant}; 4use std::{path::Path, time::Instant};
5
6use itertools::Itertools;
7use rand::{seq::SliceRandom, thread_rng};
8use rayon::prelude::*;
9use rustc_hash::FxHashSet;
5 10
6use hir::{ 11use hir::{
7 db::{AstDatabase, DefDatabase, HirDatabase}, 12 db::{AstDatabase, DefDatabase, HirDatabase},
@@ -9,13 +14,25 @@ use hir::{
9}; 14};
10use hir_def::FunctionId; 15use hir_def::FunctionId;
11use hir_ty::{Ty, TypeWalk}; 16use hir_ty::{Ty, TypeWalk};
12use itertools::Itertools; 17use ra_db::{
13use ra_db::SourceDatabaseExt; 18 salsa::{self, ParallelDatabase},
19 SourceDatabaseExt,
20};
14use ra_syntax::AstNode; 21use ra_syntax::AstNode;
15use rand::{seq::SliceRandom, thread_rng};
16use stdx::format_to; 22use stdx::format_to;
17 23
18use crate::cli::{load_cargo::load_cargo, progress_report::ProgressReport, Result, Verbosity}; 24use crate::{
25 cli::{load_cargo::load_cargo, progress_report::ProgressReport, Result, Verbosity},
26 print_memory_usage,
27};
28
29/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
30struct Snap<DB>(DB);
31impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> {
32 fn clone(&self) -> Snap<salsa::Snapshot<DB>> {
33 Snap(self.0.snapshot())
34 }
35}
19 36
20pub fn analysis_stats( 37pub fn analysis_stats(
21 verbosity: Verbosity, 38 verbosity: Verbosity,
@@ -24,30 +41,19 @@ pub fn analysis_stats(
24 only: Option<&str>, 41 only: Option<&str>,
25 with_deps: bool, 42 with_deps: bool,
26 randomize: bool, 43 randomize: bool,
44 parallel: bool,
27 load_output_dirs: bool, 45 load_output_dirs: bool,
28 with_proc_macro: bool, 46 with_proc_macro: bool,
29) -> Result<()> { 47) -> Result<()> {
30 let db_load_time = Instant::now(); 48 let db_load_time = Instant::now();
31 let (mut host, roots) = load_cargo(path, load_output_dirs, with_proc_macro)?; 49 let (host, vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?;
32 let db = host.raw_database(); 50 let db = host.raw_database();
33 println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed()); 51 println!("Database loaded {:?}", db_load_time.elapsed());
34 let analysis_time = Instant::now(); 52 let analysis_time = Instant::now();
35 let mut num_crates = 0; 53 let mut num_crates = 0;
36 let mut visited_modules = HashSet::new(); 54 let mut visited_modules = FxHashSet::default();
37 let mut visit_queue = Vec::new(); 55 let mut visit_queue = Vec::new();
38 56
39 let members =
40 roots
41 .into_iter()
42 .filter_map(|(source_root_id, project_root)| {
43 if with_deps || project_root.is_member() {
44 Some(source_root_id)
45 } else {
46 None
47 }
48 })
49 .collect::<HashSet<_>>();
50
51 let mut krates = Crate::all(db); 57 let mut krates = Crate::all(db);
52 if randomize { 58 if randomize {
53 krates.shuffle(&mut thread_rng()); 59 krates.shuffle(&mut thread_rng());
@@ -55,7 +61,10 @@ pub fn analysis_stats(
55 for krate in krates { 61 for krate in krates {
56 let module = krate.root_module(db).expect("crate without root module"); 62 let module = krate.root_module(db).expect("crate without root module");
57 let file_id = module.definition_source(db).file_id; 63 let file_id = module.definition_source(db).file_id;
58 if members.contains(&db.file_source_root(file_id.original_file(db))) { 64 let file_id = file_id.original_file(db);
65 let source_root = db.file_source_root(file_id);
66 let source_root = db.source_root(source_root);
67 if !source_root.is_library || with_deps {
59 num_crates += 1; 68 num_crates += 1;
60 visit_queue.push(module); 69 visit_queue.push(module);
61 } 70 }
@@ -98,12 +107,26 @@ pub fn analysis_stats(
98 funcs.shuffle(&mut thread_rng()); 107 funcs.shuffle(&mut thread_rng());
99 } 108 }
100 109
101 let inference_time = Instant::now();
102 let mut bar = match verbosity { 110 let mut bar = match verbosity {
103 Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), 111 Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(),
104 _ => ProgressReport::new(funcs.len() as u64), 112 _ => ProgressReport::new(funcs.len() as u64),
105 }; 113 };
106 114
115 if parallel {
116 let inference_time = Instant::now();
117 let snap = Snap(db.snapshot());
118 funcs
119 .par_iter()
120 .map_with(snap, |snap, &f| {
121 let f_id = FunctionId::from(f);
122 snap.0.body(f_id.into());
123 snap.0.infer(f_id.into());
124 })
125 .count();
126 println!("Parallel Inference: {:?}, {}", inference_time.elapsed(), ra_prof::memory_usage());
127 }
128
129 let inference_time = Instant::now();
107 bar.tick(); 130 bar.tick();
108 let mut num_exprs = 0; 131 let mut num_exprs = 0;
109 let mut num_exprs_unknown = 0; 132 let mut num_exprs_unknown = 0;
@@ -128,9 +151,9 @@ pub fn analysis_stats(
128 if verbosity.is_verbose() { 151 if verbosity.is_verbose() {
129 let src = f.source(db); 152 let src = f.source(db);
130 let original_file = src.file_id.original_file(db); 153 let original_file = src.file_id.original_file(db);
131 let path = db.file_relative_path(original_file); 154 let path = vfs.file_path(original_file);
132 let syntax_range = src.value.syntax().text_range(); 155 let syntax_range = src.value.syntax().text_range();
133 format_to!(msg, " ({:?} {:?})", path, syntax_range); 156 format_to!(msg, " ({} {:?})", path, syntax_range);
134 } 157 }
135 if verbosity.is_spammy() { 158 if verbosity.is_spammy() {
136 bar.println(msg.to_string()); 159 bar.println(msg.to_string());
@@ -196,7 +219,7 @@ pub fn analysis_stats(
196 let root = db.parse_or_expand(src.file_id).unwrap(); 219 let root = db.parse_or_expand(src.file_id).unwrap();
197 let node = src.map(|e| e.to_node(&root).syntax().clone()); 220 let node = src.map(|e| e.to_node(&root).syntax().clone());
198 let original_range = original_range(db, node.as_ref()); 221 let original_range = original_range(db, node.as_ref());
199 let path = db.file_relative_path(original_range.file_id); 222 let path = vfs.file_path(original_range.file_id);
200 let line_index = 223 let line_index =
201 host.analysis().file_line_index(original_range.file_id).unwrap(); 224 host.analysis().file_line_index(original_range.file_id).unwrap();
202 let text_range = original_range.range; 225 let text_range = original_range.range;
@@ -253,12 +276,7 @@ pub fn analysis_stats(
253 println!("Total: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage()); 276 println!("Total: {:?}, {}", analysis_time.elapsed(), ra_prof::memory_usage());
254 277
255 if memory_usage { 278 if memory_usage {
256 for (name, bytes) in host.per_query_memory_usage() { 279 print_memory_usage(host, vfs);
257 println!("{:>8} {}", bytes, name)
258 }
259 let before = ra_prof::memory_usage();
260 drop(host);
261 println!("leftover: {}", before.allocated - ra_prof::memory_usage().allocated)
262 } 280 }
263 281
264 Ok(()) 282 Ok(())
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index 60daefa3e..6f3c1c1f9 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -1,69 +1,61 @@
1//! Analyze all modules in a project for diagnostics. Exits with a non-zero status 1//! Analyze all modules in a project for diagnostics. Exits with a non-zero status
2//! code if any errors are found. 2//! code if any errors are found.
3 3
4use std::path::Path;
5
4use anyhow::anyhow; 6use anyhow::anyhow;
7use rustc_hash::FxHashSet;
8
9use hir::Crate;
5use ra_db::SourceDatabaseExt; 10use ra_db::SourceDatabaseExt;
6use ra_ide::Severity; 11use ra_ide::Severity;
7use std::{collections::HashSet, path::Path};
8 12
9use crate::cli::{load_cargo::load_cargo, Result}; 13use crate::cli::{load_cargo::load_cargo, Result};
10use hir::Semantics;
11 14
12pub fn diagnostics( 15pub fn diagnostics(
13 path: &Path, 16 path: &Path,
14 load_output_dirs: bool, 17 load_output_dirs: bool,
15 with_proc_macro: bool, 18 with_proc_macro: bool,
16 all: bool, 19 _all: bool,
17) -> Result<()> { 20) -> Result<()> {
18 let (host, roots) = load_cargo(path, load_output_dirs, with_proc_macro)?; 21 let (host, _vfs) = load_cargo(path, load_output_dirs, with_proc_macro)?;
19 let db = host.raw_database(); 22 let db = host.raw_database();
20 let analysis = host.analysis(); 23 let analysis = host.analysis();
21 let semantics = Semantics::new(db);
22 let members = roots
23 .into_iter()
24 .filter_map(|(source_root_id, project_root)| {
25 // filter out dependencies
26 if project_root.is_member() {
27 Some(source_root_id)
28 } else {
29 None
30 }
31 })
32 .collect::<HashSet<_>>();
33 24
34 let mut found_error = false; 25 let mut found_error = false;
35 let mut visited_files = HashSet::new(); 26 let mut visited_files = FxHashSet::default();
36 for source_root_id in members {
37 for file_id in db.source_root(source_root_id).walk() {
38 // Filter out files which are not actually modules (unless `--all` flag is
39 // passed). In the rust-analyzer repository this filters out the parser test files.
40 if semantics.to_module_def(file_id).is_some() || all {
41 if !visited_files.contains(&file_id) {
42 let crate_name = if let Some(module) = semantics.to_module_def(file_id) {
43 if let Some(name) = module.krate().display_name(db) {
44 format!("{}", name)
45 } else {
46 String::from("unknown")
47 }
48 } else {
49 String::from("unknown")
50 };
51 println!(
52 "processing crate: {}, module: {}",
53 crate_name,
54 db.file_relative_path(file_id)
55 );
56 for diagnostic in analysis.diagnostics(file_id).unwrap() {
57 if matches!(diagnostic.severity, Severity::Error) {
58 found_error = true;
59 }
60 27
61 println!("{:?}", diagnostic); 28 let mut work = Vec::new();
62 } 29 let krates = Crate::all(db);
30 for krate in krates {
31 let module = krate.root_module(db).expect("crate without root module");
32 let file_id = module.definition_source(db).file_id;
33 let file_id = file_id.original_file(db);
34 let source_root = db.file_source_root(file_id);
35 let source_root = db.source_root(source_root);
36 if !source_root.is_library {
37 work.push(module);
38 }
39 }
63 40
64 visited_files.insert(file_id); 41 for module in work {
42 let file_id = module.definition_source(db).file_id.original_file(db);
43 if !visited_files.contains(&file_id) {
44 let crate_name = if let Some(name) = module.krate().display_name(db) {
45 format!("{}", name)
46 } else {
47 String::from("unknown")
48 };
49 println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id));
50 for diagnostic in analysis.diagnostics(file_id).unwrap() {
51 if matches!(diagnostic.severity, Severity::Error) {
52 found_error = true;
65 } 53 }
54
55 println!("{:?}", diagnostic);
66 } 56 }
57
58 visited_files.insert(file_id);
67 } 59 }
68 } 60 }
69 61
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 8eaf75ff6..a43bf2244 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -1,173 +1,96 @@
1//! Loads a Cargo project into a static instance of analysis, without support 1//! Loads a Cargo project into a static instance of analysis, without support
2//! for incorporating changes. 2//! for incorporating changes.
3 3use std::{path::Path, sync::Arc};
4use std::path::{Path, PathBuf};
5 4
6use anyhow::Result; 5use anyhow::Result;
7use crossbeam_channel::{unbounded, Receiver}; 6use crossbeam_channel::{unbounded, Receiver};
8use ra_db::{ExternSourceId, FileId, SourceRootId}; 7use ra_db::CrateGraph;
9use ra_ide::{AnalysisChange, AnalysisHost}; 8use ra_ide::{AnalysisChange, AnalysisHost};
10use ra_project_model::{ 9use ra_project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace};
11 get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectRoot, ProjectWorkspace, 10use vfs::{loader::Handle, AbsPath, AbsPathBuf};
12};
13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
14use rustc_hash::{FxHashMap, FxHashSet};
15
16use crate::vfs_glob::RustPackageFilterBuilder;
17 11
18fn vfs_file_to_id(f: ra_vfs::VfsFile) -> FileId { 12use crate::reload::{ProjectFolders, SourceRootConfig};
19 FileId(f.0)
20}
21fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
22 SourceRootId(r.0)
23}
24 13
25pub fn load_cargo( 14pub fn load_cargo(
26 root: &Path, 15 root: &Path,
27 load_out_dirs_from_check: bool, 16 load_out_dirs_from_check: bool,
28 with_proc_macro: bool, 17 with_proc_macro: bool,
29) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { 18) -> Result<(AnalysisHost, vfs::Vfs)> {
30 let root = std::env::current_dir()?.join(root); 19 let root = AbsPathBuf::assert(std::env::current_dir()?.join(root));
31 let root = ProjectRoot::discover_single(&root)?; 20 let root = ProjectManifest::discover_single(&root)?;
32 let ws = ProjectWorkspace::load( 21 let ws = ProjectWorkspace::load(
33 root, 22 root,
34 &CargoConfig { load_out_dirs_from_check, ..Default::default() }, 23 &CargoConfig { load_out_dirs_from_check, ..Default::default() },
35 true, 24 true,
36 )?; 25 )?;
37 26
38 let mut extern_dirs = FxHashSet::default();
39 extern_dirs.extend(ws.out_dirs());
40
41 let mut project_roots = ws.to_roots();
42 project_roots.extend(extern_dirs.iter().cloned().map(PackageRoot::new_non_member));
43
44 let (sender, receiver) = unbounded(); 27 let (sender, receiver) = unbounded();
45 let sender = Box::new(move |t| sender.send(t).unwrap()); 28 let mut vfs = vfs::Vfs::default();
46 let (mut vfs, roots) = Vfs::new( 29 let mut loader = {
47 project_roots 30 let loader =
48 .iter() 31 vfs_notify::NotifyHandle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
49 .map(|pkg_root| { 32 Box::new(loader)
50 RootEntry::new( 33 };
51 pkg_root.path().to_owned(),
52 RustPackageFilterBuilder::default()
53 .set_member(pkg_root.is_member())
54 .into_vfs_filter(),
55 )
56 })
57 .collect(),
58 sender,
59 Watch(false),
60 );
61
62 let source_roots = roots
63 .into_iter()
64 .map(|vfs_root| {
65 let source_root_id = vfs_root_to_id(vfs_root);
66 let project_root = project_roots
67 .iter()
68 .find(|it| it.path() == vfs.root2path(vfs_root))
69 .unwrap()
70 .clone();
71 (source_root_id, project_root)
72 })
73 .collect::<FxHashMap<_, _>>();
74 34
75 let proc_macro_client = if !with_proc_macro { 35 let proc_macro_client = if with_proc_macro {
76 ProcMacroClient::dummy()
77 } else {
78 let path = std::env::current_exe()?; 36 let path = std::env::current_exe()?;
79 ProcMacroClient::extern_process(path, &["proc-macro"]).unwrap() 37 ProcMacroClient::extern_process(path, &["proc-macro"]).unwrap()
38 } else {
39 ProcMacroClient::dummy()
80 }; 40 };
81 let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client); 41
82 Ok((host, source_roots)) 42 let crate_graph = ws.to_crate_graph(None, &proc_macro_client, &mut |path: &AbsPath| {
43 let contents = loader.load_sync(path);
44 let path = vfs::VfsPath::from(path.to_path_buf());
45 vfs.set_file_contents(path.clone(), contents);
46 vfs.file_id(&path)
47 });
48
49 let project_folders = ProjectFolders::new(&[ws]);
50 loader.set_config(vfs::loader::Config { load: project_folders.load, watch: vec![] });
51
52 log::debug!("crate graph: {:?}", crate_graph);
53 let host = load(crate_graph, project_folders.source_root_config, &mut vfs, &receiver);
54 Ok((host, vfs))
83} 55}
84 56
85pub(crate) fn load( 57fn load(
86 source_roots: &FxHashMap<SourceRootId, PackageRoot>, 58 crate_graph: CrateGraph,
87 ws: ProjectWorkspace, 59 source_root_config: SourceRootConfig,
88 vfs: &mut Vfs, 60 vfs: &mut vfs::Vfs,
89 receiver: Receiver<VfsTask>, 61 receiver: &Receiver<vfs::loader::Message>,
90 extern_dirs: FxHashSet<PathBuf>,
91 proc_macro_client: &ProcMacroClient,
92) -> AnalysisHost { 62) -> AnalysisHost {
93 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); 63 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
94 let mut host = AnalysisHost::new(lru_cap); 64 let mut host = AnalysisHost::new(lru_cap);
95 let mut analysis_change = AnalysisChange::new(); 65 let mut analysis_change = AnalysisChange::new();
96 66
97 // wait until Vfs has loaded all roots 67 // wait until Vfs has loaded all roots
98 let mut roots_loaded = FxHashSet::default();
99 let mut extern_source_roots = FxHashMap::default();
100 for task in receiver { 68 for task in receiver {
101 vfs.handle_task(task); 69 match task {
102 let mut done = false; 70 vfs::loader::Message::Progress { n_done, n_total } => {
103 for change in vfs.commit_changes() { 71 if n_done == n_total {
104 match change { 72 break;
105 VfsChange::AddRoot { root, files } => {
106 let source_root_id = vfs_root_to_id(root);
107 let is_local = source_roots[&source_root_id].is_member();
108 log::debug!(
109 "loaded source root {:?} with path {:?}",
110 source_root_id,
111 vfs.root2path(root)
112 );
113 analysis_change.add_root(source_root_id, is_local);
114 analysis_change.set_debug_root_path(
115 source_root_id,
116 source_roots[&source_root_id].path().display().to_string(),
117 );
118
119 let vfs_root_path = vfs.root2path(root);
120 if extern_dirs.contains(&vfs_root_path) {
121 extern_source_roots.insert(vfs_root_path, ExternSourceId(root.0));
122 }
123
124 let mut file_map = FxHashMap::default();
125 for (vfs_file, path, text) in files {
126 let file_id = vfs_file_to_id(vfs_file);
127 analysis_change.add_file(source_root_id, file_id, path.clone(), text);
128 file_map.insert(path, file_id);
129 }
130 roots_loaded.insert(source_root_id);
131 if roots_loaded.len() == vfs.n_roots() {
132 done = true;
133 }
134 }
135 VfsChange::AddFile { root, file, path, text } => {
136 let source_root_id = vfs_root_to_id(root);
137 let file_id = vfs_file_to_id(file);
138 analysis_change.add_file(source_root_id, file_id, path, text);
139 } 73 }
140 VfsChange::RemoveFile { .. } | VfsChange::ChangeFile { .. } => { 74 }
141 // We just need the first scan, so just ignore these 75 vfs::loader::Message::Loaded { files } => {
76 for (path, contents) in files {
77 vfs.set_file_contents(path.into(), contents)
142 } 78 }
143 } 79 }
144 } 80 }
145 if done { 81 }
146 break; 82 let changes = vfs.take_changes();
83 for file in changes {
84 if file.exists() {
85 let contents = vfs.file_contents(file.file_id).to_vec();
86 if let Ok(text) = String::from_utf8(contents) {
87 analysis_change.change_file(file.file_id, Some(Arc::new(text)))
88 }
147 } 89 }
148 } 90 }
91 let source_roots = source_root_config.partition(&vfs);
92 analysis_change.set_roots(source_roots);
149 93
150 // FIXME: cfg options?
151 let default_cfg_options = {
152 let mut opts = get_rustc_cfg_options(None);
153 opts.insert_atom("test".into());
154 opts.insert_atom("debug_assertion".into());
155 opts
156 };
157
158 let crate_graph = ws.to_crate_graph(
159 &default_cfg_options,
160 &extern_source_roots,
161 proc_macro_client,
162 &mut |path: &Path| {
163 // Some path from metadata will be non canonicalized, e.g. /foo/../bar/lib.rs
164 let path = path.canonicalize().ok()?;
165 let vfs_file = vfs.load(&path);
166 log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
167 vfs_file.map(vfs_file_to_id)
168 },
169 );
170 log::debug!("crate graph: {:?}", crate_graph);
171 analysis_change.set_crate_graph(crate_graph); 94 analysis_change.set_crate_graph(crate_graph);
172 95
173 host.apply_change(analysis_change); 96 host.apply_change(analysis_change);
@@ -183,7 +106,7 @@ mod tests {
183 #[test] 106 #[test]
184 fn test_loading_rust_analyzer() { 107 fn test_loading_rust_analyzer() {
185 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); 108 let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
186 let (host, _roots) = load_cargo(path, false, false).unwrap(); 109 let (host, _vfs) = load_cargo(path, false, false).unwrap();
187 let n_crates = Crate::all(host.raw_database()).len(); 110 let n_crates = Crate::all(host.raw_database()).len();
188 // RA has quite a few crates, but the exact count doesn't matter 111 // RA has quite a few crates, but the exact count doesn't matter
189 assert!(n_crates > 20); 112 assert!(n_crates > 20);
diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs
new file mode 100644
index 000000000..4fb829ea5
--- /dev/null
+++ b/crates/rust-analyzer/src/cli/ssr.rs
@@ -0,0 +1,71 @@
1//! Applies structured search replace rules from the command line.
2
3use crate::cli::{load_cargo::load_cargo, Result};
4use ra_ide::SourceFileEdit;
5use ra_ssr::{MatchFinder, SsrPattern, SsrRule};
6
7pub fn apply_ssr_rules(rules: Vec<SsrRule>) -> Result<()> {
8 use ra_db::SourceDatabaseExt;
9 use ra_ide_db::symbol_index::SymbolsDatabase;
10 let (host, vfs) = load_cargo(&std::env::current_dir()?, true, true)?;
11 let db = host.raw_database();
12 let mut match_finder = MatchFinder::new(db);
13 for rule in rules {
14 match_finder.add_rule(rule);
15 }
16 let mut edits = Vec::new();
17 for &root in db.local_roots().iter() {
18 let sr = db.source_root(root);
19 for file_id in sr.iter() {
20 if let Some(edit) = match_finder.edits_for_file(file_id) {
21 edits.push(SourceFileEdit { file_id, edit });
22 }
23 }
24 }
25 for edit in edits {
26 if let Some(path) = vfs.file_path(edit.file_id).as_path() {
27 let mut contents = db.file_text(edit.file_id).to_string();
28 edit.edit.apply(&mut contents);
29 std::fs::write(path, contents)?;
30 }
31 }
32 Ok(())
33}
34
35/// Searches for `patterns`, printing debug information for any nodes whose text exactly matches
36/// `debug_snippet`. This is intended for debugging and probably isn't in it's current form useful
37/// for much else.
38pub fn search_for_patterns(patterns: Vec<SsrPattern>, debug_snippet: Option<String>) -> Result<()> {
39 use ra_db::SourceDatabaseExt;
40 use ra_ide_db::symbol_index::SymbolsDatabase;
41 let (host, vfs) = load_cargo(&std::env::current_dir()?, true, true)?;
42 let db = host.raw_database();
43 let mut match_finder = MatchFinder::new(db);
44 for pattern in patterns {
45 match_finder.add_search_pattern(pattern);
46 }
47 for &root in db.local_roots().iter() {
48 let sr = db.source_root(root);
49 for file_id in sr.iter() {
50 if let Some(debug_snippet) = &debug_snippet {
51 for debug_info in match_finder.debug_where_text_equal(file_id, debug_snippet) {
52 println!("{:#?}", debug_info);
53 }
54 } else {
55 let matches = match_finder.find_matches_in_file(file_id);
56 if !matches.matches.is_empty() {
57 let matches = matches.flattened().matches;
58 if let Some(path) = vfs.file_path(file_id).as_path() {
59 println!("{} matches in '{}'", matches.len(), path.to_string_lossy());
60 }
61 // We could possibly at some point do something more useful than just printing
62 // the matched text. For now though, that's the easiest thing to do.
63 for m in matches {
64 println!("{}", m.matched_text());
65 }
66 }
67 }
68 }
69 }
70 Ok(())
71}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index c0f7c2c0c..ed5e52871 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -9,52 +9,79 @@
9 9
10use std::{ffi::OsString, path::PathBuf}; 10use std::{ffi::OsString, path::PathBuf};
11 11
12use flycheck::FlycheckConfig;
12use lsp_types::ClientCapabilities; 13use lsp_types::ClientCapabilities;
13use ra_flycheck::FlycheckConfig; 14use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
14use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; 15use ra_project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest};
15use ra_project_model::CargoConfig;
16use serde::Deserialize; 16use serde::Deserialize;
17use vfs::AbsPathBuf;
18
19use crate::diagnostics::DiagnosticsConfig;
17 20
18#[derive(Debug, Clone)] 21#[derive(Debug, Clone)]
19pub struct Config { 22pub struct Config {
20 pub client_caps: ClientCapsConfig, 23 pub client_caps: ClientCapsConfig,
21 24
22 pub with_sysroot: bool,
23 pub publish_diagnostics: bool, 25 pub publish_diagnostics: bool,
26 pub diagnostics: DiagnosticsConfig,
24 pub lru_capacity: Option<usize>, 27 pub lru_capacity: Option<usize>,
25 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>, 28 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
26 pub files: FilesConfig, 29 pub files: FilesConfig,
27 pub notifications: NotificationsConfig, 30 pub notifications: NotificationsConfig,
28 31
32 pub cargo_autoreload: bool,
29 pub cargo: CargoConfig, 33 pub cargo: CargoConfig,
30 pub rustfmt: RustfmtConfig, 34 pub rustfmt: RustfmtConfig,
31 pub check: Option<FlycheckConfig>, 35 pub flycheck: Option<FlycheckConfig>,
32 36
33 pub inlay_hints: InlayHintsConfig, 37 pub inlay_hints: InlayHintsConfig,
34 pub completion: CompletionConfig, 38 pub completion: CompletionConfig,
35 pub assist: AssistConfig, 39 pub assist: AssistConfig,
36 pub call_info_full: bool, 40 pub call_info_full: bool,
37 pub lens: LensConfig, 41 pub lens: LensConfig,
42 pub hover: HoverConfig,
43
44 pub with_sysroot: bool,
45 pub linked_projects: Vec<LinkedProject>,
46 pub root_path: AbsPathBuf,
47}
48
49#[derive(Debug, Clone, Eq, PartialEq)]
50pub enum LinkedProject {
51 ProjectManifest(ProjectManifest),
52 InlineJsonProject(ProjectJson),
53}
54
55impl From<ProjectManifest> for LinkedProject {
56 fn from(v: ProjectManifest) -> Self {
57 LinkedProject::ProjectManifest(v)
58 }
59}
60
61impl From<ProjectJson> for LinkedProject {
62 fn from(v: ProjectJson) -> Self {
63 LinkedProject::InlineJsonProject(v)
64 }
38} 65}
39 66
40#[derive(Clone, Debug, PartialEq, Eq)] 67#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct LensConfig { 68pub struct LensConfig {
42 pub run: bool, 69 pub run: bool,
43 pub debug: bool, 70 pub debug: bool,
44 pub impementations: bool, 71 pub implementations: bool,
45} 72}
46 73
47impl Default for LensConfig { 74impl Default for LensConfig {
48 fn default() -> Self { 75 fn default() -> Self {
49 Self { run: true, debug: true, impementations: true } 76 Self { run: true, debug: true, implementations: true }
50 } 77 }
51} 78}
52 79
53impl LensConfig { 80impl LensConfig {
54 pub const NO_LENS: LensConfig = Self { run: false, debug: false, impementations: false }; 81 pub const NO_LENS: LensConfig = Self { run: false, debug: false, implementations: false };
55 82
56 pub fn any(&self) -> bool { 83 pub fn any(&self) -> bool {
57 self.impementations || self.runnable() 84 self.implementations || self.runnable()
58 } 85 }
59 86
60 pub fn none(&self) -> bool { 87 pub fn none(&self) -> bool {
@@ -85,14 +112,8 @@ pub struct NotificationsConfig {
85 112
86#[derive(Debug, Clone)] 113#[derive(Debug, Clone)]
87pub enum RustfmtConfig { 114pub enum RustfmtConfig {
88 Rustfmt { 115 Rustfmt { extra_args: Vec<String> },
89 extra_args: Vec<String>, 116 CustomCommand { command: String, args: Vec<String> },
90 },
91 #[allow(unused)]
92 CustomCommand {
93 command: String,
94 args: Vec<String>,
95 },
96} 117}
97 118
98#[derive(Debug, Clone, Default)] 119#[derive(Debug, Clone, Default)]
@@ -103,27 +124,33 @@ pub struct ClientCapsConfig {
103 pub code_action_literals: bool, 124 pub code_action_literals: bool,
104 pub work_done_progress: bool, 125 pub work_done_progress: bool,
105 pub code_action_group: bool, 126 pub code_action_group: bool,
127 pub resolve_code_action: bool,
128 pub hover_actions: bool,
129 pub status_notification: bool,
106} 130}
107 131
108impl Default for Config { 132impl Config {
109 fn default() -> Self { 133 pub fn new(root_path: AbsPathBuf) -> Self {
110 Config { 134 Config {
111 client_caps: ClientCapsConfig::default(), 135 client_caps: ClientCapsConfig::default(),
112 136
113 with_sysroot: true, 137 with_sysroot: true,
114 publish_diagnostics: true, 138 publish_diagnostics: true,
139 diagnostics: DiagnosticsConfig::default(),
115 lru_capacity: None, 140 lru_capacity: None,
116 proc_macro_srv: None, 141 proc_macro_srv: None,
117 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() }, 142 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
118 notifications: NotificationsConfig { cargo_toml_not_found: true }, 143 notifications: NotificationsConfig { cargo_toml_not_found: true },
119 144
145 cargo_autoreload: true,
120 cargo: CargoConfig::default(), 146 cargo: CargoConfig::default(),
121 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() }, 147 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
122 check: Some(FlycheckConfig::CargoCommand { 148 flycheck: Some(FlycheckConfig::CargoCommand {
123 command: "check".to_string(), 149 command: "check".to_string(),
124 all_targets: true, 150 all_targets: true,
125 all_features: true, 151 all_features: false,
126 extra_args: Vec::new(), 152 extra_args: Vec::new(),
153 features: Vec::new(),
127 }), 154 }),
128 155
129 inlay_hints: InlayHintsConfig { 156 inlay_hints: InlayHintsConfig {
@@ -141,116 +168,117 @@ impl Default for Config {
141 assist: AssistConfig::default(), 168 assist: AssistConfig::default(),
142 call_info_full: true, 169 call_info_full: true,
143 lens: LensConfig::default(), 170 lens: LensConfig::default(),
171 hover: HoverConfig::default(),
172 linked_projects: Vec::new(),
173 root_path,
144 } 174 }
145 } 175 }
146}
147 176
148impl Config { 177 pub fn update(&mut self, json: serde_json::Value) {
149 #[rustfmt::skip] 178 log::info!("Config::update({:#})", json);
150 pub fn update(&mut self, value: &serde_json::Value) { 179 let data = ConfigData::from_json(json);
151 log::info!("Config::update({:#})", value); 180
152 181 self.with_sysroot = data.withSysroot;
153 let client_caps = self.client_caps.clone(); 182 self.publish_diagnostics = data.diagnostics_enable;
154 *self = Default::default(); 183 self.diagnostics = DiagnosticsConfig {
155 self.client_caps = client_caps; 184 warnings_as_info: data.diagnostics_warningsAsInfo,
156 185 warnings_as_hint: data.diagnostics_warningsAsHint,
157 set(value, "/withSysroot", &mut self.with_sysroot); 186 };
158 set(value, "/diagnostics/enable", &mut self.publish_diagnostics); 187 self.lru_capacity = data.lruCapacity;
159 set(value, "/lruCapacity", &mut self.lru_capacity); 188 self.files.watcher = match data.files_watcher.as_str() {
160 self.files.watcher = match get(value, "/files/watcher") { 189 "notify" => FilesWatcher::Notify,
161 Some("client") => FilesWatcher::Client, 190 "client" | _ => FilesWatcher::Client,
162 Some("notify") | _ => FilesWatcher::Notify 191 };
192 self.notifications =
193 NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound };
194 self.cargo_autoreload = data.cargo_autoreload;
195 self.cargo = CargoConfig {
196 no_default_features: data.cargo_noDefaultFeatures,
197 all_features: data.cargo_allFeatures,
198 features: data.cargo_features.clone(),
199 load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck,
200 target: data.cargo_target,
163 }; 201 };
164 set(value, "/notifications/cargoTomlNotFound", &mut self.notifications.cargo_toml_not_found);
165
166 set(value, "/cargo/noDefaultFeatures", &mut self.cargo.no_default_features);
167 set(value, "/cargo/allFeatures", &mut self.cargo.all_features);
168 set(value, "/cargo/features", &mut self.cargo.features);
169 set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
170 set(value, "/cargo/target", &mut self.cargo.target);
171
172 match get(value, "/procMacro/enable") {
173 Some(true) => {
174 if let Ok(path) = std::env::current_exe() {
175 self.proc_macro_srv = Some((path, vec!["proc-macro".into()]));
176 }
177 }
178 _ => self.proc_macro_srv = None,
179 }
180 202
181 match get::<Vec<String>>(value, "/rustfmt/overrideCommand") { 203 self.proc_macro_srv = if data.procMacro_enable {
204 std::env::current_exe().ok().map(|path| (path, vec!["proc-macro".into()]))
205 } else {
206 None
207 };
208
209 self.rustfmt = match data.rustfmt_overrideCommand {
182 Some(mut args) if !args.is_empty() => { 210 Some(mut args) if !args.is_empty() => {
183 let command = args.remove(0); 211 let command = args.remove(0);
184 self.rustfmt = RustfmtConfig::CustomCommand { 212 RustfmtConfig::CustomCommand { command, args }
185 command,
186 args,
187 }
188 }
189 _ => {
190 if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt {
191 set(value, "/rustfmt/extraArgs", extra_args);
192 }
193 } 213 }
214 Some(_) | None => RustfmtConfig::Rustfmt { extra_args: data.rustfmt_extraArgs },
194 }; 215 };
195 216
196 if let Some(false) = get(value, "/checkOnSave/enable") { 217 self.flycheck = if data.checkOnSave_enable {
197 // check is disabled 218 let flycheck_config = match data.checkOnSave_overrideCommand {
198 self.check = None;
199 } else {
200 // check is enabled
201 match get::<Vec<String>>(value, "/checkOnSave/overrideCommand") {
202 // first see if the user has completely overridden the command
203 Some(mut args) if !args.is_empty() => { 219 Some(mut args) if !args.is_empty() => {
204 let command = args.remove(0); 220 let command = args.remove(0);
205 self.check = Some(FlycheckConfig::CustomCommand { 221 FlycheckConfig::CustomCommand { command, args }
206 command,
207 args,
208 });
209 }
210 // otherwise configure command customizations
211 _ => {
212 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features })
213 = &mut self.check
214 {
215 set(value, "/checkOnSave/extraArgs", extra_args);
216 set(value, "/checkOnSave/command", command);
217 set(value, "/checkOnSave/allTargets", all_targets);
218 set(value, "/checkOnSave/allFeatures", all_features);
219 }
220 } 222 }
223 Some(_) | None => FlycheckConfig::CargoCommand {
224 command: data.checkOnSave_command,
225 all_targets: data.checkOnSave_allTargets,
226 all_features: data.checkOnSave_allFeatures.unwrap_or(data.cargo_allFeatures),
227 features: data.checkOnSave_features.unwrap_or(data.cargo_features),
228 extra_args: data.checkOnSave_extraArgs,
229 },
221 }; 230 };
222 } 231 Some(flycheck_config)
223
224 set(value, "/inlayHints/typeHints", &mut self.inlay_hints.type_hints);
225 set(value, "/inlayHints/parameterHints", &mut self.inlay_hints.parameter_hints);
226 set(value, "/inlayHints/chainingHints", &mut self.inlay_hints.chaining_hints);
227 set(value, "/inlayHints/maxLength", &mut self.inlay_hints.max_length);
228 set(value, "/completion/postfix/enable", &mut self.completion.enable_postfix_completions);
229 set(value, "/completion/addCallParenthesis", &mut self.completion.add_call_parenthesis);
230 set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets);
231 set(value, "/callInfo/full", &mut self.call_info_full);
232
233 let mut lens_enabled = true;
234 set(value, "/lens/enable", &mut lens_enabled);
235 if lens_enabled {
236 set(value, "/lens/run", &mut self.lens.run);
237 set(value, "/lens/debug", &mut self.lens.debug);
238 set(value, "/lens/implementations", &mut self.lens.impementations);
239 } else { 232 } else {
240 self.lens = LensConfig::NO_LENS; 233 None
241 } 234 };
242 235
243 log::info!("Config::update() = {:#?}", self); 236 self.inlay_hints = InlayHintsConfig {
237 type_hints: data.inlayHints_typeHints,
238 parameter_hints: data.inlayHints_parameterHints,
239 chaining_hints: data.inlayHints_chainingHints,
240 max_length: data.inlayHints_maxLength,
241 };
244 242
245 fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { 243 self.completion.enable_postfix_completions = data.completion_postfix_enable;
246 value.pointer(pointer).and_then(|it| T::deserialize(it).ok()) 244 self.completion.add_call_parenthesis = data.completion_addCallParenthesis;
247 } 245 self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets;
246
247 self.call_info_full = data.callInfo_full;
248
249 self.lens = LensConfig {
250 run: data.lens_enable && data.lens_run,
251 debug: data.lens_enable && data.lens_debug,
252 implementations: data.lens_enable && data.lens_implementations,
253 };
248 254
249 fn set<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) { 255 if !data.linkedProjects.is_empty() {
250 if let Some(new_value) = get(value, pointer) { 256 self.linked_projects.clear();
251 *slot = new_value 257 for linked_project in data.linkedProjects {
258 let linked_project = match linked_project {
259 ManifestOrProjectJson::Manifest(it) => {
260 let path = self.root_path.join(it);
261 match ProjectManifest::from_manifest_file(path) {
262 Ok(it) => it.into(),
263 Err(_) => continue,
264 }
265 }
266 ManifestOrProjectJson::ProjectJson(it) => {
267 ProjectJson::new(&self.root_path, it).into()
268 }
269 };
270 self.linked_projects.push(linked_project);
252 } 271 }
253 } 272 }
273
274 self.hover = HoverConfig {
275 implementations: data.hoverActions_enable && data.hoverActions_implementations,
276 run: data.hoverActions_enable && data.hoverActions_run,
277 debug: data.hoverActions_enable && data.hoverActions_debug,
278 goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef,
279 };
280
281 log::info!("Config::update() = {:#?}", self);
254 } 282 }
255 283
256 pub fn update_caps(&mut self, caps: &ClientCapabilities) { 284 pub fn update_caps(&mut self, caps: &ClientCapabilities) {
@@ -293,13 +321,101 @@ impl Config {
293 321
294 self.assist.allow_snippets(false); 322 self.assist.allow_snippets(false);
295 if let Some(experimental) = &caps.experimental { 323 if let Some(experimental) = &caps.experimental {
296 let snippet_text_edit = 324 let get_bool =
297 experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); 325 |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true);
326
327 let snippet_text_edit = get_bool("snippetTextEdit");
298 self.assist.allow_snippets(snippet_text_edit); 328 self.assist.allow_snippets(snippet_text_edit);
299 329
300 let code_action_group = 330 self.client_caps.code_action_group = get_bool("codeActionGroup");
301 experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true); 331 self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
302 self.client_caps.code_action_group = code_action_group 332 self.client_caps.hover_actions = get_bool("hoverActions");
333 self.client_caps.status_notification = get_bool("statusNotification");
334 }
335 }
336}
337
338#[derive(Deserialize)]
339#[serde(untagged)]
340enum ManifestOrProjectJson {
341 Manifest(PathBuf),
342 ProjectJson(ProjectJsonData),
343}
344
345macro_rules! config_data {
346 (struct $name:ident { $($field:ident: $ty:ty = $default:expr,)*}) => {
347 #[allow(non_snake_case)]
348 struct $name { $($field: $ty,)* }
349 impl $name {
350 fn from_json(mut json: serde_json::Value) -> $name {
351 $name {$(
352 $field: {
353 let pointer = stringify!($field).replace('_', "/");
354 let pointer = format!("/{}", pointer);
355 json.pointer_mut(&pointer)
356 .and_then(|it| serde_json::from_value(it.take()).ok())
357 .unwrap_or($default)
358 },
359 )*}
360 }
303 } 361 }
362
363 };
364}
365
366config_data! {
367 struct ConfigData {
368 callInfo_full: bool = true,
369
370 cargo_autoreload: bool = true,
371 cargo_allFeatures: bool = false,
372 cargo_features: Vec<String> = Vec::new(),
373 cargo_loadOutDirsFromCheck: bool = false,
374 cargo_noDefaultFeatures: bool = false,
375 cargo_target: Option<String> = None,
376
377 checkOnSave_allFeatures: Option<bool> = None,
378 checkOnSave_allTargets: bool = true,
379 checkOnSave_command: String = "check".into(),
380 checkOnSave_enable: bool = false,
381 checkOnSave_extraArgs: Vec<String> = Vec::new(),
382 checkOnSave_features: Option<Vec<String>> = None,
383 checkOnSave_overrideCommand: Option<Vec<String>> = None,
384
385 completion_addCallArgumentSnippets: bool = true,
386 completion_addCallParenthesis: bool = true,
387 completion_postfix_enable: bool = true,
388
389 diagnostics_enable: bool = true,
390 diagnostics_warningsAsHint: Vec<String> = Vec::new(),
391 diagnostics_warningsAsInfo: Vec<String> = Vec::new(),
392
393 files_watcher: String = "client".into(),
394
395 hoverActions_debug: bool = true,
396 hoverActions_enable: bool = true,
397 hoverActions_gotoTypeDef: bool = true,
398 hoverActions_implementations: bool = true,
399 hoverActions_run: bool = true,
400
401 inlayHints_chainingHints: bool = true,
402 inlayHints_maxLength: Option<usize> = None,
403 inlayHints_parameterHints: bool = true,
404 inlayHints_typeHints: bool = true,
405
406 lens_debug: bool = true,
407 lens_enable: bool = true,
408 lens_implementations: bool = true,
409 lens_run: bool = true,
410
411 linkedProjects: Vec<ManifestOrProjectJson> = Vec::new(),
412 lruCapacity: Option<usize> = None,
413 notifications_cargoTomlNotFound: bool = true,
414 procMacro_enable: bool = false,
415
416 rustfmt_extraArgs: Vec<String> = Vec::new(),
417 rustfmt_overrideCommand: Option<Vec<String>> = None,
418
419 withSysroot: bool = true,
304 } 420 }
305} 421}
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 25856c543..d24c55cee 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -1,45 +1,47 @@
1//! Book keeping for keeping diagnostics easily in sync with the client. 1//! Book keeping for keeping diagnostics easily in sync with the client.
2pub(crate) mod to_proto; 2pub(crate) mod to_proto;
3 3
4use std::{collections::HashMap, sync::Arc}; 4use std::{mem, sync::Arc};
5 5
6use lsp_types::{Diagnostic, Range};
7use ra_ide::FileId; 6use ra_ide::FileId;
7use rustc_hash::{FxHashMap, FxHashSet};
8 8
9use crate::lsp_ext; 9use crate::lsp_ext;
10 10
11pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>; 11pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>;
12 12
13#[derive(Debug, Default, Clone)] 13#[derive(Debug, Default, Clone)]
14pub struct DiagnosticCollection { 14pub struct DiagnosticsConfig {
15 pub native: HashMap<FileId, Vec<Diagnostic>>, 15 pub warnings_as_info: Vec<String>,
16 pub check: HashMap<FileId, Vec<Diagnostic>>, 16 pub warnings_as_hint: Vec<String>,
17 pub check_fixes: CheckFixes,
18} 17}
19 18
20#[derive(Debug, Clone)] 19#[derive(Debug, Default, Clone)]
21pub struct Fix { 20pub(crate) struct DiagnosticCollection {
22 pub range: Range, 21 // FIXME: should be FxHashMap<FileId, Vec<ra_id::Diagnostic>>
23 pub action: lsp_ext::CodeAction, 22 pub(crate) native: FxHashMap<FileId, Vec<lsp_types::Diagnostic>>,
23 // FIXME: should be Vec<flycheck::Diagnostic>
24 pub(crate) check: FxHashMap<FileId, Vec<lsp_types::Diagnostic>>,
25 pub(crate) check_fixes: CheckFixes,
26 changes: FxHashSet<FileId>,
24} 27}
25 28
26#[derive(Debug)] 29#[derive(Debug, Clone)]
27pub enum DiagnosticTask { 30pub(crate) struct Fix {
28 ClearCheck, 31 pub(crate) range: lsp_types::Range,
29 AddCheck(FileId, Diagnostic, Vec<lsp_ext::CodeAction>), 32 pub(crate) action: lsp_ext::CodeAction,
30 SetNative(FileId, Vec<Diagnostic>),
31} 33}
32 34
33impl DiagnosticCollection { 35impl DiagnosticCollection {
34 pub fn clear_check(&mut self) -> Vec<FileId> { 36 pub(crate) fn clear_check(&mut self) {
35 Arc::make_mut(&mut self.check_fixes).clear(); 37 Arc::make_mut(&mut self.check_fixes).clear();
36 self.check.drain().map(|(key, _value)| key).collect() 38 self.changes.extend(self.check.drain().map(|(key, _value)| key))
37 } 39 }
38 40
39 pub fn add_check_diagnostic( 41 pub(crate) fn add_check_diagnostic(
40 &mut self, 42 &mut self,
41 file_id: FileId, 43 file_id: FileId,
42 diagnostic: Diagnostic, 44 diagnostic: lsp_types::Diagnostic,
43 fixes: Vec<lsp_ext::CodeAction>, 45 fixes: Vec<lsp_ext::CodeAction>,
44 ) { 46 ) {
45 let diagnostics = self.check.entry(file_id).or_default(); 47 let diagnostics = self.check.entry(file_id).or_default();
@@ -55,34 +57,36 @@ impl DiagnosticCollection {
55 .or_default() 57 .or_default()
56 .extend(fixes.into_iter().map(|action| Fix { range: diagnostic.range, action })); 58 .extend(fixes.into_iter().map(|action| Fix { range: diagnostic.range, action }));
57 diagnostics.push(diagnostic); 59 diagnostics.push(diagnostic);
60 self.changes.insert(file_id);
58 } 61 }
59 62
60 pub fn set_native_diagnostics(&mut self, file_id: FileId, diagnostics: Vec<Diagnostic>) { 63 pub(crate) fn set_native_diagnostics(
64 &mut self,
65 file_id: FileId,
66 diagnostics: Vec<lsp_types::Diagnostic>,
67 ) {
61 self.native.insert(file_id, diagnostics); 68 self.native.insert(file_id, diagnostics);
69 self.changes.insert(file_id);
62 } 70 }
63 71
64 pub fn diagnostics_for(&self, file_id: FileId) -> impl Iterator<Item = &Diagnostic> { 72 pub(crate) fn diagnostics_for(
73 &self,
74 file_id: FileId,
75 ) -> impl Iterator<Item = &lsp_types::Diagnostic> {
65 let native = self.native.get(&file_id).into_iter().flatten(); 76 let native = self.native.get(&file_id).into_iter().flatten();
66 let check = self.check.get(&file_id).into_iter().flatten(); 77 let check = self.check.get(&file_id).into_iter().flatten();
67 native.chain(check) 78 native.chain(check)
68 } 79 }
69 80
70 pub fn handle_task(&mut self, task: DiagnosticTask) -> Vec<FileId> { 81 pub(crate) fn take_changes(&mut self) -> Option<FxHashSet<FileId>> {
71 match task { 82 if self.changes.is_empty() {
72 DiagnosticTask::ClearCheck => self.clear_check(), 83 return None;
73 DiagnosticTask::AddCheck(file_id, diagnostic, fixes) => {
74 self.add_check_diagnostic(file_id, diagnostic, fixes);
75 vec![file_id]
76 }
77 DiagnosticTask::SetNative(file_id, diagnostics) => {
78 self.set_native_diagnostics(file_id, diagnostics);
79 vec![file_id]
80 }
81 } 84 }
85 Some(mem::take(&mut self.changes))
82 } 86 }
83} 87}
84 88
85fn are_diagnostics_equal(left: &Diagnostic, right: &Diagnostic) -> bool { 89fn are_diagnostics_equal(left: &lsp_types::Diagnostic, right: &lsp_types::Diagnostic) -> bool {
86 left.source == right.source 90 left.source == right.source
87 && left.severity == right.severity 91 && left.severity == right.severity
88 && left.range == right.range 92 && left.range == right.range
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index a500d670a..7be3ef984 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -1,28 +1,34 @@
1//! This module provides the functionality needed to convert diagnostics from 1//! This module provides the functionality needed to convert diagnostics from
2//! `cargo check` json format to the LSP diagnostic format. 2//! `cargo check` json format to the LSP diagnostic format.
3use std::{ 3use std::{collections::HashMap, path::Path};
4 collections::HashMap,
5 path::{Component, Path, Prefix},
6 str::FromStr,
7};
8 4
9use lsp_types::{ 5use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan};
10 Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location,
11 NumberOrString, Position, Range, TextEdit, Url,
12};
13use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion};
14use stdx::format_to; 6use stdx::format_to;
15 7
16use crate::{lsp_ext, Result}; 8use crate::{lsp_ext, to_proto::url_from_abs_path};
17 9
18/// Converts a Rust level string to a LSP severity 10use super::DiagnosticsConfig;
19fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> { 11
20 let res = match val { 12/// Determines the LSP severity from a diagnostic
21 DiagnosticLevel::Ice => DiagnosticSeverity::Error, 13fn diagnostic_severity(
22 DiagnosticLevel::Error => DiagnosticSeverity::Error, 14 config: &DiagnosticsConfig,
23 DiagnosticLevel::Warning => DiagnosticSeverity::Warning, 15 level: flycheck::DiagnosticLevel,
24 DiagnosticLevel::Note => DiagnosticSeverity::Information, 16 code: Option<flycheck::DiagnosticCode>,
25 DiagnosticLevel::Help => DiagnosticSeverity::Hint, 17) -> Option<lsp_types::DiagnosticSeverity> {
18 let res = match level {
19 DiagnosticLevel::Ice => lsp_types::DiagnosticSeverity::Error,
20 DiagnosticLevel::Error => lsp_types::DiagnosticSeverity::Error,
21 DiagnosticLevel::Warning => match &code {
22 Some(code) if config.warnings_as_hint.contains(&code.code) => {
23 lsp_types::DiagnosticSeverity::Hint
24 }
25 Some(code) if config.warnings_as_info.contains(&code.code) => {
26 lsp_types::DiagnosticSeverity::Information
27 }
28 _ => lsp_types::DiagnosticSeverity::Warning,
29 },
30 DiagnosticLevel::Note => lsp_types::DiagnosticSeverity::Information,
31 DiagnosticLevel::Help => lsp_types::DiagnosticSeverity::Hint,
26 DiagnosticLevel::Unknown => return None, 32 DiagnosticLevel::Unknown => return None,
27 }; 33 };
28 Some(res) 34 Some(res)
@@ -33,90 +39,50 @@ fn is_from_macro(file_name: &str) -> bool {
33 file_name.starts_with('<') && file_name.ends_with('>') 39 file_name.starts_with('<') && file_name.ends_with('>')
34} 40}
35 41
36/// Converts a Rust macro span to a LSP location recursively
37fn map_macro_span_to_location(
38 span_macro: &DiagnosticSpanMacroExpansion,
39 workspace_root: &Path,
40) -> Option<Location> {
41 if !is_from_macro(&span_macro.span.file_name) {
42 return Some(map_span_to_location(&span_macro.span, workspace_root));
43 }
44
45 if let Some(expansion) = &span_macro.span.expansion {
46 return map_macro_span_to_location(&expansion, workspace_root);
47 }
48
49 None
50}
51
52/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary 42/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
53fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &Path) -> Location { 43fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
54 if span.expansion.is_some() { 44 let mut span = span.clone();
55 let expansion = span.expansion.as_ref().unwrap(); 45 while let Some(expansion) = span.expansion {
56 if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) { 46 span = expansion.span;
57 return macro_range;
58 }
59 } 47 }
60 48 return location_naive(workspace_root, &span);
61 map_span_to_location_naive(span, workspace_root)
62} 49}
63 50
64/// Converts a Rust span to a LSP location 51/// Converts a Rust span to a LSP location
65fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &Path) -> Location { 52fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
66 let mut file_name = workspace_root.to_path_buf(); 53 let file_name = workspace_root.join(&span.file_name);
67 file_name.push(&span.file_name); 54 let uri = url_from_abs_path(&file_name);
68 let uri = url_from_path_with_drive_lowercasing(file_name).unwrap();
69 55
70 // FIXME: this doesn't handle UTF16 offsets correctly 56 // FIXME: this doesn't handle UTF16 offsets correctly
71 let range = Range::new( 57 let range = lsp_types::Range::new(
72 Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1), 58 lsp_types::Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
73 Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1), 59 lsp_types::Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
74 ); 60 );
75 61
76 Location { uri, range } 62 lsp_types::Location { uri, range }
77} 63}
78 64
79/// Converts a secondary Rust span to a LSP related information 65/// Converts a secondary Rust span to a LSP related inflocation(ormation
80/// 66///
81/// If the span is unlabelled this will return `None`. 67/// If the span is unlabelled this will return `None`.
82fn map_secondary_span_to_related( 68fn diagnostic_related_information(
83 span: &DiagnosticSpan,
84 workspace_root: &Path, 69 workspace_root: &Path,
85) -> Option<DiagnosticRelatedInformation> { 70 span: &DiagnosticSpan,
71) -> Option<lsp_types::DiagnosticRelatedInformation> {
86 let message = span.label.clone()?; 72 let message = span.label.clone()?;
87 let location = map_span_to_location(span, workspace_root); 73 let location = location(workspace_root, span);
88 Some(DiagnosticRelatedInformation { location, message }) 74 Some(lsp_types::DiagnosticRelatedInformation { location, message })
89}
90
91/// Determines if diagnostic is related to unused code
92fn is_unused_or_unnecessary(rd: &ra_flycheck::Diagnostic) -> bool {
93 match &rd.code {
94 Some(code) => match code.code.as_str() {
95 "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes"
96 | "unused_imports" | "unused_macros" | "unused_variables" => true,
97 _ => false,
98 },
99 None => false,
100 }
101}
102
103/// Determines if diagnostic is related to deprecated code
104fn is_deprecated(rd: &ra_flycheck::Diagnostic) -> bool {
105 match &rd.code {
106 Some(code) => code.code.as_str() == "deprecated",
107 None => false,
108 }
109} 75}
110 76
111enum MappedRustChildDiagnostic { 77enum MappedRustChildDiagnostic {
112 Related(DiagnosticRelatedInformation), 78 Related(lsp_types::DiagnosticRelatedInformation),
113 SuggestedFix(lsp_ext::CodeAction), 79 SuggestedFix(lsp_ext::CodeAction),
114 MessageLine(String), 80 MessageLine(String),
115} 81}
116 82
117fn map_rust_child_diagnostic( 83fn map_rust_child_diagnostic(
118 rd: &ra_flycheck::Diagnostic,
119 workspace_root: &Path, 84 workspace_root: &Path,
85 rd: &flycheck::Diagnostic,
120) -> MappedRustChildDiagnostic { 86) -> MappedRustChildDiagnostic {
121 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect(); 87 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
122 if spans.is_empty() { 88 if spans.is_empty() {
@@ -125,43 +91,42 @@ fn map_rust_child_diagnostic(
125 return MappedRustChildDiagnostic::MessageLine(rd.message.clone()); 91 return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
126 } 92 }
127 93
128 let mut edit_map: HashMap<Url, Vec<TextEdit>> = HashMap::new(); 94 let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
129 for &span in &spans { 95 for &span in &spans {
130 match (&span.suggestion_applicability, &span.suggested_replacement) { 96 if let (Some(Applicability::MachineApplicable), Some(suggested_replacement)) =
131 (Some(Applicability::MachineApplicable), Some(suggested_replacement)) => { 97 (&span.suggestion_applicability, &span.suggested_replacement)
132 let location = map_span_to_location(span, workspace_root); 98 {
133 let edit = TextEdit::new(location.range, suggested_replacement.clone()); 99 let location = location(workspace_root, span);
134 edit_map.entry(location.uri).or_default().push(edit); 100 let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
135 } 101 edit_map.entry(location.uri).or_default().push(edit);
136 _ => {}
137 } 102 }
138 } 103 }
139 104
140 if edit_map.is_empty() { 105 if edit_map.is_empty() {
141 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation { 106 MappedRustChildDiagnostic::Related(lsp_types::DiagnosticRelatedInformation {
142 location: map_span_to_location(spans[0], workspace_root), 107 location: location(workspace_root, spans[0]),
143 message: rd.message.clone(), 108 message: rd.message.clone(),
144 }) 109 })
145 } else { 110 } else {
146 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { 111 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
147 title: rd.message.clone(), 112 title: rd.message.clone(),
113 id: None,
148 group: None, 114 group: None,
149 kind: Some("quickfix".to_string()), 115 kind: Some(lsp_types::CodeActionKind::QUICKFIX),
150 edit: Some(lsp_ext::SnippetWorkspaceEdit { 116 edit: Some(lsp_ext::SnippetWorkspaceEdit {
151 // FIXME: there's no good reason to use edit_map here.... 117 // FIXME: there's no good reason to use edit_map here....
152 changes: Some(edit_map), 118 changes: Some(edit_map),
153 document_changes: None, 119 document_changes: None,
154 }), 120 }),
155 command: None,
156 }) 121 })
157 } 122 }
158} 123}
159 124
160#[derive(Debug)] 125#[derive(Debug)]
161pub(crate) struct MappedRustDiagnostic { 126pub(crate) struct MappedRustDiagnostic {
162 pub location: Location, 127 pub(crate) url: lsp_types::Url,
163 pub diagnostic: Diagnostic, 128 pub(crate) diagnostic: lsp_types::Diagnostic,
164 pub fixes: Vec<lsp_ext::CodeAction>, 129 pub(crate) fixes: Vec<lsp_ext::CodeAction>,
165} 130}
166 131
167/// Converts a Rust root diagnostic to LSP form 132/// Converts a Rust root diagnostic to LSP form
@@ -175,7 +140,8 @@ pub(crate) struct MappedRustDiagnostic {
175/// 140///
176/// If the diagnostic has no primary span this will return `None` 141/// If the diagnostic has no primary span this will return `None`
177pub(crate) fn map_rust_diagnostic_to_lsp( 142pub(crate) fn map_rust_diagnostic_to_lsp(
178 rd: &ra_flycheck::Diagnostic, 143 config: &DiagnosticsConfig,
144 rd: &flycheck::Diagnostic,
179 workspace_root: &Path, 145 workspace_root: &Path,
180) -> Vec<MappedRustDiagnostic> { 146) -> Vec<MappedRustDiagnostic> {
181 let primary_spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect(); 147 let primary_spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
@@ -183,7 +149,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
183 return Vec::new(); 149 return Vec::new();
184 } 150 }
185 151
186 let severity = map_level_to_severity(rd.level); 152 let severity = diagnostic_severity(config, rd.level.clone(), rd.code.clone());
187 153
188 let mut source = String::from("rustc"); 154 let mut source = String::from("rustc");
189 let mut code = rd.code.as_ref().map(|c| c.code.clone()); 155 let mut code = rd.code.as_ref().map(|c| c.code.clone());
@@ -201,7 +167,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
201 let mut tags = Vec::new(); 167 let mut tags = Vec::new();
202 168
203 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { 169 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
204 let related = map_secondary_span_to_related(secondary_span, workspace_root); 170 let related = diagnostic_related_information(workspace_root, secondary_span);
205 if let Some(related) = related { 171 if let Some(related) = related {
206 related_information.push(related); 172 related_information.push(related);
207 } 173 }
@@ -210,7 +176,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
210 let mut fixes = Vec::new(); 176 let mut fixes = Vec::new();
211 let mut message = rd.message.clone(); 177 let mut message = rd.message.clone();
212 for child in &rd.children { 178 for child in &rd.children {
213 let child = map_rust_child_diagnostic(&child, workspace_root); 179 let child = map_rust_child_diagnostic(workspace_root, &child);
214 match child { 180 match child {
215 MappedRustChildDiagnostic::Related(related) => related_information.push(related), 181 MappedRustChildDiagnostic::Related(related) => related_information.push(related),
216 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action), 182 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action),
@@ -224,18 +190,30 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
224 } 190 }
225 } 191 }
226 192
227 if is_unused_or_unnecessary(rd) { 193 if let Some(code) = &rd.code {
228 tags.push(DiagnosticTag::Unnecessary); 194 let code = code.code.as_str();
229 } 195 if matches!(
196 code,
197 "dead_code"
198 | "unknown_lints"
199 | "unreachable_code"
200 | "unused_attributes"
201 | "unused_imports"
202 | "unused_macros"
203 | "unused_variables"
204 ) {
205 tags.push(lsp_types::DiagnosticTag::Unnecessary);
206 }
230 207
231 if is_deprecated(rd) { 208 if matches!(code, "deprecated") {
232 tags.push(DiagnosticTag::Deprecated); 209 tags.push(lsp_types::DiagnosticTag::Deprecated);
210 }
233 } 211 }
234 212
235 primary_spans 213 primary_spans
236 .iter() 214 .iter()
237 .map(|primary_span| { 215 .map(|primary_span| {
238 let location = map_span_to_location(&primary_span, workspace_root); 216 let location = location(workspace_root, &primary_span);
239 217
240 let mut message = message.clone(); 218 let mut message = message.clone();
241 if needs_primary_span_label { 219 if needs_primary_span_label {
@@ -247,17 +225,16 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
247 // If error occurs from macro expansion, add related info pointing to 225 // If error occurs from macro expansion, add related info pointing to
248 // where the error originated 226 // where the error originated
249 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { 227 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
250 let def_loc = map_span_to_location_naive(&primary_span, workspace_root); 228 related_information.push(lsp_types::DiagnosticRelatedInformation {
251 related_information.push(DiagnosticRelatedInformation { 229 location: location_naive(workspace_root, &primary_span),
252 location: def_loc,
253 message: "Error originated from macro here".to_string(), 230 message: "Error originated from macro here".to_string(),
254 }); 231 });
255 } 232 }
256 233
257 let diagnostic = Diagnostic { 234 let diagnostic = lsp_types::Diagnostic {
258 range: location.range, 235 range: location.range,
259 severity, 236 severity,
260 code: code.clone().map(NumberOrString::String), 237 code: code.clone().map(lsp_types::NumberOrString::String),
261 source: Some(source.clone()), 238 source: Some(source.clone()),
262 message, 239 message,
263 related_information: if related_information.is_empty() { 240 related_information: if related_information.is_empty() {
@@ -268,77 +245,32 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
268 tags: if tags.is_empty() { None } else { Some(tags.clone()) }, 245 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
269 }; 246 };
270 247
271 MappedRustDiagnostic { location, diagnostic, fixes: fixes.clone() } 248 MappedRustDiagnostic { url: location.uri, diagnostic, fixes: fixes.clone() }
272 }) 249 })
273 .collect() 250 .collect()
274} 251}
275 252
276/// Returns a `Url` object from a given path, will lowercase drive letters if present.
277/// This will only happen when processing windows paths.
278///
279/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
280pub fn url_from_path_with_drive_lowercasing(path: impl AsRef<Path>) -> Result<Url> {
281 let component_has_windows_drive = path.as_ref().components().any(|comp| {
282 if let Component::Prefix(c) = comp {
283 return matches!(c.kind(), Prefix::Disk(_) | Prefix::VerbatimDisk(_));
284 }
285 false
286 });
287
288 // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
289 let res = if component_has_windows_drive {
290 let url_original = Url::from_file_path(&path)
291 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;
292
293 let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect();
294
295 // There is a drive partition, but we never found a colon.
296 // This should not happen, but in this case we just pass it through.
297 if drive_partition.len() == 1 {
298 return Ok(url_original);
299 }
300
301 let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
302 let url = Url::from_str(&joined).expect("This came from a valid `Url`");
303
304 url
305 } else {
306 Url::from_file_path(&path)
307 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?
308 };
309 Ok(res)
310}
311
312#[cfg(test)] 253#[cfg(test)]
254#[cfg(not(windows))]
313mod tests { 255mod tests {
314 use super::*; 256 use super::*;
315 257
316 // `Url` is not able to parse windows paths on unix machines. 258 use expect::{expect_file, ExpectFile};
317 #[test]
318 #[cfg(target_os = "windows")]
319 fn test_lowercase_drive_letter_with_drive() {
320 let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap();
321 259
322 assert_eq!(url.to_string(), "file:///c:/Test"); 260 fn check(diagnostics_json: &str, expect: ExpectFile) {
261 check_with_config(DiagnosticsConfig::default(), diagnostics_json, expect)
323 } 262 }
324 263
325 #[test] 264 fn check_with_config(config: DiagnosticsConfig, diagnostics_json: &str, expect: ExpectFile) {
326 #[cfg(target_os = "windows")] 265 let diagnostic: flycheck::Diagnostic = serde_json::from_str(diagnostics_json).unwrap();
327 fn test_drive_without_colon_passthrough() { 266 let workspace_root = Path::new("/test/");
328 let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap(); 267 let actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root);
329 268 expect.assert_debug_eq(&actual)
330 assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
331 }
332
333 #[cfg(not(windows))]
334 fn parse_diagnostic(val: &str) -> ra_flycheck::Diagnostic {
335 serde_json::from_str::<ra_flycheck::Diagnostic>(val).unwrap()
336 } 269 }
337 270
338 #[test] 271 #[test]
339 #[cfg(not(windows))] 272 fn rustc_incompatible_type_for_trait() {
340 fn snap_rustc_incompatible_type_for_trait() { 273 check(
341 let diag = parse_diagnostic(
342 r##"{ 274 r##"{
343 "message": "method `next` has an incompatible type for trait", 275 "message": "method `next` has an incompatible type for trait",
344 "code": { 276 "code": {
@@ -382,17 +314,97 @@ mod tests {
382 "rendered": "error[E0053]: method `next` has an incompatible type for trait\n --> compiler/ty/list_iter.rs:52:5\n |\n52 | fn next(&self) -> Option<&'list ty::Ref<M>> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability\n |\n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`\n\n" 314 "rendered": "error[E0053]: method `next` has an incompatible type for trait\n --> compiler/ty/list_iter.rs:52:5\n |\n52 | fn next(&self) -> Option<&'list ty::Ref<M>> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability\n |\n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`\n\n"
383 } 315 }
384 "##, 316 "##,
317 expect_file!["crates/rust-analyzer/test_data/rustc_incompatible_type_for_trait.txt"],
385 ); 318 );
319 }
386 320
387 let workspace_root = Path::new("/test/"); 321 #[test]
388 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 322 fn rustc_unused_variable() {
389 insta::assert_debug_snapshot!(diag); 323 check(
324 r##"{
325 "message": "unused variable: `foo`",
326 "code": {
327 "code": "unused_variables",
328 "explanation": null
329 },
330 "level": "warning",
331 "spans": [
332 {
333 "file_name": "driver/subcommand/repl.rs",
334 "byte_start": 9228,
335 "byte_end": 9231,
336 "line_start": 291,
337 "line_end": 291,
338 "column_start": 9,
339 "column_end": 12,
340 "is_primary": true,
341 "text": [
342 {
343 "text": " let foo = 42;",
344 "highlight_start": 9,
345 "highlight_end": 12
346 }
347 ],
348 "label": null,
349 "suggested_replacement": null,
350 "suggestion_applicability": null,
351 "expansion": null
352 }
353 ],
354 "children": [
355 {
356 "message": "#[warn(unused_variables)] on by default",
357 "code": null,
358 "level": "note",
359 "spans": [],
360 "children": [],
361 "rendered": null
362 },
363 {
364 "message": "consider prefixing with an underscore",
365 "code": null,
366 "level": "help",
367 "spans": [
368 {
369 "file_name": "driver/subcommand/repl.rs",
370 "byte_start": 9228,
371 "byte_end": 9231,
372 "line_start": 291,
373 "line_end": 291,
374 "column_start": 9,
375 "column_end": 12,
376 "is_primary": true,
377 "text": [
378 {
379 "text": " let foo = 42;",
380 "highlight_start": 9,
381 "highlight_end": 12
382 }
383 ],
384 "label": null,
385 "suggested_replacement": "_foo",
386 "suggestion_applicability": "MachineApplicable",
387 "expansion": null
388 }
389 ],
390 "children": [],
391 "rendered": null
392 }
393 ],
394 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
395 }"##,
396 expect_file!["crates/rust-analyzer/test_data/rustc_unused_variable.txt"],
397 );
390 } 398 }
391 399
392 #[test] 400 #[test]
393 #[cfg(not(windows))] 401 #[cfg(not(windows))]
394 fn snap_rustc_unused_variable() { 402 fn rustc_unused_variable_as_info() {
395 let diag = parse_diagnostic( 403 check_with_config(
404 DiagnosticsConfig {
405 warnings_as_info: vec!["unused_variables".to_string()],
406 ..DiagnosticsConfig::default()
407 },
396 r##"{ 408 r##"{
397 "message": "unused variable: `foo`", 409 "message": "unused variable: `foo`",
398 "code": { 410 "code": {
@@ -465,17 +477,97 @@ mod tests {
465 ], 477 ],
466 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n" 478 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
467 }"##, 479 }"##,
480 expect_file!["crates/rust-analyzer/test_data/rustc_unused_variable_as_info.txt"],
468 ); 481 );
469
470 let workspace_root = Path::new("/test/");
471 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
472 insta::assert_debug_snapshot!(diag);
473 } 482 }
474 483
475 #[test] 484 #[test]
476 #[cfg(not(windows))] 485 #[cfg(not(windows))]
477 fn snap_rustc_wrong_number_of_parameters() { 486 fn rustc_unused_variable_as_hint() {
478 let diag = parse_diagnostic( 487 check_with_config(
488 DiagnosticsConfig {
489 warnings_as_hint: vec!["unused_variables".to_string()],
490 ..DiagnosticsConfig::default()
491 },
492 r##"{
493 "message": "unused variable: `foo`",
494 "code": {
495 "code": "unused_variables",
496 "explanation": null
497 },
498 "level": "warning",
499 "spans": [
500 {
501 "file_name": "driver/subcommand/repl.rs",
502 "byte_start": 9228,
503 "byte_end": 9231,
504 "line_start": 291,
505 "line_end": 291,
506 "column_start": 9,
507 "column_end": 12,
508 "is_primary": true,
509 "text": [
510 {
511 "text": " let foo = 42;",
512 "highlight_start": 9,
513 "highlight_end": 12
514 }
515 ],
516 "label": null,
517 "suggested_replacement": null,
518 "suggestion_applicability": null,
519 "expansion": null
520 }
521 ],
522 "children": [
523 {
524 "message": "#[warn(unused_variables)] on by default",
525 "code": null,
526 "level": "note",
527 "spans": [],
528 "children": [],
529 "rendered": null
530 },
531 {
532 "message": "consider prefixing with an underscore",
533 "code": null,
534 "level": "help",
535 "spans": [
536 {
537 "file_name": "driver/subcommand/repl.rs",
538 "byte_start": 9228,
539 "byte_end": 9231,
540 "line_start": 291,
541 "line_end": 291,
542 "column_start": 9,
543 "column_end": 12,
544 "is_primary": true,
545 "text": [
546 {
547 "text": " let foo = 42;",
548 "highlight_start": 9,
549 "highlight_end": 12
550 }
551 ],
552 "label": null,
553 "suggested_replacement": "_foo",
554 "suggestion_applicability": "MachineApplicable",
555 "expansion": null
556 }
557 ],
558 "children": [],
559 "rendered": null
560 }
561 ],
562 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
563 }"##,
564 expect_file!["crates/rust-analyzer/test_data/rustc_unused_variable_as_hint.txt"],
565 );
566 }
567
568 #[test]
569 fn rustc_wrong_number_of_parameters() {
570 check(
479 r##"{ 571 r##"{
480 "message": "this function takes 2 parameters but 3 parameters were supplied", 572 "message": "this function takes 2 parameters but 3 parameters were supplied",
481 "code": { 573 "code": {
@@ -590,17 +682,13 @@ mod tests {
590 "children": [], 682 "children": [],
591 "rendered": "error[E0061]: this function takes 2 parameters but 3 parameters were supplied\n --> compiler/ty/select.rs:104:18\n |\n104 | self.add_evidence(target_fixed, evidence_fixed, false);\n | ^^^^^^^^^^^^ expected 2 parameters\n...\n219 | / pub fn add_evidence(\n220 | | &mut self,\n221 | | target_poly: &ty::Ref<ty::Poly>,\n222 | | evidence_poly: &ty::Ref<ty::Poly>,\n... |\n230 | | }\n231 | | }\n | |_____- defined here\n\n" 683 "rendered": "error[E0061]: this function takes 2 parameters but 3 parameters were supplied\n --> compiler/ty/select.rs:104:18\n |\n104 | self.add_evidence(target_fixed, evidence_fixed, false);\n | ^^^^^^^^^^^^ expected 2 parameters\n...\n219 | / pub fn add_evidence(\n220 | | &mut self,\n221 | | target_poly: &ty::Ref<ty::Poly>,\n222 | | evidence_poly: &ty::Ref<ty::Poly>,\n... |\n230 | | }\n231 | | }\n | |_____- defined here\n\n"
592 }"##, 684 }"##,
685 expect_file!["crates/rust-analyzer/test_data/rustc_wrong_number_of_parameters.txt"],
593 ); 686 );
594
595 let workspace_root = Path::new("/test/");
596 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
597 insta::assert_debug_snapshot!(diag);
598 } 687 }
599 688
600 #[test] 689 #[test]
601 #[cfg(not(windows))] 690 fn clippy_pass_by_ref() {
602 fn snap_clippy_pass_by_ref() { 691 check(
603 let diag = parse_diagnostic(
604 r##"{ 692 r##"{
605 "message": "this argument is passed by reference, but would be more efficient if passed by value", 693 "message": "this argument is passed by reference, but would be more efficient if passed by value",
606 "code": { 694 "code": {
@@ -711,17 +799,13 @@ mod tests {
711 ], 799 ],
712 "rendered": "warning: this argument is passed by reference, but would be more efficient if passed by value\n --> compiler/mir/tagset.rs:42:24\n |\n42 | pub fn is_disjoint(&self, other: Self) -> bool {\n | ^^^^^ help: consider passing by value instead: `self`\n |\nnote: lint level defined here\n --> compiler/lib.rs:1:9\n |\n1 | #![warn(clippy::all)]\n | ^^^^^^^^^^^\n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref\n\n" 800 "rendered": "warning: this argument is passed by reference, but would be more efficient if passed by value\n --> compiler/mir/tagset.rs:42:24\n |\n42 | pub fn is_disjoint(&self, other: Self) -> bool {\n | ^^^^^ help: consider passing by value instead: `self`\n |\nnote: lint level defined here\n --> compiler/lib.rs:1:9\n |\n1 | #![warn(clippy::all)]\n | ^^^^^^^^^^^\n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref\n\n"
713 }"##, 801 }"##,
802 expect_file!["crates/rust-analyzer/test_data/clippy_pass_by_ref.txt"],
714 ); 803 );
715
716 let workspace_root = Path::new("/test/");
717 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
718 insta::assert_debug_snapshot!(diag);
719 } 804 }
720 805
721 #[test] 806 #[test]
722 #[cfg(not(windows))] 807 fn rustc_mismatched_type() {
723 fn snap_rustc_mismatched_type() { 808 check(
724 let diag = parse_diagnostic(
725 r##"{ 809 r##"{
726 "message": "mismatched types", 810 "message": "mismatched types",
727 "code": { 811 "code": {
@@ -755,17 +839,13 @@ mod tests {
755 "children": [], 839 "children": [],
756 "rendered": "error[E0308]: mismatched types\n --> runtime/compiler_support.rs:48:65\n |\n48 | let layout = alloc::Layout::from_size_align_unchecked(size, align);\n | ^^^^^ expected usize, found u32\n\n" 840 "rendered": "error[E0308]: mismatched types\n --> runtime/compiler_support.rs:48:65\n |\n48 | let layout = alloc::Layout::from_size_align_unchecked(size, align);\n | ^^^^^ expected usize, found u32\n\n"
757 }"##, 841 }"##,
842 expect_file!["crates/rust-analyzer/test_data/rustc_mismatched_type.txt"],
758 ); 843 );
759
760 let workspace_root = Path::new("/test/");
761 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
762 insta::assert_debug_snapshot!(diag);
763 } 844 }
764 845
765 #[test] 846 #[test]
766 #[cfg(not(windows))] 847 fn handles_macro_location() {
767 fn snap_handles_macro_location() { 848 check(
768 let diag = parse_diagnostic(
769 r##"{ 849 r##"{
770 "rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n", 850 "rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n",
771 "children": [ 851 "children": [
@@ -1027,17 +1107,13 @@ mod tests {
1027 } 1107 }
1028 ] 1108 ]
1029 }"##, 1109 }"##,
1110 expect_file!["crates/rust-analyzer/test_data/handles_macro_location.txt"],
1030 ); 1111 );
1031
1032 let workspace_root = Path::new("/test/");
1033 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
1034 insta::assert_debug_snapshot!(diag);
1035 } 1112 }
1036 1113
1037 #[test] 1114 #[test]
1038 #[cfg(not(windows))] 1115 fn macro_compiler_error() {
1039 fn snap_macro_compiler_error() { 1116 check(
1040 let diag = parse_diagnostic(
1041 r##"{ 1117 r##"{
1042 "rendered": "error: Please register your known path in the path module\n --> crates/ra_hir_def/src/path.rs:265:9\n |\n265 | compile_error!(\"Please register your known path in the path module\")\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | \n ::: crates/ra_hir_def/src/data.rs:80:16\n |\n80 | let path = path![std::future::Future];\n | -------------------------- in this macro invocation\n\n", 1118 "rendered": "error: Please register your known path in the path module\n --> crates/ra_hir_def/src/path.rs:265:9\n |\n265 | compile_error!(\"Please register your known path in the path module\")\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | \n ::: crates/ra_hir_def/src/data.rs:80:16\n |\n80 | let path = path![std::future::Future];\n | -------------------------- in this macro invocation\n\n",
1043 "children": [], 1119 "children": [],
@@ -1257,17 +1333,13 @@ mod tests {
1257 ] 1333 ]
1258 } 1334 }
1259 "##, 1335 "##,
1336 expect_file!["crates/rust-analyzer/test_data/macro_compiler_error.txt"],
1260 ); 1337 );
1261
1262 let workspace_root = Path::new("/test/");
1263 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
1264 insta::assert_debug_snapshot!(diag);
1265 } 1338 }
1266 1339
1267 #[test] 1340 #[test]
1268 #[cfg(not(windows))]
1269 fn snap_multi_line_fix() { 1341 fn snap_multi_line_fix() {
1270 let diag = parse_diagnostic( 1342 check(
1271 r##"{ 1343 r##"{
1272 "rendered": "warning: returning the result of a let binding from a block\n --> src/main.rs:4:5\n |\n3 | let a = (0..10).collect();\n | -------------------------- unnecessary let binding\n4 | a\n | ^\n |\n = note: `#[warn(clippy::let_and_return)]` on by default\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return\nhelp: return the expression directly\n |\n3 | \n4 | (0..10).collect()\n |\n\n", 1344 "rendered": "warning: returning the result of a let binding from a block\n --> src/main.rs:4:5\n |\n3 | let a = (0..10).collect();\n | -------------------------- unnecessary let binding\n4 | a\n | ^\n |\n = note: `#[warn(clippy::let_and_return)]` on by default\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return\nhelp: return the expression directly\n |\n3 | \n4 | (0..10).collect()\n |\n\n",
1273 "children": [ 1345 "children": [
@@ -1391,10 +1463,7 @@ mod tests {
1391 ] 1463 ]
1392 } 1464 }
1393 "##, 1465 "##,
1466 expect_file!["crates/rust-analyzer/test_data/snap_multi_line_fix.txt"],
1394 ); 1467 );
1395
1396 let workspace_root = Path::new("/test/");
1397 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
1398 insta::assert_debug_snapshot!(diag);
1399 } 1468 }
1400} 1469}
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs
new file mode 100644
index 000000000..891fdb96d
--- /dev/null
+++ b/crates/rust-analyzer/src/dispatch.rs
@@ -0,0 +1,170 @@
1//! A visitor for downcasting arbitrary request (JSON) into a specific type.
2use std::panic;
3
4use serde::{de::DeserializeOwned, Serialize};
5
6use crate::{
7 global_state::{GlobalState, GlobalStateSnapshot},
8 lsp_utils::is_canceled,
9 main_loop::Task,
10 LspError, Result,
11};
12
13pub(crate) struct RequestDispatcher<'a> {
14 pub(crate) req: Option<lsp_server::Request>,
15 pub(crate) global_state: &'a mut GlobalState,
16}
17
18impl<'a> RequestDispatcher<'a> {
19 /// Dispatches the request onto the current thread
20 pub(crate) fn on_sync<R>(
21 &mut self,
22 f: fn(&mut GlobalState, R::Params) -> Result<R::Result>,
23 ) -> Result<&mut Self>
24 where
25 R: lsp_types::request::Request + 'static,
26 R::Params: DeserializeOwned + panic::UnwindSafe + 'static,
27 R::Result: Serialize + 'static,
28 {
29 let (id, params) = match self.parse::<R>() {
30 Some(it) => it,
31 None => {
32 return Ok(self);
33 }
34 };
35 let world = panic::AssertUnwindSafe(&mut *self.global_state);
36 let response = panic::catch_unwind(move || {
37 let result = f(world.0, params);
38 result_to_response::<R>(id, result)
39 })
40 .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?;
41 self.global_state.respond(response);
42 Ok(self)
43 }
44
45 /// Dispatches the request onto thread pool
46 pub(crate) fn on<R>(
47 &mut self,
48 f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
49 ) -> Result<&mut Self>
50 where
51 R: lsp_types::request::Request + 'static,
52 R::Params: DeserializeOwned + Send + 'static,
53 R::Result: Serialize + 'static,
54 {
55 let (id, params) = match self.parse::<R>() {
56 Some(it) => it,
57 None => {
58 return Ok(self);
59 }
60 };
61
62 self.global_state.task_pool.handle.spawn({
63 let world = self.global_state.snapshot();
64 move || {
65 let result = f(world, params);
66 Task::Response(result_to_response::<R>(id, result))
67 }
68 });
69
70 Ok(self)
71 }
72
73 pub(crate) fn finish(&mut self) {
74 if let Some(req) = self.req.take() {
75 log::error!("unknown request: {:?}", req);
76 let response = lsp_server::Response::new_err(
77 req.id,
78 lsp_server::ErrorCode::MethodNotFound as i32,
79 "unknown request".to_string(),
80 );
81 self.global_state.respond(response)
82 }
83 }
84
85 fn parse<R>(&mut self) -> Option<(lsp_server::RequestId, R::Params)>
86 where
87 R: lsp_types::request::Request + 'static,
88 R::Params: DeserializeOwned + 'static,
89 {
90 let req = self.req.take()?;
91 let (id, params) = match req.extract::<R::Params>(R::METHOD) {
92 Ok(it) => it,
93 Err(req) => {
94 self.req = Some(req);
95 return None;
96 }
97 };
98 Some((id, params))
99 }
100}
101
102fn result_to_response<R>(
103 id: lsp_server::RequestId,
104 result: Result<R::Result>,
105) -> lsp_server::Response
106where
107 R: lsp_types::request::Request + 'static,
108 R::Params: DeserializeOwned + 'static,
109 R::Result: Serialize + 'static,
110{
111 match result {
112 Ok(resp) => lsp_server::Response::new_ok(id, &resp),
113 Err(e) => match e.downcast::<LspError>() {
114 Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message),
115 Err(e) => {
116 if is_canceled(&*e) {
117 lsp_server::Response::new_err(
118 id,
119 lsp_server::ErrorCode::ContentModified as i32,
120 "content modified".to_string(),
121 )
122 } else {
123 lsp_server::Response::new_err(
124 id,
125 lsp_server::ErrorCode::InternalError as i32,
126 e.to_string(),
127 )
128 }
129 }
130 },
131 }
132}
133
134pub(crate) struct NotificationDispatcher<'a> {
135 pub(crate) not: Option<lsp_server::Notification>,
136 pub(crate) global_state: &'a mut GlobalState,
137}
138
139impl<'a> NotificationDispatcher<'a> {
140 pub(crate) fn on<N>(
141 &mut self,
142 f: fn(&mut GlobalState, N::Params) -> Result<()>,
143 ) -> Result<&mut Self>
144 where
145 N: lsp_types::notification::Notification + 'static,
146 N::Params: DeserializeOwned + Send + 'static,
147 {
148 let not = match self.not.take() {
149 Some(it) => it,
150 None => return Ok(self),
151 };
152 let params = match not.extract::<N::Params>(N::METHOD) {
153 Ok(it) => it,
154 Err(not) => {
155 self.not = Some(not);
156 return Ok(self);
157 }
158 };
159 f(self.global_state, params)?;
160 Ok(self)
161 }
162
163 pub(crate) fn finish(&mut self) {
164 if let Some(not) = &self.not {
165 if !not.method.starts_with("$/") {
166 log::error!("unhandled notification: {:?}", not);
167 }
168 }
169 }
170}
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs
index 4bb16a496..9f8ce3b99 100644
--- a/crates/rust-analyzer/src/from_proto.rs
+++ b/crates/rust-analyzer/src/from_proto.rs
@@ -1,9 +1,21 @@
1//! Conversion lsp_types types to rust-analyzer specific ones. 1//! Conversion lsp_types types to rust-analyzer specific ones.
2use std::convert::TryFrom;
3
2use ra_db::{FileId, FilePosition, FileRange}; 4use ra_db::{FileId, FilePosition, FileRange};
3use ra_ide::{LineCol, LineIndex}; 5use ra_ide::{AssistKind, LineCol, LineIndex};
4use ra_syntax::{TextRange, TextSize}; 6use ra_syntax::{TextRange, TextSize};
7use vfs::AbsPathBuf;
8
9use crate::{global_state::GlobalStateSnapshot, Result};
10
11pub(crate) fn abs_path(url: &lsp_types::Url) -> Result<AbsPathBuf> {
12 let path = url.to_file_path().map_err(|()| "url is not a file")?;
13 Ok(AbsPathBuf::try_from(path).unwrap())
14}
5 15
6use crate::{world::WorldSnapshot, Result}; 16pub(crate) fn vfs_path(url: &lsp_types::Url) -> Result<vfs::VfsPath> {
17 abs_path(url).map(vfs::VfsPath::from)
18}
7 19
8pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { 20pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize {
9 let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 }; 21 let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 };
@@ -16,27 +28,41 @@ pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Tex
16 TextRange::new(start, end) 28 TextRange::new(start, end)
17} 29}
18 30
19pub(crate) fn file_id(world: &WorldSnapshot, url: &lsp_types::Url) -> Result<FileId> { 31pub(crate) fn file_id(world: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> {
20 world.uri_to_file_id(url) 32 world.url_to_file_id(url)
21} 33}
22 34
23pub(crate) fn file_position( 35pub(crate) fn file_position(
24 world: &WorldSnapshot, 36 world: &GlobalStateSnapshot,
25 tdpp: lsp_types::TextDocumentPositionParams, 37 tdpp: lsp_types::TextDocumentPositionParams,
26) -> Result<FilePosition> { 38) -> Result<FilePosition> {
27 let file_id = file_id(world, &tdpp.text_document.uri)?; 39 let file_id = file_id(world, &tdpp.text_document.uri)?;
28 let line_index = world.analysis().file_line_index(file_id)?; 40 let line_index = world.analysis.file_line_index(file_id)?;
29 let offset = offset(&*line_index, tdpp.position); 41 let offset = offset(&*line_index, tdpp.position);
30 Ok(FilePosition { file_id, offset }) 42 Ok(FilePosition { file_id, offset })
31} 43}
32 44
33pub(crate) fn file_range( 45pub(crate) fn file_range(
34 world: &WorldSnapshot, 46 world: &GlobalStateSnapshot,
35 text_document_identifier: lsp_types::TextDocumentIdentifier, 47 text_document_identifier: lsp_types::TextDocumentIdentifier,
36 range: lsp_types::Range, 48 range: lsp_types::Range,
37) -> Result<FileRange> { 49) -> Result<FileRange> {
38 let file_id = file_id(world, &text_document_identifier.uri)?; 50 let file_id = file_id(world, &text_document_identifier.uri)?;
39 let line_index = world.analysis().file_line_index(file_id)?; 51 let line_index = world.analysis.file_line_index(file_id)?;
40 let range = text_range(&line_index, range); 52 let range = text_range(&line_index, range);
41 Ok(FileRange { file_id, range }) 53 Ok(FileRange { file_id, range })
42} 54}
55
56pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind> {
57 let assist_kind = match &kind {
58 k if k == &lsp_types::CodeActionKind::EMPTY => AssistKind::None,
59 k if k == &lsp_types::CodeActionKind::QUICKFIX => AssistKind::QuickFix,
60 k if k == &lsp_types::CodeActionKind::REFACTOR => AssistKind::Refactor,
61 k if k == &lsp_types::CodeActionKind::REFACTOR_EXTRACT => AssistKind::RefactorExtract,
62 k if k == &lsp_types::CodeActionKind::REFACTOR_INLINE => AssistKind::RefactorInline,
63 k if k == &lsp_types::CodeActionKind::REFACTOR_REWRITE => AssistKind::RefactorRewrite,
64 _ => return None,
65 };
66
67 Some(assist_kind)
68}
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
new file mode 100644
index 000000000..94973b90a
--- /dev/null
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -0,0 +1,292 @@
1//! The context or environment in which the language server functions. In our
2//! server implementation this is know as the `WorldState`.
3//!
4//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
5
6use std::{sync::Arc, time::Instant};
7
8use crossbeam_channel::{unbounded, Receiver, Sender};
9use flycheck::FlycheckHandle;
10use lsp_types::Url;
11use parking_lot::RwLock;
12use ra_db::{CrateId, VfsPath};
13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId};
14use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target};
15use rustc_hash::{FxHashMap, FxHashSet};
16
17use crate::{
18 config::Config,
19 diagnostics::{CheckFixes, DiagnosticCollection},
20 from_proto,
21 line_endings::LineEndings,
22 main_loop::Task,
23 reload::SourceRootConfig,
24 request_metrics::{LatestRequests, RequestMetrics},
25 thread_pool::TaskPool,
26 to_proto::url_from_abs_path,
27 Result,
28};
29use ra_prof::profile;
30
31#[derive(Eq, PartialEq, Copy, Clone)]
32pub(crate) enum Status {
33 Loading,
34 Ready,
35 Invalid,
36 NeedsReload,
37}
38
39impl Default for Status {
40 fn default() -> Self {
41 Status::Loading
42 }
43}
44
45// Enforces drop order
46pub(crate) struct Handle<H, C> {
47 pub(crate) handle: H,
48 pub(crate) receiver: C,
49}
50
51pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
52pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
53
54/// `GlobalState` is the primary mutable state of the language server
55///
56/// The most interesting components are `vfs`, which stores a consistent
57/// snapshot of the file systems, and `analysis_host`, which stores our
58/// incremental salsa database.
59///
60/// Note that this struct has more than on impl in various modules!
61pub(crate) struct GlobalState {
62 sender: Sender<lsp_server::Message>,
63 req_queue: ReqQueue,
64 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
65 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
66 pub(crate) flycheck: Option<FlycheckHandle>,
67 pub(crate) flycheck_sender: Sender<flycheck::Message>,
68 pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
69 pub(crate) config: Config,
70 pub(crate) analysis_host: AnalysisHost,
71 pub(crate) diagnostics: DiagnosticCollection,
72 pub(crate) mem_docs: FxHashSet<VfsPath>,
73 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
74 pub(crate) status: Status,
75 pub(crate) source_root_config: SourceRootConfig,
76 pub(crate) proc_macro_client: ProcMacroClient,
77 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
78 latest_requests: Arc<RwLock<LatestRequests>>,
79}
80
81/// An immutable snapshot of the world's state at a point in time.
82pub(crate) struct GlobalStateSnapshot {
83 pub(crate) config: Config,
84 pub(crate) analysis: Analysis,
85 pub(crate) check_fixes: CheckFixes,
86 pub(crate) latest_requests: Arc<RwLock<LatestRequests>>,
87 vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
88 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
89}
90
91impl GlobalState {
92 pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
93 let loader = {
94 let (sender, receiver) = unbounded::<vfs::loader::Message>();
95 let handle: vfs_notify::NotifyHandle =
96 vfs::loader::Handle::spawn(Box::new(move |msg| sender.send(msg).unwrap()));
97 let handle = Box::new(handle) as Box<dyn vfs::loader::Handle>;
98 Handle { handle, receiver }
99 };
100
101 let task_pool = {
102 let (sender, receiver) = unbounded();
103 let handle = TaskPool::new(sender);
104 Handle { handle, receiver }
105 };
106
107 let analysis_host = AnalysisHost::new(config.lru_capacity);
108 let (flycheck_sender, flycheck_receiver) = unbounded();
109 GlobalState {
110 sender,
111 req_queue: ReqQueue::default(),
112 task_pool,
113 loader,
114 flycheck: None,
115 flycheck_sender,
116 flycheck_receiver,
117 config,
118 analysis_host,
119 diagnostics: Default::default(),
120 mem_docs: FxHashSet::default(),
121 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
122 status: Status::default(),
123 source_root_config: SourceRootConfig::default(),
124 proc_macro_client: ProcMacroClient::dummy(),
125 workspaces: Arc::new(Vec::new()),
126 latest_requests: Default::default(),
127 }
128 }
129
130 pub(crate) fn process_changes(&mut self) -> bool {
131 let _p = profile("GlobalState::process_changes");
132 let mut fs_changes = Vec::new();
133 let mut has_fs_changes = false;
134
135 let change = {
136 let mut change = AnalysisChange::new();
137 let (vfs, line_endings_map) = &mut *self.vfs.write();
138 let changed_files = vfs.take_changes();
139 if changed_files.is_empty() {
140 return false;
141 }
142
143 for file in changed_files {
144 if file.is_created_or_deleted() {
145 if let Some(path) = vfs.file_path(file.file_id).as_path() {
146 fs_changes.push((path.to_path_buf(), file.change_kind));
147 has_fs_changes = true;
148 }
149 }
150
151 let text = if file.exists() {
152 let bytes = vfs.file_contents(file.file_id).to_vec();
153 match String::from_utf8(bytes).ok() {
154 Some(text) => {
155 let (text, line_endings) = LineEndings::normalize(text);
156 line_endings_map.insert(file.file_id, line_endings);
157 Some(Arc::new(text))
158 }
159 None => None,
160 }
161 } else {
162 None
163 };
164 change.change_file(file.file_id, text);
165 }
166 if has_fs_changes {
167 let roots = self.source_root_config.partition(&vfs);
168 change.set_roots(roots);
169 }
170 change
171 };
172
173 self.analysis_host.apply_change(change);
174 self.maybe_refresh(&fs_changes);
175 true
176 }
177
178 pub(crate) fn snapshot(&self) -> GlobalStateSnapshot {
179 GlobalStateSnapshot {
180 config: self.config.clone(),
181 workspaces: Arc::clone(&self.workspaces),
182 analysis: self.analysis_host.analysis(),
183 vfs: Arc::clone(&self.vfs),
184 latest_requests: Arc::clone(&self.latest_requests),
185 check_fixes: Arc::clone(&self.diagnostics.check_fixes),
186 }
187 }
188
189 pub(crate) fn send_request<R: lsp_types::request::Request>(
190 &mut self,
191 params: R::Params,
192 handler: ReqHandler,
193 ) {
194 let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler);
195 self.send(request.into());
196 }
197 pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
198 let handler = self.req_queue.outgoing.complete(response.id.clone());
199 handler(self, response)
200 }
201
202 pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
203 &mut self,
204 params: N::Params,
205 ) {
206 let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
207 self.send(not.into());
208 }
209
210 pub(crate) fn register_request(
211 &mut self,
212 request: &lsp_server::Request,
213 request_received: Instant,
214 ) {
215 self.req_queue
216 .incoming
217 .register(request.id.clone(), (request.method.clone(), request_received));
218 }
219 pub(crate) fn respond(&mut self, response: lsp_server::Response) {
220 if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
221 let duration = start.elapsed();
222 log::info!("handled req#{} in {:?}", response.id, duration);
223 let metrics = RequestMetrics { id: response.id.clone(), method, duration };
224 self.latest_requests.write().record(metrics);
225 self.send(response.into());
226 }
227 }
228 pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
229 if let Some(response) = self.req_queue.incoming.cancel(request_id) {
230 self.send(response.into());
231 }
232 }
233
234 fn send(&mut self, message: lsp_server::Message) {
235 self.sender.send(message).unwrap()
236 }
237}
238
239impl Drop for GlobalState {
240 fn drop(&mut self) {
241 self.analysis_host.request_cancellation()
242 }
243}
244
245impl GlobalStateSnapshot {
246 pub(crate) fn url_to_file_id(&self, url: &Url) -> Result<FileId> {
247 url_to_file_id(&self.vfs.read().0, url)
248 }
249
250 pub(crate) fn file_id_to_url(&self, id: FileId) -> Url {
251 file_id_to_url(&self.vfs.read().0, id)
252 }
253
254 pub(crate) fn file_line_endings(&self, id: FileId) -> LineEndings {
255 self.vfs.read().1[&id]
256 }
257
258 pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url {
259 let mut base = self.vfs.read().0.file_path(file_id);
260 base.pop();
261 let path = base.join(path).unwrap();
262 let path = path.as_path().unwrap();
263 url_from_abs_path(&path)
264 }
265
266 pub(crate) fn cargo_target_for_crate_root(
267 &self,
268 crate_id: CrateId,
269 ) -> Option<(&CargoWorkspace, Target)> {
270 let file_id = self.analysis.crate_root(crate_id).ok()?;
271 let path = self.vfs.read().0.file_path(file_id);
272 let path = path.as_path()?;
273 self.workspaces.iter().find_map(|ws| match ws {
274 ProjectWorkspace::Cargo { cargo, .. } => {
275 cargo.target_by_root(&path).map(|it| (cargo, it))
276 }
277 ProjectWorkspace::Json { .. } => None,
278 })
279 }
280}
281
282pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
283 let path = vfs.file_path(id);
284 let path = path.as_path().unwrap();
285 url_from_abs_path(&path)
286}
287
288pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> {
289 let path = from_proto::vfs_path(url)?;
290 let res = vfs.file_id(&path).ok_or_else(|| format!("file not found: {}", path))?;
291 Ok(res)
292}
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 1f910ff82..d28c700f1 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -11,41 +11,50 @@ use lsp_server::ErrorCode;
11use lsp_types::{ 11use lsp_types::{
12 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, 12 CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
13 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, 13 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
14 CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, 14 CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams,
15 DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, 15 DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location,
16 MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, 16 Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams,
17 SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, 17 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
18 SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, 18 TextDocumentIdentifier, Url, WorkspaceEdit,
19}; 19};
20use ra_cfg::CfgExpr;
21use ra_ide::{ 20use ra_ide::{
22 FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 21 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query,
23 TextEdit, 22 RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit,
24}; 23};
25use ra_prof::profile; 24use ra_prof::profile;
26use ra_project_model::TargetKind; 25use ra_project_model::TargetKind;
27use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize}; 26use ra_syntax::{algo, ast, AstNode, SyntaxKind, TextRange, TextSize};
28use rustc_hash::FxHashMap;
29use serde::{Deserialize, Serialize}; 27use serde::{Deserialize, Serialize};
30use serde_json::to_value; 28use serde_json::to_value;
31use stdx::format_to; 29use stdx::{format_to, split_delim};
32 30
33use crate::{ 31use crate::{
34 cargo_target_spec::CargoTargetSpec, 32 cargo_target_spec::CargoTargetSpec,
35 config::RustfmtConfig, 33 config::RustfmtConfig,
36 diagnostics::DiagnosticTask,
37 from_json, from_proto, 34 from_json, from_proto,
35 global_state::{GlobalState, GlobalStateSnapshot},
38 lsp_ext::{self, InlayHint, InlayHintsParams}, 36 lsp_ext::{self, InlayHint, InlayHintsParams},
39 to_proto, 37 to_proto, LspError, Result,
40 world::WorldSnapshot,
41 LspError, Result,
42}; 38};
43 39
44pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { 40pub(crate) fn handle_analyzer_status(snap: GlobalStateSnapshot, _: ()) -> Result<String> {
45 let _p = profile("handle_analyzer_status"); 41 let _p = profile("handle_analyzer_status");
46 let mut buf = world.status(); 42
43 let mut buf = String::new();
44 if snap.workspaces.is_empty() {
45 buf.push_str("no workspaces\n")
46 } else {
47 buf.push_str("workspaces:\n");
48 for w in snap.workspaces.iter() {
49 format_to!(buf, "{} packages loaded\n", w.n_packages());
50 }
51 }
52 buf.push_str("\nanalysis:\n");
53 buf.push_str(
54 &snap.analysis.status().unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
55 );
47 format_to!(buf, "\n\nrequests:\n"); 56 format_to!(buf, "\n\nrequests:\n");
48 let requests = world.latest_requests.read(); 57 let requests = snap.latest_requests.read();
49 for (is_last, r) in requests.iter() { 58 for (is_last, r) in requests.iter() {
50 let mark = if is_last { "*" } else { " " }; 59 let mark = if is_last { "*" } else { " " };
51 format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis()); 60 format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis());
@@ -53,38 +62,49 @@ pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> {
53 Ok(buf) 62 Ok(buf)
54} 63}
55 64
56pub fn handle_syntax_tree( 65pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> {
57 world: WorldSnapshot, 66 let _p = profile("handle_memory_usage");
67 let mem = state.analysis_host.per_query_memory_usage();
68
69 let mut out = String::new();
70 for (name, bytes) in mem {
71 format_to!(out, "{:>8} {}\n", bytes, name);
72 }
73 Ok(out)
74}
75
76pub(crate) fn handle_syntax_tree(
77 snap: GlobalStateSnapshot,
58 params: lsp_ext::SyntaxTreeParams, 78 params: lsp_ext::SyntaxTreeParams,
59) -> Result<String> { 79) -> Result<String> {
60 let _p = profile("handle_syntax_tree"); 80 let _p = profile("handle_syntax_tree");
61 let id = from_proto::file_id(&world, &params.text_document.uri)?; 81 let id = from_proto::file_id(&snap, &params.text_document.uri)?;
62 let line_index = world.analysis().file_line_index(id)?; 82 let line_index = snap.analysis.file_line_index(id)?;
63 let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); 83 let text_range = params.range.map(|r| from_proto::text_range(&line_index, r));
64 let res = world.analysis().syntax_tree(id, text_range)?; 84 let res = snap.analysis.syntax_tree(id, text_range)?;
65 Ok(res) 85 Ok(res)
66} 86}
67 87
68pub fn handle_expand_macro( 88pub(crate) fn handle_expand_macro(
69 world: WorldSnapshot, 89 snap: GlobalStateSnapshot,
70 params: lsp_ext::ExpandMacroParams, 90 params: lsp_ext::ExpandMacroParams,
71) -> Result<Option<lsp_ext::ExpandedMacro>> { 91) -> Result<Option<lsp_ext::ExpandedMacro>> {
72 let _p = profile("handle_expand_macro"); 92 let _p = profile("handle_expand_macro");
73 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 93 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
74 let line_index = world.analysis().file_line_index(file_id)?; 94 let line_index = snap.analysis.file_line_index(file_id)?;
75 let offset = from_proto::offset(&line_index, params.position); 95 let offset = from_proto::offset(&line_index, params.position);
76 96
77 let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; 97 let res = snap.analysis.expand_macro(FilePosition { file_id, offset })?;
78 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion })) 98 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
79} 99}
80 100
81pub fn handle_selection_range( 101pub(crate) fn handle_selection_range(
82 world: WorldSnapshot, 102 snap: GlobalStateSnapshot,
83 params: lsp_types::SelectionRangeParams, 103 params: lsp_types::SelectionRangeParams,
84) -> Result<Option<Vec<lsp_types::SelectionRange>>> { 104) -> Result<Option<Vec<lsp_types::SelectionRange>>> {
85 let _p = profile("handle_selection_range"); 105 let _p = profile("handle_selection_range");
86 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 106 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
87 let line_index = world.analysis().file_line_index(file_id)?; 107 let line_index = snap.analysis.file_line_index(file_id)?;
88 let res: Result<Vec<lsp_types::SelectionRange>> = params 108 let res: Result<Vec<lsp_types::SelectionRange>> = params
89 .positions 109 .positions
90 .into_iter() 110 .into_iter()
@@ -96,7 +116,7 @@ pub fn handle_selection_range(
96 loop { 116 loop {
97 ranges.push(range); 117 ranges.push(range);
98 let frange = FileRange { file_id, range }; 118 let frange = FileRange { file_id, range };
99 let next = world.analysis().extend_selection(frange)?; 119 let next = snap.analysis.extend_selection(frange)?;
100 if next == range { 120 if next == range {
101 break; 121 break;
102 } else { 122 } else {
@@ -121,19 +141,19 @@ pub fn handle_selection_range(
121 Ok(Some(res?)) 141 Ok(Some(res?))
122} 142}
123 143
124pub fn handle_matching_brace( 144pub(crate) fn handle_matching_brace(
125 world: WorldSnapshot, 145 snap: GlobalStateSnapshot,
126 params: lsp_ext::MatchingBraceParams, 146 params: lsp_ext::MatchingBraceParams,
127) -> Result<Vec<Position>> { 147) -> Result<Vec<Position>> {
128 let _p = profile("handle_matching_brace"); 148 let _p = profile("handle_matching_brace");
129 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 149 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
130 let line_index = world.analysis().file_line_index(file_id)?; 150 let line_index = snap.analysis.file_line_index(file_id)?;
131 let res = params 151 let res = params
132 .positions 152 .positions
133 .into_iter() 153 .into_iter()
134 .map(|position| { 154 .map(|position| {
135 let offset = from_proto::offset(&line_index, position); 155 let offset = from_proto::offset(&line_index, position);
136 let offset = match world.analysis().matching_brace(FilePosition { file_id, offset }) { 156 let offset = match snap.analysis.matching_brace(FilePosition { file_id, offset }) {
137 Ok(Some(matching_brace_offset)) => matching_brace_offset, 157 Ok(Some(matching_brace_offset)) => matching_brace_offset,
138 Err(_) | Ok(None) => offset, 158 Err(_) | Ok(None) => offset,
139 }; 159 };
@@ -143,18 +163,18 @@ pub fn handle_matching_brace(
143 Ok(res) 163 Ok(res)
144} 164}
145 165
146pub fn handle_join_lines( 166pub(crate) fn handle_join_lines(
147 world: WorldSnapshot, 167 snap: GlobalStateSnapshot,
148 params: lsp_ext::JoinLinesParams, 168 params: lsp_ext::JoinLinesParams,
149) -> Result<Vec<lsp_types::TextEdit>> { 169) -> Result<Vec<lsp_types::TextEdit>> {
150 let _p = profile("handle_join_lines"); 170 let _p = profile("handle_join_lines");
151 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 171 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
152 let line_index = world.analysis().file_line_index(file_id)?; 172 let line_index = snap.analysis.file_line_index(file_id)?;
153 let line_endings = world.file_line_endings(file_id); 173 let line_endings = snap.file_line_endings(file_id);
154 let mut res = TextEdit::default(); 174 let mut res = TextEdit::default();
155 for range in params.ranges { 175 for range in params.ranges {
156 let range = from_proto::text_range(&line_index, range); 176 let range = from_proto::text_range(&line_index, range);
157 let edit = world.analysis().join_lines(FileRange { file_id, range })?; 177 let edit = snap.analysis.join_lines(FileRange { file_id, range })?;
158 match res.union(edit) { 178 match res.union(edit) {
159 Ok(()) => (), 179 Ok(()) => (),
160 Err(_edit) => { 180 Err(_edit) => {
@@ -166,38 +186,38 @@ pub fn handle_join_lines(
166 Ok(res) 186 Ok(res)
167} 187}
168 188
169pub fn handle_on_enter( 189pub(crate) fn handle_on_enter(
170 world: WorldSnapshot, 190 snap: GlobalStateSnapshot,
171 params: lsp_types::TextDocumentPositionParams, 191 params: lsp_types::TextDocumentPositionParams,
172) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> { 192) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
173 let _p = profile("handle_on_enter"); 193 let _p = profile("handle_on_enter");
174 let position = from_proto::file_position(&world, params)?; 194 let position = from_proto::file_position(&snap, params)?;
175 let edit = match world.analysis().on_enter(position)? { 195 let edit = match snap.analysis.on_enter(position)? {
176 None => return Ok(None), 196 None => return Ok(None),
177 Some(it) => it, 197 Some(it) => it,
178 }; 198 };
179 let line_index = world.analysis().file_line_index(position.file_id)?; 199 let line_index = snap.analysis.file_line_index(position.file_id)?;
180 let line_endings = world.file_line_endings(position.file_id); 200 let line_endings = snap.file_line_endings(position.file_id);
181 let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit); 201 let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit);
182 Ok(Some(edit)) 202 Ok(Some(edit))
183} 203}
184 204
185// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. 205// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
186pub fn handle_on_type_formatting( 206pub(crate) fn handle_on_type_formatting(
187 world: WorldSnapshot, 207 snap: GlobalStateSnapshot,
188 params: lsp_types::DocumentOnTypeFormattingParams, 208 params: lsp_types::DocumentOnTypeFormattingParams,
189) -> Result<Option<Vec<lsp_types::TextEdit>>> { 209) -> Result<Option<Vec<lsp_types::TextEdit>>> {
190 let _p = profile("handle_on_type_formatting"); 210 let _p = profile("handle_on_type_formatting");
191 let mut position = from_proto::file_position(&world, params.text_document_position)?; 211 let mut position = from_proto::file_position(&snap, params.text_document_position)?;
192 let line_index = world.analysis().file_line_index(position.file_id)?; 212 let line_index = snap.analysis.file_line_index(position.file_id)?;
193 let line_endings = world.file_line_endings(position.file_id); 213 let line_endings = snap.file_line_endings(position.file_id);
194 214
195 // in `ra_ide`, the `on_type` invariant is that 215 // in `ra_ide`, the `on_type` invariant is that
196 // `text.char_at(position) == typed_char`. 216 // `text.char_at(position) == typed_char`.
197 position.offset -= TextSize::of('.'); 217 position.offset -= TextSize::of('.');
198 let char_typed = params.ch.chars().next().unwrap_or('\0'); 218 let char_typed = params.ch.chars().next().unwrap_or('\0');
199 assert!({ 219 assert!({
200 let text = world.analysis().file_text(position.file_id)?; 220 let text = snap.analysis.file_text(position.file_id)?;
201 text[usize::from(position.offset)..].starts_with(char_typed) 221 text[usize::from(position.offset)..].starts_with(char_typed)
202 }); 222 });
203 223
@@ -209,7 +229,7 @@ pub fn handle_on_type_formatting(
209 return Ok(None); 229 return Ok(None);
210 } 230 }
211 231
212 let edit = world.analysis().on_char_typed(position, char_typed)?; 232 let edit = snap.analysis.on_char_typed(position, char_typed)?;
213 let mut edit = match edit { 233 let mut edit = match edit {
214 Some(it) => it, 234 Some(it) => it,
215 None => return Ok(None), 235 None => return Ok(None),
@@ -222,17 +242,17 @@ pub fn handle_on_type_formatting(
222 Ok(Some(change)) 242 Ok(Some(change))
223} 243}
224 244
225pub fn handle_document_symbol( 245pub(crate) fn handle_document_symbol(
226 world: WorldSnapshot, 246 snap: GlobalStateSnapshot,
227 params: lsp_types::DocumentSymbolParams, 247 params: lsp_types::DocumentSymbolParams,
228) -> Result<Option<lsp_types::DocumentSymbolResponse>> { 248) -> Result<Option<lsp_types::DocumentSymbolResponse>> {
229 let _p = profile("handle_document_symbol"); 249 let _p = profile("handle_document_symbol");
230 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 250 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
231 let line_index = world.analysis().file_line_index(file_id)?; 251 let line_index = snap.analysis.file_line_index(file_id)?;
232 252
233 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); 253 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
234 254
235 for symbol in world.analysis().file_structure(file_id)? { 255 for symbol in snap.analysis.file_structure(file_id)? {
236 let doc_symbol = DocumentSymbol { 256 let doc_symbol = DocumentSymbol {
237 name: symbol.label, 257 name: symbol.label,
238 detail: symbol.detail, 258 detail: symbol.detail,
@@ -258,10 +278,10 @@ pub fn handle_document_symbol(
258 } 278 }
259 } 279 }
260 280
261 let res = if world.config.client_caps.hierarchical_symbols { 281 let res = if snap.config.client_caps.hierarchical_symbols {
262 document_symbols.into() 282 document_symbols.into()
263 } else { 283 } else {
264 let url = to_proto::url(&world, file_id)?; 284 let url = to_proto::url(&snap, file_id);
265 let mut symbol_information = Vec::<SymbolInformation>::new(); 285 let mut symbol_information = Vec::<SymbolInformation>::new();
266 for symbol in document_symbols { 286 for symbol in document_symbols {
267 flatten_document_symbol(&symbol, None, &url, &mut symbol_information); 287 flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
@@ -290,8 +310,8 @@ pub fn handle_document_symbol(
290 } 310 }
291} 311}
292 312
293pub fn handle_workspace_symbol( 313pub(crate) fn handle_workspace_symbol(
294 world: WorldSnapshot, 314 snap: GlobalStateSnapshot,
295 params: lsp_types::WorkspaceSymbolParams, 315 params: lsp_types::WorkspaceSymbolParams,
296) -> Result<Option<Vec<SymbolInformation>>> { 316) -> Result<Option<Vec<SymbolInformation>>> {
297 let _p = profile("handle_workspace_symbol"); 317 let _p = profile("handle_workspace_symbol");
@@ -309,23 +329,24 @@ pub fn handle_workspace_symbol(
309 q.limit(128); 329 q.limit(128);
310 q 330 q
311 }; 331 };
312 let mut res = exec_query(&world, query)?; 332 let mut res = exec_query(&snap, query)?;
313 if res.is_empty() && !all_symbols { 333 if res.is_empty() && !all_symbols {
314 let mut query = Query::new(params.query); 334 let mut query = Query::new(params.query);
315 query.limit(128); 335 query.limit(128);
316 res = exec_query(&world, query)?; 336 res = exec_query(&snap, query)?;
317 } 337 }
318 338
319 return Ok(Some(res)); 339 return Ok(Some(res));
320 340
321 fn exec_query(world: &WorldSnapshot, query: Query) -> Result<Vec<SymbolInformation>> { 341 fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> {
322 let mut res = Vec::new(); 342 let mut res = Vec::new();
323 for nav in world.analysis().symbol_search(query)? { 343 for nav in snap.analysis.symbol_search(query)? {
344 let container_name = nav.container_name().map(|v| v.to_string());
324 let info = SymbolInformation { 345 let info = SymbolInformation {
325 name: nav.name().to_string(), 346 name: nav.name().to_string(),
326 kind: to_proto::symbol_kind(nav.kind()), 347 kind: to_proto::symbol_kind(nav.kind()),
327 location: to_proto::location(world, nav.file_range())?, 348 location: to_proto::location_from_nav(snap, nav)?,
328 container_name: nav.container_name().map(|v| v.to_string()), 349 container_name,
329 deprecated: None, 350 deprecated: None,
330 }; 351 };
331 res.push(info); 352 res.push(info);
@@ -334,89 +355,98 @@ pub fn handle_workspace_symbol(
334 } 355 }
335} 356}
336 357
337pub fn handle_goto_definition( 358pub(crate) fn handle_goto_definition(
338 world: WorldSnapshot, 359 snap: GlobalStateSnapshot,
339 params: lsp_types::GotoDefinitionParams, 360 params: lsp_types::GotoDefinitionParams,
340) -> Result<Option<lsp_types::GotoDefinitionResponse>> { 361) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
341 let _p = profile("handle_goto_definition"); 362 let _p = profile("handle_goto_definition");
342 let position = from_proto::file_position(&world, params.text_document_position_params)?; 363 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
343 let nav_info = match world.analysis().goto_definition(position)? { 364 let nav_info = match snap.analysis.goto_definition(position)? {
344 None => return Ok(None), 365 None => return Ok(None),
345 Some(it) => it, 366 Some(it) => it,
346 }; 367 };
347 let src = FileRange { file_id: position.file_id, range: nav_info.range }; 368 let src = FileRange { file_id: position.file_id, range: nav_info.range };
348 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; 369 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
349 Ok(Some(res)) 370 Ok(Some(res))
350} 371}
351 372
352pub fn handle_goto_implementation( 373pub(crate) fn handle_goto_implementation(
353 world: WorldSnapshot, 374 snap: GlobalStateSnapshot,
354 params: lsp_types::request::GotoImplementationParams, 375 params: lsp_types::request::GotoImplementationParams,
355) -> Result<Option<lsp_types::request::GotoImplementationResponse>> { 376) -> Result<Option<lsp_types::request::GotoImplementationResponse>> {
356 let _p = profile("handle_goto_implementation"); 377 let _p = profile("handle_goto_implementation");
357 let position = from_proto::file_position(&world, params.text_document_position_params)?; 378 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
358 let nav_info = match world.analysis().goto_implementation(position)? { 379 let nav_info = match snap.analysis.goto_implementation(position)? {
359 None => return Ok(None), 380 None => return Ok(None),
360 Some(it) => it, 381 Some(it) => it,
361 }; 382 };
362 let src = FileRange { file_id: position.file_id, range: nav_info.range }; 383 let src = FileRange { file_id: position.file_id, range: nav_info.range };
363 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; 384 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
364 Ok(Some(res)) 385 Ok(Some(res))
365} 386}
366 387
367pub fn handle_goto_type_definition( 388pub(crate) fn handle_goto_type_definition(
368 world: WorldSnapshot, 389 snap: GlobalStateSnapshot,
369 params: lsp_types::request::GotoTypeDefinitionParams, 390 params: lsp_types::request::GotoTypeDefinitionParams,
370) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> { 391) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> {
371 let _p = profile("handle_goto_type_definition"); 392 let _p = profile("handle_goto_type_definition");
372 let position = from_proto::file_position(&world, params.text_document_position_params)?; 393 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
373 let nav_info = match world.analysis().goto_type_definition(position)? { 394 let nav_info = match snap.analysis.goto_type_definition(position)? {
374 None => return Ok(None), 395 None => return Ok(None),
375 Some(it) => it, 396 Some(it) => it,
376 }; 397 };
377 let src = FileRange { file_id: position.file_id, range: nav_info.range }; 398 let src = FileRange { file_id: position.file_id, range: nav_info.range };
378 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; 399 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
379 Ok(Some(res)) 400 Ok(Some(res))
380} 401}
381 402
382pub fn handle_parent_module( 403pub(crate) fn handle_parent_module(
383 world: WorldSnapshot, 404 snap: GlobalStateSnapshot,
384 params: lsp_types::TextDocumentPositionParams, 405 params: lsp_types::TextDocumentPositionParams,
385) -> Result<Option<lsp_types::GotoDefinitionResponse>> { 406) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
386 let _p = profile("handle_parent_module"); 407 let _p = profile("handle_parent_module");
387 let position = from_proto::file_position(&world, params)?; 408 let position = from_proto::file_position(&snap, params)?;
388 let navs = world.analysis().parent_module(position)?; 409 let navs = snap.analysis.parent_module(position)?;
389 let res = to_proto::goto_definition_response(&world, None, navs)?; 410 let res = to_proto::goto_definition_response(&snap, None, navs)?;
390 Ok(Some(res)) 411 Ok(Some(res))
391} 412}
392 413
393pub fn handle_runnables( 414pub(crate) fn handle_runnables(
394 world: WorldSnapshot, 415 snap: GlobalStateSnapshot,
395 params: lsp_ext::RunnablesParams, 416 params: lsp_ext::RunnablesParams,
396) -> Result<Vec<lsp_ext::Runnable>> { 417) -> Result<Vec<lsp_ext::Runnable>> {
397 let _p = profile("handle_runnables"); 418 let _p = profile("handle_runnables");
398 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 419 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
399 let line_index = world.analysis().file_line_index(file_id)?; 420 let line_index = snap.analysis.file_line_index(file_id)?;
400 let offset = params.position.map(|it| from_proto::offset(&line_index, it)); 421 let offset = params.position.map(|it| from_proto::offset(&line_index, it));
422 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
423
424 let expect_test = match offset {
425 Some(offset) => {
426 let source_file = snap.analysis.parse(file_id)?;
427 algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset)
428 .and_then(|it| it.path()?.segment()?.name_ref())
429 .map_or(false, |it| it.text() == "expect" || it.text() == "expect_file")
430 }
431 None => false,
432 };
433
401 let mut res = Vec::new(); 434 let mut res = Vec::new();
402 let workspace_root = world.workspace_root_for(file_id); 435 for runnable in snap.analysis.runnables(file_id)? {
403 let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?;
404 for runnable in world.analysis().runnables(file_id)? {
405 if let Some(offset) = offset { 436 if let Some(offset) = offset {
406 if !runnable.range.contains_inclusive(offset) { 437 if !runnable.nav.full_range().contains_inclusive(offset) {
407 continue; 438 continue;
408 } 439 }
409 } 440 }
410 // Do not suggest binary run on other target than binary 441 if should_skip_target(&runnable, cargo_spec.as_ref()) {
411 if let RunnableKind::Bin = runnable.kind { 442 continue;
412 if let Some(spec) = &cargo_spec { 443 }
413 match spec.target_kind { 444 let mut runnable = to_proto::runnable(&snap, file_id, runnable)?;
414 TargetKind::Bin => {} 445 if expect_test {
415 _ => continue, 446 runnable.label = format!("{} + expect", runnable.label);
416 } 447 runnable.args.expect_test = Some(true);
417 }
418 } 448 }
419 res.push(to_lsp_runnable(&world, file_id, runnable)?); 449 res.push(runnable);
420 } 450 }
421 451
422 // Add `cargo check` and `cargo test` for the whole package 452 // Add `cargo check` and `cargo test` for the whole package
@@ -424,42 +454,50 @@ pub fn handle_runnables(
424 Some(spec) => { 454 Some(spec) => {
425 for &cmd in ["check", "test"].iter() { 455 for &cmd in ["check", "test"].iter() {
426 res.push(lsp_ext::Runnable { 456 res.push(lsp_ext::Runnable {
427 range: Default::default(),
428 label: format!("cargo {} -p {}", cmd, spec.package), 457 label: format!("cargo {} -p {}", cmd, spec.package),
429 bin: "cargo".to_string(), 458 location: None,
430 args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], 459 kind: lsp_ext::RunnableKind::Cargo,
431 extra_args: Vec::new(), 460 args: lsp_ext::CargoRunnable {
432 env: FxHashMap::default(), 461 workspace_root: Some(spec.workspace_root.clone().into()),
433 cwd: workspace_root.map(|root| root.to_owned()), 462 cargo_args: vec![
463 cmd.to_string(),
464 "--package".to_string(),
465 spec.package.clone(),
466 ],
467 executable_args: Vec::new(),
468 expect_test: None,
469 },
434 }) 470 })
435 } 471 }
436 } 472 }
437 None => { 473 None => {
438 res.push(lsp_ext::Runnable { 474 res.push(lsp_ext::Runnable {
439 range: Default::default(),
440 label: "cargo check --workspace".to_string(), 475 label: "cargo check --workspace".to_string(),
441 bin: "cargo".to_string(), 476 location: None,
442 args: vec!["check".to_string(), "--workspace".to_string()], 477 kind: lsp_ext::RunnableKind::Cargo,
443 extra_args: Vec::new(), 478 args: lsp_ext::CargoRunnable {
444 env: FxHashMap::default(), 479 workspace_root: None,
445 cwd: workspace_root.map(|root| root.to_owned()), 480 cargo_args: vec!["check".to_string(), "--workspace".to_string()],
481 executable_args: Vec::new(),
482 expect_test: None,
483 },
446 }); 484 });
447 } 485 }
448 } 486 }
449 Ok(res) 487 Ok(res)
450} 488}
451 489
452pub fn handle_completion( 490pub(crate) fn handle_completion(
453 world: WorldSnapshot, 491 snap: GlobalStateSnapshot,
454 params: lsp_types::CompletionParams, 492 params: lsp_types::CompletionParams,
455) -> Result<Option<lsp_types::CompletionResponse>> { 493) -> Result<Option<lsp_types::CompletionResponse>> {
456 let _p = profile("handle_completion"); 494 let _p = profile("handle_completion");
457 let position = from_proto::file_position(&world, params.text_document_position)?; 495 let position = from_proto::file_position(&snap, params.text_document_position)?;
458 let completion_triggered_after_single_colon = { 496 let completion_triggered_after_single_colon = {
459 let mut res = false; 497 let mut res = false;
460 if let Some(ctx) = params.context { 498 if let Some(ctx) = params.context {
461 if ctx.trigger_character.unwrap_or_default() == ":" { 499 if ctx.trigger_character.unwrap_or_default() == ":" {
462 let source_file = world.analysis().parse(position.file_id)?; 500 let source_file = snap.analysis.parse(position.file_id)?;
463 let syntax = source_file.syntax(); 501 let syntax = source_file.syntax();
464 let text = syntax.text(); 502 let text = syntax.text();
465 if let Some(next_char) = text.char_at(position.offset) { 503 if let Some(next_char) = text.char_at(position.offset) {
@@ -477,12 +515,12 @@ pub fn handle_completion(
477 return Ok(None); 515 return Ok(None);
478 } 516 }
479 517
480 let items = match world.analysis().completions(&world.config.completion, position)? { 518 let items = match snap.analysis.completions(&snap.config.completion, position)? {
481 None => return Ok(None), 519 None => return Ok(None),
482 Some(items) => items, 520 Some(items) => items,
483 }; 521 };
484 let line_index = world.analysis().file_line_index(position.file_id)?; 522 let line_index = snap.analysis.file_line_index(position.file_id)?;
485 let line_endings = world.file_line_endings(position.file_id); 523 let line_endings = snap.file_line_endings(position.file_id);
486 let items: Vec<CompletionItem> = items 524 let items: Vec<CompletionItem> = items
487 .into_iter() 525 .into_iter()
488 .map(|item| to_proto::completion_item(&line_index, line_endings, item)) 526 .map(|item| to_proto::completion_item(&line_index, line_endings, item))
@@ -491,16 +529,16 @@ pub fn handle_completion(
491 Ok(Some(items.into())) 529 Ok(Some(items.into()))
492} 530}
493 531
494pub fn handle_folding_range( 532pub(crate) fn handle_folding_range(
495 world: WorldSnapshot, 533 snap: GlobalStateSnapshot,
496 params: FoldingRangeParams, 534 params: FoldingRangeParams,
497) -> Result<Option<Vec<FoldingRange>>> { 535) -> Result<Option<Vec<FoldingRange>>> {
498 let _p = profile("handle_folding_range"); 536 let _p = profile("handle_folding_range");
499 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 537 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
500 let folds = world.analysis().folding_ranges(file_id)?; 538 let folds = snap.analysis.folding_ranges(file_id)?;
501 let text = world.analysis().file_text(file_id)?; 539 let text = snap.analysis.file_text(file_id)?;
502 let line_index = world.analysis().file_line_index(file_id)?; 540 let line_index = snap.analysis.file_line_index(file_id)?;
503 let line_folding_only = world.config.client_caps.line_folding_only; 541 let line_folding_only = snap.config.client_caps.line_folding_only;
504 let res = folds 542 let res = folds
505 .into_iter() 543 .into_iter()
506 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) 544 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it))
@@ -508,17 +546,17 @@ pub fn handle_folding_range(
508 Ok(Some(res)) 546 Ok(Some(res))
509} 547}
510 548
511pub fn handle_signature_help( 549pub(crate) fn handle_signature_help(
512 world: WorldSnapshot, 550 snap: GlobalStateSnapshot,
513 params: lsp_types::SignatureHelpParams, 551 params: lsp_types::SignatureHelpParams,
514) -> Result<Option<lsp_types::SignatureHelp>> { 552) -> Result<Option<lsp_types::SignatureHelp>> {
515 let _p = profile("handle_signature_help"); 553 let _p = profile("handle_signature_help");
516 let position = from_proto::file_position(&world, params.text_document_position_params)?; 554 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
517 let call_info = match world.analysis().call_info(position)? { 555 let call_info = match snap.analysis.call_info(position)? {
518 None => return Ok(None), 556 None => return Ok(None),
519 Some(it) => it, 557 Some(it) => it,
520 }; 558 };
521 let concise = !world.config.call_info_full; 559 let concise = !snap.config.call_info_full;
522 let mut active_parameter = call_info.active_parameter.map(|it| it as i64); 560 let mut active_parameter = call_info.active_parameter.map(|it| it as i64);
523 if concise && call_info.signature.has_self_param { 561 if concise && call_info.signature.has_self_param {
524 active_parameter = active_parameter.map(|it| it.saturating_sub(1)); 562 active_parameter = active_parameter.map(|it| it.saturating_sub(1));
@@ -532,46 +570,53 @@ pub fn handle_signature_help(
532 })) 570 }))
533} 571}
534 572
535pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Result<Option<Hover>> { 573pub(crate) fn handle_hover(
574 snap: GlobalStateSnapshot,
575 params: lsp_types::HoverParams,
576) -> Result<Option<lsp_ext::Hover>> {
536 let _p = profile("handle_hover"); 577 let _p = profile("handle_hover");
537 let position = from_proto::file_position(&world, params.text_document_position_params)?; 578 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
538 let info = match world.analysis().hover(position)? { 579 let info = match snap.analysis.hover(position)? {
539 None => return Ok(None), 580 None => return Ok(None),
540 Some(info) => info, 581 Some(info) => info,
541 }; 582 };
542 let line_index = world.analysis.file_line_index(position.file_id)?; 583 let line_index = snap.analysis.file_line_index(position.file_id)?;
543 let range = to_proto::range(&line_index, info.range); 584 let range = to_proto::range(&line_index, info.range);
544 let res = Hover { 585 let hover = lsp_ext::Hover {
545 contents: HoverContents::Markup(MarkupContent { 586 hover: lsp_types::Hover {
546 kind: MarkupKind::Markdown, 587 contents: HoverContents::Markup(to_proto::markup_content(info.info.markup)),
547 value: crate::markdown::format_docs(&info.info.to_markup()), 588 range: Some(range),
548 }), 589 },
549 range: Some(range), 590 actions: prepare_hover_actions(&snap, position.file_id, &info.info.actions),
550 }; 591 };
551 Ok(Some(res)) 592
593 Ok(Some(hover))
552} 594}
553 595
554pub fn handle_prepare_rename( 596pub(crate) fn handle_prepare_rename(
555 world: WorldSnapshot, 597 snap: GlobalStateSnapshot,
556 params: lsp_types::TextDocumentPositionParams, 598 params: lsp_types::TextDocumentPositionParams,
557) -> Result<Option<PrepareRenameResponse>> { 599) -> Result<Option<PrepareRenameResponse>> {
558 let _p = profile("handle_prepare_rename"); 600 let _p = profile("handle_prepare_rename");
559 let position = from_proto::file_position(&world, params)?; 601 let position = from_proto::file_position(&snap, params)?;
560 602
561 let optional_change = world.analysis().rename(position, "dummy")?; 603 let optional_change = snap.analysis.rename(position, "dummy")?;
562 let range = match optional_change { 604 let range = match optional_change {
563 None => return Ok(None), 605 None => return Ok(None),
564 Some(it) => it.range, 606 Some(it) => it.range,
565 }; 607 };
566 608
567 let line_index = world.analysis().file_line_index(position.file_id)?; 609 let line_index = snap.analysis.file_line_index(position.file_id)?;
568 let range = to_proto::range(&line_index, range); 610 let range = to_proto::range(&line_index, range);
569 Ok(Some(PrepareRenameResponse::Range(range))) 611 Ok(Some(PrepareRenameResponse::Range(range)))
570} 612}
571 613
572pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { 614pub(crate) fn handle_rename(
615 snap: GlobalStateSnapshot,
616 params: RenameParams,
617) -> Result<Option<WorkspaceEdit>> {
573 let _p = profile("handle_rename"); 618 let _p = profile("handle_rename");
574 let position = from_proto::file_position(&world, params.text_document_position)?; 619 let position = from_proto::file_position(&snap, params.text_document_position)?;
575 620
576 if params.new_name.is_empty() { 621 if params.new_name.is_empty() {
577 return Err(LspError::new( 622 return Err(LspError::new(
@@ -581,61 +626,61 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio
581 .into()); 626 .into());
582 } 627 }
583 628
584 let optional_change = world.analysis().rename(position, &*params.new_name)?; 629 let optional_change = snap.analysis.rename(position, &*params.new_name)?;
585 let source_change = match optional_change { 630 let source_change = match optional_change {
586 None => return Ok(None), 631 None => return Ok(None),
587 Some(it) => it.info, 632 Some(it) => it.info,
588 }; 633 };
589 let workspace_edit = to_proto::workspace_edit(&world, source_change)?; 634 let workspace_edit = to_proto::workspace_edit(&snap, source_change)?;
590 Ok(Some(workspace_edit)) 635 Ok(Some(workspace_edit))
591} 636}
592 637
593pub fn handle_references( 638pub(crate) fn handle_references(
594 world: WorldSnapshot, 639 snap: GlobalStateSnapshot,
595 params: lsp_types::ReferenceParams, 640 params: lsp_types::ReferenceParams,
596) -> Result<Option<Vec<Location>>> { 641) -> Result<Option<Vec<Location>>> {
597 let _p = profile("handle_references"); 642 let _p = profile("handle_references");
598 let position = from_proto::file_position(&world, params.text_document_position)?; 643 let position = from_proto::file_position(&snap, params.text_document_position)?;
599 644
600 let refs = match world.analysis().find_all_refs(position, None)? { 645 let refs = match snap.analysis.find_all_refs(position, None)? {
601 None => return Ok(None), 646 None => return Ok(None),
602 Some(refs) => refs, 647 Some(refs) => refs,
603 }; 648 };
604 649
605 let locations = if params.context.include_declaration { 650 let locations = if params.context.include_declaration {
606 refs.into_iter() 651 refs.into_iter()
607 .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) 652 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok())
608 .collect() 653 .collect()
609 } else { 654 } else {
610 // Only iterate over the references if include_declaration was false 655 // Only iterate over the references if include_declaration was false
611 refs.references() 656 refs.references()
612 .iter() 657 .iter()
613 .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) 658 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok())
614 .collect() 659 .collect()
615 }; 660 };
616 661
617 Ok(Some(locations)) 662 Ok(Some(locations))
618} 663}
619 664
620pub fn handle_formatting( 665pub(crate) fn handle_formatting(
621 world: WorldSnapshot, 666 snap: GlobalStateSnapshot,
622 params: DocumentFormattingParams, 667 params: DocumentFormattingParams,
623) -> Result<Option<Vec<lsp_types::TextEdit>>> { 668) -> Result<Option<Vec<lsp_types::TextEdit>>> {
624 let _p = profile("handle_formatting"); 669 let _p = profile("handle_formatting");
625 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 670 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
626 let file = world.analysis().file_text(file_id)?; 671 let file = snap.analysis.file_text(file_id)?;
627 let crate_ids = world.analysis().crate_for(file_id)?; 672 let crate_ids = snap.analysis.crate_for(file_id)?;
628 673
629 let file_line_index = world.analysis().file_line_index(file_id)?; 674 let file_line_index = snap.analysis.file_line_index(file_id)?;
630 let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); 675 let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str()));
631 676
632 let mut rustfmt = match &world.config.rustfmt { 677 let mut rustfmt = match &snap.config.rustfmt {
633 RustfmtConfig::Rustfmt { extra_args } => { 678 RustfmtConfig::Rustfmt { extra_args } => {
634 let mut cmd = process::Command::new("rustfmt"); 679 let mut cmd = process::Command::new(ra_toolchain::rustfmt());
635 cmd.args(extra_args); 680 cmd.args(extra_args);
636 if let Some(&crate_id) = crate_ids.first() { 681 if let Some(&crate_id) = crate_ids.first() {
637 // Assume all crates are in the same edition 682 // Assume all crates are in the same edition
638 let edition = world.analysis().crate_edition(crate_id)?; 683 let edition = snap.analysis.crate_edition(crate_id)?;
639 cmd.arg("--edition"); 684 cmd.arg("--edition");
640 cmd.arg(edition.to_string()); 685 cmd.arg(edition.to_string());
641 } 686 }
@@ -693,141 +738,180 @@ pub fn handle_formatting(
693 }])) 738 }]))
694} 739}
695 740
696pub fn handle_code_action( 741fn handle_fixes(
697 world: WorldSnapshot, 742 snap: &GlobalStateSnapshot,
698 params: lsp_types::CodeActionParams, 743 params: &lsp_types::CodeActionParams,
699) -> Result<Option<Vec<lsp_ext::CodeAction>>> { 744 res: &mut Vec<lsp_ext::CodeAction>,
700 let _p = profile("handle_code_action"); 745) -> Result<()> {
701 // We intentionally don't support command-based actions, as those either 746 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
702 // requires custom client-code anyway, or requires server-initiated edits. 747 let line_index = snap.analysis.file_line_index(file_id)?;
703 // Server initiated edits break causality, so we avoid those as well.
704 if !world.config.client_caps.code_action_literals {
705 return Ok(None);
706 }
707
708 let file_id = from_proto::file_id(&world, &params.text_document.uri)?;
709 let line_index = world.analysis().file_line_index(file_id)?;
710 let range = from_proto::text_range(&line_index, params.range); 748 let range = from_proto::text_range(&line_index, params.range);
711 let frange = FileRange { file_id, range };
712 749
713 let diagnostics = world.analysis().diagnostics(file_id)?; 750 match &params.context.only {
714 let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); 751 Some(v) => {
752 if !v.iter().any(|it| {
753 it == &lsp_types::CodeActionKind::EMPTY
754 || it == &lsp_types::CodeActionKind::QUICKFIX
755 }) {
756 return Ok(());
757 }
758 }
759 None => {}
760 };
761
762 let diagnostics = snap.analysis.diagnostics(file_id)?;
715 763
716 let fixes_from_diagnostics = diagnostics 764 let fixes_from_diagnostics = diagnostics
717 .into_iter() 765 .into_iter()
718 .filter_map(|d| Some((d.range, d.fix?))) 766 .filter_map(|d| Some((d.range, d.fix?)))
719 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) 767 .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some())
720 .map(|(_range, fix)| fix); 768 .map(|(_range, fix)| fix);
721
722 for fix in fixes_from_diagnostics { 769 for fix in fixes_from_diagnostics {
723 let title = fix.label; 770 let title = fix.label;
724 let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?; 771 let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
725 let action = 772 let action = lsp_ext::CodeAction {
726 lsp_ext::CodeAction { title, group: None, kind: None, edit: Some(edit), command: None }; 773 title,
774 id: None,
775 group: None,
776 kind: Some(CodeActionKind::QUICKFIX),
777 edit: Some(edit),
778 };
727 res.push(action); 779 res.push(action);
728 } 780 }
729 781
730 for fix in world.check_fixes.get(&file_id).into_iter().flatten() { 782 for fix in snap.check_fixes.get(&file_id).into_iter().flatten() {
731 let fix_range = from_proto::text_range(&line_index, fix.range); 783 let fix_range = from_proto::text_range(&line_index, fix.range);
732 if fix_range.intersect(range).is_none() { 784 if fix_range.intersect(range).is_none() {
733 continue; 785 continue;
734 } 786 }
735 res.push(fix.action.clone()); 787 res.push(fix.action.clone());
736 } 788 }
789 Ok(())
790}
791
792pub(crate) fn handle_code_action(
793 mut snap: GlobalStateSnapshot,
794 params: lsp_types::CodeActionParams,
795) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
796 let _p = profile("handle_code_action");
797 // We intentionally don't support command-based actions, as those either
798 // requires custom client-code anyway, or requires server-initiated edits.
799 // Server initiated edits break causality, so we avoid those as well.
800 if !snap.config.client_caps.code_action_literals {
801 return Ok(None);
802 }
803
804 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
805 let line_index = snap.analysis.file_line_index(file_id)?;
806 let range = from_proto::text_range(&line_index, params.range);
807 let frange = FileRange { file_id, range };
808
809 snap.config.assist.allowed = params
810 .clone()
811 .context
812 .only
813 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
814
815 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
816
817 handle_fixes(&snap, &params, &mut res)?;
737 818
738 for assist in world.analysis().assists(&world.config.assist, frange)?.into_iter() { 819 if snap.config.client_caps.resolve_code_action {
739 res.push(to_proto::code_action(&world, assist)?.into()); 820 for (index, assist) in
821 snap.analysis.unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate()
822 {
823 res.push(to_proto::unresolved_code_action(&snap, assist, index)?);
824 }
825 } else {
826 for assist in snap.analysis.resolved_assists(&snap.config.assist, frange)?.into_iter() {
827 res.push(to_proto::resolved_code_action(&snap, assist)?);
828 }
740 } 829 }
830
741 Ok(Some(res)) 831 Ok(Some(res))
742} 832}
743 833
744pub fn handle_code_lens( 834pub(crate) fn handle_resolve_code_action(
745 world: WorldSnapshot, 835 mut snap: GlobalStateSnapshot,
836 params: lsp_ext::ResolveCodeActionParams,
837) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> {
838 let _p = profile("handle_resolve_code_action");
839 let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?;
840 let line_index = snap.analysis.file_line_index(file_id)?;
841 let range = from_proto::text_range(&line_index, params.code_action_params.range);
842 let frange = FileRange { file_id, range };
843
844 snap.config.assist.allowed = params
845 .code_action_params
846 .context
847 .only
848 .map(|it| it.into_iter().filter_map(from_proto::assist_kind).collect());
849
850 let assists = snap.analysis.resolved_assists(&snap.config.assist, frange)?;
851 let (id_string, index) = split_delim(&params.id, ':').unwrap();
852 let index = index.parse::<usize>().unwrap();
853 let assist = &assists[index];
854 assert!(assist.assist.id.0 == id_string);
855 Ok(to_proto::resolved_code_action(&snap, assist.clone())?.edit)
856}
857
858pub(crate) fn handle_code_lens(
859 snap: GlobalStateSnapshot,
746 params: lsp_types::CodeLensParams, 860 params: lsp_types::CodeLensParams,
747) -> Result<Option<Vec<CodeLens>>> { 861) -> Result<Option<Vec<CodeLens>>> {
748 let _p = profile("handle_code_lens"); 862 let _p = profile("handle_code_lens");
749 let mut lenses: Vec<CodeLens> = Default::default(); 863 let mut lenses: Vec<CodeLens> = Default::default();
750 864
751 if world.config.lens.none() { 865 if snap.config.lens.none() {
752 // early return before any db query! 866 // early return before any db query!
753 return Ok(Some(lenses)); 867 return Ok(Some(lenses));
754 } 868 }
755 869
756 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 870 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
757 let line_index = world.analysis().file_line_index(file_id)?; 871 let line_index = snap.analysis.file_line_index(file_id)?;
758 let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; 872 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
759 873
760 if world.config.lens.runnable() { 874 if snap.config.lens.runnable() {
761 // Gather runnables 875 // Gather runnables
762 for runnable in world.analysis().runnables(file_id)? { 876 for runnable in snap.analysis.runnables(file_id)? {
763 let (run_title, debugee) = match &runnable.kind { 877 if should_skip_target(&runnable, cargo_spec.as_ref()) {
764 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { 878 continue;
765 ("▶\u{fe0e} Run Test", true) 879 }
766 }
767 RunnableKind::DocTest { .. } => {
768 // cargo does not support -no-run for doctests
769 ("▶\u{fe0e} Run Doctest", false)
770 }
771 RunnableKind::Bench { .. } => {
772 // Nothing wrong with bench debugging
773 ("Run Bench", true)
774 }
775 RunnableKind::Bin => {
776 // Do not suggest binary run on other target than binary
777 match &cargo_spec {
778 Some(spec) => match spec.target_kind {
779 TargetKind::Bin => ("Run", true),
780 _ => continue,
781 },
782 None => continue,
783 }
784 }
785 };
786 880
787 let mut r = to_lsp_runnable(&world, file_id, runnable)?; 881 let action = runnable.action();
788 if world.config.lens.run { 882 let range = to_proto::range(&line_index, runnable.nav.range());
883 let r = to_proto::runnable(&snap, file_id, runnable)?;
884 if snap.config.lens.run {
789 let lens = CodeLens { 885 let lens = CodeLens {
790 range: r.range, 886 range,
791 command: Some(Command { 887 command: Some(run_single_command(&r, action.run_title)),
792 title: run_title.to_string(),
793 command: "rust-analyzer.runSingle".into(),
794 arguments: Some(vec![to_value(&r).unwrap()]),
795 }),
796 data: None, 888 data: None,
797 }; 889 };
798 lenses.push(lens); 890 lenses.push(lens);
799 } 891 }
800 892
801 if debugee && world.config.lens.debug { 893 if action.debugee && snap.config.lens.debug {
802 if r.args[0] == "run" { 894 let debug_lens =
803 r.args[0] = "build".into(); 895 CodeLens { range, command: Some(debug_single_command(&r)), data: None };
804 } else {
805 r.args.push("--no-run".into());
806 }
807 let debug_lens = CodeLens {
808 range: r.range,
809 command: Some(Command {
810 title: "Debug".into(),
811 command: "rust-analyzer.debugSingle".into(),
812 arguments: Some(vec![to_value(r).unwrap()]),
813 }),
814 data: None,
815 };
816 lenses.push(debug_lens); 896 lenses.push(debug_lens);
817 } 897 }
818 } 898 }
819 } 899 }
820 900
821 if world.config.lens.impementations { 901 if snap.config.lens.implementations {
822 // Handle impls 902 // Handle impls
823 lenses.extend( 903 lenses.extend(
824 world 904 snap.analysis
825 .analysis()
826 .file_structure(file_id)? 905 .file_structure(file_id)?
827 .into_iter() 906 .into_iter()
828 .filter(|it| match it.kind { 907 .filter(|it| {
829 SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true, 908 matches!(
830 _ => false, 909 it.kind,
910 SyntaxKind::TRAIT_DEF
911 | SyntaxKind::STRUCT_DEF
912 | SyntaxKind::ENUM_DEF
913 | SyntaxKind::UNION_DEF
914 )
831 }) 915 })
832 .map(|it| { 916 .map(|it| {
833 let range = to_proto::range(&line_index, it.node_range); 917 let range = to_proto::range(&line_index, it.node_range);
@@ -857,14 +941,17 @@ enum CodeLensResolveData {
857 Impls(lsp_types::request::GotoImplementationParams), 941 Impls(lsp_types::request::GotoImplementationParams),
858} 942}
859 943
860pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { 944pub(crate) fn handle_code_lens_resolve(
945 snap: GlobalStateSnapshot,
946 code_lens: CodeLens,
947) -> Result<CodeLens> {
861 let _p = profile("handle_code_lens_resolve"); 948 let _p = profile("handle_code_lens_resolve");
862 let data = code_lens.data.unwrap(); 949 let data = code_lens.data.unwrap();
863 let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?; 950 let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?;
864 match resolve { 951 match resolve {
865 Some(CodeLensResolveData::Impls(lens_params)) => { 952 Some(CodeLensResolveData::Impls(lens_params)) => {
866 let locations: Vec<Location> = 953 let locations: Vec<Location> =
867 match handle_goto_implementation(world, lens_params.clone())? { 954 match handle_goto_implementation(snap, lens_params.clone())? {
868 Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc], 955 Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc],
869 Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs, 956 Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs,
870 Some(lsp_types::GotoDefinitionResponse::Link(links)) => links 957 Some(lsp_types::GotoDefinitionResponse::Link(links)) => links
@@ -874,24 +961,13 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re
874 _ => vec![], 961 _ => vec![],
875 }; 962 };
876 963
877 let title = if locations.len() == 1 { 964 let title = implementation_title(locations.len());
878 "1 implementation".into() 965 let cmd = show_references_command(
879 } else {
880 format!("{} implementations", locations.len())
881 };
882
883 // We cannot use the 'editor.action.showReferences' command directly
884 // because that command requires vscode types which we convert in the handler
885 // on the client side.
886 let cmd = Command {
887 title, 966 title,
888 command: "rust-analyzer.showReferences".into(), 967 &lens_params.text_document_position_params.text_document.uri,
889 arguments: Some(vec![ 968 code_lens.range.start,
890 to_value(&lens_params.text_document_position_params.text_document.uri).unwrap(), 969 locations,
891 to_value(code_lens.range.start).unwrap(), 970 );
892 to_value(locations).unwrap(),
893 ]),
894 };
895 Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) 971 Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None })
896 } 972 }
897 None => Ok(CodeLens { 973 None => Ok(CodeLens {
@@ -902,16 +978,16 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re
902 } 978 }
903} 979}
904 980
905pub fn handle_document_highlight( 981pub(crate) fn handle_document_highlight(
906 world: WorldSnapshot, 982 snap: GlobalStateSnapshot,
907 params: lsp_types::DocumentHighlightParams, 983 params: lsp_types::DocumentHighlightParams,
908) -> Result<Option<Vec<DocumentHighlight>>> { 984) -> Result<Option<Vec<DocumentHighlight>>> {
909 let _p = profile("handle_document_highlight"); 985 let _p = profile("handle_document_highlight");
910 let position = from_proto::file_position(&world, params.text_document_position_params)?; 986 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
911 let line_index = world.analysis().file_line_index(position.file_id)?; 987 let line_index = snap.analysis.file_line_index(position.file_id)?;
912 988
913 let refs = match world 989 let refs = match snap
914 .analysis() 990 .analysis
915 .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))? 991 .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))?
916 { 992 {
917 None => return Ok(None), 993 None => return Ok(None),
@@ -929,21 +1005,24 @@ pub fn handle_document_highlight(
929 Ok(Some(res)) 1005 Ok(Some(res))
930} 1006}
931 1007
932pub fn handle_ssr( 1008pub(crate) fn handle_ssr(
933 world: WorldSnapshot, 1009 snap: GlobalStateSnapshot,
934 params: lsp_ext::SsrParams, 1010 params: lsp_ext::SsrParams,
935) -> Result<lsp_types::WorkspaceEdit> { 1011) -> Result<lsp_types::WorkspaceEdit> {
936 let _p = profile("handle_ssr"); 1012 let _p = profile("handle_ssr");
937 let source_change = 1013 let source_change =
938 world.analysis().structural_search_replace(&params.query, params.parse_only)??; 1014 snap.analysis.structural_search_replace(&params.query, params.parse_only)??;
939 to_proto::workspace_edit(&world, source_change) 1015 to_proto::workspace_edit(&snap, source_change)
940} 1016}
941 1017
942pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { 1018pub(crate) fn publish_diagnostics(
1019 snap: &GlobalStateSnapshot,
1020 file_id: FileId,
1021) -> Result<Vec<Diagnostic>> {
943 let _p = profile("publish_diagnostics"); 1022 let _p = profile("publish_diagnostics");
944 let line_index = world.analysis().file_line_index(file_id)?; 1023 let line_index = snap.analysis.file_line_index(file_id)?;
945 let diagnostics: Vec<Diagnostic> = world 1024 let diagnostics: Vec<Diagnostic> = snap
946 .analysis() 1025 .analysis
947 .diagnostics(file_id)? 1026 .diagnostics(file_id)?
948 .into_iter() 1027 .into_iter()
949 .map(|d| Diagnostic { 1028 .map(|d| Diagnostic {
@@ -956,90 +1035,32 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia
956 tags: None, 1035 tags: None,
957 }) 1036 })
958 .collect(); 1037 .collect();
959 Ok(DiagnosticTask::SetNative(file_id, diagnostics)) 1038 Ok(diagnostics)
960}
961
962fn to_lsp_runnable(
963 world: &WorldSnapshot,
964 file_id: FileId,
965 runnable: Runnable,
966) -> Result<lsp_ext::Runnable> {
967 let spec = CargoTargetSpec::for_file(world, file_id)?;
968 let target = spec.as_ref().map(|s| s.target.clone());
969 let mut features_needed = vec![];
970 for cfg_expr in &runnable.cfg_exprs {
971 collect_minimal_features_needed(cfg_expr, &mut features_needed);
972 }
973 let (args, extra_args) =
974 CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?;
975 let line_index = world.analysis().file_line_index(file_id)?;
976 let label = match &runnable.kind {
977 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
978 RunnableKind::TestMod { path } => format!("test-mod {}", path),
979 RunnableKind::Bench { test_id } => format!("bench {}", test_id),
980 RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
981 RunnableKind::Bin => {
982 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
983 }
984 };
985 Ok(lsp_ext::Runnable {
986 range: to_proto::range(&line_index, runnable.range),
987 label,
988 bin: "cargo".to_string(),
989 args,
990 extra_args,
991 env: {
992 let mut m = FxHashMap::default();
993 m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
994 m
995 },
996 cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()),
997 })
998} 1039}
999 1040
1000/// Fill minimal features needed 1041pub(crate) fn handle_inlay_hints(
1001fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) { 1042 snap: GlobalStateSnapshot,
1002 match cfg_expr {
1003 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
1004 CfgExpr::All(preds) => {
1005 preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features));
1006 }
1007 CfgExpr::Any(preds) => {
1008 for cfg in preds {
1009 let len_features = features.len();
1010 collect_minimal_features_needed(cfg, features);
1011 if len_features != features.len() {
1012 break;
1013 }
1014 }
1015 }
1016 _ => {}
1017 }
1018}
1019
1020pub fn handle_inlay_hints(
1021 world: WorldSnapshot,
1022 params: InlayHintsParams, 1043 params: InlayHintsParams,
1023) -> Result<Vec<InlayHint>> { 1044) -> Result<Vec<InlayHint>> {
1024 let _p = profile("handle_inlay_hints"); 1045 let _p = profile("handle_inlay_hints");
1025 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 1046 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1026 let analysis = world.analysis(); 1047 let line_index = snap.analysis.file_line_index(file_id)?;
1027 let line_index = analysis.file_line_index(file_id)?; 1048 Ok(snap
1028 Ok(analysis 1049 .analysis
1029 .inlay_hints(file_id, &world.config.inlay_hints)? 1050 .inlay_hints(file_id, &snap.config.inlay_hints)?
1030 .into_iter() 1051 .into_iter()
1031 .map(|it| to_proto::inlay_int(&line_index, it)) 1052 .map(|it| to_proto::inlay_int(&line_index, it))
1032 .collect()) 1053 .collect())
1033} 1054}
1034 1055
1035pub fn handle_call_hierarchy_prepare( 1056pub(crate) fn handle_call_hierarchy_prepare(
1036 world: WorldSnapshot, 1057 snap: GlobalStateSnapshot,
1037 params: CallHierarchyPrepareParams, 1058 params: CallHierarchyPrepareParams,
1038) -> Result<Option<Vec<CallHierarchyItem>>> { 1059) -> Result<Option<Vec<CallHierarchyItem>>> {
1039 let _p = profile("handle_call_hierarchy_prepare"); 1060 let _p = profile("handle_call_hierarchy_prepare");
1040 let position = from_proto::file_position(&world, params.text_document_position_params)?; 1061 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
1041 1062
1042 let nav_info = match world.analysis().call_hierarchy(position)? { 1063 let nav_info = match snap.analysis.call_hierarchy(position)? {
1043 None => return Ok(None), 1064 None => return Ok(None),
1044 Some(it) => it, 1065 Some(it) => it,
1045 }; 1066 };
@@ -1048,24 +1069,24 @@ pub fn handle_call_hierarchy_prepare(
1048 let res = navs 1069 let res = navs
1049 .into_iter() 1070 .into_iter()
1050 .filter(|it| it.kind() == SyntaxKind::FN_DEF) 1071 .filter(|it| it.kind() == SyntaxKind::FN_DEF)
1051 .map(|it| to_proto::call_hierarchy_item(&world, it)) 1072 .map(|it| to_proto::call_hierarchy_item(&snap, it))
1052 .collect::<Result<Vec<_>>>()?; 1073 .collect::<Result<Vec<_>>>()?;
1053 1074
1054 Ok(Some(res)) 1075 Ok(Some(res))
1055} 1076}
1056 1077
1057pub fn handle_call_hierarchy_incoming( 1078pub(crate) fn handle_call_hierarchy_incoming(
1058 world: WorldSnapshot, 1079 snap: GlobalStateSnapshot,
1059 params: CallHierarchyIncomingCallsParams, 1080 params: CallHierarchyIncomingCallsParams,
1060) -> Result<Option<Vec<CallHierarchyIncomingCall>>> { 1081) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
1061 let _p = profile("handle_call_hierarchy_incoming"); 1082 let _p = profile("handle_call_hierarchy_incoming");
1062 let item = params.item; 1083 let item = params.item;
1063 1084
1064 let doc = TextDocumentIdentifier::new(item.uri); 1085 let doc = TextDocumentIdentifier::new(item.uri);
1065 let frange = from_proto::file_range(&world, doc, item.range)?; 1086 let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
1066 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; 1087 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1067 1088
1068 let call_items = match world.analysis().incoming_calls(fpos)? { 1089 let call_items = match snap.analysis.incoming_calls(fpos)? {
1069 None => return Ok(None), 1090 None => return Ok(None),
1070 Some(it) => it, 1091 Some(it) => it,
1071 }; 1092 };
@@ -1074,8 +1095,8 @@ pub fn handle_call_hierarchy_incoming(
1074 1095
1075 for call_item in call_items.into_iter() { 1096 for call_item in call_items.into_iter() {
1076 let file_id = call_item.target.file_id(); 1097 let file_id = call_item.target.file_id();
1077 let line_index = world.analysis().file_line_index(file_id)?; 1098 let line_index = snap.analysis.file_line_index(file_id)?;
1078 let item = to_proto::call_hierarchy_item(&world, call_item.target)?; 1099 let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1079 res.push(CallHierarchyIncomingCall { 1100 res.push(CallHierarchyIncomingCall {
1080 from: item, 1101 from: item,
1081 from_ranges: call_item 1102 from_ranges: call_item
@@ -1089,18 +1110,18 @@ pub fn handle_call_hierarchy_incoming(
1089 Ok(Some(res)) 1110 Ok(Some(res))
1090} 1111}
1091 1112
1092pub fn handle_call_hierarchy_outgoing( 1113pub(crate) fn handle_call_hierarchy_outgoing(
1093 world: WorldSnapshot, 1114 snap: GlobalStateSnapshot,
1094 params: CallHierarchyOutgoingCallsParams, 1115 params: CallHierarchyOutgoingCallsParams,
1095) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> { 1116) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
1096 let _p = profile("handle_call_hierarchy_outgoing"); 1117 let _p = profile("handle_call_hierarchy_outgoing");
1097 let item = params.item; 1118 let item = params.item;
1098 1119
1099 let doc = TextDocumentIdentifier::new(item.uri); 1120 let doc = TextDocumentIdentifier::new(item.uri);
1100 let frange = from_proto::file_range(&world, doc, item.range)?; 1121 let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
1101 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; 1122 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1102 1123
1103 let call_items = match world.analysis().outgoing_calls(fpos)? { 1124 let call_items = match snap.analysis.outgoing_calls(fpos)? {
1104 None => return Ok(None), 1125 None => return Ok(None),
1105 Some(it) => it, 1126 Some(it) => it,
1106 }; 1127 };
@@ -1109,8 +1130,8 @@ pub fn handle_call_hierarchy_outgoing(
1109 1130
1110 for call_item in call_items.into_iter() { 1131 for call_item in call_items.into_iter() {
1111 let file_id = call_item.target.file_id(); 1132 let file_id = call_item.target.file_id();
1112 let line_index = world.analysis().file_line_index(file_id)?; 1133 let line_index = snap.analysis.file_line_index(file_id)?;
1113 let item = to_proto::call_hierarchy_item(&world, call_item.target)?; 1134 let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1114 res.push(CallHierarchyOutgoingCall { 1135 res.push(CallHierarchyOutgoingCall {
1115 to: item, 1136 to: item,
1116 from_ranges: call_item 1137 from_ranges: call_item
@@ -1124,83 +1145,204 @@ pub fn handle_call_hierarchy_outgoing(
1124 Ok(Some(res)) 1145 Ok(Some(res))
1125} 1146}
1126 1147
1127pub fn handle_semantic_tokens( 1148pub(crate) fn handle_semantic_tokens(
1128 world: WorldSnapshot, 1149 snap: GlobalStateSnapshot,
1129 params: SemanticTokensParams, 1150 params: SemanticTokensParams,
1130) -> Result<Option<SemanticTokensResult>> { 1151) -> Result<Option<SemanticTokensResult>> {
1131 let _p = profile("handle_semantic_tokens"); 1152 let _p = profile("handle_semantic_tokens");
1132 1153
1133 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 1154 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1134 let text = world.analysis().file_text(file_id)?; 1155 let text = snap.analysis.file_text(file_id)?;
1135 let line_index = world.analysis().file_line_index(file_id)?; 1156 let line_index = snap.analysis.file_line_index(file_id)?;
1136 1157
1137 let highlights = world.analysis().highlight(file_id)?; 1158 let highlights = snap.analysis.highlight(file_id)?;
1138 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1159 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1139 Ok(Some(semantic_tokens.into())) 1160 Ok(Some(semantic_tokens.into()))
1140} 1161}
1141 1162
1142pub fn handle_semantic_tokens_range( 1163pub(crate) fn handle_semantic_tokens_range(
1143 world: WorldSnapshot, 1164 snap: GlobalStateSnapshot,
1144 params: SemanticTokensRangeParams, 1165 params: SemanticTokensRangeParams,
1145) -> Result<Option<SemanticTokensRangeResult>> { 1166) -> Result<Option<SemanticTokensRangeResult>> {
1146 let _p = profile("handle_semantic_tokens_range"); 1167 let _p = profile("handle_semantic_tokens_range");
1147 1168
1148 let frange = from_proto::file_range(&world, params.text_document, params.range)?; 1169 let frange = from_proto::file_range(&snap, params.text_document, params.range)?;
1149 let text = world.analysis().file_text(frange.file_id)?; 1170 let text = snap.analysis.file_text(frange.file_id)?;
1150 let line_index = world.analysis().file_line_index(frange.file_id)?; 1171 let line_index = snap.analysis.file_line_index(frange.file_id)?;
1151 1172
1152 let highlights = world.analysis().highlight_range(frange)?; 1173 let highlights = snap.analysis.highlight_range(frange)?;
1153 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1174 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1154 Ok(Some(semantic_tokens.into())) 1175 Ok(Some(semantic_tokens.into()))
1155} 1176}
1156 1177
1157#[cfg(test)] 1178fn implementation_title(count: usize) -> String {
1158mod tests { 1179 if count == 1 {
1159 use super::*; 1180 "1 implementation".into()
1181 } else {
1182 format!("{} implementations", count)
1183 }
1184}
1185
1186fn show_references_command(
1187 title: String,
1188 uri: &lsp_types::Url,
1189 position: lsp_types::Position,
1190 locations: Vec<lsp_types::Location>,
1191) -> Command {
1192 // We cannot use the 'editor.action.showReferences' command directly
1193 // because that command requires vscode types which we convert in the handler
1194 // on the client side.
1195
1196 Command {
1197 title,
1198 command: "rust-analyzer.showReferences".into(),
1199 arguments: Some(vec![
1200 to_value(uri).unwrap(),
1201 to_value(position).unwrap(),
1202 to_value(locations).unwrap(),
1203 ]),
1204 }
1205}
1206
1207fn run_single_command(runnable: &lsp_ext::Runnable, title: &str) -> Command {
1208 Command {
1209 title: title.to_string(),
1210 command: "rust-analyzer.runSingle".into(),
1211 arguments: Some(vec![to_value(runnable).unwrap()]),
1212 }
1213}
1160 1214
1161 use mbe::{ast_to_token_tree, TokenMap}; 1215fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command {
1162 use ra_cfg::parse_cfg; 1216 Command {
1163 use ra_syntax::{ 1217 title: "Debug".into(),
1164 ast::{self, AstNode}, 1218 command: "rust-analyzer.debugSingle".into(),
1165 SmolStr, 1219 arguments: Some(vec![to_value(runnable).unwrap()]),
1220 }
1221}
1222
1223fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> {
1224 let value = if snap.config.client_caps.location_link {
1225 let link = to_proto::location_link(snap, None, nav.clone()).ok()?;
1226 to_value(link).ok()?
1227 } else {
1228 let range = FileRange { file_id: nav.file_id(), range: nav.range() };
1229 let location = to_proto::location(snap, range).ok()?;
1230 to_value(location).ok()?
1166 }; 1231 };
1167 1232
1168 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) { 1233 Some(Command {
1169 let source_file = ast::SourceFile::parse(input).ok().unwrap(); 1234 title: nav.name().to_string(),
1170 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); 1235 command: "rust-analyzer.gotoLocation".into(),
1171 ast_to_token_tree(&tt).unwrap() 1236 arguments: Some(vec![value]),
1237 })
1238}
1239
1240fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink {
1241 lsp_ext::CommandLink { tooltip: Some(tooltip), command }
1242}
1243
1244fn show_impl_command_link(
1245 snap: &GlobalStateSnapshot,
1246 position: &FilePosition,
1247) -> Option<lsp_ext::CommandLinkGroup> {
1248 if snap.config.hover.implementations {
1249 if let Some(nav_data) = snap.analysis.goto_implementation(*position).unwrap_or(None) {
1250 let uri = to_proto::url(snap, position.file_id);
1251 let line_index = snap.analysis.file_line_index(position.file_id).ok()?;
1252 let position = to_proto::position(&line_index, position.offset);
1253 let locations: Vec<_> = nav_data
1254 .info
1255 .into_iter()
1256 .filter_map(|nav| to_proto::location_from_nav(snap, nav).ok())
1257 .collect();
1258 let title = implementation_title(locations.len());
1259 let command = show_references_command(title, &uri, position, locations);
1260
1261 return Some(lsp_ext::CommandLinkGroup {
1262 commands: vec![to_command_link(command, "Go to implementations".into())],
1263 ..Default::default()
1264 });
1265 }
1172 } 1266 }
1267 None
1268}
1173 1269
1174 #[test] 1270fn runnable_action_links(
1175 fn test_cfg_expr_minimal_features_needed() { 1271 snap: &GlobalStateSnapshot,
1176 let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#); 1272 file_id: FileId,
1177 let cfg_expr = parse_cfg(&subtree); 1273 runnable: Runnable,
1178 let mut min_features = vec![]; 1274) -> Option<lsp_ext::CommandLinkGroup> {
1179 collect_minimal_features_needed(&cfg_expr, &mut min_features); 1275 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?;
1276 if !snap.config.hover.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) {
1277 return None;
1278 }
1180 1279
1181 assert_eq!(min_features, vec![SmolStr::new("baz")]); 1280 let action: &'static _ = runnable.action();
1281 to_proto::runnable(snap, file_id, runnable).ok().map(|r| {
1282 let mut group = lsp_ext::CommandLinkGroup::default();
1182 1283
1183 let (subtree, _) = 1284 if snap.config.hover.run {
1184 get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#); 1285 let run_command = run_single_command(&r, action.run_title);
1185 let cfg_expr = parse_cfg(&subtree); 1286 group.commands.push(to_command_link(run_command, r.label.clone()));
1287 }
1186 1288
1187 let mut min_features = vec![]; 1289 if snap.config.hover.debug {
1188 collect_minimal_features_needed(&cfg_expr, &mut min_features); 1290 let dbg_command = debug_single_command(&r);
1189 assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]); 1291 group.commands.push(to_command_link(dbg_command, r.label));
1292 }
1293
1294 group
1295 })
1296}
1190 1297
1191 let (subtree, _) = 1298fn goto_type_action_links(
1192 get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#); 1299 snap: &GlobalStateSnapshot,
1193 let cfg_expr = parse_cfg(&subtree); 1300 nav_targets: &[HoverGotoTypeData],
1301) -> Option<lsp_ext::CommandLinkGroup> {
1302 if !snap.config.hover.goto_type_def || nav_targets.is_empty() {
1303 return None;
1304 }
1305
1306 Some(lsp_ext::CommandLinkGroup {
1307 title: Some("Go to ".into()),
1308 commands: nav_targets
1309 .iter()
1310 .filter_map(|it| {
1311 goto_location_command(snap, &it.nav)
1312 .map(|cmd| to_command_link(cmd, it.mod_path.clone()))
1313 })
1314 .collect(),
1315 })
1316}
1194 1317
1195 let mut min_features = vec![]; 1318fn prepare_hover_actions(
1196 collect_minimal_features_needed(&cfg_expr, &mut min_features); 1319 snap: &GlobalStateSnapshot,
1197 assert_eq!(min_features, vec![SmolStr::new("baz")]); 1320 file_id: FileId,
1321 actions: &[HoverAction],
1322) -> Vec<lsp_ext::CommandLinkGroup> {
1323 if snap.config.hover.none() || !snap.config.client_caps.hover_actions {
1324 return Vec::new();
1325 }
1198 1326
1199 let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#); 1327 actions
1200 let cfg_expr = parse_cfg(&subtree); 1328 .iter()
1329 .filter_map(|it| match it {
1330 HoverAction::Implementaion(position) => show_impl_command_link(snap, position),
1331 HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()),
1332 HoverAction::GoToType(targets) => goto_type_action_links(snap, targets),
1333 })
1334 .collect()
1335}
1201 1336
1202 let mut min_features = vec![]; 1337fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> bool {
1203 collect_minimal_features_needed(&cfg_expr, &mut min_features); 1338 match runnable.kind {
1204 assert!(min_features.is_empty()); 1339 RunnableKind::Bin => {
1340 // Do not suggest binary run on other target than binary
1341 match &cargo_spec {
1342 Some(spec) => !matches!(spec.target_kind, TargetKind::Bin | TargetKind::Example),
1343 None => true,
1344 }
1345 }
1346 _ => false,
1205 } 1347 }
1206} 1348}
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index 57d0e9218..369830973 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -17,30 +17,74 @@ macro_rules! eprintln {
17 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; 17 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
18} 18}
19 19
20mod vfs_glob; 20mod global_state;
21mod reload;
22mod main_loop;
23mod dispatch;
24mod handlers;
21mod caps; 25mod caps;
22mod cargo_target_spec; 26mod cargo_target_spec;
23mod to_proto; 27mod to_proto;
24mod from_proto; 28mod from_proto;
25mod main_loop; 29mod semantic_tokens;
26mod markdown; 30mod markdown;
31mod diagnostics;
32mod line_endings;
33mod request_metrics;
34mod lsp_utils;
35mod thread_pool;
27pub mod lsp_ext; 36pub mod lsp_ext;
28pub mod config; 37pub mod config;
29mod world;
30mod diagnostics;
31mod semantic_tokens;
32 38
33use serde::de::DeserializeOwned; 39use serde::de::DeserializeOwned;
34 40
35pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; 41pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>;
36pub use crate::{ 42pub use crate::{caps::server_capabilities, main_loop::main_loop};
37 caps::server_capabilities, 43use ra_ide::AnalysisHost;
38 main_loop::LspError, 44use std::fmt;
39 main_loop::{main_loop, show_message}, 45use vfs::Vfs;
40};
41 46
42pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { 47pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> {
43 let res = T::deserialize(&json) 48 let res = T::deserialize(&json)
44 .map_err(|e| format!("Failed to deserialize {}: {}; {}", what, e, json))?; 49 .map_err(|e| format!("Failed to deserialize {}: {}; {}", what, e, json))?;
45 Ok(res) 50 Ok(res)
46} 51}
52
53#[derive(Debug)]
54struct LspError {
55 code: i32,
56 message: String,
57}
58
59impl LspError {
60 fn new(code: i32, message: String) -> LspError {
61 LspError { code, message }
62 }
63}
64
65impl fmt::Display for LspError {
66 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 write!(f, "Language Server request failed with {}. ({})", self.code, self.message)
68 }
69}
70
71impl std::error::Error for LspError {}
72
73fn print_memory_usage(mut host: AnalysisHost, vfs: Vfs) {
74 let mut mem = host.per_query_memory_usage();
75
76 let before = ra_prof::memory_usage();
77 drop(vfs);
78 let vfs = before.allocated - ra_prof::memory_usage().allocated;
79 mem.push(("VFS".into(), vfs));
80
81 let before = ra_prof::memory_usage();
82 drop(host);
83 mem.push(("Unaccounted".into(), before.allocated - ra_prof::memory_usage().allocated));
84
85 mem.push(("Remaining".into(), ra_prof::memory_usage().allocated));
86
87 for (name, bytes) in mem {
88 println!("{:>8} {}", bytes, name);
89 }
90}
diff --git a/crates/rust-analyzer/src/line_endings.rs b/crates/rust-analyzer/src/line_endings.rs
new file mode 100644
index 000000000..9f892f32e
--- /dev/null
+++ b/crates/rust-analyzer/src/line_endings.rs
@@ -0,0 +1,52 @@
1//! We maintain invariant that all internal strings use `\n` as line separator.
2//! This module does line ending conversion and detection (so that we can
3//! convert back to `\r\n` on the way out).
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub(crate) enum LineEndings {
7 Unix,
8 Dos,
9}
10
11impl LineEndings {
12 /// Replaces `\r\n` with `\n` in-place in `src`.
13 pub(crate) fn normalize(src: String) -> (String, LineEndings) {
14 if !src.as_bytes().contains(&b'\r') {
15 return (src, LineEndings::Unix);
16 }
17
18 // We replace `\r\n` with `\n` in-place, which doesn't break utf-8 encoding.
19 // While we *can* call `as_mut_vec` and do surgery on the live string
20 // directly, let's rather steal the contents of `src`. This makes the code
21 // safe even if a panic occurs.
22
23 let mut buf = src.into_bytes();
24 let mut gap_len = 0;
25 let mut tail = buf.as_mut_slice();
26 loop {
27 let idx = match find_crlf(&tail[gap_len..]) {
28 None => tail.len(),
29 Some(idx) => idx + gap_len,
30 };
31 tail.copy_within(gap_len..idx, 0);
32 tail = &mut tail[idx - gap_len..];
33 if tail.len() == gap_len {
34 break;
35 }
36 gap_len += 1;
37 }
38
39 // Account for removed `\r`.
40 // After `set_len`, `buf` is guaranteed to contain utf-8 again.
41 let new_len = buf.len() - gap_len;
42 let src = unsafe {
43 buf.set_len(new_len);
44 String::from_utf8_unchecked(buf)
45 };
46 return (src, LineEndings::Dos);
47
48 fn find_crlf(src: &[u8]) -> Option<usize> {
49 src.iter().zip(src.iter().skip(1)).position(|it| it == (&b'\r', &b'\n'))
50 }
51 }
52}
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index acb1dacb6..e216966a9 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -3,8 +3,9 @@
3use std::{collections::HashMap, path::PathBuf}; 3use std::{collections::HashMap, path::PathBuf};
4 4
5use lsp_types::request::Request; 5use lsp_types::request::Request;
6use lsp_types::{Position, Range, TextDocumentIdentifier}; 6use lsp_types::{
7use rustc_hash::FxHashMap; 7 notification::Notification, CodeActionKind, Position, Range, TextDocumentIdentifier,
8};
8use serde::{Deserialize, Serialize}; 9use serde::{Deserialize, Serialize};
9 10
10pub enum AnalyzerStatus {} 11pub enum AnalyzerStatus {}
@@ -15,12 +16,20 @@ impl Request for AnalyzerStatus {
15 const METHOD: &'static str = "rust-analyzer/analyzerStatus"; 16 const METHOD: &'static str = "rust-analyzer/analyzerStatus";
16} 17}
17 18
18pub enum CollectGarbage {} 19pub enum MemoryUsage {}
19 20
20impl Request for CollectGarbage { 21impl Request for MemoryUsage {
22 type Params = ();
23 type Result = String;
24 const METHOD: &'static str = "rust-analyzer/memoryUsage";
25}
26
27pub enum ReloadWorkspace {}
28
29impl Request for ReloadWorkspace {
21 type Params = (); 30 type Params = ();
22 type Result = (); 31 type Result = ();
23 const METHOD: &'static str = "rust-analyzer/collectGarbage"; 32 const METHOD: &'static str = "rust-analyzer/reloadWorkspace";
24} 33}
25 34
26pub enum SyntaxTree {} 35pub enum SyntaxTree {}
@@ -98,6 +107,22 @@ pub struct JoinLinesParams {
98 pub ranges: Vec<Range>, 107 pub ranges: Vec<Range>,
99} 108}
100 109
110pub enum ResolveCodeActionRequest {}
111
112impl Request for ResolveCodeActionRequest {
113 type Params = ResolveCodeActionParams;
114 type Result = Option<SnippetWorkspaceEdit>;
115 const METHOD: &'static str = "experimental/resolveCodeAction";
116}
117
118/// Params for the ResolveCodeActionRequest
119#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
120#[serde(rename_all = "camelCase")]
121pub struct ResolveCodeActionParams {
122 pub code_action_params: lsp_types::CodeActionParams,
123 pub id: String,
124}
125
101pub enum OnEnter {} 126pub enum OnEnter {}
102 127
103impl Request for OnEnter { 128impl Request for OnEnter {
@@ -111,7 +136,7 @@ pub enum Runnables {}
111impl Request for Runnables { 136impl Request for Runnables {
112 type Params = RunnablesParams; 137 type Params = RunnablesParams;
113 type Result = Vec<Runnable>; 138 type Result = Vec<Runnable>;
114 const METHOD: &'static str = "rust-analyzer/runnables"; 139 const METHOD: &'static str = "experimental/runnables";
115} 140}
116 141
117#[derive(Serialize, Deserialize, Debug)] 142#[derive(Serialize, Deserialize, Debug)]
@@ -124,13 +149,30 @@ pub struct RunnablesParams {
124#[derive(Deserialize, Serialize, Debug)] 149#[derive(Deserialize, Serialize, Debug)]
125#[serde(rename_all = "camelCase")] 150#[serde(rename_all = "camelCase")]
126pub struct Runnable { 151pub struct Runnable {
127 pub range: Range,
128 pub label: String, 152 pub label: String,
129 pub bin: String, 153 #[serde(skip_serializing_if = "Option::is_none")]
130 pub args: Vec<String>, 154 pub location: Option<lsp_types::LocationLink>,
131 pub extra_args: Vec<String>, 155 pub kind: RunnableKind,
132 pub env: FxHashMap<String, String>, 156 pub args: CargoRunnable,
133 pub cwd: Option<PathBuf>, 157}
158
159#[derive(Serialize, Deserialize, Debug)]
160#[serde(rename_all = "lowercase")]
161pub enum RunnableKind {
162 Cargo,
163}
164
165#[derive(Deserialize, Serialize, Debug)]
166#[serde(rename_all = "camelCase")]
167pub struct CargoRunnable {
168 #[serde(skip_serializing_if = "Option::is_none")]
169 pub workspace_root: Option<PathBuf>,
170 // command, --package and --lib stuff
171 pub cargo_args: Vec<String>,
172 // stuff after --
173 pub executable_args: Vec<String>,
174 #[serde(skip_serializing_if = "Option::is_none")]
175 pub expect_test: Option<bool>,
134} 176}
135 177
136pub enum InlayHints {} 178pub enum InlayHints {}
@@ -176,6 +218,22 @@ pub struct SsrParams {
176 pub parse_only: bool, 218 pub parse_only: bool,
177} 219}
178 220
221pub enum StatusNotification {}
222
223#[serde(rename_all = "camelCase")]
224#[derive(Serialize, Deserialize)]
225pub enum Status {
226 Loading,
227 Ready,
228 NeedsReload,
229 Invalid,
230}
231
232impl Notification for StatusNotification {
233 type Params = Status;
234 const METHOD: &'static str = "rust-analyzer/status";
235}
236
179pub enum CodeActionRequest {} 237pub enum CodeActionRequest {}
180 238
181impl Request for CodeActionRequest { 239impl Request for CodeActionRequest {
@@ -188,11 +246,14 @@ impl Request for CodeActionRequest {
188pub struct CodeAction { 246pub struct CodeAction {
189 pub title: String, 247 pub title: String,
190 #[serde(skip_serializing_if = "Option::is_none")] 248 #[serde(skip_serializing_if = "Option::is_none")]
191 pub group: Option<String>, 249 pub id: Option<String>,
192 #[serde(skip_serializing_if = "Option::is_none")] 250 #[serde(skip_serializing_if = "Option::is_none")]
193 pub kind: Option<String>, 251 pub group: Option<String>,
194 #[serde(skip_serializing_if = "Option::is_none")] 252 #[serde(skip_serializing_if = "Option::is_none")]
195 pub command: Option<lsp_types::Command>, 253 pub kind: Option<CodeActionKind>,
254 // We don't handle commands on the client-side
255 // #[serde(skip_serializing_if = "Option::is_none")]
256 // pub command: Option<lsp_types::Command>,
196 #[serde(skip_serializing_if = "Option::is_none")] 257 #[serde(skip_serializing_if = "Option::is_none")]
197 pub edit: Option<SnippetWorkspaceEdit>, 258 pub edit: Option<SnippetWorkspaceEdit>,
198} 259}
@@ -228,3 +289,35 @@ pub struct SnippetTextEdit {
228 #[serde(skip_serializing_if = "Option::is_none")] 289 #[serde(skip_serializing_if = "Option::is_none")]
229 pub insert_text_format: Option<lsp_types::InsertTextFormat>, 290 pub insert_text_format: Option<lsp_types::InsertTextFormat>,
230} 291}
292
293pub enum HoverRequest {}
294
295impl Request for HoverRequest {
296 type Params = lsp_types::HoverParams;
297 type Result = Option<Hover>;
298 const METHOD: &'static str = "textDocument/hover";
299}
300
301#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
302pub struct Hover {
303 #[serde(flatten)]
304 pub hover: lsp_types::Hover,
305 #[serde(skip_serializing_if = "Vec::is_empty")]
306 pub actions: Vec<CommandLinkGroup>,
307}
308
309#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
310pub struct CommandLinkGroup {
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub title: Option<String>,
313 pub commands: Vec<CommandLink>,
314}
315
316// LSP v3.15 Command does not have a `tooltip` field, vscode supports one.
317#[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)]
318pub struct CommandLink {
319 #[serde(flatten)]
320 pub command: lsp_types::Command,
321 #[serde(skip_serializing_if = "Option::is_none")]
322 pub tooltip: Option<String>,
323}
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
new file mode 100644
index 000000000..0bc3ff115
--- /dev/null
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -0,0 +1,195 @@
1//! Utilities for LSP-related boilerplate code.
2use std::{error::Error, ops::Range};
3
4use lsp_server::Notification;
5use ra_db::Canceled;
6use ra_ide::LineIndex;
7
8use crate::{from_proto, global_state::GlobalState};
9
10pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool {
11 e.downcast_ref::<Canceled>().is_some()
12}
13
14pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
15 notification: &Notification,
16) -> bool {
17 notification.method == N::METHOD
18}
19
20#[derive(Debug, Eq, PartialEq)]
21pub(crate) enum Progress {
22 Begin,
23 Report,
24 End,
25}
26
27impl Progress {
28 pub(crate) fn percentage(done: usize, total: usize) -> f64 {
29 (done as f64 / total.max(1) as f64) * 100.0
30 }
31}
32
33impl GlobalState {
34 pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) {
35 let message = message.into();
36 self.send_notification::<lsp_types::notification::ShowMessage>(
37 lsp_types::ShowMessageParams { typ, message },
38 )
39 }
40
41 pub(crate) fn report_progress(
42 &mut self,
43 title: &str,
44 state: Progress,
45 message: Option<String>,
46 percentage: Option<f64>,
47 ) {
48 if !self.config.client_caps.work_done_progress {
49 return;
50 }
51 let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
52 let work_done_progress = match state {
53 Progress::Begin => {
54 self.send_request::<lsp_types::request::WorkDoneProgressCreate>(
55 lsp_types::WorkDoneProgressCreateParams { token: token.clone() },
56 |_, _| (),
57 );
58
59 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
60 title: title.into(),
61 cancellable: None,
62 message,
63 percentage,
64 })
65 }
66 Progress::Report => {
67 lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
68 cancellable: None,
69 message,
70 percentage,
71 })
72 }
73 Progress::End => {
74 lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message })
75 }
76 };
77 self.send_notification::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
78 token,
79 value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress),
80 });
81 }
82}
83
84pub(crate) fn apply_document_changes(
85 old_text: &mut String,
86 content_changes: Vec<lsp_types::TextDocumentContentChangeEvent>,
87) {
88 let mut line_index = LineIndex::new(old_text);
89 // The changes we got must be applied sequentially, but can cross lines so we
90 // have to keep our line index updated.
91 // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we
92 // remember the last valid line in the index and only rebuild it if needed.
93 // The VFS will normalize the end of lines to `\n`.
94 enum IndexValid {
95 All,
96 UpToLineExclusive(u64),
97 }
98
99 impl IndexValid {
100 fn covers(&self, line: u64) -> bool {
101 match *self {
102 IndexValid::UpToLineExclusive(to) => to > line,
103 _ => true,
104 }
105 }
106 }
107
108 let mut index_valid = IndexValid::All;
109 for change in content_changes {
110 match change.range {
111 Some(range) => {
112 if !index_valid.covers(range.end.line) {
113 line_index = LineIndex::new(&old_text);
114 }
115 index_valid = IndexValid::UpToLineExclusive(range.start.line);
116 let range = from_proto::text_range(&line_index, range);
117 old_text.replace_range(Range::<usize>::from(range), &change.text);
118 }
119 None => {
120 *old_text = change.text;
121 index_valid = IndexValid::UpToLineExclusive(0);
122 }
123 }
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use lsp_types::{Position, Range, TextDocumentContentChangeEvent};
130
131 use super::*;
132
133 #[test]
134 fn test_apply_document_changes() {
135 macro_rules! c {
136 [$($sl:expr, $sc:expr; $el:expr, $ec:expr => $text:expr),+] => {
137 vec![$(TextDocumentContentChangeEvent {
138 range: Some(Range {
139 start: Position { line: $sl, character: $sc },
140 end: Position { line: $el, character: $ec },
141 }),
142 range_length: None,
143 text: String::from($text),
144 }),+]
145 };
146 }
147
148 let mut text = String::new();
149 apply_document_changes(&mut text, vec![]);
150 assert_eq!(text, "");
151 apply_document_changes(
152 &mut text,
153 vec![TextDocumentContentChangeEvent {
154 range: None,
155 range_length: None,
156 text: String::from("the"),
157 }],
158 );
159 assert_eq!(text, "the");
160 apply_document_changes(&mut text, c![0, 3; 0, 3 => " quick"]);
161 assert_eq!(text, "the quick");
162 apply_document_changes(&mut text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]);
163 assert_eq!(text, "quick foxes");
164 apply_document_changes(&mut text, c![0, 11; 0, 11 => "\ndream"]);
165 assert_eq!(text, "quick foxes\ndream");
166 apply_document_changes(&mut text, c![1, 0; 1, 0 => "have "]);
167 assert_eq!(text, "quick foxes\nhave dream");
168 apply_document_changes(
169 &mut text,
170 c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"],
171 );
172 assert_eq!(text, "the quick foxes\nhave quiet dreams\n");
173 apply_document_changes(&mut text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]);
174 assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n");
175 apply_document_changes(
176 &mut text,
177 c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"],
178 );
179 assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n");
180 apply_document_changes(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]);
181 assert_eq!(text, "the quick \nthey have quiet dreams\n");
182
183 text = String::from("❤️");
184 apply_document_changes(&mut text, c![0, 0; 0, 0 => "a"]);
185 assert_eq!(text, "a❤️");
186
187 text = String::from("a\nb");
188 apply_document_changes(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
189 assert_eq!(text, "adcb");
190
191 text = String::from("a\nb");
192 apply_document_changes(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
193 assert_eq!(text, "ațc\ncb");
194 }
195}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index f1287d52c..a41f7f564 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -1,75 +1,30 @@
1//! The main loop of `rust-analyzer` responsible for dispatching LSP 1//! The main loop of `rust-analyzer` responsible for dispatching LSP
2//! requests/replies and notifications back to the client. 2//! requests/replies and notifications back to the client.
3
4mod handlers;
5mod subscriptions;
6pub(crate) mod pending_requests;
7
8use std::{ 3use std::{
9 borrow::Cow, 4 env, fmt, panic,
10 env,
11 error::Error,
12 fmt,
13 ops::Range,
14 panic,
15 path::PathBuf,
16 sync::Arc,
17 time::{Duration, Instant}, 5 time::{Duration, Instant},
18}; 6};
19 7
20use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; 8use crossbeam_channel::{select, Receiver};
21use itertools::Itertools; 9use lsp_server::{Connection, Notification, Request, Response};
22use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 10use lsp_types::notification::Notification as _;
23use lsp_types::{ 11use ra_db::VfsPath;
24 DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, 12use ra_ide::{Canceled, FileId};
25 WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
26 WorkDoneProgressReport,
27};
28use ra_flycheck::{CheckTask, Status};
29use ra_ide::{Canceled, FileId, LibraryData, LineIndex, SourceRootId};
30use ra_prof::profile; 13use ra_prof::profile;
31use ra_project_model::{PackageRoot, ProjectWorkspace};
32use ra_vfs::{VfsFile, VfsTask, Watch};
33use relative_path::RelativePathBuf;
34use rustc_hash::FxHashSet;
35use serde::{de::DeserializeOwned, Serialize};
36use threadpool::ThreadPool;
37 14
38use crate::{ 15use crate::{
39 config::{Config, FilesWatcher}, 16 config::Config,
40 diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask}, 17 dispatch::{NotificationDispatcher, RequestDispatcher},
41 from_proto, lsp_ext, 18 from_proto,
42 main_loop::{ 19 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status},
43 pending_requests::{PendingRequest, PendingRequests}, 20 handlers, lsp_ext,
44 subscriptions::Subscriptions, 21 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
45 },
46 world::{WorldSnapshot, WorldState},
47 Result, 22 Result,
48}; 23};
24use ra_project_model::ProjectWorkspace;
25use vfs::ChangeKind;
49 26
50#[derive(Debug)] 27pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
51pub struct LspError {
52 pub code: i32,
53 pub message: String,
54}
55
56impl LspError {
57 pub const UNKNOWN_FILE: i32 = -32900;
58
59 pub fn new(code: i32, message: String) -> LspError {
60 LspError { code, message }
61 }
62}
63
64impl fmt::Display for LspError {
65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 write!(f, "Language Server request failed with {}. ({})", self.code, self.message)
67 }
68}
69
70impl Error for LspError {}
71
72pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> {
73 log::info!("initial config: {:#?}", config); 28 log::info!("initial config: {:#?}", config);
74 29
75 // Windows scheduler implements priority boosts: if thread waits for an 30 // Windows scheduler implements priority boosts: if thread waits for an
@@ -91,163 +46,22 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
91 SetThreadPriority(thread, thread_priority_above_normal); 46 SetThreadPriority(thread, thread_priority_above_normal);
92 } 47 }
93 48
94 let mut loop_state = LoopState::default(); 49 GlobalState::new(connection.sender.clone(), config).run(connection.receiver)
95 let mut world_state = {
96 let workspaces = {
97 // FIXME: support dynamic workspace loading.
98 let project_roots: FxHashSet<_> = ws_roots
99 .iter()
100 .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok())
101 .flatten()
102 .collect();
103
104 if project_roots.is_empty() && config.notifications.cargo_toml_not_found {
105 show_message(
106 lsp_types::MessageType::Error,
107 format!(
108 "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}",
109 ws_roots.iter().format_with(", ", |it, f| f(&it.display()))
110 ),
111 &connection.sender,
112 );
113 };
114
115 project_roots
116 .into_iter()
117 .filter_map(|root| {
118 ra_project_model::ProjectWorkspace::load(
119 root,
120 &config.cargo,
121 config.with_sysroot,
122 )
123 .map_err(|err| {
124 log::error!("failed to load workspace: {:#}", err);
125 show_message(
126 lsp_types::MessageType::Error,
127 format!("rust-analyzer failed to load workspace: {:#}", err),
128 &connection.sender,
129 );
130 })
131 .ok()
132 })
133 .collect::<Vec<_>>()
134 };
135
136 let globs = config
137 .files
138 .exclude
139 .iter()
140 .map(|glob| crate::vfs_glob::Glob::new(glob))
141 .collect::<std::result::Result<Vec<_>, _>>()?;
142
143 if let FilesWatcher::Client = config.files.watcher {
144 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
145 watchers: workspaces
146 .iter()
147 .flat_map(ProjectWorkspace::to_roots)
148 .filter(PackageRoot::is_member)
149 .map(|root| format!("{}/**/*.rs", root.path().display()))
150 .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
151 .collect(),
152 };
153 let registration = lsp_types::Registration {
154 id: "file-watcher".to_string(),
155 method: "workspace/didChangeWatchedFiles".to_string(),
156 register_options: Some(serde_json::to_value(registration_options).unwrap()),
157 };
158 let params = lsp_types::RegistrationParams { registrations: vec![registration] };
159 let request = request_new::<lsp_types::request::RegisterCapability>(
160 loop_state.next_request_id(),
161 params,
162 );
163 connection.sender.send(request.into()).unwrap();
164 }
165
166 WorldState::new(
167 ws_roots,
168 workspaces,
169 config.lru_capacity,
170 &globs,
171 Watch(matches!(config.files.watcher, FilesWatcher::Notify)),
172 config,
173 )
174 };
175
176 loop_state.roots_total = world_state.vfs.read().n_roots();
177
178 let pool = ThreadPool::default();
179 let (task_sender, task_receiver) = unbounded::<Task>();
180 let (libdata_sender, libdata_receiver) = unbounded::<LibraryData>();
181
182 log::info!("server initialized, serving requests");
183 {
184 let task_sender = task_sender;
185 let libdata_sender = libdata_sender;
186 loop {
187 log::trace!("selecting");
188 let event = select! {
189 recv(&connection.receiver) -> msg => match msg {
190 Ok(msg) => Event::Msg(msg),
191 Err(RecvError) => return Err("client exited without shutdown".into()),
192 },
193 recv(task_receiver) -> task => Event::Task(task.unwrap()),
194 recv(world_state.task_receiver) -> task => match task {
195 Ok(task) => Event::Vfs(task),
196 Err(RecvError) => return Err("vfs died".into()),
197 },
198 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()),
199 recv(world_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task {
200 Ok(task) => Event::CheckWatcher(task),
201 Err(RecvError) => return Err("check watcher died".into()),
202 }
203 };
204 if let Event::Msg(Message::Request(req)) = &event {
205 if connection.handle_shutdown(&req)? {
206 break;
207 };
208 }
209 loop_turn(
210 &pool,
211 &task_sender,
212 &libdata_sender,
213 &connection,
214 &mut world_state,
215 &mut loop_state,
216 event,
217 )?;
218 }
219 }
220 world_state.analysis_host.request_cancellation();
221 log::info!("waiting for tasks to finish...");
222 task_receiver.into_iter().for_each(|task| {
223 on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut world_state)
224 });
225 libdata_receiver.into_iter().for_each(drop);
226 log::info!("...tasks have finished");
227 log::info!("joining threadpool...");
228 pool.join();
229 drop(pool);
230 log::info!("...threadpool has finished");
231
232 let vfs = Arc::try_unwrap(world_state.vfs).expect("all snapshots should be dead");
233 drop(vfs);
234
235 Ok(())
236}
237
238#[derive(Debug)]
239enum Task {
240 Respond(Response),
241 Notify(Notification),
242 Diagnostic(DiagnosticTask),
243} 50}
244 51
245enum Event { 52enum Event {
246 Msg(Message), 53 Lsp(lsp_server::Message),
247 Task(Task), 54 Task(Task),
248 Vfs(VfsTask), 55 Vfs(vfs::loader::Message),
249 Lib(LibraryData), 56 Flycheck(flycheck::Message),
250 CheckWatcher(CheckTask), 57}
58
59#[derive(Debug)]
60pub(crate) enum Task {
61 Response(Response),
62 Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
63 Workspaces(Vec<anyhow::Result<ProjectWorkspace>>),
64 Unit,
251} 65}
252 66
253impl fmt::Debug for Event { 67impl fmt::Debug for Event {
@@ -257,19 +71,14 @@ impl fmt::Debug for Event {
257 }; 71 };
258 72
259 match self { 73 match self {
260 Event::Msg(Message::Notification(not)) => { 74 Event::Lsp(lsp_server::Message::Notification(not)) => {
261 if notification_is::<lsp_types::notification::DidOpenTextDocument>(not) 75 if notification_is::<lsp_types::notification::DidOpenTextDocument>(not)
262 || notification_is::<lsp_types::notification::DidChangeTextDocument>(not) 76 || notification_is::<lsp_types::notification::DidChangeTextDocument>(not)
263 { 77 {
264 return debug_verbose_not(not, f); 78 return debug_verbose_not(not, f);
265 } 79 }
266 } 80 }
267 Event::Task(Task::Notify(not)) => { 81 Event::Task(Task::Response(resp)) => {
268 if notification_is::<lsp_types::notification::PublishDiagnostics>(not) {
269 return debug_verbose_not(not, f);
270 }
271 }
272 Event::Task(Task::Respond(resp)) => {
273 return f 82 return f
274 .debug_struct("Response") 83 .debug_struct("Response")
275 .field("id", &resp.id) 84 .field("id", &resp.id)
@@ -279,874 +88,441 @@ impl fmt::Debug for Event {
279 _ => (), 88 _ => (),
280 } 89 }
281 match self { 90 match self {
282 Event::Msg(it) => fmt::Debug::fmt(it, f), 91 Event::Lsp(it) => fmt::Debug::fmt(it, f),
283 Event::Task(it) => fmt::Debug::fmt(it, f), 92 Event::Task(it) => fmt::Debug::fmt(it, f),
284 Event::Vfs(it) => fmt::Debug::fmt(it, f), 93 Event::Vfs(it) => fmt::Debug::fmt(it, f),
285 Event::Lib(it) => fmt::Debug::fmt(it, f), 94 Event::Flycheck(it) => fmt::Debug::fmt(it, f),
286 Event::CheckWatcher(it) => fmt::Debug::fmt(it, f),
287 } 95 }
288 } 96 }
289} 97}
290 98
291#[derive(Debug, Default)] 99impl GlobalState {
292struct LoopState { 100 fn next_event(&self, inbox: &Receiver<lsp_server::Message>) -> Option<Event> {
293 next_request_id: u64, 101 select! {
294 pending_responses: FxHashSet<RequestId>, 102 recv(inbox) -> msg =>
295 pending_requests: PendingRequests, 103 msg.ok().map(Event::Lsp),
296 subscriptions: Subscriptions,
297 // We try not to index more than MAX_IN_FLIGHT_LIBS libraries at the same
298 // time to always have a thread ready to react to input.
299 in_flight_libraries: usize,
300 pending_libraries: Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>,
301 workspace_loaded: bool,
302 roots_progress_reported: Option<usize>,
303 roots_scanned: usize,
304 roots_total: usize,
305 configuration_request_id: Option<RequestId>,
306}
307
308impl LoopState {
309 fn next_request_id(&mut self) -> RequestId {
310 self.next_request_id += 1;
311 let res: RequestId = self.next_request_id.into();
312 let inserted = self.pending_responses.insert(res.clone());
313 assert!(inserted);
314 res
315 }
316}
317
318fn loop_turn(
319 pool: &ThreadPool,
320 task_sender: &Sender<Task>,
321 libdata_sender: &Sender<LibraryData>,
322 connection: &Connection,
323 world_state: &mut WorldState,
324 loop_state: &mut LoopState,
325 event: Event,
326) -> Result<()> {
327 let loop_start = Instant::now();
328
329 // NOTE: don't count blocking select! call as a loop-turn time
330 let _p = profile("main_loop_inner/loop-turn");
331 log::info!("loop turn = {:?}", event);
332 let queue_count = pool.queued_count();
333 if queue_count > 0 {
334 log::info!("queued count = {}", queue_count);
335 }
336
337 match event {
338 Event::Task(task) => {
339 on_task(task, &connection.sender, &mut loop_state.pending_requests, world_state);
340 world_state.maybe_collect_garbage();
341 }
342 Event::Vfs(task) => {
343 world_state.vfs.write().handle_task(task);
344 }
345 Event::Lib(lib) => {
346 world_state.add_lib(lib);
347 world_state.maybe_collect_garbage();
348 loop_state.in_flight_libraries -= 1;
349 loop_state.roots_scanned += 1;
350 }
351 Event::CheckWatcher(task) => on_check_task(task, world_state, task_sender)?,
352 Event::Msg(msg) => match msg {
353 Message::Request(req) => on_request(
354 world_state,
355 &mut loop_state.pending_requests,
356 pool,
357 task_sender,
358 &connection.sender,
359 loop_start,
360 req,
361 )?,
362 Message::Notification(not) => {
363 on_notification(&connection.sender, world_state, loop_state, not)?;
364 }
365 Message::Response(resp) => {
366 let removed = loop_state.pending_responses.remove(&resp.id);
367 if !removed {
368 log::error!("unexpected response: {:?}", resp)
369 }
370
371 if Some(&resp.id) == loop_state.configuration_request_id.as_ref() {
372 loop_state.configuration_request_id = None;
373 log::debug!("config update response: '{:?}", resp);
374 let Response { error, result, .. } = resp;
375 104
376 match (error, result) { 105 recv(self.task_pool.receiver) -> task =>
377 (Some(err), _) => { 106 Some(Event::Task(task.unwrap())),
378 log::error!("failed to fetch the server settings: {:?}", err)
379 }
380 (None, Some(configs)) => {
381 if let Some(new_config) = configs.get(0) {
382 let mut config = world_state.config.clone();
383 config.update(&new_config);
384 world_state.update_configuration(config);
385 }
386 }
387 (None, None) => {
388 log::error!("received empty server settings response from the client")
389 }
390 }
391 }
392 }
393 },
394 };
395 107
396 let mut state_changed = false; 108 recv(self.loader.receiver) -> task =>
397 if let Some(changes) = world_state.process_changes(&mut loop_state.roots_scanned) { 109 Some(Event::Vfs(task.unwrap())),
398 state_changed = true;
399 loop_state.pending_libraries.extend(changes);
400 }
401 110
402 let max_in_flight_libs = pool.max_count().saturating_sub(2).max(1); 111 recv(self.flycheck_receiver) -> task =>
403 while loop_state.in_flight_libraries < max_in_flight_libs { 112 Some(Event::Flycheck(task.unwrap())),
404 let (root, files) = match loop_state.pending_libraries.pop() {
405 Some(it) => it,
406 None => break,
407 };
408
409 loop_state.in_flight_libraries += 1;
410 let sender = libdata_sender.clone();
411 pool.execute(move || {
412 log::info!("indexing {:?} ... ", root);
413 let data = LibraryData::prepare(root, files);
414 sender.send(data).unwrap();
415 });
416 }
417
418 let show_progress =
419 !loop_state.workspace_loaded && world_state.config.client_caps.work_done_progress;
420
421 if !loop_state.workspace_loaded
422 && loop_state.roots_scanned == loop_state.roots_total
423 && loop_state.pending_libraries.is_empty()
424 && loop_state.in_flight_libraries == 0
425 {
426 state_changed = true;
427 loop_state.workspace_loaded = true;
428 if let Some(flycheck) = &world_state.flycheck {
429 flycheck.update();
430 } 113 }
431 } 114 }
432 115
433 if show_progress { 116 fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
434 send_startup_progress(&connection.sender, loop_state); 117 if self.config.linked_projects.is_empty() && self.config.notifications.cargo_toml_not_found
435 } 118 {
436 119 self.show_message(
437 if state_changed && loop_state.workspace_loaded {
438 update_file_notifications_on_threadpool(
439 pool,
440 world_state.snapshot(),
441 task_sender.clone(),
442 loop_state.subscriptions.subscriptions(),
443 );
444 pool.execute({
445 let subs = loop_state.subscriptions.subscriptions();
446 let snap = world_state.snapshot();
447 move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ())
448 });
449 }
450
451 let loop_duration = loop_start.elapsed();
452 if loop_duration > Duration::from_millis(100) {
453 log::error!("overly long loop turn: {:?}", loop_duration);
454 if env::var("RA_PROFILE").is_ok() {
455 show_message(
456 lsp_types::MessageType::Error, 120 lsp_types::MessageType::Error,
457 format!("overly long loop turn: {:?}", loop_duration), 121 "rust-analyzer failed to discover workspace".to_string(),
458 &connection.sender,
459 ); 122 );
460 } 123 };
461 }
462 124
463 Ok(()) 125 let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
464} 126 include_text: Some(false),
127 text_document_registration_options: lsp_types::TextDocumentRegistrationOptions {
128 document_selector: Some(vec![
129 lsp_types::DocumentFilter {
130 language: None,
131 scheme: None,
132 pattern: Some("**/*.rs".into()),
133 },
134 lsp_types::DocumentFilter {
135 language: None,
136 scheme: None,
137 pattern: Some("**/Cargo.toml".into()),
138 },
139 lsp_types::DocumentFilter {
140 language: None,
141 scheme: None,
142 pattern: Some("**/Cargo.lock".into()),
143 },
144 ]),
145 },
146 };
465 147
466fn on_task( 148 let registration = lsp_types::Registration {
467 task: Task, 149 id: "textDocument/didSave".to_string(),
468 msg_sender: &Sender<Message>, 150 method: "textDocument/didSave".to_string(),
469 pending_requests: &mut PendingRequests, 151 register_options: Some(serde_json::to_value(save_registration_options).unwrap()),
470 state: &mut WorldState, 152 };
471) { 153 self.send_request::<lsp_types::request::RegisterCapability>(
472 match task { 154 lsp_types::RegistrationParams { registrations: vec![registration] },
473 Task::Respond(response) => { 155 |_, _| (),
474 if let Some(completed) = pending_requests.finish(&response.id) { 156 );
475 log::info!("handled req#{} in {:?}", completed.id, completed.duration);
476 state.complete_request(completed);
477 msg_sender.send(response.into()).unwrap();
478 }
479 }
480 Task::Notify(n) => {
481 msg_sender.send(n.into()).unwrap();
482 }
483 Task::Diagnostic(task) => on_diagnostic_task(task, msg_sender, state),
484 }
485}
486 157
487fn on_request( 158 self.fetch_workspaces();
488 world: &mut WorldState,
489 pending_requests: &mut PendingRequests,
490 pool: &ThreadPool,
491 task_sender: &Sender<Task>,
492 msg_sender: &Sender<Message>,
493 request_received: Instant,
494 req: Request,
495) -> Result<()> {
496 let mut pool_dispatcher = PoolDispatcher {
497 req: Some(req),
498 pool,
499 world,
500 task_sender,
501 msg_sender,
502 pending_requests,
503 request_received,
504 };
505 pool_dispatcher
506 .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.collect_garbage()))?
507 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
508 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
509 .on_sync::<lsp_types::request::SelectionRangeRequest>(|s, p| {
510 handlers::handle_selection_range(s.snapshot(), p)
511 })?
512 .on_sync::<lsp_ext::MatchingBrace>(|s, p| handlers::handle_matching_brace(s.snapshot(), p))?
513 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)?
514 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)?
515 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)?
516 .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)?
517 .on::<lsp_ext::Runnables>(handlers::handle_runnables)?
518 .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)?
519 .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)?
520 .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)?
521 .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)?
522 .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
523 .on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)?
524 .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)?
525 .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)?
526 .on::<lsp_types::request::Completion>(handlers::handle_completion)?
527 .on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)?
528 .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)?
529 .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)?
530 .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)?
531 .on::<lsp_types::request::HoverRequest>(handlers::handle_hover)?
532 .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)?
533 .on::<lsp_types::request::Rename>(handlers::handle_rename)?
534 .on::<lsp_types::request::References>(handlers::handle_references)?
535 .on::<lsp_types::request::Formatting>(handlers::handle_formatting)?
536 .on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight)?
537 .on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)?
538 .on::<lsp_types::request::CallHierarchyIncomingCalls>(
539 handlers::handle_call_hierarchy_incoming,
540 )?
541 .on::<lsp_types::request::CallHierarchyOutgoingCalls>(
542 handlers::handle_call_hierarchy_outgoing,
543 )?
544 .on::<lsp_types::request::SemanticTokensRequest>(handlers::handle_semantic_tokens)?
545 .on::<lsp_types::request::SemanticTokensRangeRequest>(
546 handlers::handle_semantic_tokens_range,
547 )?
548 .on::<lsp_ext::Ssr>(handlers::handle_ssr)?
549 .finish();
550 Ok(())
551}
552 159
553fn on_notification( 160 while let Some(event) = self.next_event(&inbox) {
554 msg_sender: &Sender<Message>, 161 if let Event::Lsp(lsp_server::Message::Notification(not)) = &event {
555 state: &mut WorldState, 162 if not.method == lsp_types::notification::Exit::METHOD {
556 loop_state: &mut LoopState, 163 return Ok(());
557 not: Notification, 164 }
558) -> Result<()> {
559 let not = match notification_cast::<lsp_types::notification::Cancel>(not) {
560 Ok(params) => {
561 let id: RequestId = match params.id {
562 NumberOrString::Number(id) => id.into(),
563 NumberOrString::String(id) => id.into(),
564 };
565 if loop_state.pending_requests.cancel(&id) {
566 let response = Response::new_err(
567 id,
568 ErrorCode::RequestCanceled as i32,
569 "canceled by client".to_string(),
570 );
571 msg_sender.send(response.into()).unwrap()
572 }
573 return Ok(());
574 }
575 Err(not) => not,
576 };
577 let not = match notification_cast::<lsp_types::notification::DidOpenTextDocument>(not) {
578 Ok(params) => {
579 let uri = params.text_document.uri;
580 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
581 if let Some(file_id) =
582 state.vfs.write().add_file_overlay(&path, params.text_document.text)
583 {
584 loop_state.subscriptions.add_sub(FileId(file_id.0));
585 }
586 return Ok(());
587 }
588 Err(not) => not,
589 };
590 let not = match notification_cast::<lsp_types::notification::DidChangeTextDocument>(not) {
591 Ok(params) => {
592 let DidChangeTextDocumentParams { text_document, content_changes } = params;
593 let world = state.snapshot();
594 let file_id = from_proto::file_id(&world, &text_document.uri)?;
595 let line_index = world.analysis().file_line_index(file_id)?;
596 let uri = text_document.uri;
597 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
598 state.vfs.write().change_file_overlay(&path, |old_text| {
599 apply_document_changes(old_text, Cow::Borrowed(&line_index), content_changes);
600 });
601 return Ok(());
602 }
603 Err(not) => not,
604 };
605 let not = match notification_cast::<lsp_types::notification::DidSaveTextDocument>(not) {
606 Ok(_params) => {
607 if let Some(flycheck) = &state.flycheck {
608 flycheck.update();
609 }
610 return Ok(());
611 }
612 Err(not) => not,
613 };
614 let not = match notification_cast::<lsp_types::notification::DidCloseTextDocument>(not) {
615 Ok(params) => {
616 let uri = params.text_document.uri;
617 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
618 if let Some(file_id) = state.vfs.write().remove_file_overlay(path.as_path()) {
619 loop_state.subscriptions.remove_sub(FileId(file_id.0));
620 } 165 }
621 let params = 166 self.handle_event(event)?
622 lsp_types::PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None };
623 let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params);
624 msg_sender.send(not.into()).unwrap();
625 return Ok(());
626 } 167 }
627 Err(not) => not,
628 };
629 let not = match notification_cast::<lsp_types::notification::DidChangeConfiguration>(not) {
630 Ok(_) => {
631 // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
632 // this notification's parameters should be ignored and the actual config queried separately.
633 let request_id = loop_state.next_request_id();
634 let request = request_new::<lsp_types::request::WorkspaceConfiguration>(
635 request_id.clone(),
636 lsp_types::ConfigurationParams {
637 items: vec![lsp_types::ConfigurationItem {
638 scope_uri: None,
639 section: Some("rust-analyzer".to_string()),
640 }],
641 },
642 );
643 msg_sender.send(request.into())?;
644 loop_state.configuration_request_id = Some(request_id);
645 168
646 return Ok(()); 169 Err("client exited without proper shutdown sequence")?
647 }
648 Err(not) => not,
649 };
650 let not = match notification_cast::<lsp_types::notification::DidChangeWatchedFiles>(not) {
651 Ok(params) => {
652 let mut vfs = state.vfs.write();
653 for change in params.changes {
654 let uri = change.uri;
655 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
656 vfs.notify_changed(path)
657 }
658 return Ok(());
659 }
660 Err(not) => not,
661 };
662 if not.method.starts_with("$/") {
663 return Ok(());
664 } 170 }
665 log::error!("unhandled notification: {:?}", not);
666 Ok(())
667}
668
669fn apply_document_changes(
670 old_text: &mut String,
671 mut line_index: Cow<'_, LineIndex>,
672 content_changes: Vec<TextDocumentContentChangeEvent>,
673) {
674 // Remove when https://github.com/rust-analyzer/rust-analyzer/issues/4263 is fixed.
675 let backup_text = old_text.clone();
676 let backup_changes = content_changes.clone();
677 171
678 // The changes we got must be applied sequentially, but can cross lines so we 172 fn handle_event(&mut self, event: Event) -> Result<()> {
679 // have to keep our line index updated. 173 let loop_start = Instant::now();
680 // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we 174 // NOTE: don't count blocking select! call as a loop-turn time
681 // remember the last valid line in the index and only rebuild it if needed. 175 let _p = profile("GlobalState::handle_event");
682 enum IndexValid {
683 All,
684 UpToLineExclusive(u64),
685 }
686 176
687 impl IndexValid { 177 log::info!("handle_event({:?})", event);
688 fn covers(&self, line: u64) -> bool { 178 let queue_count = self.task_pool.handle.len();
689 match *self { 179 if queue_count > 0 {
690 IndexValid::UpToLineExclusive(to) => to > line, 180 log::info!("queued count = {}", queue_count);
691 _ => true,
692 }
693 } 181 }
694 }
695 182
696 let mut index_valid = IndexValid::All; 183 let prev_status = self.status;
697 for change in content_changes { 184 match event {
698 match change.range { 185 Event::Lsp(msg) => match msg {
699 Some(range) => { 186 lsp_server::Message::Request(req) => self.on_request(loop_start, req)?,
700 if !index_valid.covers(range.end.line) { 187 lsp_server::Message::Notification(not) => {
701 line_index = Cow::Owned(LineIndex::new(&old_text)); 188 self.on_notification(not)?;
702 } 189 }
703 index_valid = IndexValid::UpToLineExclusive(range.start.line); 190 lsp_server::Message::Response(resp) => self.complete_request(resp),
704 let range = from_proto::text_range(&line_index, range); 191 },
705 let mut text = old_text.to_owned(); 192 Event::Task(task) => {
706 match std::panic::catch_unwind(move || { 193 match task {
707 text.replace_range(Range::<usize>::from(range), &change.text); 194 Task::Response(response) => self.respond(response),
708 text 195 Task::Diagnostics(diagnostics_per_file) => {
709 }) { 196 for (file_id, diagnostics) in diagnostics_per_file {
710 Ok(t) => *old_text = t, 197 self.diagnostics.set_native_diagnostics(file_id, diagnostics)
711 Err(e) => { 198 }
712 eprintln!("Bug in incremental text synchronization. Please report the following output on https://github.com/rust-analyzer/rust-analyzer/issues/4263");
713 dbg!(&backup_text);
714 dbg!(&backup_changes);
715 std::panic::resume_unwind(e);
716 } 199 }
200 Task::Workspaces(workspaces) => self.switch_workspaces(workspaces),
201 Task::Unit => (),
717 } 202 }
203 self.analysis_host.maybe_collect_garbage();
718 } 204 }
719 None => { 205 Event::Vfs(mut task) => {
720 *old_text = change.text; 206 let _p = profile("GlobalState::handle_event/vfs");
721 index_valid = IndexValid::UpToLineExclusive(0); 207 loop {
722 } 208 match task {
723 } 209 vfs::loader::Message::Loaded { files } => {
724 } 210 let vfs = &mut self.vfs.write().0;
725} 211 for (path, contents) in files {
726 212 let path = VfsPath::from(path);
727fn on_check_task( 213 if !self.mem_docs.contains(&path) {
728 task: CheckTask, 214 vfs.set_file_contents(path, contents)
729 world_state: &mut WorldState, 215 }
730 task_sender: &Sender<Task>, 216 }
731) -> Result<()> { 217 }
732 match task { 218 vfs::loader::Message::Progress { n_total, n_done } => {
733 CheckTask::ClearDiagnostics => { 219 if n_total == 0 {
734 task_sender.send(Task::Diagnostic(DiagnosticTask::ClearCheck))?; 220 self.transition(Status::Invalid);
735 } 221 } else {
736 222 let state = if n_done == 0 {
737 CheckTask::AddDiagnostic { workspace_root, diagnostic } => { 223 self.transition(Status::Loading);
738 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( 224 Progress::Begin
739 &diagnostic, 225 } else if n_done < n_total {
740 &workspace_root, 226 Progress::Report
741 ); 227 } else {
742 for diag in diagnostics { 228 assert_eq!(n_done, n_total);
743 let path = diag 229 self.transition(Status::Ready);
744 .location 230 Progress::End
745 .uri 231 };
746 .to_file_path() 232 self.report_progress(
747 .map_err(|()| format!("invalid uri: {}", diag.location.uri))?; 233 "roots scanned",
748 let file_id = match world_state.vfs.read().path2file(&path) { 234 state,
749 Some(file) => FileId(file.0), 235 Some(format!("{}/{}", n_done, n_total)),
750 None => { 236 Some(Progress::percentage(n_done, n_total)),
751 log::error!( 237 )
752 "File with cargo diagnostic not found in VFS: {}", 238 }
753 path.display() 239 }
754 );
755 return Ok(());
756 }
757 };
758
759 task_sender.send(Task::Diagnostic(DiagnosticTask::AddCheck(
760 file_id,
761 diag.diagnostic,
762 diag.fixes.into_iter().map(|it| it.into()).collect(),
763 )))?;
764 }
765 }
766
767 CheckTask::Status(status) => {
768 if world_state.config.client_caps.work_done_progress {
769 let progress = match status {
770 Status::Being => {
771 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
772 title: "Running `cargo check`".to_string(),
773 cancellable: Some(false),
774 message: None,
775 percentage: None,
776 })
777 }
778 Status::Progress(target) => {
779 lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
780 cancellable: Some(false),
781 message: Some(target),
782 percentage: None,
783 })
784 } 240 }
785 Status::End => { 241 // Coalesce many VFS event into a single loop turn
786 lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { 242 task = match self.loader.receiver.try_recv() {
787 message: None, 243 Ok(task) => task,
788 }) 244 Err(_) => break,
789 } 245 }
790 }; 246 }
791
792 let params = lsp_types::ProgressParams {
793 token: lsp_types::ProgressToken::String(
794 "rustAnalyzer/cargoWatcher".to_string(),
795 ),
796 value: lsp_types::ProgressParamsValue::WorkDone(progress),
797 };
798 let not = notification_new::<lsp_types::notification::Progress>(params);
799 task_sender.send(Task::Notify(not)).unwrap();
800 } 247 }
801 } 248 Event::Flycheck(task) => match task {
802 }; 249 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
250 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
251 &self.config.diagnostics,
252 &diagnostic,
253 &workspace_root,
254 );
255 for diag in diagnostics {
256 match url_to_file_id(&self.vfs.read().0, &diag.url) {
257 Ok(file_id) => self.diagnostics.add_check_diagnostic(
258 file_id,
259 diag.diagnostic,
260 diag.fixes,
261 ),
262 Err(err) => {
263 log::error!("File with cargo diagnostic not found in VFS: {}", err);
264 }
265 };
266 }
267 }
803 268
804 Ok(()) 269 flycheck::Message::Progress(status) => {
805} 270 let (state, message) = match status {
271 flycheck::Progress::DidStart => {
272 self.diagnostics.clear_check();
273 (Progress::Begin, None)
274 }
275 flycheck::Progress::DidCheckCrate(target) => {
276 (Progress::Report, Some(target))
277 }
278 flycheck::Progress::DidCancel => (Progress::End, None),
279 flycheck::Progress::DidFinish(result) => {
280 if let Err(err) = result {
281 log::error!("cargo check failed: {}", err)
282 }
283 (Progress::End, None)
284 }
285 };
806 286
807fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: &mut WorldState) { 287 self.report_progress("cargo check", state, message, None);
808 let subscriptions = state.diagnostics.handle_task(task); 288 }
289 },
290 }
809 291
810 for file_id in subscriptions { 292 let state_changed = self.process_changes();
811 let path = state.vfs.read().file2path(VfsFile(file_id.0)); 293 if prev_status == Status::Loading && self.status == Status::Ready {
812 let uri = match url_from_path_with_drive_lowercasing(&path) { 294 if let Some(flycheck) = &self.flycheck {
813 Ok(uri) => uri, 295 flycheck.update();
814 Err(err) => {
815 log::error!("Couldn't convert path to url ({}): {}", err, path.display());
816 continue;
817 } 296 }
818 };
819
820 let diagnostics = state.diagnostics.diagnostics_for(file_id).cloned().collect();
821 let params = lsp_types::PublishDiagnosticsParams { uri, diagnostics, version: None };
822 let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params);
823 msg_sender.send(not.into()).unwrap();
824 }
825}
826
827fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) {
828 let total: usize = loop_state.roots_total;
829 let prev = loop_state.roots_progress_reported;
830 let progress = loop_state.roots_scanned;
831 loop_state.roots_progress_reported = Some(progress);
832
833 match (prev, loop_state.workspace_loaded) {
834 (None, false) => {
835 let work_done_progress_create = request_new::<lsp_types::request::WorkDoneProgressCreate>(
836 loop_state.next_request_id(),
837 WorkDoneProgressCreateParams {
838 token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()),
839 },
840 );
841 sender.send(work_done_progress_create.into()).unwrap();
842 send_startup_progress_notif(
843 sender,
844 WorkDoneProgress::Begin(WorkDoneProgressBegin {
845 title: "rust-analyzer".into(),
846 cancellable: None,
847 message: Some(format!("{}/{} packages", progress, total)),
848 percentage: Some(100.0 * progress as f64 / total as f64),
849 }),
850 );
851 } 297 }
852 (Some(prev), false) if progress != prev => send_startup_progress_notif(
853 sender,
854 WorkDoneProgress::Report(WorkDoneProgressReport {
855 cancellable: None,
856 message: Some(format!("{}/{} packages", progress, total)),
857 percentage: Some(100.0 * progress as f64 / total as f64),
858 }),
859 ),
860 (_, true) => send_startup_progress_notif(
861 sender,
862 WorkDoneProgress::End(WorkDoneProgressEnd {
863 message: Some(format!("rust-analyzer loaded, {} packages", progress)),
864 }),
865 ),
866 _ => {}
867 }
868 298
869 fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) { 299 if self.status == Status::Ready && (state_changed || prev_status == Status::Loading) {
870 let notif = 300 let subscriptions = self
871 notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams { 301 .mem_docs
872 token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()), 302 .iter()
873 value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress), 303 .map(|path| self.vfs.read().0.file_id(&path).unwrap())
874 }); 304 .collect::<Vec<_>>();
875 sender.send(notif.into()).unwrap();
876 }
877}
878
879struct PoolDispatcher<'a> {
880 req: Option<Request>,
881 pool: &'a ThreadPool,
882 world: &'a mut WorldState,
883 pending_requests: &'a mut PendingRequests,
884 msg_sender: &'a Sender<Message>,
885 task_sender: &'a Sender<Task>,
886 request_received: Instant,
887}
888 305
889impl<'a> PoolDispatcher<'a> { 306 self.update_file_notifications_on_threadpool(subscriptions);
890 /// Dispatches the request onto the current thread 307 }
891 fn on_sync<R>(
892 &mut self,
893 f: fn(&mut WorldState, R::Params) -> Result<R::Result>,
894 ) -> Result<&mut Self>
895 where
896 R: lsp_types::request::Request + 'static,
897 R::Params: DeserializeOwned + panic::UnwindSafe + 'static,
898 R::Result: Serialize + 'static,
899 {
900 let (id, params) = match self.parse::<R>() {
901 Some(it) => it,
902 None => {
903 return Ok(self);
904 }
905 };
906 let world = panic::AssertUnwindSafe(&mut *self.world);
907 let task = panic::catch_unwind(move || {
908 let result = f(world.0, params);
909 result_to_task::<R>(id, result)
910 })
911 .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?;
912 on_task(task, self.msg_sender, self.pending_requests, self.world);
913 Ok(self)
914 }
915 308
916 /// Dispatches the request onto thread pool 309 if let Some(diagnostic_changes) = self.diagnostics.take_changes() {
917 fn on<R>(&mut self, f: fn(WorldSnapshot, R::Params) -> Result<R::Result>) -> Result<&mut Self> 310 for file_id in diagnostic_changes {
918 where 311 let url = file_id_to_url(&self.vfs.read().0, file_id);
919 R: lsp_types::request::Request + 'static, 312 let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect();
920 R::Params: DeserializeOwned + Send + 'static, 313 self.send_notification::<lsp_types::notification::PublishDiagnostics>(
921 R::Result: Serialize + 'static, 314 lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None },
922 { 315 );
923 let (id, params) = match self.parse::<R>() {
924 Some(it) => it,
925 None => {
926 return Ok(self);
927 } 316 }
928 }; 317 }
929 318
930 self.pool.execute({ 319 let loop_duration = loop_start.elapsed();
931 let world = self.world.snapshot(); 320 if loop_duration > Duration::from_millis(100) {
932 let sender = self.task_sender.clone(); 321 log::warn!("overly long loop turn: {:?}", loop_duration);
933 move || { 322 if env::var("RA_PROFILE").is_ok() {
934 let result = f(world, params); 323 self.show_message(
935 let task = result_to_task::<R>(id, result); 324 lsp_types::MessageType::Error,
936 sender.send(task).unwrap(); 325 format!("overly long loop turn: {:?}", loop_duration),
326 )
937 } 327 }
938 }); 328 }
939 329 Ok(())
940 Ok(self)
941 } 330 }
942 331
943 fn parse<R>(&mut self) -> Option<(RequestId, R::Params)> 332 fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
944 where 333 self.register_request(&req, request_received);
945 R: lsp_types::request::Request + 'static, 334
946 R::Params: DeserializeOwned + 'static, 335 RequestDispatcher { req: Some(req), global_state: self }
947 { 336 .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces()))?
948 let req = self.req.take()?; 337 .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
949 let (id, params) = match req.extract::<R::Params>(R::METHOD) { 338 .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
950 Ok(it) => it, 339 .on_sync::<lsp_types::request::Shutdown>(|_, ()| Ok(()))?
951 Err(req) => { 340 .on_sync::<lsp_types::request::SelectionRangeRequest>(|s, p| {
952 self.req = Some(req); 341 handlers::handle_selection_range(s.snapshot(), p)
953 return None; 342 })?
954 } 343 .on_sync::<lsp_ext::MatchingBrace>(|s, p| {
955 }; 344 handlers::handle_matching_brace(s.snapshot(), p)
956 self.pending_requests.start(PendingRequest { 345 })?
957 id: id.clone(), 346 .on_sync::<lsp_ext::MemoryUsage>(|s, p| handlers::handle_memory_usage(s, p))?
958 method: R::METHOD.to_string(), 347 .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)?
959 received: self.request_received, 348 .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)?
960 }); 349 .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)?
961 Some((id, params)) 350 .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)?
351 .on::<lsp_ext::Runnables>(handlers::handle_runnables)?
352 .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)?
353 .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)?
354 .on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action)?
355 .on::<lsp_ext::HoverRequest>(handlers::handle_hover)?
356 .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)?
357 .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)?
358 .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)?
359 .on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)?
360 .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)?
361 .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)?
362 .on::<lsp_types::request::Completion>(handlers::handle_completion)?
363 .on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)?
364 .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)?
365 .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)?
366 .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)?
367 .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)?
368 .on::<lsp_types::request::Rename>(handlers::handle_rename)?
369 .on::<lsp_types::request::References>(handlers::handle_references)?
370 .on::<lsp_types::request::Formatting>(handlers::handle_formatting)?
371 .on::<lsp_types::request::DocumentHighlightRequest>(
372 handlers::handle_document_highlight,
373 )?
374 .on::<lsp_types::request::CallHierarchyPrepare>(
375 handlers::handle_call_hierarchy_prepare,
376 )?
377 .on::<lsp_types::request::CallHierarchyIncomingCalls>(
378 handlers::handle_call_hierarchy_incoming,
379 )?
380 .on::<lsp_types::request::CallHierarchyOutgoingCalls>(
381 handlers::handle_call_hierarchy_outgoing,
382 )?
383 .on::<lsp_types::request::SemanticTokensRequest>(handlers::handle_semantic_tokens)?
384 .on::<lsp_types::request::SemanticTokensRangeRequest>(
385 handlers::handle_semantic_tokens_range,
386 )?
387 .on::<lsp_ext::Ssr>(handlers::handle_ssr)?
388 .finish();
389 Ok(())
962 } 390 }
963 391 fn on_notification(&mut self, not: Notification) -> Result<()> {
964 fn finish(&mut self) { 392 NotificationDispatcher { not: Some(not), global_state: self }
965 match self.req.take() { 393 .on::<lsp_types::notification::Cancel>(|this, params| {
966 None => (), 394 let id: lsp_server::RequestId = match params.id {
967 Some(req) => { 395 lsp_types::NumberOrString::Number(id) => id.into(),
968 log::error!("unknown request: {:?}", req); 396 lsp_types::NumberOrString::String(id) => id.into(),
969 let resp = Response::new_err( 397 };
970 req.id, 398 this.cancel(id);
971 ErrorCode::MethodNotFound as i32, 399 Ok(())
972 "unknown request".to_string(), 400 })?
401 .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
402 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
403 if !this.mem_docs.insert(path.clone()) {
404 log::error!("duplicate DidOpenTextDocument: {}", path)
405 }
406 this.vfs
407 .write()
408 .0
409 .set_file_contents(path, Some(params.text_document.text.into_bytes()));
410 }
411 Ok(())
412 })?
413 .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| {
414 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
415 assert!(this.mem_docs.contains(&path));
416 let vfs = &mut this.vfs.write().0;
417 let file_id = vfs.file_id(&path).unwrap();
418 let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap();
419 apply_document_changes(&mut text, params.content_changes);
420 vfs.set_file_contents(path, Some(text.into_bytes()))
421 }
422 Ok(())
423 })?
424 .on::<lsp_types::notification::DidCloseTextDocument>(|this, params| {
425 if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
426 if !this.mem_docs.remove(&path) {
427 log::error!("orphan DidCloseTextDocument: {}", path)
428 }
429 if let Some(path) = path.as_path() {
430 this.loader.handle.invalidate(path.to_path_buf());
431 }
432 }
433 this.send_notification::<lsp_types::notification::PublishDiagnostics>(
434 lsp_types::PublishDiagnosticsParams {
435 uri: params.text_document.uri,
436 diagnostics: Vec::new(),
437 version: None,
438 },
973 ); 439 );
974 self.msg_sender.send(resp.into()).unwrap(); 440 Ok(())
975 } 441 })?
976 } 442 .on::<lsp_types::notification::DidSaveTextDocument>(|this, params| {
977 } 443 if let Some(flycheck) = &this.flycheck {
978} 444 flycheck.update();
979
980fn result_to_task<R>(id: RequestId, result: Result<R::Result>) -> Task
981where
982 R: lsp_types::request::Request + 'static,
983 R::Params: DeserializeOwned + 'static,
984 R::Result: Serialize + 'static,
985{
986 let response = match result {
987 Ok(resp) => Response::new_ok(id, &resp),
988 Err(e) => match e.downcast::<LspError>() {
989 Ok(lsp_error) => {
990 if lsp_error.code == LspError::UNKNOWN_FILE {
991 // Work-around for https://github.com/rust-analyzer/rust-analyzer/issues/1521
992 Response::new_ok(id, ())
993 } else {
994 Response::new_err(id, lsp_error.code, lsp_error.message)
995 } 445 }
996 } 446 if let Ok(abs_path) = from_proto::abs_path(&params.text_document.uri) {
997 Err(e) => { 447 this.maybe_refresh(&[(abs_path, ChangeKind::Modify)]);
998 if is_canceled(&e) {
999 Response::new_err(
1000 id,
1001 ErrorCode::ContentModified as i32,
1002 "content modified".to_string(),
1003 )
1004 } else {
1005 Response::new_err(id, ErrorCode::InternalError as i32, e.to_string())
1006 } 448 }
1007 } 449 Ok(())
1008 }, 450 })?
1009 }; 451 .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
1010 Task::Respond(response) 452 // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
1011} 453 // this notification's parameters should be ignored and the actual config queried separately.
1012 454 this.send_request::<lsp_types::request::WorkspaceConfiguration>(
1013fn update_file_notifications_on_threadpool( 455 lsp_types::ConfigurationParams {
1014 pool: &ThreadPool, 456 items: vec![lsp_types::ConfigurationItem {
1015 world: WorldSnapshot, 457 scope_uri: None,
1016 task_sender: Sender<Task>, 458 section: Some("rust-analyzer".to_string()),
1017 subscriptions: Vec<FileId>, 459 }],
1018) { 460 },
1019 log::trace!("updating notifications for {:?}", subscriptions); 461 |this, resp| {
1020 if world.config.publish_diagnostics { 462 log::debug!("config update response: '{:?}", resp);
1021 pool.execute(move || { 463 let Response { error, result, .. } = resp;
1022 for file_id in subscriptions { 464
1023 match handlers::publish_diagnostics(&world, file_id) { 465 match (error, result) {
1024 Err(e) => { 466 (Some(err), _) => {
1025 if !is_canceled(&e) { 467 log::error!("failed to fetch the server settings: {:?}", err)
1026 log::error!("failed to compute diagnostics: {:?}", e); 468 }
469 (None, Some(mut configs)) => {
470 if let Some(json) = configs.get_mut(0) {
471 let mut config = this.config.clone();
472 config.update(json.take());
473 this.update_configuration(config);
474 }
475 }
476 (None, None) => log::error!(
477 "received empty server settings response from the client"
478 ),
1027 } 479 }
1028 } 480 },
1029 Ok(task) => { 481 );
1030 task_sender.send(Task::Diagnostic(task)).unwrap(); 482
483 return Ok(());
484 })?
485 .on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| {
486 for change in params.changes {
487 if let Ok(path) = from_proto::abs_path(&change.uri) {
488 this.loader.handle.invalidate(path);
1031 } 489 }
1032 } 490 }
1033 } 491 Ok(())
1034 }) 492 })?
493 .finish();
494 Ok(())
1035 } 495 }
1036} 496 fn update_file_notifications_on_threadpool(&mut self, subscriptions: Vec<FileId>) {
1037 497 log::trace!("updating notifications for {:?}", subscriptions);
1038pub fn show_message( 498 if self.config.publish_diagnostics {
1039 typ: lsp_types::MessageType, 499 let snapshot = self.snapshot();
1040 message: impl Into<String>, 500 let subscriptions = subscriptions.clone();
1041 sender: &Sender<Message>, 501 self.task_pool.handle.spawn(move || {
1042) { 502 let diagnostics = subscriptions
1043 let message = message.into(); 503 .into_iter()
1044 let params = lsp_types::ShowMessageParams { typ, message }; 504 .filter_map(|file_id| {
1045 let not = notification_new::<lsp_types::notification::ShowMessage>(params); 505 handlers::publish_diagnostics(&snapshot, file_id)
1046 sender.send(not.into()).unwrap(); 506 .map_err(|err| {
1047} 507 if !is_canceled(&*err) {
1048 508 log::error!("failed to compute diagnostics: {:?}", err);
1049fn is_canceled(e: &Box<dyn std::error::Error + Send + Sync>) -> bool { 509 }
1050 e.downcast_ref::<Canceled>().is_some() 510 ()
1051} 511 })
1052 512 .ok()
1053fn notification_is<N: lsp_types::notification::Notification>(notification: &Notification) -> bool { 513 .map(|diags| (file_id, diags))
1054 notification.method == N::METHOD 514 })
1055} 515 .collect::<Vec<_>>();
1056 516 Task::Diagnostics(diagnostics)
1057fn notification_cast<N>(notification: Notification) -> std::result::Result<N::Params, Notification> 517 })
1058where
1059 N: lsp_types::notification::Notification,
1060 N::Params: DeserializeOwned,
1061{
1062 notification.extract(N::METHOD)
1063}
1064
1065fn notification_new<N>(params: N::Params) -> Notification
1066where
1067 N: lsp_types::notification::Notification,
1068 N::Params: Serialize,
1069{
1070 Notification::new(N::METHOD.to_string(), params)
1071}
1072
1073fn request_new<R>(id: RequestId, params: R::Params) -> Request
1074where
1075 R: lsp_types::request::Request,
1076 R::Params: Serialize,
1077{
1078 Request::new(id, R::METHOD.to_string(), params)
1079}
1080
1081#[cfg(test)]
1082mod tests {
1083 use std::borrow::Cow;
1084
1085 use lsp_types::{Position, Range, TextDocumentContentChangeEvent};
1086 use ra_ide::LineIndex;
1087
1088 #[test]
1089 fn apply_document_changes() {
1090 fn run(text: &mut String, changes: Vec<TextDocumentContentChangeEvent>) {
1091 let line_index = Cow::Owned(LineIndex::new(&text));
1092 super::apply_document_changes(text, line_index, changes);
1093 }
1094
1095 macro_rules! c {
1096 [$($sl:expr, $sc:expr; $el:expr, $ec:expr => $text:expr),+] => {
1097 vec![$(TextDocumentContentChangeEvent {
1098 range: Some(Range {
1099 start: Position { line: $sl, character: $sc },
1100 end: Position { line: $el, character: $ec },
1101 }),
1102 range_length: None,
1103 text: String::from($text),
1104 }),+]
1105 };
1106 } 518 }
1107 519 self.task_pool.handle.spawn({
1108 let mut text = String::new(); 520 let subs = subscriptions;
1109 run(&mut text, vec![]); 521 let snap = self.snapshot();
1110 assert_eq!(text, ""); 522 move || {
1111 run( 523 snap.analysis.prime_caches(subs).unwrap_or_else(|_: Canceled| ());
1112 &mut text, 524 Task::Unit
1113 vec![TextDocumentContentChangeEvent { 525 }
1114 range: None, 526 });
1115 range_length: None,
1116 text: String::from("the"),
1117 }],
1118 );
1119 assert_eq!(text, "the");
1120 run(&mut text, c![0, 3; 0, 3 => " quick"]);
1121 assert_eq!(text, "the quick");
1122 run(&mut text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]);
1123 assert_eq!(text, "quick foxes");
1124 run(&mut text, c![0, 11; 0, 11 => "\ndream"]);
1125 assert_eq!(text, "quick foxes\ndream");
1126 run(&mut text, c![1, 0; 1, 0 => "have "]);
1127 assert_eq!(text, "quick foxes\nhave dream");
1128 run(&mut text, c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"]);
1129 assert_eq!(text, "the quick foxes\nhave quiet dreams\n");
1130 run(&mut text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]);
1131 assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n");
1132 run(
1133 &mut text,
1134 c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"],
1135 );
1136 assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n");
1137 run(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]);
1138 assert_eq!(text, "the quick \nthey have quiet dreams\n");
1139
1140 text = String::from("❤️");
1141 run(&mut text, c![0, 0; 0, 0 => "a"]);
1142 assert_eq!(text, "a❤️");
1143
1144 text = String::from("a\nb");
1145 run(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]);
1146 assert_eq!(text, "adcb");
1147
1148 text = String::from("a\nb");
1149 run(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]);
1150 assert_eq!(text, "ațc\ncb");
1151 } 527 }
1152} 528}
diff --git a/crates/rust-analyzer/src/main_loop/pending_requests.rs b/crates/rust-analyzer/src/main_loop/pending_requests.rs
deleted file mode 100644
index 73b33e419..000000000
--- a/crates/rust-analyzer/src/main_loop/pending_requests.rs
+++ /dev/null
@@ -1,75 +0,0 @@
1//! Data structures that keep track of inflight requests.
2
3use std::time::{Duration, Instant};
4
5use lsp_server::RequestId;
6use rustc_hash::FxHashMap;
7
8#[derive(Debug)]
9pub struct CompletedRequest {
10 pub id: RequestId,
11 pub method: String,
12 pub duration: Duration,
13}
14
15#[derive(Debug)]
16pub(crate) struct PendingRequest {
17 pub(crate) id: RequestId,
18 pub(crate) method: String,
19 pub(crate) received: Instant,
20}
21
22impl From<PendingRequest> for CompletedRequest {
23 fn from(pending: PendingRequest) -> CompletedRequest {
24 CompletedRequest {
25 id: pending.id,
26 method: pending.method,
27 duration: pending.received.elapsed(),
28 }
29 }
30}
31
32#[derive(Debug, Default)]
33pub(crate) struct PendingRequests {
34 map: FxHashMap<RequestId, PendingRequest>,
35}
36
37impl PendingRequests {
38 pub(crate) fn start(&mut self, request: PendingRequest) {
39 let id = request.id.clone();
40 let prev = self.map.insert(id.clone(), request);
41 assert!(prev.is_none(), "duplicate request with id {}", id);
42 }
43 pub(crate) fn cancel(&mut self, id: &RequestId) -> bool {
44 self.map.remove(id).is_some()
45 }
46 pub(crate) fn finish(&mut self, id: &RequestId) -> Option<CompletedRequest> {
47 self.map.remove(id).map(CompletedRequest::from)
48 }
49}
50
51const N_COMPLETED_REQUESTS: usize = 10;
52
53#[derive(Debug, Default)]
54pub struct LatestRequests {
55 // hand-rolling VecDeque here to print things in a nicer way
56 buf: [Option<CompletedRequest>; N_COMPLETED_REQUESTS],
57 idx: usize,
58}
59
60impl LatestRequests {
61 pub(crate) fn record(&mut self, request: CompletedRequest) {
62 // special case: don't track status request itself
63 if request.method == "rust-analyzer/analyzerStatus" {
64 return;
65 }
66 let idx = self.idx;
67 self.buf[idx] = Some(request);
68 self.idx = (idx + 1) % N_COMPLETED_REQUESTS;
69 }
70
71 pub(crate) fn iter(&self) -> impl Iterator<Item = (bool, &CompletedRequest)> {
72 let idx = self.idx;
73 self.buf.iter().enumerate().filter_map(move |(i, req)| Some((i == idx, req.as_ref()?)))
74 }
75}
diff --git a/crates/rust-analyzer/src/main_loop/subscriptions.rs b/crates/rust-analyzer/src/main_loop/subscriptions.rs
deleted file mode 100644
index 2c76418be..000000000
--- a/crates/rust-analyzer/src/main_loop/subscriptions.rs
+++ /dev/null
@@ -1,22 +0,0 @@
1//! Keeps track of file subscriptions -- the set of currently opened files for
2//! which we want to publish diagnostics, syntax highlighting, etc.
3
4use ra_ide::FileId;
5use rustc_hash::FxHashSet;
6
7#[derive(Default, Debug)]
8pub(crate) struct Subscriptions {
9 subs: FxHashSet<FileId>,
10}
11
12impl Subscriptions {
13 pub(crate) fn add_sub(&mut self, file_id: FileId) {
14 self.subs.insert(file_id);
15 }
16 pub(crate) fn remove_sub(&mut self, file_id: FileId) {
17 self.subs.remove(&file_id);
18 }
19 pub(crate) fn subscriptions(&self) -> Vec<FileId> {
20 self.subs.iter().copied().collect()
21 }
22}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
new file mode 100644
index 000000000..3f4dbdd8c
--- /dev/null
+++ b/crates/rust-analyzer/src/reload.rs
@@ -0,0 +1,324 @@
1//! Project loading & configuration updates
2use std::{mem, sync::Arc};
3
4use flycheck::FlycheckHandle;
5use ra_db::{CrateGraph, SourceRoot, VfsPath};
6use ra_ide::AnalysisChange;
7use ra_prof::profile;
8use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace};
9use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
10
11use crate::{
12 config::{Config, FilesWatcher, LinkedProject},
13 global_state::{GlobalState, Status},
14 lsp_ext,
15 main_loop::Task,
16};
17
18impl GlobalState {
19 pub(crate) fn update_configuration(&mut self, config: Config) {
20 let _p = profile("GlobalState::update_configuration");
21 let old_config = mem::replace(&mut self.config, config);
22 if self.config.lru_capacity != old_config.lru_capacity {
23 self.analysis_host.update_lru_capacity(old_config.lru_capacity);
24 }
25 if self.config.linked_projects != old_config.linked_projects {
26 self.fetch_workspaces()
27 } else if self.config.flycheck != old_config.flycheck {
28 self.reload_flycheck();
29 }
30 }
31 pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) {
32 if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
33 return;
34 }
35 match self.status {
36 Status::Loading | Status::NeedsReload => return,
37 Status::Ready | Status::Invalid => (),
38 }
39 if self.config.cargo_autoreload {
40 self.fetch_workspaces();
41 } else {
42 self.transition(Status::NeedsReload);
43 }
44
45 fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
46 const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
47 const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
48
49 if path.ends_with("Cargo.toml") || path.ends_with("Cargo.lock") {
50 return true;
51 }
52 if change_kind == ChangeKind::Modify {
53 return false;
54 }
55 if path.extension().unwrap_or_default() != "rs" {
56 return false;
57 }
58 if IMPLICIT_TARGET_FILES.iter().any(|it| path.ends_with(it)) {
59 return true;
60 }
61 let parent = match path.parent() {
62 Some(it) => it,
63 None => return false,
64 };
65 if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.ends_with(it)) {
66 return true;
67 }
68 if path.ends_with("main.rs") {
69 let grand_parent = match parent.parent() {
70 Some(it) => it,
71 None => return false,
72 };
73 if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.ends_with(it)) {
74 return true;
75 }
76 }
77 false
78 }
79 }
80 pub(crate) fn transition(&mut self, new_status: Status) {
81 self.status = new_status;
82 if self.config.client_caps.status_notification {
83 let lsp_status = match new_status {
84 Status::Loading => lsp_ext::Status::Loading,
85 Status::Ready => lsp_ext::Status::Ready,
86 Status::Invalid => lsp_ext::Status::Invalid,
87 Status::NeedsReload => lsp_ext::Status::NeedsReload,
88 };
89 self.send_notification::<lsp_ext::StatusNotification>(lsp_status);
90 }
91 }
92 pub(crate) fn fetch_workspaces(&mut self) {
93 self.task_pool.handle.spawn({
94 let linked_projects = self.config.linked_projects.clone();
95 let cargo_config = self.config.cargo.clone();
96 let with_sysroot = self.config.with_sysroot.clone();
97 move || {
98 let workspaces = linked_projects
99 .iter()
100 .map(|project| match project {
101 LinkedProject::ProjectManifest(manifest) => {
102 ra_project_model::ProjectWorkspace::load(
103 manifest.clone(),
104 &cargo_config,
105 with_sysroot,
106 )
107 }
108 LinkedProject::InlineJsonProject(it) => {
109 Ok(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
110 }
111 })
112 .collect::<Vec<_>>();
113 Task::Workspaces(workspaces)
114 }
115 });
116 }
117 pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) {
118 let _p = profile("GlobalState::switch_workspaces");
119 log::info!("reloading projects: {:?}", self.config.linked_projects);
120
121 let mut has_errors = false;
122 let workspaces = workspaces
123 .into_iter()
124 .filter_map(|res| {
125 res.map_err(|err| {
126 has_errors = true;
127 log::error!("failed to load workspace: {:#}", err);
128 if self.workspaces.is_empty() {
129 self.show_message(
130 lsp_types::MessageType::Error,
131 format!("rust-analyzer failed to load workspace: {:#}", err),
132 );
133 }
134 })
135 .ok()
136 })
137 .collect::<Vec<_>>();
138
139 if &*self.workspaces == &workspaces {
140 return;
141 }
142
143 if !self.workspaces.is_empty() && has_errors {
144 return;
145 }
146
147 if let FilesWatcher::Client = self.config.files.watcher {
148 let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions {
149 watchers: workspaces
150 .iter()
151 .flat_map(ProjectWorkspace::to_roots)
152 .filter(PackageRoot::is_member)
153 .map(|root| format!("{}/**/*.rs", root.path().display()))
154 .map(|glob_pattern| lsp_types::FileSystemWatcher { glob_pattern, kind: None })
155 .collect(),
156 };
157 let registration = lsp_types::Registration {
158 id: "workspace/didChangeWatchedFiles".to_string(),
159 method: "workspace/didChangeWatchedFiles".to_string(),
160 register_options: Some(serde_json::to_value(registration_options).unwrap()),
161 };
162 self.send_request::<lsp_types::request::RegisterCapability>(
163 lsp_types::RegistrationParams { registrations: vec![registration] },
164 |_, _| (),
165 );
166 }
167
168 let mut change = AnalysisChange::new();
169
170 let project_folders = ProjectFolders::new(&workspaces);
171
172 self.proc_macro_client = match &self.config.proc_macro_srv {
173 None => ProcMacroClient::dummy(),
174 Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
175 Ok(it) => it,
176 Err(err) => {
177 log::error!(
178 "Failed to run ra_proc_macro_srv from path {}, error: {:?}",
179 path.display(),
180 err
181 );
182 ProcMacroClient::dummy()
183 }
184 },
185 };
186
187 let watch = match self.config.files.watcher {
188 FilesWatcher::Client => vec![],
189 FilesWatcher::Notify => project_folders.watch,
190 };
191 self.loader.handle.set_config(vfs::loader::Config { load: project_folders.load, watch });
192
193 // Create crate graph from all the workspaces
194 let crate_graph = {
195 let mut crate_graph = CrateGraph::default();
196 let vfs = &mut self.vfs.write().0;
197 let loader = &mut self.loader;
198 let mut load = |path: &AbsPath| {
199 let contents = loader.handle.load_sync(path);
200 let path = vfs::VfsPath::from(path.to_path_buf());
201 vfs.set_file_contents(path.clone(), contents);
202 vfs.file_id(&path)
203 };
204 for ws in workspaces.iter() {
205 crate_graph.extend(ws.to_crate_graph(
206 self.config.cargo.target.as_deref(),
207 &self.proc_macro_client,
208 &mut load,
209 ));
210 }
211
212 crate_graph
213 };
214 change.set_crate_graph(crate_graph);
215
216 self.source_root_config = project_folders.source_root_config;
217 self.workspaces = Arc::new(workspaces);
218
219 self.analysis_host.apply_change(change);
220 self.process_changes();
221 self.reload_flycheck();
222 }
223
224 fn reload_flycheck(&mut self) {
225 let config = match self.config.flycheck.clone() {
226 Some(it) => it,
227 None => {
228 self.flycheck = None;
229 return;
230 }
231 };
232
233 let sender = self.flycheck_sender.clone();
234 let sender = Box::new(move |msg| sender.send(msg).unwrap());
235 self.flycheck = self
236 .workspaces
237 .iter()
238 // FIXME: Figure out the multi-workspace situation
239 .find_map(|w| match w {
240 ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some(cargo),
241 ProjectWorkspace::Json { .. } => None,
242 })
243 .map(move |cargo| {
244 let cargo_project_root = cargo.workspace_root().to_path_buf();
245 FlycheckHandle::spawn(sender, config.clone(), cargo_project_root.into())
246 })
247 }
248}
249
250#[derive(Default)]
251pub(crate) struct ProjectFolders {
252 pub(crate) load: Vec<vfs::loader::Entry>,
253 pub(crate) watch: Vec<usize>,
254 pub(crate) source_root_config: SourceRootConfig,
255}
256
257impl ProjectFolders {
258 pub(crate) fn new(workspaces: &[ProjectWorkspace]) -> ProjectFolders {
259 let mut res = ProjectFolders::default();
260 let mut fsc = FileSetConfig::builder();
261 let mut local_filesets = vec![];
262
263 for root in workspaces.iter().flat_map(|it| it.to_roots()) {
264 let path = root.path().to_owned();
265
266 let mut file_set_roots: Vec<VfsPath> = vec![];
267
268 let entry = if root.is_member() {
269 vfs::loader::Entry::local_cargo_package(path.to_path_buf())
270 } else {
271 vfs::loader::Entry::cargo_package_dependency(path.to_path_buf())
272 };
273 res.load.push(entry);
274 if root.is_member() {
275 res.watch.push(res.load.len() - 1);
276 }
277
278 if let Some(out_dir) = root.out_dir() {
279 let out_dir = out_dir.to_path_buf();
280 res.load.push(vfs::loader::Entry::rs_files_recursively(out_dir.clone()));
281 if root.is_member() {
282 res.watch.push(res.load.len() - 1);
283 }
284 file_set_roots.push(out_dir.into());
285 }
286 file_set_roots.push(path.to_path_buf().into());
287
288 if root.is_member() {
289 local_filesets.push(fsc.len());
290 }
291 fsc.add_file_set(file_set_roots)
292 }
293
294 let fsc = fsc.build();
295 res.source_root_config = SourceRootConfig { fsc, local_filesets };
296
297 res
298 }
299}
300
301#[derive(Default, Debug)]
302pub(crate) struct SourceRootConfig {
303 pub(crate) fsc: FileSetConfig,
304 pub(crate) local_filesets: Vec<usize>,
305}
306
307impl SourceRootConfig {
308 pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
309 let _p = profile("SourceRootConfig::partition");
310 self.fsc
311 .partition(vfs)
312 .into_iter()
313 .enumerate()
314 .map(|(idx, file_set)| {
315 let is_local = self.local_filesets.contains(&idx);
316 if is_local {
317 SourceRoot::new_local(file_set)
318 } else {
319 SourceRoot::new_library(file_set)
320 }
321 })
322 .collect()
323 }
324}
diff --git a/crates/rust-analyzer/src/request_metrics.rs b/crates/rust-analyzer/src/request_metrics.rs
new file mode 100644
index 000000000..b1019e2d6
--- /dev/null
+++ b/crates/rust-analyzer/src/request_metrics.rs
@@ -0,0 +1,37 @@
1//! Records stats about requests
2use std::time::Duration;
3
4use lsp_server::RequestId;
5
6#[derive(Debug)]
7pub(crate) struct RequestMetrics {
8 pub(crate) id: RequestId,
9 pub(crate) method: String,
10 pub(crate) duration: Duration,
11}
12
13const N_COMPLETED_REQUESTS: usize = 10;
14
15#[derive(Debug, Default)]
16pub(crate) struct LatestRequests {
17 // hand-rolling VecDeque here to print things in a nicer way
18 buf: [Option<RequestMetrics>; N_COMPLETED_REQUESTS],
19 idx: usize,
20}
21
22impl LatestRequests {
23 pub(crate) fn record(&mut self, request: RequestMetrics) {
24 // special case: don't track status request itself
25 if request.method == "rust-analyzer/analyzerStatus" {
26 return;
27 }
28 let idx = self.idx;
29 self.buf[idx] = Some(request);
30 self.idx = (idx + 1) % N_COMPLETED_REQUESTS;
31 }
32
33 pub(crate) fn iter(&self) -> impl Iterator<Item = (bool, &RequestMetrics)> {
34 let idx = self.idx;
35 self.buf.iter().enumerate().filter_map(move |(i, req)| Some((i == idx, req.as_ref()?)))
36 }
37}
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 6f125c37c..576bd8adc 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -39,12 +39,15 @@ define_semantic_token_types![
39 (BOOLEAN, "boolean"), 39 (BOOLEAN, "boolean"),
40 (BUILTIN_TYPE, "builtinType"), 40 (BUILTIN_TYPE, "builtinType"),
41 (ENUM_MEMBER, "enumMember"), 41 (ENUM_MEMBER, "enumMember"),
42 (ESCAPE_SEQUENCE, "escapeSequence"),
43 (FORMAT_SPECIFIER, "formatSpecifier"),
44 (GENERIC, "generic"),
42 (LIFETIME, "lifetime"), 45 (LIFETIME, "lifetime"),
46 (PUNCTUATION, "punctuation"),
43 (SELF_KEYWORD, "selfKeyword"), 47 (SELF_KEYWORD, "selfKeyword"),
44 (TYPE_ALIAS, "typeAlias"), 48 (TYPE_ALIAS, "typeAlias"),
45 (UNION, "union"), 49 (UNION, "union"),
46 (UNRESOLVED_REFERENCE, "unresolvedReference"), 50 (UNRESOLVED_REFERENCE, "unresolvedReference"),
47 (FORMAT_SPECIFIER, "formatSpecifier"),
48]; 51];
49 52
50macro_rules! define_semantic_token_modifiers { 53macro_rules! define_semantic_token_modifiers {
@@ -67,6 +70,7 @@ macro_rules! define_semantic_token_modifiers {
67define_semantic_token_modifiers![ 70define_semantic_token_modifiers![
68 (CONSTANT, "constant"), 71 (CONSTANT, "constant"),
69 (CONTROL_FLOW, "controlFlow"), 72 (CONTROL_FLOW, "controlFlow"),
73 (INJECTED, "injected"),
70 (MUTABLE, "mutable"), 74 (MUTABLE, "mutable"),
71 (UNSAFE, "unsafe"), 75 (UNSAFE, "unsafe"),
72 (ATTRIBUTE_MODIFIER, "attribute"), 76 (ATTRIBUTE_MODIFIER, "attribute"),
diff --git a/crates/rust-analyzer/src/thread_pool.rs b/crates/rust-analyzer/src/thread_pool.rs
new file mode 100644
index 000000000..4fa502925
--- /dev/null
+++ b/crates/rust-analyzer/src/thread_pool.rs
@@ -0,0 +1,35 @@
1//! A thin wrapper around `ThreadPool` to make sure that we join all things
2//! properly.
3use crossbeam_channel::Sender;
4
5pub(crate) struct TaskPool<T> {
6 sender: Sender<T>,
7 inner: threadpool::ThreadPool,
8}
9
10impl<T> TaskPool<T> {
11 pub(crate) fn new(sender: Sender<T>) -> TaskPool<T> {
12 TaskPool { sender, inner: threadpool::ThreadPool::default() }
13 }
14
15 pub(crate) fn spawn<F>(&mut self, task: F)
16 where
17 F: FnOnce() -> T + Send + 'static,
18 T: Send + 'static,
19 {
20 self.inner.execute({
21 let sender = self.sender.clone();
22 move || sender.send(task()).unwrap()
23 })
24 }
25
26 pub(crate) fn len(&self) -> usize {
27 self.inner.queued_count()
28 }
29}
30
31impl<T> Drop for TaskPool<T> {
32 fn drop(&mut self) {
33 self.inner.join()
34 }
35}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 2fbbb4e63..2fcae9ca3 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -1,15 +1,20 @@
1//! Conversion of rust-analyzer specific types to lsp_types equivalents. 1//! Conversion of rust-analyzer specific types to lsp_types equivalents.
2use std::path::{self, Path};
3
4use itertools::Itertools;
2use ra_db::{FileId, FileRange}; 5use ra_db::{FileId, FileRange};
3use ra_ide::{ 6use ra_ide::{
4 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, 7 Assist, AssistKind, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold,
5 FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, 8 FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange,
6 InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, 9 Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget,
7 SourceChange, SourceFileEdit, TextEdit, 10 ReferenceAccess, ResolvedAssist, Runnable, Severity, SourceChange, SourceFileEdit, TextEdit,
8}; 11};
9use ra_syntax::{SyntaxKind, TextRange, TextSize}; 12use ra_syntax::{SyntaxKind, TextRange, TextSize};
10use ra_vfs::LineEndings;
11 13
12use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; 14use crate::{
15 cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot,
16 line_endings::LineEndings, lsp_ext, semantic_tokens, Result,
17};
13 18
14pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { 19pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
15 let line_col = line_index.line_col(offset); 20 let line_col = line_index.line_col(offset);
@@ -95,6 +100,7 @@ pub(crate) fn completion_item_kind(
95 CompletionItemKind::TypeParam => lsp_types::CompletionItemKind::TypeParameter, 100 CompletionItemKind::TypeParam => lsp_types::CompletionItemKind::TypeParameter,
96 CompletionItemKind::Macro => lsp_types::CompletionItemKind::Method, 101 CompletionItemKind::Macro => lsp_types::CompletionItemKind::Method,
97 CompletionItemKind::Attribute => lsp_types::CompletionItemKind::EnumMember, 102 CompletionItemKind::Attribute => lsp_types::CompletionItemKind::EnumMember,
103 CompletionItemKind::UnresolvedReference => lsp_types::CompletionItemKind::Reference,
98 } 104 }
99} 105}
100 106
@@ -290,6 +296,7 @@ fn semantic_token_type_and_modifiers(
290 HighlightTag::SelfType => lsp_types::SemanticTokenType::TYPE, 296 HighlightTag::SelfType => lsp_types::SemanticTokenType::TYPE,
291 HighlightTag::Field => lsp_types::SemanticTokenType::PROPERTY, 297 HighlightTag::Field => lsp_types::SemanticTokenType::PROPERTY,
292 HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION, 298 HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION,
299 HighlightTag::Generic => semantic_tokens::GENERIC,
293 HighlightTag::Module => lsp_types::SemanticTokenType::NAMESPACE, 300 HighlightTag::Module => lsp_types::SemanticTokenType::NAMESPACE,
294 HighlightTag::Constant => { 301 HighlightTag::Constant => {
295 mods |= semantic_tokens::CONSTANT; 302 mods |= semantic_tokens::CONSTANT;
@@ -302,6 +309,7 @@ fn semantic_token_type_and_modifiers(
302 } 309 }
303 HighlightTag::EnumVariant => semantic_tokens::ENUM_MEMBER, 310 HighlightTag::EnumVariant => semantic_tokens::ENUM_MEMBER,
304 HighlightTag::Macro => lsp_types::SemanticTokenType::MACRO, 311 HighlightTag::Macro => lsp_types::SemanticTokenType::MACRO,
312 HighlightTag::ValueParam => lsp_types::SemanticTokenType::PARAMETER,
305 HighlightTag::Local => lsp_types::SemanticTokenType::VARIABLE, 313 HighlightTag::Local => lsp_types::SemanticTokenType::VARIABLE,
306 HighlightTag::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, 314 HighlightTag::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER,
307 HighlightTag::Lifetime => semantic_tokens::LIFETIME, 315 HighlightTag::Lifetime => semantic_tokens::LIFETIME,
@@ -318,12 +326,16 @@ fn semantic_token_type_and_modifiers(
318 HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, 326 HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
319 HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, 327 HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
320 HighlightTag::Operator => lsp_types::SemanticTokenType::OPERATOR, 328 HighlightTag::Operator => lsp_types::SemanticTokenType::OPERATOR,
329 HighlightTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
330 HighlightTag::Punctuation => semantic_tokens::PUNCTUATION,
321 }; 331 };
322 332
323 for modifier in highlight.modifiers.iter() { 333 for modifier in highlight.modifiers.iter() {
324 let modifier = match modifier { 334 let modifier = match modifier {
325 HighlightModifier::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER, 335 HighlightModifier::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER,
326 HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION, 336 HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION,
337 HighlightModifier::Documentation => lsp_types::SemanticTokenModifier::DOCUMENTATION,
338 HighlightModifier::Injected => semantic_tokens::INJECTED,
327 HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW, 339 HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW,
328 HighlightModifier::Mutable => semantic_tokens::MUTABLE, 340 HighlightModifier::Mutable => semantic_tokens::MUTABLE,
329 HighlightModifier::Unsafe => semantic_tokens::UNSAFE, 341 HighlightModifier::Unsafe => semantic_tokens::UNSAFE,
@@ -343,7 +355,7 @@ pub(crate) fn folding_range(
343 let kind = match fold.kind { 355 let kind = match fold.kind {
344 FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), 356 FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
345 FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), 357 FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
346 FoldKind::Mods | FoldKind::Block => None, 358 FoldKind::Mods | FoldKind::Block | FoldKind::ArgList => None,
347 }; 359 };
348 360
349 let range = range(line_index, fold.range); 361 let range = range(line_index, fold.range);
@@ -382,41 +394,87 @@ pub(crate) fn folding_range(
382 } 394 }
383} 395}
384 396
385pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::Url> { 397pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> lsp_types::Url {
386 world.file_id_to_uri(file_id) 398 snap.file_id_to_url(file_id)
399}
400
401/// Returns a `Url` object from a given path, will lowercase drive letters if present.
402/// This will only happen when processing windows paths.
403///
404/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
405pub(crate) fn url_from_abs_path(path: &Path) -> lsp_types::Url {
406 assert!(path.is_absolute());
407 let url = lsp_types::Url::from_file_path(path).unwrap();
408 match path.components().next() {
409 Some(path::Component::Prefix(prefix)) if matches!(prefix.kind(), path::Prefix::Disk(_) | path::Prefix::VerbatimDisk(_)) =>
410 {
411 // Need to lowercase driver letter
412 }
413 _ => return url,
414 }
415
416 let driver_letter_range = {
417 let (scheme, drive_letter, _rest) = match url.as_str().splitn(3, ':').collect_tuple() {
418 Some(it) => it,
419 None => return url,
420 };
421 let start = scheme.len() + ':'.len_utf8();
422 start..(start + drive_letter.len())
423 };
424
425 // Note: lowercasing the `path` itself doesn't help, the `Url::parse`
426 // machinery *also* canonicalizes the drive letter. So, just massage the
427 // string in place.
428 let mut url = url.into_string();
429 url[driver_letter_range].make_ascii_lowercase();
430 lsp_types::Url::parse(&url).unwrap()
387} 431}
388 432
389pub(crate) fn versioned_text_document_identifier( 433pub(crate) fn versioned_text_document_identifier(
390 world: &WorldSnapshot, 434 snap: &GlobalStateSnapshot,
391 file_id: FileId, 435 file_id: FileId,
392 version: Option<i64>, 436 version: Option<i64>,
393) -> Result<lsp_types::VersionedTextDocumentIdentifier> { 437) -> lsp_types::VersionedTextDocumentIdentifier {
394 let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(world, file_id)?, version }; 438 lsp_types::VersionedTextDocumentIdentifier { uri: url(snap, file_id), version }
395 Ok(res)
396} 439}
397 440
398pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result<lsp_types::Location> { 441pub(crate) fn location(
399 let url = url(world, frange.file_id)?; 442 snap: &GlobalStateSnapshot,
400 let line_index = world.analysis().file_line_index(frange.file_id)?; 443 frange: FileRange,
444) -> Result<lsp_types::Location> {
445 let url = url(snap, frange.file_id);
446 let line_index = snap.analysis.file_line_index(frange.file_id)?;
401 let range = range(&line_index, frange.range); 447 let range = range(&line_index, frange.range);
402 let loc = lsp_types::Location::new(url, range); 448 let loc = lsp_types::Location::new(url, range);
403 Ok(loc) 449 Ok(loc)
404} 450}
405 451
452/// Perefer using `location_link`, if the client has the cap.
453pub(crate) fn location_from_nav(
454 snap: &GlobalStateSnapshot,
455 nav: NavigationTarget,
456) -> Result<lsp_types::Location> {
457 let url = url(snap, nav.file_id());
458 let line_index = snap.analysis.file_line_index(nav.file_id())?;
459 let range = range(&line_index, nav.full_range());
460 let loc = lsp_types::Location::new(url, range);
461 Ok(loc)
462}
463
406pub(crate) fn location_link( 464pub(crate) fn location_link(
407 world: &WorldSnapshot, 465 snap: &GlobalStateSnapshot,
408 src: Option<FileRange>, 466 src: Option<FileRange>,
409 target: NavigationTarget, 467 target: NavigationTarget,
410) -> Result<lsp_types::LocationLink> { 468) -> Result<lsp_types::LocationLink> {
411 let origin_selection_range = match src { 469 let origin_selection_range = match src {
412 Some(src) => { 470 Some(src) => {
413 let line_index = world.analysis().file_line_index(src.file_id)?; 471 let line_index = snap.analysis.file_line_index(src.file_id)?;
414 let range = range(&line_index, src.range); 472 let range = range(&line_index, src.range);
415 Some(range) 473 Some(range)
416 } 474 }
417 None => None, 475 None => None,
418 }; 476 };
419 let (target_uri, target_range, target_selection_range) = location_info(world, target)?; 477 let (target_uri, target_range, target_selection_range) = location_info(snap, target)?;
420 let res = lsp_types::LocationLink { 478 let res = lsp_types::LocationLink {
421 origin_selection_range, 479 origin_selection_range,
422 target_uri, 480 target_uri,
@@ -427,12 +485,12 @@ pub(crate) fn location_link(
427} 485}
428 486
429fn location_info( 487fn location_info(
430 world: &WorldSnapshot, 488 snap: &GlobalStateSnapshot,
431 target: NavigationTarget, 489 target: NavigationTarget,
432) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { 490) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> {
433 let line_index = world.analysis().file_line_index(target.file_id())?; 491 let line_index = snap.analysis.file_line_index(target.file_id())?;
434 492
435 let target_uri = url(world, target.file_id())?; 493 let target_uri = url(snap, target.file_id());
436 let target_range = range(&line_index, target.full_range()); 494 let target_range = range(&line_index, target.full_range());
437 let target_selection_range = 495 let target_selection_range =
438 target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range); 496 target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range);
@@ -440,14 +498,14 @@ fn location_info(
440} 498}
441 499
442pub(crate) fn goto_definition_response( 500pub(crate) fn goto_definition_response(
443 world: &WorldSnapshot, 501 snap: &GlobalStateSnapshot,
444 src: Option<FileRange>, 502 src: Option<FileRange>,
445 targets: Vec<NavigationTarget>, 503 targets: Vec<NavigationTarget>,
446) -> Result<lsp_types::GotoDefinitionResponse> { 504) -> Result<lsp_types::GotoDefinitionResponse> {
447 if world.config.client_caps.location_link { 505 if snap.config.client_caps.location_link {
448 let links = targets 506 let links = targets
449 .into_iter() 507 .into_iter()
450 .map(|nav| location_link(world, src, nav)) 508 .map(|nav| location_link(snap, src, nav))
451 .collect::<Result<Vec<_>>>()?; 509 .collect::<Result<Vec<_>>>()?;
452 Ok(links.into()) 510 Ok(links.into())
453 } else { 511 } else {
@@ -455,7 +513,7 @@ pub(crate) fn goto_definition_response(
455 .into_iter() 513 .into_iter()
456 .map(|nav| { 514 .map(|nav| {
457 location( 515 location(
458 world, 516 snap,
459 FileRange { 517 FileRange {
460 file_id: nav.file_id(), 518 file_id: nav.file_id(),
461 range: nav.focus_range().unwrap_or(nav.range()), 519 range: nav.focus_range().unwrap_or(nav.range()),
@@ -468,13 +526,13 @@ pub(crate) fn goto_definition_response(
468} 526}
469 527
470pub(crate) fn snippet_text_document_edit( 528pub(crate) fn snippet_text_document_edit(
471 world: &WorldSnapshot, 529 snap: &GlobalStateSnapshot,
472 is_snippet: bool, 530 is_snippet: bool,
473 source_file_edit: SourceFileEdit, 531 source_file_edit: SourceFileEdit,
474) -> Result<lsp_ext::SnippetTextDocumentEdit> { 532) -> Result<lsp_ext::SnippetTextDocumentEdit> {
475 let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; 533 let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id, None);
476 let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; 534 let line_index = snap.analysis.file_line_index(source_file_edit.file_id)?;
477 let line_endings = world.file_line_endings(source_file_edit.file_id); 535 let line_endings = snap.file_line_endings(source_file_edit.file_id);
478 let edits = source_file_edit 536 let edits = source_file_edit
479 .edit 537 .edit
480 .into_iter() 538 .into_iter()
@@ -484,34 +542,33 @@ pub(crate) fn snippet_text_document_edit(
484} 542}
485 543
486pub(crate) fn resource_op( 544pub(crate) fn resource_op(
487 world: &WorldSnapshot, 545 snap: &GlobalStateSnapshot,
488 file_system_edit: FileSystemEdit, 546 file_system_edit: FileSystemEdit,
489) -> Result<lsp_types::ResourceOp> { 547) -> lsp_types::ResourceOp {
490 let res = match file_system_edit { 548 match file_system_edit {
491 FileSystemEdit::CreateFile { source_root, path } => { 549 FileSystemEdit::CreateFile { anchor, dst } => {
492 let uri = world.path_to_uri(source_root, &path)?; 550 let uri = snap.anchored_path(anchor, &dst);
493 lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) 551 lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None })
494 } 552 }
495 FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { 553 FileSystemEdit::MoveFile { src, anchor, dst } => {
496 let old_uri = world.file_id_to_uri(src)?; 554 let old_uri = snap.file_id_to_url(src);
497 let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; 555 let new_uri = snap.anchored_path(anchor, &dst);
498 lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) 556 lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None })
499 } 557 }
500 }; 558 }
501 Ok(res)
502} 559}
503 560
504pub(crate) fn snippet_workspace_edit( 561pub(crate) fn snippet_workspace_edit(
505 world: &WorldSnapshot, 562 snap: &GlobalStateSnapshot,
506 source_change: SourceChange, 563 source_change: SourceChange,
507) -> Result<lsp_ext::SnippetWorkspaceEdit> { 564) -> Result<lsp_ext::SnippetWorkspaceEdit> {
508 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); 565 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
509 for op in source_change.file_system_edits { 566 for op in source_change.file_system_edits {
510 let op = resource_op(&world, op)?; 567 let op = resource_op(&snap, op);
511 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op)); 568 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op));
512 } 569 }
513 for edit in source_change.source_file_edits { 570 for edit in source_change.source_file_edits {
514 let edit = snippet_text_document_edit(&world, source_change.is_snippet, edit)?; 571 let edit = snippet_text_document_edit(&snap, source_change.is_snippet, edit)?;
515 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); 572 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
516 } 573 }
517 let workspace_edit = 574 let workspace_edit =
@@ -520,11 +577,11 @@ pub(crate) fn snippet_workspace_edit(
520} 577}
521 578
522pub(crate) fn workspace_edit( 579pub(crate) fn workspace_edit(
523 world: &WorldSnapshot, 580 snap: &GlobalStateSnapshot,
524 source_change: SourceChange, 581 source_change: SourceChange,
525) -> Result<lsp_types::WorkspaceEdit> { 582) -> Result<lsp_types::WorkspaceEdit> {
526 assert!(!source_change.is_snippet); 583 assert!(!source_change.is_snippet);
527 snippet_workspace_edit(world, source_change).map(|it| it.into()) 584 snippet_workspace_edit(snap, source_change).map(|it| it.into())
528} 585}
529 586
530impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { 587impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
@@ -562,45 +619,110 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
562 } 619 }
563} 620}
564 621
565pub fn call_hierarchy_item( 622pub(crate) fn call_hierarchy_item(
566 world: &WorldSnapshot, 623 snap: &GlobalStateSnapshot,
567 target: NavigationTarget, 624 target: NavigationTarget,
568) -> Result<lsp_types::CallHierarchyItem> { 625) -> Result<lsp_types::CallHierarchyItem> {
569 let name = target.name().to_string(); 626 let name = target.name().to_string();
570 let detail = target.description().map(|it| it.to_string()); 627 let detail = target.description().map(|it| it.to_string());
571 let kind = symbol_kind(target.kind()); 628 let kind = symbol_kind(target.kind());
572 let (uri, range, selection_range) = location_info(world, target)?; 629 let (uri, range, selection_range) = location_info(snap, target)?;
573 Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) 630 Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range })
574} 631}
575 632
633pub(crate) fn code_action_kind(kind: AssistKind) -> lsp_types::CodeActionKind {
634 match kind {
635 AssistKind::None | AssistKind::Generate => lsp_types::CodeActionKind::EMPTY,
636 AssistKind::QuickFix => lsp_types::CodeActionKind::QUICKFIX,
637 AssistKind::Refactor => lsp_types::CodeActionKind::REFACTOR,
638 AssistKind::RefactorExtract => lsp_types::CodeActionKind::REFACTOR_EXTRACT,
639 AssistKind::RefactorInline => lsp_types::CodeActionKind::REFACTOR_INLINE,
640 AssistKind::RefactorRewrite => lsp_types::CodeActionKind::REFACTOR_REWRITE,
641 }
642}
643
644pub(crate) fn unresolved_code_action(
645 snap: &GlobalStateSnapshot,
646 assist: Assist,
647 index: usize,
648) -> Result<lsp_ext::CodeAction> {
649 let res = lsp_ext::CodeAction {
650 title: assist.label,
651 id: Some(format!("{}:{}", assist.id.0.to_owned(), index.to_string())),
652 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
653 kind: Some(code_action_kind(assist.id.1)),
654 edit: None,
655 };
656 Ok(res)
657}
658
659pub(crate) fn resolved_code_action(
660 snap: &GlobalStateSnapshot,
661 assist: ResolvedAssist,
662) -> Result<lsp_ext::CodeAction> {
663 let change = assist.source_change;
664 unresolved_code_action(snap, assist.assist, 0).and_then(|it| {
665 Ok(lsp_ext::CodeAction {
666 id: None,
667 edit: Some(snippet_workspace_edit(snap, change)?),
668 ..it
669 })
670 })
671}
672
673pub(crate) fn runnable(
674 snap: &GlobalStateSnapshot,
675 file_id: FileId,
676 runnable: Runnable,
677) -> Result<lsp_ext::Runnable> {
678 let spec = CargoTargetSpec::for_file(snap, file_id)?;
679 let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone());
680 let target = spec.as_ref().map(|s| s.target.clone());
681 let (cargo_args, executable_args) =
682 CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg_exprs)?;
683 let label = runnable.label(target);
684 let location = location_link(snap, None, runnable.nav)?;
685
686 Ok(lsp_ext::Runnable {
687 label,
688 location: Some(location),
689 kind: lsp_ext::RunnableKind::Cargo,
690 args: lsp_ext::CargoRunnable {
691 workspace_root: workspace_root.map(|it| it.into()),
692 cargo_args,
693 executable_args,
694 expect_test: None,
695 },
696 })
697}
698
699pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent {
700 lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value: markup.into() }
701}
702
576#[cfg(test)] 703#[cfg(test)]
577mod tests { 704mod tests {
578 use test_utils::extract_ranges; 705 use ra_ide::Analysis;
579 706
580 use super::*; 707 use super::*;
581 708
582 #[test] 709 #[test]
583 fn conv_fold_line_folding_only_fixup() { 710 fn conv_fold_line_folding_only_fixup() {
584 let text = r#"<fold>mod a; 711 let text = r#"mod a;
585mod b; 712mod b;
586mod c;</fold> 713mod c;
587 714
588fn main() <fold>{ 715fn main() {
589 if cond <fold>{ 716 if cond {
590 a::do_a(); 717 a::do_a();
591 }</fold> else <fold>{ 718 } else {
592 b::do_b(); 719 b::do_b();
593 }</fold> 720 }
594}</fold>"#; 721}"#;
595 722
596 let (ranges, text) = extract_ranges(text, "fold"); 723 let (analysis, file_id) = Analysis::from_single_file(text.to_string());
597 assert_eq!(ranges.len(), 4); 724 let folds = analysis.folding_ranges(file_id).unwrap();
598 let folds = vec![ 725 assert_eq!(folds.len(), 4);
599 Fold { range: ranges[0], kind: FoldKind::Mods },
600 Fold { range: ranges[1], kind: FoldKind::Block },
601 Fold { range: ranges[2], kind: FoldKind::Block },
602 Fold { range: ranges[3], kind: FoldKind::Block },
603 ];
604 726
605 let line_index = LineIndex::new(&text); 727 let line_index = LineIndex::new(&text);
606 let converted: Vec<lsp_types::FoldingRange> = 728 let converted: Vec<lsp_types::FoldingRange> =
@@ -615,15 +737,19 @@ fn main() <fold>{
615 assert_eq!(folding_range.end_character, None); 737 assert_eq!(folding_range.end_character, None);
616 } 738 }
617 } 739 }
618}
619 740
620pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_ext::CodeAction> { 741 // `Url` is not able to parse windows paths on unix machines.
621 let res = lsp_ext::CodeAction { 742 #[test]
622 title: assist.label, 743 #[cfg(target_os = "windows")]
623 group: if world.config.client_caps.code_action_group { assist.group_label } else { None }, 744 fn test_lowercase_drive_letter_with_drive() {
624 kind: Some(String::new()), 745 let url = url_from_abs_path(Path::new("C:\\Test"));
625 edit: Some(snippet_workspace_edit(world, assist.source_change)?), 746 assert_eq!(url.to_string(), "file:///c:/Test");
626 command: None, 747 }
627 }; 748
628 Ok(res) 749 #[test]
750 #[cfg(target_os = "windows")]
751 fn test_drive_without_colon_passthrough() {
752 let url = url_from_abs_path(Path::new(r#"\\localhost\C$\my_dir"#));
753 assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
754 }
629} 755}
diff --git a/crates/rust-analyzer/src/vfs_glob.rs b/crates/rust-analyzer/src/vfs_glob.rs
deleted file mode 100644
index ff37a7008..000000000
--- a/crates/rust-analyzer/src/vfs_glob.rs
+++ /dev/null
@@ -1,98 +0,0 @@
1//! Exclusion rules for vfs.
2//!
3//! By default, we include only `.rs` files, and skip some know offenders like
4//! `/target` or `/node_modules` altogether.
5//!
6//! It's also possible to add custom exclusion globs.
7
8use globset::{GlobSet, GlobSetBuilder};
9use ra_vfs::{Filter, RelativePath};
10
11pub use globset::{Glob, GlobBuilder};
12
13const ALWAYS_IGNORED: &[&str] = &["target/**", "**/node_modules/**", "**/.git/**"];
14const IGNORED_FOR_NON_MEMBERS: &[&str] = &["examples/**", "tests/**", "benches/**"];
15
16pub struct RustPackageFilterBuilder {
17 is_member: bool,
18 exclude: GlobSetBuilder,
19}
20
21impl Default for RustPackageFilterBuilder {
22 fn default() -> RustPackageFilterBuilder {
23 RustPackageFilterBuilder { is_member: false, exclude: GlobSetBuilder::new() }
24 }
25}
26
27impl RustPackageFilterBuilder {
28 pub fn set_member(mut self, is_member: bool) -> RustPackageFilterBuilder {
29 self.is_member = is_member;
30 self
31 }
32
33 pub fn exclude(mut self, globs: impl IntoIterator<Item = Glob>) -> RustPackageFilterBuilder {
34 for glob in globs.into_iter() {
35 self.exclude.add(glob);
36 }
37 self
38 }
39
40 pub fn into_vfs_filter(self) -> Box<dyn Filter> {
41 let RustPackageFilterBuilder { is_member, mut exclude } = self;
42 for &glob in ALWAYS_IGNORED {
43 exclude.add(Glob::new(glob).unwrap());
44 }
45 if !is_member {
46 for &glob in IGNORED_FOR_NON_MEMBERS {
47 exclude.add(Glob::new(glob).unwrap());
48 }
49 }
50 Box::new(RustPackageFilter { exclude: exclude.build().unwrap() })
51 }
52}
53
54struct RustPackageFilter {
55 exclude: GlobSet,
56}
57
58impl Filter for RustPackageFilter {
59 fn include_dir(&self, dir_path: &RelativePath) -> bool {
60 !self.exclude.is_match(dir_path.as_str())
61 }
62
63 fn include_file(&self, file_path: &RelativePath) -> bool {
64 file_path.extension() == Some("rs")
65 }
66}
67
68#[test]
69fn test_globs() {
70 let filter = RustPackageFilterBuilder::default().set_member(true).into_vfs_filter();
71
72 assert!(filter.include_dir(RelativePath::new("src/tests")));
73 assert!(filter.include_dir(RelativePath::new("src/target")));
74 assert!(filter.include_dir(RelativePath::new("tests")));
75 assert!(filter.include_dir(RelativePath::new("benches")));
76
77 assert!(!filter.include_dir(RelativePath::new("target")));
78 assert!(!filter.include_dir(RelativePath::new("src/foo/.git")));
79 assert!(!filter.include_dir(RelativePath::new("foo/node_modules")));
80
81 let filter = RustPackageFilterBuilder::default().set_member(false).into_vfs_filter();
82
83 assert!(filter.include_dir(RelativePath::new("src/tests")));
84 assert!(filter.include_dir(RelativePath::new("src/target")));
85
86 assert!(!filter.include_dir(RelativePath::new("target")));
87 assert!(!filter.include_dir(RelativePath::new("src/foo/.git")));
88 assert!(!filter.include_dir(RelativePath::new("foo/node_modules")));
89 assert!(!filter.include_dir(RelativePath::new("tests")));
90 assert!(!filter.include_dir(RelativePath::new("benches")));
91
92 let filter = RustPackageFilterBuilder::default()
93 .set_member(true)
94 .exclude(std::iter::once(Glob::new("src/llvm-project/**").unwrap()))
95 .into_vfs_filter();
96
97 assert!(!filter.include_dir(RelativePath::new("src/llvm-project/clang")));
98}
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
deleted file mode 100644
index 367272925..000000000
--- a/crates/rust-analyzer/src/world.rs
+++ /dev/null
@@ -1,343 +0,0 @@
1//! The context or environment in which the language server functions. In our
2//! server implementation this is know as the `WorldState`.
3//!
4//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
5
6use std::{
7 path::{Path, PathBuf},
8 sync::Arc,
9};
10
11use crossbeam_channel::{unbounded, Receiver};
12use lsp_types::Url;
13use parking_lot::RwLock;
14use ra_flycheck::{Flycheck, FlycheckConfig};
15use ra_ide::{
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId,
17};
18use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace};
19use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
20use relative_path::RelativePathBuf;
21use stdx::format_to;
22
23use crate::{
24 config::Config,
25 diagnostics::{
26 to_proto::url_from_path_with_drive_lowercasing, CheckFixes, DiagnosticCollection,
27 },
28 main_loop::pending_requests::{CompletedRequest, LatestRequests},
29 vfs_glob::{Glob, RustPackageFilterBuilder},
30 LspError, Result,
31};
32use ra_db::ExternSourceId;
33use rustc_hash::{FxHashMap, FxHashSet};
34
35fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> {
36 // FIXME: Figure out the multi-workspace situation
37 workspaces
38 .iter()
39 .find_map(|w| match w {
40 ProjectWorkspace::Cargo { cargo, .. } => Some(cargo),
41 ProjectWorkspace::Json { .. } => None,
42 })
43 .map(|cargo| {
44 let cargo_project_root = cargo.workspace_root().to_path_buf();
45 Some(Flycheck::new(config.clone(), cargo_project_root))
46 })
47 .unwrap_or_else(|| {
48 log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
49 None
50 })
51}
52
53/// `WorldState` is the primary mutable state of the language server
54///
55/// The most interesting components are `vfs`, which stores a consistent
56/// snapshot of the file systems, and `analysis_host`, which stores our
57/// incremental salsa database.
58#[derive(Debug)]
59pub struct WorldState {
60 pub config: Config,
61 pub roots: Vec<PathBuf>,
62 pub workspaces: Arc<Vec<ProjectWorkspace>>,
63 pub analysis_host: AnalysisHost,
64 pub vfs: Arc<RwLock<Vfs>>,
65 pub task_receiver: Receiver<VfsTask>,
66 pub latest_requests: Arc<RwLock<LatestRequests>>,
67 pub flycheck: Option<Flycheck>,
68 pub diagnostics: DiagnosticCollection,
69 pub proc_macro_client: ProcMacroClient,
70}
71
72/// An immutable snapshot of the world's state at a point in time.
73pub struct WorldSnapshot {
74 pub config: Config,
75 pub workspaces: Arc<Vec<ProjectWorkspace>>,
76 pub analysis: Analysis,
77 pub latest_requests: Arc<RwLock<LatestRequests>>,
78 pub check_fixes: CheckFixes,
79 vfs: Arc<RwLock<Vfs>>,
80}
81
82impl WorldState {
83 pub fn new(
84 folder_roots: Vec<PathBuf>,
85 workspaces: Vec<ProjectWorkspace>,
86 lru_capacity: Option<usize>,
87 exclude_globs: &[Glob],
88 watch: Watch,
89 config: Config,
90 ) -> WorldState {
91 let mut change = AnalysisChange::new();
92
93 let extern_dirs: FxHashSet<_> =
94 workspaces.iter().flat_map(ProjectWorkspace::out_dirs).collect();
95
96 let roots: Vec<_> = {
97 let create_filter = |is_member| {
98 RustPackageFilterBuilder::default()
99 .set_member(is_member)
100 .exclude(exclude_globs.iter().cloned())
101 .into_vfs_filter()
102 };
103 folder_roots
104 .iter()
105 .map(|path| RootEntry::new(path.clone(), create_filter(true)))
106 .chain(workspaces.iter().flat_map(ProjectWorkspace::to_roots).map(|pkg_root| {
107 RootEntry::new(pkg_root.path().to_owned(), create_filter(pkg_root.is_member()))
108 }))
109 .chain(
110 extern_dirs
111 .iter()
112 .map(|path| RootEntry::new(path.to_owned(), create_filter(false))),
113 )
114 .collect()
115 };
116
117 let (task_sender, task_receiver) = unbounded();
118 let task_sender = Box::new(move |t| task_sender.send(t).unwrap());
119 let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch);
120
121 let mut extern_source_roots = FxHashMap::default();
122 for r in vfs_roots {
123 let vfs_root_path = vfs.root2path(r);
124 let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it));
125 change.add_root(SourceRootId(r.0), is_local);
126 change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string());
127
128 // FIXME: add path2root in vfs to simpily this logic
129 if extern_dirs.contains(&vfs_root_path) {
130 extern_source_roots.insert(vfs_root_path, ExternSourceId(r.0));
131 }
132 }
133
134 // FIXME: Read default cfgs from config
135 let default_cfg_options = {
136 let mut opts = get_rustc_cfg_options(config.cargo.target.as_ref());
137 opts.insert_atom("test".into());
138 opts.insert_atom("debug_assertion".into());
139 opts
140 };
141
142 let proc_macro_client = match &config.proc_macro_srv {
143 None => ProcMacroClient::dummy(),
144 Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
145 Ok(it) => it,
146 Err(err) => {
147 log::error!(
148 "Failed to run ra_proc_macro_srv from path {}, error: {:?}",
149 path.display(),
150 err
151 );
152 ProcMacroClient::dummy()
153 }
154 },
155 };
156
157 // Create crate graph from all the workspaces
158 let mut crate_graph = CrateGraph::default();
159 let mut load = |path: &Path| {
160 // Some path from metadata will be non canonicalized, e.g. /foo/../bar/lib.rs
161 let path = path.canonicalize().ok()?;
162 let vfs_file = vfs.load(&path);
163 vfs_file.map(|f| FileId(f.0))
164 };
165 for ws in workspaces.iter() {
166 crate_graph.extend(ws.to_crate_graph(
167 &default_cfg_options,
168 &extern_source_roots,
169 &proc_macro_client,
170 &mut load,
171 ));
172 }
173 change.set_crate_graph(crate_graph);
174
175 let flycheck = config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c));
176
177 let mut analysis_host = AnalysisHost::new(lru_capacity);
178 analysis_host.apply_change(change);
179 WorldState {
180 config,
181 roots: folder_roots,
182 workspaces: Arc::new(workspaces),
183 analysis_host,
184 vfs: Arc::new(RwLock::new(vfs)),
185 task_receiver,
186 latest_requests: Default::default(),
187 flycheck,
188 diagnostics: Default::default(),
189 proc_macro_client,
190 }
191 }
192
193 pub fn update_configuration(&mut self, config: Config) {
194 self.analysis_host.update_lru_capacity(config.lru_capacity);
195 if config.check != self.config.check {
196 self.flycheck =
197 config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it));
198 }
199
200 self.config = config;
201 }
202
203 /// Returns a vec of libraries
204 /// FIXME: better API here
205 pub fn process_changes(
206 &mut self,
207 roots_scanned: &mut usize,
208 ) -> Option<Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>> {
209 let changes = self.vfs.write().commit_changes();
210 if changes.is_empty() {
211 return None;
212 }
213 let mut libs = Vec::new();
214 let mut change = AnalysisChange::new();
215 for c in changes {
216 match c {
217 VfsChange::AddRoot { root, files } => {
218 let root_path = self.vfs.read().root2path(root);
219 let is_local = self.roots.iter().any(|r| root_path.starts_with(r));
220 if is_local {
221 *roots_scanned += 1;
222 for (file, path, text) in files {
223 change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
224 }
225 } else {
226 let files = files
227 .into_iter()
228 .map(|(vfsfile, path, text)| (FileId(vfsfile.0), path, text))
229 .collect();
230 libs.push((SourceRootId(root.0), files));
231 }
232 }
233 VfsChange::AddFile { root, file, path, text } => {
234 change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
235 }
236 VfsChange::RemoveFile { root, file, path } => {
237 change.remove_file(SourceRootId(root.0), FileId(file.0), path)
238 }
239 VfsChange::ChangeFile { file, text } => {
240 change.change_file(FileId(file.0), text);
241 }
242 }
243 }
244 self.analysis_host.apply_change(change);
245 Some(libs)
246 }
247
248 pub fn add_lib(&mut self, data: LibraryData) {
249 let mut change = AnalysisChange::new();
250 change.add_library(data);
251 self.analysis_host.apply_change(change);
252 }
253
254 pub fn snapshot(&self) -> WorldSnapshot {
255 WorldSnapshot {
256 config: self.config.clone(),
257 workspaces: Arc::clone(&self.workspaces),
258 analysis: self.analysis_host.analysis(),
259 vfs: Arc::clone(&self.vfs),
260 latest_requests: Arc::clone(&self.latest_requests),
261 check_fixes: Arc::clone(&self.diagnostics.check_fixes),
262 }
263 }
264
265 pub fn maybe_collect_garbage(&mut self) {
266 self.analysis_host.maybe_collect_garbage()
267 }
268
269 pub fn collect_garbage(&mut self) {
270 self.analysis_host.collect_garbage()
271 }
272
273 pub fn complete_request(&mut self, request: CompletedRequest) {
274 self.latest_requests.write().record(request)
275 }
276}
277
278impl WorldSnapshot {
279 pub fn analysis(&self) -> &Analysis {
280 &self.analysis
281 }
282
283 pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> {
284 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
285 let file = self.vfs.read().path2file(&path).ok_or_else(|| {
286 // Show warning as this file is outside current workspace
287 // FIXME: just handle such files, and remove `LspError::UNKNOWN_FILE`.
288 LspError {
289 code: LspError::UNKNOWN_FILE,
290 message: "Rust file outside current workspace is not supported yet.".to_string(),
291 }
292 })?;
293 Ok(FileId(file.0))
294 }
295
296 pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> {
297 let path = self.vfs.read().file2path(VfsFile(id.0));
298 let url = url_from_path_with_drive_lowercasing(path)?;
299
300 Ok(url)
301 }
302
303 pub fn file_id_to_path(&self, id: FileId) -> PathBuf {
304 self.vfs.read().file2path(VfsFile(id.0))
305 }
306
307 pub fn file_line_endings(&self, id: FileId) -> LineEndings {
308 self.vfs.read().file_line_endings(VfsFile(id.0))
309 }
310
311 pub fn path_to_uri(&self, root: SourceRootId, path: &RelativePathBuf) -> Result<Url> {
312 let base = self.vfs.read().root2path(VfsRoot(root.0));
313 let path = path.to_path(base);
314 let url = Url::from_file_path(&path)
315 .map_err(|_| format!("can't convert path to url: {}", path.display()))?;
316 Ok(url)
317 }
318
319 pub fn status(&self) -> String {
320 let mut buf = String::new();
321 if self.workspaces.is_empty() {
322 buf.push_str("no workspaces\n")
323 } else {
324 buf.push_str("workspaces:\n");
325 for w in self.workspaces.iter() {
326 format_to!(buf, "{} packages loaded\n", w.n_packages());
327 }
328 }
329 buf.push_str("\nanalysis:\n");
330 buf.push_str(
331 &self
332 .analysis
333 .status()
334 .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()),
335 );
336 buf
337 }
338
339 pub fn workspace_root_for(&self, file_id: FileId) -> Option<&Path> {
340 let path = self.vfs.read().file2path(VfsFile(file_id.0));
341 self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path))
342 }
343}
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap b/crates/rust-analyzer/test_data/clippy_pass_by_ref.txt
index d7f9ec049..d06517126 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap
+++ b/crates/rust-analyzer/test_data/clippy_pass_by_ref.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/compiler/mir/tagset.rs",
8 uri: "file:///test/compiler/mir/tagset.rs",
9 range: Range {
10 start: Position {
11 line: 41,
12 character: 23,
13 },
14 end: Position {
15 line: 41,
16 character: 28,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap b/crates/rust-analyzer/test_data/handles_macro_location.txt
index a59faf254..f5de2f07f 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap
+++ b/crates/rust-analyzer/test_data/handles_macro_location.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/src/main.rs",
8 uri: "file:///test/src/main.rs",
9 range: Range {
10 start: Position {
11 line: 1,
12 character: 4,
13 },
14 end: Position {
15 line: 1,
16 character: 26,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap b/crates/rust-analyzer/test_data/macro_compiler_error.txt
index 3c78e7f36..f695db73c 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap
+++ b/crates/rust-analyzer/test_data/macro_compiler_error.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/crates/ra_hir_def/src/data.rs",
8 uri: "file:///test/crates/ra_hir_def/src/data.rs",
9 range: Range {
10 start: Position {
11 line: 79,
12 character: 15,
13 },
14 end: Position {
15 line: 79,
16 character: 41,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap b/crates/rust-analyzer/test_data/rustc_incompatible_type_for_trait.txt
index 46d0c56d2..fc54440be 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap
+++ b/crates/rust-analyzer/test_data/rustc_incompatible_type_for_trait.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/compiler/ty/list_iter.rs",
8 uri: "file:///test/compiler/ty/list_iter.rs",
9 range: Range {
10 start: Position {
11 line: 51,
12 character: 4,
13 },
14 end: Position {
15 line: 51,
16 character: 47,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap b/crates/rust-analyzer/test_data/rustc_mismatched_type.txt
index 4182929ba..c269af218 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap
+++ b/crates/rust-analyzer/test_data/rustc_mismatched_type.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/runtime/compiler_support.rs",
8 uri: "file:///test/runtime/compiler_support.rs",
9 range: Range {
10 start: Position {
11 line: 47,
12 character: 64,
13 },
14 end: Position {
15 line: 47,
16 character: 69,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/test_data/rustc_unused_variable.txt
index 6dd3fcb2e..084632757 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
+++ b/crates/rust-analyzer/test_data/rustc_unused_variable.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/driver/subcommand/repl.rs",
8 uri: "file:///test/driver/subcommand/repl.rs",
9 range: Range {
10 start: Position {
11 line: 290,
12 character: 8,
13 },
14 end: Position {
15 line: 290,
16 character: 11,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
@@ -50,11 +34,13 @@ expression: diag
50 fixes: [ 34 fixes: [
51 CodeAction { 35 CodeAction {
52 title: "consider prefixing with an underscore", 36 title: "consider prefixing with an underscore",
37 id: None,
53 group: None, 38 group: None,
54 kind: Some( 39 kind: Some(
55 "quickfix", 40 CodeActionKind(
41 "quickfix",
42 ),
56 ), 43 ),
57 command: None,
58 edit: Some( 44 edit: Some(
59 SnippetWorkspaceEdit { 45 SnippetWorkspaceEdit {
60 changes: Some( 46 changes: Some(
diff --git a/crates/rust-analyzer/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/test_data/rustc_unused_variable_as_hint.txt
new file mode 100644
index 000000000..d637923c5
--- /dev/null
+++ b/crates/rust-analyzer/test_data/rustc_unused_variable_as_hint.txt
@@ -0,0 +1,71 @@
1[
2 MappedRustDiagnostic {
3 url: "file:///test/driver/subcommand/repl.rs",
4 diagnostic: Diagnostic {
5 range: Range {
6 start: Position {
7 line: 290,
8 character: 8,
9 },
10 end: Position {
11 line: 290,
12 character: 11,
13 },
14 },
15 severity: Some(
16 Hint,
17 ),
18 code: Some(
19 String(
20 "unused_variables",
21 ),
22 ),
23 source: Some(
24 "rustc",
25 ),
26 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
27 related_information: None,
28 tags: Some(
29 [
30 Unnecessary,
31 ],
32 ),
33 },
34 fixes: [
35 CodeAction {
36 title: "consider prefixing with an underscore",
37 id: None,
38 group: None,
39 kind: Some(
40 CodeActionKind(
41 "quickfix",
42 ),
43 ),
44 edit: Some(
45 SnippetWorkspaceEdit {
46 changes: Some(
47 {
48 "file:///test/driver/subcommand/repl.rs": [
49 TextEdit {
50 range: Range {
51 start: Position {
52 line: 290,
53 character: 8,
54 },
55 end: Position {
56 line: 290,
57 character: 11,
58 },
59 },
60 new_text: "_foo",
61 },
62 ],
63 },
64 ),
65 document_changes: None,
66 },
67 ),
68 },
69 ],
70 },
71]
diff --git a/crates/rust-analyzer/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/test_data/rustc_unused_variable_as_info.txt
new file mode 100644
index 000000000..6b48f16ed
--- /dev/null
+++ b/crates/rust-analyzer/test_data/rustc_unused_variable_as_info.txt
@@ -0,0 +1,71 @@
1[
2 MappedRustDiagnostic {
3 url: "file:///test/driver/subcommand/repl.rs",
4 diagnostic: Diagnostic {
5 range: Range {
6 start: Position {
7 line: 290,
8 character: 8,
9 },
10 end: Position {
11 line: 290,
12 character: 11,
13 },
14 },
15 severity: Some(
16 Information,
17 ),
18 code: Some(
19 String(
20 "unused_variables",
21 ),
22 ),
23 source: Some(
24 "rustc",
25 ),
26 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
27 related_information: None,
28 tags: Some(
29 [
30 Unnecessary,
31 ],
32 ),
33 },
34 fixes: [
35 CodeAction {
36 title: "consider prefixing with an underscore",
37 id: None,
38 group: None,
39 kind: Some(
40 CodeActionKind(
41 "quickfix",
42 ),
43 ),
44 edit: Some(
45 SnippetWorkspaceEdit {
46 changes: Some(
47 {
48 "file:///test/driver/subcommand/repl.rs": [
49 TextEdit {
50 range: Range {
51 start: Position {
52 line: 290,
53 character: 8,
54 },
55 end: Position {
56 line: 290,
57 character: 11,
58 },
59 },
60 new_text: "_foo",
61 },
62 ],
63 },
64 ),
65 document_changes: None,
66 },
67 ),
68 },
69 ],
70 },
71]
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap b/crates/rust-analyzer/test_data/rustc_wrong_number_of_parameters.txt
index f6ab05004..efe37261d 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap
+++ b/crates/rust-analyzer/test_data/rustc_wrong_number_of_parameters.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/compiler/ty/select.rs",
8 uri: "file:///test/compiler/ty/select.rs",
9 range: Range {
10 start: Position {
11 line: 103,
12 character: 17,
13 },
14 end: Position {
15 line: 103,
16 character: 29,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap b/crates/rust-analyzer/test_data/snap_multi_line_fix.txt
index c40cfdcdc..2c4cbea16 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
+++ b/crates/rust-analyzer/test_data/snap_multi_line_fix.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/src/main.rs",
8 uri: "file:///test/src/main.rs",
9 range: Range {
10 start: Position {
11 line: 3,
12 character: 4,
13 },
14 end: Position {
15 line: 3,
16 character: 5,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
@@ -65,11 +49,13 @@ expression: diag
65 fixes: [ 49 fixes: [
66 CodeAction { 50 CodeAction {
67 title: "return the expression directly", 51 title: "return the expression directly",
52 id: None,
68 group: None, 53 group: None,
69 kind: Some( 54 kind: Some(
70 "quickfix", 55 CodeActionKind(
56 "quickfix",
57 ),
71 ), 58 ),
72 command: None,
73 edit: Some( 59 edit: Some(
74 SnippetWorkspaceEdit { 60 SnippetWorkspaceEdit {
75 changes: Some( 61 changes: Some(
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 405ddb362..7b908d30c 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -4,9 +4,7 @@ use std::{collections::HashMap, path::PathBuf, time::Instant};
4 4
5use lsp_types::{ 5use lsp_types::{
6 notification::DidOpenTextDocument, 6 notification::DidOpenTextDocument,
7 request::{ 7 request::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest},
8 CodeActionRequest, Completion, Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest,
9 },
10 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, 8 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
11 DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams, 9 DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams,
12 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, 10 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams,
@@ -31,12 +29,12 @@ fn completes_items_from_standard_library() {
31 let project_start = Instant::now(); 29 let project_start = Instant::now();
32 let server = Project::with_fixture( 30 let server = Project::with_fixture(
33 r#" 31 r#"
34//- Cargo.toml 32//- /Cargo.toml
35[package] 33[package]
36name = "foo" 34name = "foo"
37version = "0.0.0" 35version = "0.0.0"
38 36
39//- src/lib.rs 37//- /src/lib.rs
40use std::collections::Spam; 38use std::collections::Spam;
41"#, 39"#,
42 ) 40 )
@@ -54,81 +52,35 @@ use std::collections::Spam;
54 partial_result_params: PartialResultParams::default(), 52 partial_result_params: PartialResultParams::default(),
55 work_done_progress_params: WorkDoneProgressParams::default(), 53 work_done_progress_params: WorkDoneProgressParams::default(),
56 }); 54 });
57 assert!(format!("{}", res).contains("HashMap")); 55 assert!(res.to_string().contains("HashMap"));
58 eprintln!("completion took {:?}", completion_start.elapsed()); 56 eprintln!("completion took {:?}", completion_start.elapsed());
59} 57}
60 58
61#[test] 59#[test]
62fn test_runnables_no_project() {
63 if skip_slow_tests() {
64 return;
65 }
66
67 let server = project(
68 r"
69//- lib.rs
70#[test]
71fn foo() {
72}
73",
74 );
75 server.wait_until_workspace_is_loaded();
76 server.request::<Runnables>(
77 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
78 json!([
79 {
80 "args": [ "test" ],
81 "extraArgs": [ "foo", "--nocapture" ],
82 "bin": "cargo",
83 "env": { "RUST_BACKTRACE": "short" },
84 "cwd": null,
85 "label": "test foo",
86 "range": {
87 "end": { "character": 1, "line": 2 },
88 "start": { "character": 0, "line": 0 }
89 }
90 },
91 {
92 "args": ["check", "--workspace"],
93 "extraArgs": [],
94 "bin": "cargo",
95 "env": {},
96 "cwd": null,
97 "label": "cargo check --workspace",
98 "range": {
99 "end": { "character": 0, "line": 0 },
100 "start": { "character": 0, "line": 0 }
101 }
102 }
103 ]),
104 );
105}
106
107#[test]
108fn test_runnables_project() { 60fn test_runnables_project() {
109 if skip_slow_tests() { 61 if skip_slow_tests() {
110 return; 62 return;
111 } 63 }
112 64
113 let code = r#" 65 let code = r#"
114//- foo/Cargo.toml 66//- /foo/Cargo.toml
115[package] 67[package]
116name = "foo" 68name = "foo"
117version = "0.0.0" 69version = "0.0.0"
118 70
119//- foo/src/lib.rs 71//- /foo/src/lib.rs
120pub fn foo() {} 72pub fn foo() {}
121 73
122//- foo/tests/spam.rs 74//- /foo/tests/spam.rs
123#[test] 75#[test]
124fn test_eggs() {} 76fn test_eggs() {}
125 77
126//- bar/Cargo.toml 78//- /bar/Cargo.toml
127[package] 79[package]
128name = "bar" 80name = "bar"
129version = "0.0.0" 81version = "0.0.0"
130 82
131//- bar/src/main.rs 83//- /bar/src/main.rs
132fn main() {} 84fn main() {}
133"#; 85"#;
134 86
@@ -138,42 +90,44 @@ fn main() {}
138 server.request::<Runnables>( 90 server.request::<Runnables>(
139 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, 91 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
140 json!([ 92 json!([
141 { 93 {
142 "args": [ "test", "--package", "foo", "--test", "spam" ], 94 "args": {
143 "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], 95 "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
144 "bin": "cargo", 96 "executableArgs": ["test_eggs", "--exact", "--nocapture"],
145 "env": { "RUST_BACKTRACE": "short" }, 97 "workspaceRoot": server.path().join("foo")
146 "label": "test test_eggs",
147 "range": {
148 "end": { "character": 17, "line": 1 },
149 "start": { "character": 0, "line": 0 }
150 },
151 "cwd": server.path().join("foo")
152 }, 98 },
153 { 99 "kind": "cargo",
154 "args": [ "check", "--package", "foo" ], 100 "label": "test test_eggs",
155 "extraArgs": [], 101 "location": {
156 "bin": "cargo", 102 "targetRange": {
157 "env": {}, 103 "end": { "character": 17, "line": 1 },
158 "label": "cargo check -p foo",
159 "range": {
160 "end": { "character": 0, "line": 0 },
161 "start": { "character": 0, "line": 0 } 104 "start": { "character": 0, "line": 0 }
162 }, 105 },
163 "cwd": server.path().join("foo") 106 "targetSelectionRange": {
164 }, 107 "end": { "character": 12, "line": 1 },
165 { 108 "start": { "character": 3, "line": 1 }
166 "args": [ "test", "--package", "foo" ],
167 "extraArgs": [],
168 "bin": "cargo",
169 "env": {},
170 "label": "cargo test -p foo",
171 "range": {
172 "end": { "character": 0, "line": 0 },
173 "start": { "character": 0, "line": 0 }
174 }, 109 },
175 "cwd": server.path().join("foo") 110 "targetUri": "file:///[..]/tests/spam.rs"
176 } 111 }
112 },
113 {
114 "args": {
115 "cargoArgs": ["check", "--package", "foo"],
116 "executableArgs": [],
117 "workspaceRoot": server.path().join("foo")
118 },
119 "kind": "cargo",
120 "label": "cargo check -p foo"
121 },
122 {
123 "args": {
124 "cargoArgs": ["test", "--package", "foo"],
125 "executableArgs": [],
126 "workspaceRoot": server.path().join("foo")
127 },
128 "kind": "cargo",
129 "label": "cargo test -p foo"
130 }
177 ]), 131 ]),
178 ); 132 );
179} 133}
@@ -186,12 +140,12 @@ fn test_format_document() {
186 140
187 let server = project( 141 let server = project(
188 r#" 142 r#"
189//- Cargo.toml 143//- /Cargo.toml
190[package] 144[package]
191name = "foo" 145name = "foo"
192version = "0.0.0" 146version = "0.0.0"
193 147
194//- src/lib.rs 148//- /src/lib.rs
195mod bar; 149mod bar;
196 150
197fn main() { 151fn main() {
@@ -224,14 +178,8 @@ fn main() {}
224pub use std::collections::HashMap; 178pub use std::collections::HashMap;
225"#, 179"#,
226 "range": { 180 "range": {
227 "end": { 181 "end": { "character": 0, "line": 6 },
228 "character": 0, 182 "start": { "character": 0, "line": 0 }
229 "line": 7
230 },
231 "start": {
232 "character": 0,
233 "line": 0
234 }
235 } 183 }
236 } 184 }
237 ]), 185 ]),
@@ -246,13 +194,13 @@ fn test_format_document_2018() {
246 194
247 let server = project( 195 let server = project(
248 r#" 196 r#"
249//- Cargo.toml 197//- /Cargo.toml
250[package] 198[package]
251name = "foo" 199name = "foo"
252version = "0.0.0" 200version = "0.0.0"
253edition = "2018" 201edition = "2018"
254 202
255//- src/lib.rs 203//- /src/lib.rs
256mod bar; 204mod bar;
257 205
258async fn test() { 206async fn test() {
@@ -290,14 +238,8 @@ fn main() {}
290pub use std::collections::HashMap; 238pub use std::collections::HashMap;
291"#, 239"#,
292 "range": { 240 "range": {
293 "end": { 241 "end": { "character": 0, "line": 9 },
294 "character": 0, 242 "start": { "character": 0, "line": 0 }
295 "line": 10
296 },
297 "start": {
298 "character": 0,
299 "line": 0
300 }
301 } 243 }
302 } 244 }
303 ]), 245 ]),
@@ -312,12 +254,12 @@ fn test_missing_module_code_action() {
312 254
313 let server = project( 255 let server = project(
314 r#" 256 r#"
315//- Cargo.toml 257//- /Cargo.toml
316[package] 258[package]
317name = "foo" 259name = "foo"
318version = "0.0.0" 260version = "0.0.0"
319 261
320//- src/lib.rs 262//- /src/lib.rs
321mod bar; 263mod bar;
322 264
323fn main() {} 265fn main() {}
@@ -342,6 +284,7 @@ fn main() {}
342 } 284 }
343 ] 285 ]
344 }, 286 },
287 "kind": "quickfix",
345 "title": "Create module" 288 "title": "Create module"
346 }]), 289 }]),
347 ); 290 );
@@ -374,17 +317,16 @@ fn test_missing_module_code_action_in_json_project() {
374 "root_module": path.join("src/lib.rs"), 317 "root_module": path.join("src/lib.rs"),
375 "deps": [], 318 "deps": [],
376 "edition": "2015", 319 "edition": "2015",
377 "atom_cfgs": [], 320 "cfg": [ "cfg_atom_1", "feature=cfg_1"],
378 "key_value_cfgs": {}
379 } ] 321 } ]
380 }); 322 });
381 323
382 let code = format!( 324 let code = format!(
383 r#" 325 r#"
384//- rust-project.json 326//- /rust-project.json
385{PROJECT} 327{PROJECT}
386 328
387//- src/lib.rs 329//- /src/lib.rs
388mod bar; 330mod bar;
389 331
390fn main() {{}} 332fn main() {{}}
@@ -413,6 +355,7 @@ fn main() {{}}
413 } 355 }
414 ] 356 ]
415 }, 357 },
358 "kind": "quickfix",
416 "title": "Create module" 359 "title": "Create module"
417 }]), 360 }]),
418 ); 361 );
@@ -436,15 +379,15 @@ fn diagnostics_dont_block_typing() {
436 } 379 }
437 380
438 let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect(); 381 let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect();
439 let libs: String = (0..10).map(|i| format!("//- src/m{}.rs\nfn foo() {{}}\n\n", i)).collect(); 382 let libs: String = (0..10).map(|i| format!("//- /src/m{}.rs\nfn foo() {{}}\n\n", i)).collect();
440 let server = Project::with_fixture(&format!( 383 let server = Project::with_fixture(&format!(
441 r#" 384 r#"
442//- Cargo.toml 385//- /Cargo.toml
443[package] 386[package]
444name = "foo" 387name = "foo"
445version = "0.0.0" 388version = "0.0.0"
446 389
447//- src/lib.rs 390//- /src/lib.rs
448{} 391{}
449 392
450{} 393{}
@@ -494,16 +437,17 @@ fn preserves_dos_line_endings() {
494 437
495 let server = Project::with_fixture( 438 let server = Project::with_fixture(
496 &" 439 &"
497//- Cargo.toml 440//- /Cargo.toml
498[package] 441[package]
499name = \"foo\" 442name = \"foo\"
500version = \"0.0.0\" 443version = \"0.0.0\"
501 444
502//- src/main.rs 445//- /src/main.rs
503/// Some Docs\r\nfn main() {} 446/// Some Docs\r\nfn main() {}
504", 447",
505 ) 448 )
506 .server(); 449 .server();
450 server.wait_until_workspace_is_loaded();
507 451
508 server.request::<OnEnter>( 452 server.request::<OnEnter>(
509 TextDocumentPositionParams { 453 TextDocumentPositionParams {
@@ -529,12 +473,12 @@ fn out_dirs_check() {
529 473
530 let server = Project::with_fixture( 474 let server = Project::with_fixture(
531 r###" 475 r###"
532//- Cargo.toml 476//- /Cargo.toml
533[package] 477[package]
534name = "foo" 478name = "foo"
535version = "0.0.0" 479version = "0.0.0"
536 480
537//- build.rs 481//- /build.rs
538use std::{env, fs, path::Path}; 482use std::{env, fs, path::Path};
539 483
540fn main() { 484fn main() {
@@ -549,7 +493,12 @@ fn main() {
549 println!("cargo:rustc-cfg=featlike=\"set\""); 493 println!("cargo:rustc-cfg=featlike=\"set\"");
550 println!("cargo:rerun-if-changed=build.rs"); 494 println!("cargo:rerun-if-changed=build.rs");
551} 495}
552//- src/main.rs 496//- /src/main.rs
497#[rustc_builtin_macro] macro_rules! include {}
498#[rustc_builtin_macro] macro_rules! include_str {}
499#[rustc_builtin_macro] macro_rules! concat {}
500#[rustc_builtin_macro] macro_rules! env {}
501
553include!(concat!(env!("OUT_DIR"), "/hello.rs")); 502include!(concat!(env!("OUT_DIR"), "/hello.rs"));
554 503
555#[cfg(atom_cfg)] 504#[cfg(atom_cfg)]
@@ -564,10 +513,9 @@ struct B;
564fn main() { 513fn main() {
565 let va = A; 514 let va = A;
566 let vb = B; 515 let vb = B;
567 message(); 516 let should_be_str = message();
517 let another_str = include_str!("main.rs");
568} 518}
569
570fn main() { message(); }
571"###, 519"###,
572 ) 520 )
573 .with_config(|config| { 521 .with_config(|config| {
@@ -575,54 +523,43 @@ fn main() { message(); }
575 }) 523 })
576 .server(); 524 .server();
577 server.wait_until_workspace_is_loaded(); 525 server.wait_until_workspace_is_loaded();
578 let res = server.send_request::<GotoDefinition>(GotoDefinitionParams { 526 let res = server.send_request::<HoverRequest>(HoverParams {
527 text_document_position_params: TextDocumentPositionParams::new(
528 server.doc_id("src/main.rs"),
529 Position::new(19, 10),
530 ),
531 work_done_progress_params: Default::default(),
532 });
533 assert!(res.to_string().contains("&str"));
534 let res = server.send_request::<HoverRequest>(HoverParams {
579 text_document_position_params: TextDocumentPositionParams::new( 535 text_document_position_params: TextDocumentPositionParams::new(
580 server.doc_id("src/main.rs"), 536 server.doc_id("src/main.rs"),
581 Position::new(14, 8), 537 Position::new(20, 10),
582 ), 538 ),
583 work_done_progress_params: Default::default(), 539 work_done_progress_params: Default::default(),
584 partial_result_params: Default::default(),
585 }); 540 });
586 assert!(format!("{}", res).contains("hello.rs")); 541 assert!(res.to_string().contains("&str"));
587 server.request::<GotoTypeDefinition>( 542 server.request::<GotoTypeDefinition>(
588 GotoDefinitionParams { 543 GotoDefinitionParams {
589 text_document_position_params: TextDocumentPositionParams::new( 544 text_document_position_params: TextDocumentPositionParams::new(
590 server.doc_id("src/main.rs"), 545 server.doc_id("src/main.rs"),
591 Position::new(12, 9), 546 Position::new(17, 9),
592 ), 547 ),
593 work_done_progress_params: Default::default(), 548 work_done_progress_params: Default::default(),
594 partial_result_params: Default::default(), 549 partial_result_params: Default::default(),
595 }, 550 },
596 json!([{ 551 json!([{
597 "originSelectionRange": { 552 "originSelectionRange": {
598 "end": { 553 "end": { "character": 10, "line": 17 },
599 "character": 10, 554 "start": { "character": 8, "line": 17 }
600 "line": 12
601 },
602 "start": {
603 "character": 8,
604 "line": 12
605 }
606 }, 555 },
607 "targetRange": { 556 "targetRange": {
608 "end": { 557 "end": { "character": 9, "line": 8 },
609 "character": 9, 558 "start": { "character": 0, "line": 7 }
610 "line": 3
611 },
612 "start": {
613 "character": 0,
614 "line": 2
615 }
616 }, 559 },
617 "targetSelectionRange": { 560 "targetSelectionRange": {
618 "end": { 561 "end": { "character": 8, "line": 8 },
619 "character": 8, 562 "start": { "character": 7, "line": 8 }
620 "line": 3
621 },
622 "start": {
623 "character": 7,
624 "line": 3
625 }
626 }, 563 },
627 "targetUri": "file:///[..]src/main.rs" 564 "targetUri": "file:///[..]src/main.rs"
628 }]), 565 }]),
@@ -631,41 +568,23 @@ fn main() { message(); }
631 GotoDefinitionParams { 568 GotoDefinitionParams {
632 text_document_position_params: TextDocumentPositionParams::new( 569 text_document_position_params: TextDocumentPositionParams::new(
633 server.doc_id("src/main.rs"), 570 server.doc_id("src/main.rs"),
634 Position::new(13, 9), 571 Position::new(18, 9),
635 ), 572 ),
636 work_done_progress_params: Default::default(), 573 work_done_progress_params: Default::default(),
637 partial_result_params: Default::default(), 574 partial_result_params: Default::default(),
638 }, 575 },
639 json!([{ 576 json!([{
640 "originSelectionRange": { 577 "originSelectionRange": {
641 "end": { 578 "end": { "character": 10, "line": 18 },
642 "character": 10, 579 "start": { "character": 8, "line": 18 }
643 "line": 13
644 },
645 "start": {
646 "character": 8,
647 "line":13
648 }
649 }, 580 },
650 "targetRange": { 581 "targetRange": {
651 "end": { 582 "end": { "character": 9, "line": 12 },
652 "character": 9, 583 "start": { "character": 0, "line":11 }
653 "line": 7
654 },
655 "start": {
656 "character": 0,
657 "line":6
658 }
659 }, 584 },
660 "targetSelectionRange": { 585 "targetSelectionRange": {
661 "end": { 586 "end": { "character": 8, "line": 12 },
662 "character": 8, 587 "start": { "character": 7, "line": 12 }
663 "line": 7
664 },
665 "start": {
666 "character": 7,
667 "line": 7
668 }
669 }, 588 },
670 "targetUri": "file:///[..]src/main.rs" 589 "targetUri": "file:///[..]src/main.rs"
671 }]), 590 }]),
@@ -679,7 +598,7 @@ fn resolve_proc_macro() {
679 } 598 }
680 let server = Project::with_fixture( 599 let server = Project::with_fixture(
681 r###" 600 r###"
682//- foo/Cargo.toml 601//- /foo/Cargo.toml
683[package] 602[package]
684name = "foo" 603name = "foo"
685version = "0.0.0" 604version = "0.0.0"
@@ -687,7 +606,7 @@ edition = "2018"
687[dependencies] 606[dependencies]
688bar = {path = "../bar"} 607bar = {path = "../bar"}
689 608
690//- foo/src/main.rs 609//- /foo/src/main.rs
691use bar::Bar; 610use bar::Bar;
692trait Bar { 611trait Bar {
693 fn bar(); 612 fn bar();
@@ -698,7 +617,7 @@ fn main() {
698 Foo::bar(); 617 Foo::bar();
699} 618}
700 619
701//- bar/Cargo.toml 620//- /bar/Cargo.toml
702[package] 621[package]
703name = "bar" 622name = "bar"
704version = "0.0.0" 623version = "0.0.0"
@@ -707,7 +626,7 @@ edition = "2018"
707[lib] 626[lib]
708proc-macro = true 627proc-macro = true
709 628
710//- bar/src/lib.rs 629//- /bar/src/lib.rs
711extern crate proc_macro; 630extern crate proc_macro;
712use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; 631use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
713macro_rules! t { 632macro_rules! t {
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index 66a6f4d54..e51796d36 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -9,20 +9,19 @@ use std::{
9use crossbeam_channel::{after, select, Receiver}; 9use crossbeam_channel::{after, select, Receiver};
10use lsp_server::{Connection, Message, Notification, Request}; 10use lsp_server::{Connection, Message, Notification, Request};
11use lsp_types::{ 11use lsp_types::{
12 notification::{DidOpenTextDocument, Exit}, 12 notification::Exit, request::Shutdown, TextDocumentIdentifier, Url, WorkDoneProgress,
13 request::Shutdown,
14 DidOpenTextDocumentParams, TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress,
15}; 13};
16use lsp_types::{ProgressParams, ProgressParamsValue}; 14use lsp_types::{ProgressParams, ProgressParamsValue};
17use serde::Serialize; 15use ra_project_model::ProjectManifest;
18use serde_json::{to_string_pretty, Value};
19use tempfile::TempDir;
20use test_utils::{find_mismatch, parse_fixture};
21
22use rust_analyzer::{ 16use rust_analyzer::{
23 config::{ClientCapsConfig, Config}, 17 config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject},
24 main_loop, 18 main_loop,
25}; 19};
20use serde::Serialize;
21use serde_json::{to_string_pretty, Value};
22use tempfile::TempDir;
23use test_utils::{find_mismatch, Fixture};
24use vfs::AbsPathBuf;
26 25
27pub struct Project<'a> { 26pub struct Project<'a> {
28 fixture: &'a str, 27 fixture: &'a str,
@@ -42,7 +41,7 @@ impl<'a> Project<'a> {
42 self 41 self
43 } 42 }
44 43
45 pub fn root(mut self, path: &str) -> Project<'a> { 44 pub(crate) fn root(mut self, path: &str) -> Project<'a> {
46 self.roots.push(path.into()); 45 self.roots.push(path.into());
47 self 46 self
48 } 47 }
@@ -65,16 +64,23 @@ impl<'a> Project<'a> {
65 ra_prof::init_from(crate::PROFILE); 64 ra_prof::init_from(crate::PROFILE);
66 }); 65 });
67 66
68 let mut paths = vec![]; 67 for entry in Fixture::parse(self.fixture) {
69 68 let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]);
70 for entry in parse_fixture(self.fixture) {
71 let path = tmp_dir.path().join(entry.meta.path().as_str());
72 fs::create_dir_all(path.parent().unwrap()).unwrap(); 69 fs::create_dir_all(path.parent().unwrap()).unwrap();
73 fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); 70 fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
74 paths.push((path, entry.text));
75 } 71 }
76 72
77 let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); 73 let tmp_dir_path = AbsPathBuf::assert(tmp_dir.path().to_path_buf());
74 let mut roots =
75 self.roots.into_iter().map(|root| tmp_dir_path.join(root)).collect::<Vec<_>>();
76 if roots.is_empty() {
77 roots.push(tmp_dir_path.clone());
78 }
79 let linked_projects = roots
80 .into_iter()
81 .map(|it| ProjectManifest::discover_single(&it).unwrap())
82 .map(LinkedProject::from)
83 .collect::<Vec<_>>();
78 84
79 let mut config = Config { 85 let mut config = Config {
80 client_caps: ClientCapsConfig { 86 client_caps: ClientCapsConfig {
@@ -84,14 +90,15 @@ impl<'a> Project<'a> {
84 ..Default::default() 90 ..Default::default()
85 }, 91 },
86 with_sysroot: self.with_sysroot, 92 with_sysroot: self.with_sysroot,
87 ..Config::default() 93 linked_projects,
94 files: FilesConfig { watcher: FilesWatcher::Client, exclude: Vec::new() },
95 ..Config::new(tmp_dir_path)
88 }; 96 };
89
90 if let Some(f) = &self.config { 97 if let Some(f) = &self.config {
91 f(&mut config) 98 f(&mut config)
92 } 99 }
93 100
94 Server::new(tmp_dir, config, roots, paths) 101 Server::new(tmp_dir, config)
95 } 102 }
96} 103}
97 104
@@ -109,36 +116,15 @@ pub struct Server {
109} 116}
110 117
111impl Server { 118impl Server {
112 fn new( 119 fn new(dir: TempDir, config: Config) -> Server {
113 dir: TempDir,
114 config: Config,
115 roots: Vec<PathBuf>,
116 files: Vec<(PathBuf, String)>,
117 ) -> Server {
118 let path = dir.path().to_path_buf();
119
120 let roots = if roots.is_empty() { vec![path] } else { roots };
121 let (connection, client) = Connection::memory(); 120 let (connection, client) = Connection::memory();
122 121
123 let _thread = jod_thread::Builder::new() 122 let _thread = jod_thread::Builder::new()
124 .name("test server".to_string()) 123 .name("test server".to_string())
125 .spawn(move || main_loop(roots, config, connection).unwrap()) 124 .spawn(move || main_loop(config, connection).unwrap())
126 .expect("failed to spawn a thread"); 125 .expect("failed to spawn a thread");
127 126
128 let res = 127 Server { req_id: Cell::new(1), dir, messages: Default::default(), client, _thread }
129 Server { req_id: Cell::new(1), dir, messages: Default::default(), client, _thread };
130
131 for (path, text) in files {
132 res.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
133 text_document: TextDocumentItem {
134 uri: Url::from_file_path(path).unwrap(),
135 language_id: "rust".to_string(),
136 version: 0,
137 text,
138 },
139 })
140 }
141 res
142 } 128 }
143 129
144 pub fn doc_id(&self, rel_path: &str) -> TextDocumentIdentifier { 130 pub fn doc_id(&self, rel_path: &str) -> TextDocumentIdentifier {
@@ -188,8 +174,21 @@ impl Server {
188 self.client.sender.send(r.into()).unwrap(); 174 self.client.sender.send(r.into()).unwrap();
189 while let Some(msg) = self.recv() { 175 while let Some(msg) = self.recv() {
190 match msg { 176 match msg {
191 Message::Request(req) if req.method == "window/workDoneProgress/create" => (), 177 Message::Request(req) => {
192 Message::Request(req) => panic!("unexpected request: {:?}", req), 178 if req.method == "window/workDoneProgress/create" {
179 continue;
180 }
181 if req.method == "client/registerCapability" {
182 let params = req.params.to_string();
183 if ["workspace/didChangeWatchedFiles", "textDocument/didSave"]
184 .iter()
185 .any(|&it| params.contains(it))
186 {
187 continue;
188 }
189 }
190 panic!("unexpected request: {:?}", req)
191 }
193 Message::Notification(_) => (), 192 Message::Notification(_) => (),
194 Message::Response(res) => { 193 Message::Response(res) => {
195 assert_eq!(res.id, id); 194 assert_eq!(res.id, id);
@@ -209,7 +208,7 @@ impl Server {
209 ProgressParams { 208 ProgressParams {
210 token: lsp_types::ProgressToken::String(ref token), 209 token: lsp_types::ProgressToken::String(ref token),
211 value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)), 210 value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)),
212 } if token == "rustAnalyzer/startup" => true, 211 } if token == "rustAnalyzer/roots scanned" => true,
213 _ => false, 212 _ => false,
214 } 213 }
215 } 214 }
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml
index f9e380c10..4c0b85861 100644
--- a/crates/stdx/Cargo.toml
+++ b/crates/stdx/Cargo.toml
@@ -3,6 +3,7 @@ name = "stdx"
3version = "0.1.0" 3version = "0.1.0"
4authors = ["rust-analyzer developers"] 4authors = ["rust-analyzer developers"]
5edition = "2018" 5edition = "2018"
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 71a57fba2..988853ed2 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -1,31 +1,13 @@
1//! Missing batteries for standard libraries. 1//! Missing batteries for standard libraries.
2
3use std::{cell::Cell, fmt, time::Instant}; 2use std::{cell::Cell, fmt, time::Instant};
4 3
4mod macros;
5
5#[inline(always)] 6#[inline(always)]
6pub fn is_ci() -> bool { 7pub fn is_ci() -> bool {
7 option_env!("CI").is_some() 8 option_env!("CI").is_some()
8} 9}
9 10
10#[macro_export]
11macro_rules! eprintln {
12 ($($tt:tt)*) => {{
13 if $crate::is_ci() {
14 panic!("Forgot to remove debug-print?")
15 }
16 std::eprintln!($($tt)*)
17 }}
18}
19
20/// Appends formatted string to a `String`.
21#[macro_export]
22macro_rules! format_to {
23 ($buf:expr) => ();
24 ($buf:expr, $lit:literal $($arg:tt)*) => {
25 { use ::std::fmt::Write as _; let _ = ::std::write!($buf, $lit $($arg)*); }
26 };
27}
28
29pub trait SepBy: Sized { 11pub trait SepBy: Sized {
30 /// Returns an `impl fmt::Display`, which joins elements via a separator. 12 /// Returns an `impl fmt::Display`, which joins elements via a separator.
31 fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self>; 13 fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self>;
@@ -88,6 +70,8 @@ where
88 Ok(()) 70 Ok(())
89 } 71 }
90} 72}
73
74#[must_use]
91pub fn timeit(label: &'static str) -> impl Drop { 75pub fn timeit(label: &'static str) -> impl Drop {
92 struct Guard { 76 struct Guard {
93 label: &'static str, 77 label: &'static str,
@@ -124,3 +108,90 @@ pub fn replace(buf: &mut String, from: char, to: &str) {
124 // FIXME: do this in place. 108 // FIXME: do this in place.
125 *buf = buf.replace(from, to) 109 *buf = buf.replace(from, to)
126} 110}
111
112pub fn split_delim(haystack: &str, delim: char) -> Option<(&str, &str)> {
113 let idx = haystack.find(delim)?;
114 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
115}
116
117pub fn trim_indent(mut text: &str) -> String {
118 if text.starts_with('\n') {
119 text = &text[1..];
120 }
121 let indent = text
122 .lines()
123 .filter(|it| !it.trim().is_empty())
124 .map(|it| it.len() - it.trim_start().len())
125 .min()
126 .unwrap_or(0);
127 lines_with_ends(text)
128 .map(
129 |line| {
130 if line.len() <= indent {
131 line.trim_start_matches(' ')
132 } else {
133 &line[indent..]
134 }
135 },
136 )
137 .collect()
138}
139
140pub fn lines_with_ends(text: &str) -> LinesWithEnds {
141 LinesWithEnds { text }
142}
143
144pub struct LinesWithEnds<'a> {
145 text: &'a str,
146}
147
148impl<'a> Iterator for LinesWithEnds<'a> {
149 type Item = &'a str;
150 fn next(&mut self) -> Option<&'a str> {
151 if self.text.is_empty() {
152 return None;
153 }
154 let idx = self.text.find('\n').map_or(self.text.len(), |it| it + 1);
155 let (res, next) = self.text.split_at(idx);
156 self.text = next;
157 Some(res)
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn test_trim_indent() {
167 assert_eq!(trim_indent(""), "");
168 assert_eq!(
169 trim_indent(
170 "
171 hello
172 world
173"
174 ),
175 "hello\nworld\n"
176 );
177 assert_eq!(
178 trim_indent(
179 "
180 hello
181 world"
182 ),
183 "hello\nworld"
184 );
185 assert_eq!(trim_indent(" hello\n world\n"), "hello\nworld\n");
186 assert_eq!(
187 trim_indent(
188 "
189 fn main() {
190 return 92;
191 }
192 "
193 ),
194 "fn main() {\n return 92;\n}\n"
195 );
196 }
197}
diff --git a/crates/stdx/src/macros.rs b/crates/stdx/src/macros.rs
new file mode 100644
index 000000000..bf298460f
--- /dev/null
+++ b/crates/stdx/src/macros.rs
@@ -0,0 +1,40 @@
1//! Convenience macros.
2#[macro_export]
3macro_rules! eprintln {
4 ($($tt:tt)*) => {{
5 if $crate::is_ci() {
6 panic!("Forgot to remove debug-print?")
7 }
8 std::eprintln!($($tt)*)
9 }}
10}
11
12/// Appends formatted string to a `String`.
13#[macro_export]
14macro_rules! format_to {
15 ($buf:expr) => ();
16 ($buf:expr, $lit:literal $($arg:tt)*) => {
17 { use ::std::fmt::Write as _; let _ = ::std::write!($buf, $lit $($arg)*); }
18 };
19}
20
21// Generates `From` impls for `Enum E { Foo(Foo), Bar(Bar) }` enums
22#[macro_export]
23macro_rules! impl_from {
24 ($($variant:ident $(($($sub_variant:ident),*))?),* for $enum:ident) => {
25 $(
26 impl From<$variant> for $enum {
27 fn from(it: $variant) -> $enum {
28 $enum::$variant(it)
29 }
30 }
31 $($(
32 impl From<$sub_variant> for $enum {
33 fn from(it: $sub_variant) -> $enum {
34 $enum::$variant($variant::$sub_variant(it))
35 }
36 }
37 )*)?
38 )*
39 }
40}
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml
index 4d185b01c..e719f4f7c 100644
--- a/crates/test_utils/Cargo.toml
+++ b/crates/test_utils/Cargo.toml
@@ -3,15 +3,15 @@ edition = "2018"
3name = "test_utils" 3name = "test_utils"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6license = "MIT OR Apache-2.0"
6 7
7[lib] 8[lib]
8doctest = false 9doctest = false
9 10
10[dependencies] 11[dependencies]
12# Avoid adding deps here, this crate is widely used in tests it should compile fast!
11difference = "2.0.0" 13difference = "2.0.0"
12text-size = "1.0.0" 14text-size = "1.0.0"
13serde_json = "1.0.48" 15serde_json = "1.0.48"
14relative-path = "1.0.0"
15rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
16 17stdx = { path = "../stdx" }
17ra_cfg = { path = "../ra_cfg" } \ No newline at end of file
diff --git a/crates/test_utils/src/fixture.rs b/crates/test_utils/src/fixture.rs
new file mode 100644
index 000000000..ed764046b
--- /dev/null
+++ b/crates/test_utils/src/fixture.rs
@@ -0,0 +1,142 @@
1//! Defines `Fixture` -- a convenient way to describe the initial state of
2//! rust-analyzer database from a single string.
3
4use rustc_hash::FxHashMap;
5use stdx::{lines_with_ends, split_delim, trim_indent};
6
7#[derive(Debug, Eq, PartialEq)]
8pub struct Fixture {
9 pub path: String,
10 pub text: String,
11 pub krate: Option<String>,
12 pub deps: Vec<String>,
13 pub cfg_atoms: Vec<String>,
14 pub cfg_key_values: Vec<(String, String)>,
15 pub edition: Option<String>,
16 pub env: FxHashMap<String, String>,
17}
18
19impl Fixture {
20 /// Parses text which looks like this:
21 ///
22 /// ```not_rust
23 /// //- some meta
24 /// line 1
25 /// line 2
26 /// // - other meta
27 /// ```
28 pub fn parse(ra_fixture: &str) -> Vec<Fixture> {
29 let fixture = trim_indent(ra_fixture);
30
31 let mut res: Vec<Fixture> = Vec::new();
32
33 let default = if ra_fixture.contains("//-") { None } else { Some("//- /main.rs") };
34
35 for (ix, line) in default.into_iter().chain(lines_with_ends(&fixture)).enumerate() {
36 if line.contains("//-") {
37 assert!(
38 line.starts_with("//-"),
39 "Metadata line {} has invalid indentation. \
40 All metadata lines need to have the same indentation.\n\
41 The offending line: {:?}",
42 ix,
43 line
44 );
45 }
46
47 if line.starts_with("//-") {
48 let meta = Fixture::parse_meta_line(line);
49 res.push(meta)
50 } else if let Some(entry) = res.last_mut() {
51 entry.text.push_str(line);
52 }
53 }
54
55 res
56 }
57
58 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
59 fn parse_meta_line(meta: &str) -> Fixture {
60 assert!(meta.starts_with("//-"));
61 let meta = meta["//-".len()..].trim();
62 let components = meta.split_ascii_whitespace().collect::<Vec<_>>();
63
64 let path = components[0].to_string();
65 assert!(path.starts_with('/'));
66
67 let mut krate = None;
68 let mut deps = Vec::new();
69 let mut edition = None;
70 let mut cfg_atoms = Vec::new();
71 let mut cfg_key_values = Vec::new();
72 let mut env = FxHashMap::default();
73 for component in components[1..].iter() {
74 let (key, value) = split_delim(component, ':').unwrap();
75 match key {
76 "crate" => krate = Some(value.to_string()),
77 "deps" => deps = value.split(',').map(|it| it.to_string()).collect(),
78 "edition" => edition = Some(value.to_string()),
79 "cfg" => {
80 for entry in value.split(',') {
81 match split_delim(entry, '=') {
82 Some((k, v)) => cfg_key_values.push((k.to_string(), v.to_string())),
83 None => cfg_atoms.push(entry.to_string()),
84 }
85 }
86 }
87 "env" => {
88 for key in value.split(',') {
89 if let Some((k, v)) = split_delim(key, '=') {
90 env.insert(k.into(), v.into());
91 }
92 }
93 }
94 _ => panic!("bad component: {:?}", component),
95 }
96 }
97
98 Fixture {
99 path,
100 text: String::new(),
101 krate: krate,
102 deps,
103 cfg_atoms,
104 cfg_key_values,
105 edition,
106 env,
107 }
108 }
109}
110
111#[test]
112#[should_panic]
113fn parse_fixture_checks_further_indented_metadata() {
114 Fixture::parse(
115 r"
116 //- /lib.rs
117 mod bar;
118
119 fn foo() {}
120 //- /bar.rs
121 pub fn baz() {}
122 ",
123 );
124}
125
126#[test]
127fn parse_fixture_gets_full_meta() {
128 let parsed = Fixture::parse(
129 r"
130 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
131 mod m;
132 ",
133 );
134 assert_eq!(1, parsed.len());
135
136 let meta = &parsed[0];
137 assert_eq!("mod m;\n", meta.text);
138
139 assert_eq!("foo", meta.krate.as_ref().unwrap());
140 assert_eq!("/lib.rs", meta.path);
141 assert_eq!(2, meta.env.len());
142}
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 1bd97215c..ad586c882 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -8,20 +8,22 @@
8 8
9#[macro_use] 9#[macro_use]
10pub mod mark; 10pub mod mark;
11mod fixture;
11 12
12use std::{ 13use std::{
13 fs, 14 convert::{TryFrom, TryInto},
14 path::{Path, PathBuf}, 15 env, fs,
16 path::PathBuf,
15}; 17};
16 18
17pub use ra_cfg::CfgOptions;
18
19pub use relative_path::{RelativePath, RelativePathBuf};
20pub use rustc_hash::FxHashMap;
21use serde_json::Value; 19use serde_json::Value;
20use stdx::lines_with_ends;
22use text_size::{TextRange, TextSize}; 21use text_size::{TextRange, TextSize};
23 22
24pub use difference::Changeset as __Changeset; 23pub use difference::Changeset as __Changeset;
24pub use rustc_hash::FxHashMap;
25
26pub use crate::fixture::Fixture;
25 27
26pub const CURSOR_MARKER: &str = "<|>"; 28pub const CURSOR_MARKER: &str = "<|>";
27 29
@@ -43,7 +45,7 @@ macro_rules! assert_eq_text {
43 if left.trim() == right.trim() { 45 if left.trim() == right.trim() {
44 eprintln!("Left:\n{:?}\n\nRight:\n{:?}\n\nWhitespace difference\n", left, right); 46 eprintln!("Left:\n{:?}\n\nRight:\n{:?}\n\nWhitespace difference\n", left, right);
45 } else { 47 } else {
46 let changeset = $crate::__Changeset::new(right, left, "\n"); 48 let changeset = $crate::__Changeset::new(left, right, "\n");
47 eprintln!("Left:\n{}\n\nRight:\n{}\n\nDiff:\n{}\n", left, right, changeset); 49 eprintln!("Left:\n{}\n\nRight:\n{}\n\nDiff:\n{}\n", left, right, changeset);
48 } 50 }
49 eprintln!($($tt)*); 51 eprintln!($($tt)*);
@@ -97,7 +99,7 @@ impl From<RangeOrOffset> for TextRange {
97 fn from(selection: RangeOrOffset) -> Self { 99 fn from(selection: RangeOrOffset) -> Self {
98 match selection { 100 match selection {
99 RangeOrOffset::Range(it) => it, 101 RangeOrOffset::Range(it) => it,
100 RangeOrOffset::Offset(it) => TextRange::new(it, it), 102 RangeOrOffset::Offset(it) => TextRange::empty(it),
101 } 103 }
102 } 104 }
103} 105}
@@ -116,8 +118,8 @@ pub fn extract_range_or_offset(text: &str) -> (RangeOrOffset, String) {
116} 118}
117 119
118/// Extracts ranges, marked with `<tag> </tag>` pairs from the `text` 120/// Extracts ranges, marked with `<tag> </tag>` pairs from the `text`
119pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) { 121pub fn extract_tags(mut text: &str, tag: &str) -> (Vec<(TextRange, Option<String>)>, String) {
120 let open = format!("<{}>", tag); 122 let open = format!("<{}", tag);
121 let close = format!("</{}>", tag); 123 let close = format!("</{}>", tag);
122 let mut ranges = Vec::new(); 124 let mut ranges = Vec::new();
123 let mut res = String::new(); 125 let mut res = String::new();
@@ -132,22 +134,35 @@ pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) {
132 res.push_str(&text[..i]); 134 res.push_str(&text[..i]);
133 text = &text[i..]; 135 text = &text[i..];
134 if text.starts_with(&open) { 136 if text.starts_with(&open) {
135 text = &text[open.len()..]; 137 let close_open = text.find('>').unwrap();
138 let attr = text[open.len()..close_open].trim();
139 let attr = if attr.is_empty() { None } else { Some(attr.to_string()) };
140 text = &text[close_open + '>'.len_utf8()..];
136 let from = TextSize::of(&res); 141 let from = TextSize::of(&res);
137 stack.push(from); 142 stack.push((from, attr));
138 } else if text.starts_with(&close) { 143 } else if text.starts_with(&close) {
139 text = &text[close.len()..]; 144 text = &text[close.len()..];
140 let from = stack.pop().unwrap_or_else(|| panic!("unmatched </{}>", tag)); 145 let (from, attr) =
146 stack.pop().unwrap_or_else(|| panic!("unmatched </{}>", tag));
141 let to = TextSize::of(&res); 147 let to = TextSize::of(&res);
142 ranges.push(TextRange::new(from, to)); 148 ranges.push((TextRange::new(from, to), attr));
149 } else {
150 res.push('<');
151 text = &text['<'.len_utf8()..];
143 } 152 }
144 } 153 }
145 } 154 }
146 } 155 }
147 assert!(stack.is_empty(), "unmatched <{}>", tag); 156 assert!(stack.is_empty(), "unmatched <{}>", tag);
148 ranges.sort_by_key(|r| (r.start(), r.end())); 157 ranges.sort_by_key(|r| (r.0.start(), r.0.end()));
149 (ranges, res) 158 (ranges, res)
150} 159}
160#[test]
161fn test_extract_tags() {
162 let (tags, text) = extract_tags(r#"<tag fn>fn <tag>main</tag>() {}</tag>"#, "tag");
163 let actual = tags.into_iter().map(|(range, attr)| (&text[range], attr)).collect::<Vec<_>>();
164 assert_eq!(actual, vec![("fn main() {}", Some("fn".into())), ("main", None),]);
165}
151 166
152/// Inserts `<|>` marker into the `text` at `offset`. 167/// Inserts `<|>` marker into the `text` at `offset`.
153pub fn add_cursor(text: &str, offset: TextSize) -> String { 168pub fn add_cursor(text: &str, offset: TextSize) -> String {
@@ -159,294 +174,108 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String {
159 res 174 res
160} 175}
161 176
162#[derive(Debug, Eq, PartialEq)] 177/// Extracts `//^ some text` annotations
163pub struct FixtureEntry { 178pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
164 pub meta: FixtureMeta, 179 let mut res = Vec::new();
165 pub text: String, 180 let mut prev_line_start: Option<TextSize> = None;
166} 181 let mut line_start: TextSize = 0.into();
167 182 let mut prev_line_annotations: Vec<(TextSize, usize)> = Vec::new();
168#[derive(Debug, Eq, PartialEq)] 183 for line in lines_with_ends(text) {
169pub enum FixtureMeta { 184 let mut this_line_annotations = Vec::new();
170 Root { path: RelativePathBuf }, 185 if let Some(idx) = line.find("//") {
171 File(FileMeta), 186 let annotation_offset = TextSize::of(&line[..idx + "//".len()]);
172} 187 for annotation in extract_line_annotations(&line[idx + "//".len()..]) {
173 188 match annotation {
174#[derive(Debug, Eq, PartialEq)] 189 LineAnnotation::Annotation { mut range, content } => {
175pub struct FileMeta { 190 range += annotation_offset;
176 pub path: RelativePathBuf, 191 this_line_annotations.push((range.end(), res.len()));
177 pub crate_name: Option<String>, 192 res.push((range + prev_line_start.unwrap(), content))
178 pub deps: Vec<String>, 193 }
179 pub cfg: CfgOptions, 194 LineAnnotation::Continuation { mut offset, content } => {
180 pub edition: Option<String>, 195 offset += annotation_offset;
181 pub env: FxHashMap<String, String>, 196 let &(_, idx) = prev_line_annotations
182} 197 .iter()
183 198 .find(|&&(off, _idx)| off == offset)
184impl FixtureMeta { 199 .unwrap();
185 pub fn path(&self) -> &RelativePath { 200 res[idx].1.push('\n');
186 match self { 201 res[idx].1.push_str(&content);
187 FixtureMeta::Root { path } => &path, 202 res[idx].1.push('\n');
188 FixtureMeta::File(f) => &f.path, 203 }
189 }
190 }
191
192 pub fn crate_name(&self) -> Option<&String> {
193 match self {
194 FixtureMeta::File(f) => f.crate_name.as_ref(),
195 _ => None,
196 }
197 }
198
199 pub fn cfg_options(&self) -> Option<&CfgOptions> {
200 match self {
201 FixtureMeta::File(f) => Some(&f.cfg),
202 _ => None,
203 }
204 }
205
206 pub fn edition(&self) -> Option<&String> {
207 match self {
208 FixtureMeta::File(f) => f.edition.as_ref(),
209 _ => None,
210 }
211 }
212
213 pub fn env(&self) -> impl Iterator<Item = (&String, &String)> {
214 struct EnvIter<'a> {
215 iter: Option<std::collections::hash_map::Iter<'a, String, String>>,
216 }
217
218 impl<'a> EnvIter<'a> {
219 fn new(meta: &'a FixtureMeta) -> Self {
220 Self {
221 iter: match meta {
222 FixtureMeta::File(f) => Some(f.env.iter()),
223 _ => None,
224 },
225 } 204 }
226 } 205 }
227 } 206 }
228 207
229 impl<'a> Iterator for EnvIter<'a> { 208 prev_line_start = Some(line_start);
230 type Item = (&'a String, &'a String); 209 line_start += TextSize::of(line);
231 fn next(&mut self) -> Option<Self::Item> {
232 self.iter.as_mut().and_then(|i| i.next())
233 }
234 }
235
236 EnvIter::new(self)
237 }
238}
239 210
240/// Parses text which looks like this: 211 prev_line_annotations = this_line_annotations;
241///
242/// ```not_rust
243/// //- some meta
244/// line 1
245/// line 2
246/// // - other meta
247/// ```
248pub fn parse_fixture(ra_fixture: &str) -> Vec<FixtureEntry> {
249 let fixture = indent_first_line(ra_fixture);
250 let margin = fixture_margin(&fixture);
251
252 let mut lines = fixture
253 .split('\n') // don't use `.lines` to not drop `\r\n`
254 .enumerate()
255 .filter_map(|(ix, line)| {
256 if line.len() >= margin {
257 assert!(line[..margin].trim().is_empty());
258 let line_content = &line[margin..];
259 if !line_content.starts_with("//-") {
260 assert!(
261 !line_content.contains("//-"),
262 r#"Metadata line {} has invalid indentation. All metadata lines need to have the same indentation.
263The offending line: {:?}"#,
264 ix,
265 line
266 );
267 }
268 Some(line_content)
269 } else {
270 assert!(line.trim().is_empty());
271 None
272 }
273 });
274
275 let mut res: Vec<FixtureEntry> = Vec::new();
276 for line in lines.by_ref() {
277 if line.starts_with("//-") {
278 let meta = line["//-".len()..].trim().to_string();
279 let meta = parse_meta(&meta);
280 res.push(FixtureEntry { meta, text: String::new() })
281 } else if let Some(entry) = res.last_mut() {
282 entry.text.push_str(line);
283 entry.text.push('\n');
284 }
285 } 212 }
286 res 213 res
287} 214}
288 215
289//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo 216enum LineAnnotation {
290fn parse_meta(meta: &str) -> FixtureMeta { 217 Annotation { range: TextRange, content: String },
291 let components = meta.split_ascii_whitespace().collect::<Vec<_>>(); 218 Continuation { offset: TextSize, content: String },
292 219}
293 if components[0] == "root" {
294 let path: RelativePathBuf = components[1].into();
295 assert!(path.starts_with("/") && path.ends_with("/"));
296 return FixtureMeta::Root { path };
297 }
298 220
299 let path: RelativePathBuf = components[0].into(); 221fn extract_line_annotations(mut line: &str) -> Vec<LineAnnotation> {
300 assert!(path.starts_with("/")); 222 let mut res = Vec::new();
301 223 let mut offset: TextSize = 0.into();
302 let mut krate = None; 224 let marker: fn(char) -> bool = if line.contains('^') { |c| c == '^' } else { |c| c == '|' };
303 let mut deps = Vec::new(); 225 loop {
304 let mut edition = None; 226 match line.find(marker) {
305 let mut cfg = CfgOptions::default(); 227 Some(idx) => {
306 let mut env = FxHashMap::default(); 228 offset += TextSize::try_from(idx).unwrap();
307 for component in components[1..].iter() { 229 line = &line[idx..];
308 let (key, value) = split1(component, ':').unwrap();
309 match key {
310 "crate" => krate = Some(value.to_string()),
311 "deps" => deps = value.split(',').map(|it| it.to_string()).collect(),
312 "edition" => edition = Some(value.to_string()),
313 "cfg" => {
314 for key in value.split(',') {
315 match split1(key, '=') {
316 None => cfg.insert_atom(key.into()),
317 Some((k, v)) => cfg.insert_key_value(k.into(), v.into()),
318 }
319 }
320 }
321 "env" => {
322 for key in value.split(',') {
323 if let Some((k, v)) = split1(key, '=') {
324 env.insert(k.into(), v.into());
325 }
326 }
327 } 230 }
328 _ => panic!("bad component: {:?}", component), 231 None => break,
232 };
233
234 let mut len = line.chars().take_while(|&it| it == '^').count();
235 let mut continuation = false;
236 if len == 0 {
237 assert!(line.starts_with('|'));
238 continuation = true;
239 len = 1;
329 } 240 }
241 let range = TextRange::at(offset, len.try_into().unwrap());
242 let next = line[len..].find(marker).map_or(line.len(), |it| it + len);
243 let content = line[len..][..next - len].trim().to_string();
244
245 let annotation = if continuation {
246 LineAnnotation::Continuation { offset: range.end(), content }
247 } else {
248 LineAnnotation::Annotation { range, content }
249 };
250 res.push(annotation);
251
252 line = &line[next..];
253 offset += TextSize::try_from(next).unwrap();
330 } 254 }
331 255
332 FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env }) 256 res
333}
334
335fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
336 let idx = haystack.find(delim)?;
337 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
338}
339
340/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines.
341/// This allows fixtures to start off in a different indentation, e.g. to align the first line with
342/// the other lines visually:
343/// ```
344/// let fixture = "//- /lib.rs
345/// mod foo;
346/// //- /foo.rs
347/// fn bar() {}
348/// ";
349/// assert_eq!(fixture_margin(fixture),
350/// " //- /lib.rs
351/// mod foo;
352/// //- /foo.rs
353/// fn bar() {}
354/// ")
355/// ```
356fn indent_first_line(fixture: &str) -> String {
357 if fixture.is_empty() {
358 return String::new();
359 }
360 let mut lines = fixture.lines();
361 let first_line = lines.next().unwrap();
362 if first_line.contains("//-") {
363 let rest = lines.collect::<Vec<_>>().join("\n");
364 let fixed_margin = fixture_margin(&rest);
365 let fixed_indent = fixed_margin - indent_len(first_line);
366 format!("\n{}{}\n{}", " ".repeat(fixed_indent), first_line, rest)
367 } else {
368 fixture.to_owned()
369 }
370}
371
372fn fixture_margin(fixture: &str) -> usize {
373 fixture
374 .lines()
375 .filter(|it| it.trim_start().starts_with("//-"))
376 .map(indent_len)
377 .next()
378 .expect("empty fixture")
379}
380
381fn indent_len(s: &str) -> usize {
382 s.len() - s.trim_start().len()
383} 257}
384 258
385#[test] 259#[test]
386#[should_panic] 260fn test_extract_annotations() {
387fn parse_fixture_checks_further_indented_metadata() { 261 let text = stdx::trim_indent(
388 parse_fixture( 262 r#"
389 r" 263fn main() {
390 //- /lib.rs 264 let (x, y) = (9, 2);
391 mod bar; 265 //^ def ^ def
392 266 zoo + 1
393 fn foo() {} 267} //^^^ type:
394 //- /bar.rs 268 // | i32
395 pub fn baz() {} 269 "#,
396 ",
397 ); 270 );
398} 271 let res = extract_annotations(&text)
399 272 .into_iter()
400#[test] 273 .map(|(range, ann)| (&text[range], ann))
401fn parse_fixture_can_handle_dedented_first_line() { 274 .collect::<Vec<_>>();
402 let fixture = "//- /lib.rs
403 mod foo;
404 //- /foo.rs
405 struct Bar;
406";
407 assert_eq!( 275 assert_eq!(
408 parse_fixture(fixture), 276 res,
409 parse_fixture( 277 vec![("x", "def".into()), ("y", "def".into()), ("zoo", "type:\ni32\n".into()),]
410 "//- /lib.rs
411mod foo;
412//- /foo.rs
413struct Bar;
414"
415 )
416 )
417}
418
419#[test]
420fn parse_fixture_gets_full_meta() {
421 let parsed = parse_fixture(
422 r"
423 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
424 mod m;
425 ",
426 ); 278 );
427 assert_eq!(1, parsed.len());
428
429 let parsed = &parsed[0];
430 assert_eq!("mod m;\n\n", parsed.text);
431
432 let meta = &parsed.meta;
433 assert_eq!("foo", meta.crate_name().unwrap());
434 assert_eq!("/lib.rs", meta.path());
435 assert!(meta.cfg_options().is_some());
436 assert_eq!(2, meta.env().count());
437}
438
439/// Same as `parse_fixture`, except it allow empty fixture
440pub fn parse_single_fixture(ra_fixture: &str) -> Option<FixtureEntry> {
441 if !ra_fixture.lines().any(|it| it.trim_start().starts_with("//-")) {
442 return None;
443 }
444
445 let fixtures = parse_fixture(ra_fixture);
446 if fixtures.len() > 1 {
447 panic!("too many fixtures");
448 }
449 fixtures.into_iter().nth(0)
450} 279}
451 280
452// Comparison functionality borrowed from cargo: 281// Comparison functionality borrowed from cargo:
@@ -536,85 +365,6 @@ pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a
536 } 365 }
537} 366}
538 367
539/// Calls callback `f` with input code and file paths for each `.rs` file in `test_data_dir`
540/// subdirectories defined by `paths`.
541///
542/// If the content of the matching output file differs from the output of `f()`
543/// the test will fail.
544///
545/// If there is no matching output file it will be created and filled with the
546/// output of `f()`, but the test will fail.
547pub fn dir_tests<F>(test_data_dir: &Path, paths: &[&str], outfile_extension: &str, f: F)
548where
549 F: Fn(&str, &Path) -> String,
550{
551 for (path, input_code) in collect_rust_files(test_data_dir, paths) {
552 let actual = f(&input_code, &path);
553 let path = path.with_extension(outfile_extension);
554 if !path.exists() {
555 println!("\nfile: {}", path.display());
556 println!("No .txt file with expected result, creating...\n");
557 println!("{}\n{}", input_code, actual);
558 fs::write(&path, &actual).unwrap();
559 panic!("No expected result");
560 }
561 let expected = read_text(&path);
562 assert_equal_text(&expected, &actual, &path);
563 }
564}
565
566/// Collects all `.rs` files from `dir` subdirectories defined by `paths`.
567pub fn collect_rust_files(root_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, String)> {
568 paths
569 .iter()
570 .flat_map(|path| {
571 let path = root_dir.to_owned().join(path);
572 rust_files_in_dir(&path).into_iter()
573 })
574 .map(|path| {
575 let text = read_text(&path);
576 (path, text)
577 })
578 .collect()
579}
580
581/// Collects paths to all `.rs` files from `dir` in a sorted `Vec<PathBuf>`.
582fn rust_files_in_dir(dir: &Path) -> Vec<PathBuf> {
583 let mut acc = Vec::new();
584 for file in fs::read_dir(&dir).unwrap() {
585 let file = file.unwrap();
586 let path = file.path();
587 if path.extension().unwrap_or_default() == "rs" {
588 acc.push(path);
589 }
590 }
591 acc.sort();
592 acc
593}
594
595/// Returns the path to the root directory of `rust-analyzer` project.
596pub fn project_dir() -> PathBuf {
597 let dir = env!("CARGO_MANIFEST_DIR");
598 PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned()
599}
600
601/// Read file and normalize newlines.
602///
603/// `rustc` seems to always normalize `\r\n` newlines to `\n`:
604///
605/// ```
606/// let s = "
607/// ";
608/// assert_eq!(s.as_bytes(), &[10]);
609/// ```
610///
611/// so this should always be correct.
612pub fn read_text(path: &Path) -> String {
613 fs::read_to_string(path)
614 .unwrap_or_else(|_| panic!("File at {:?} should be valid", path))
615 .replace("\r\n", "\n")
616}
617
618/// Returns `false` if slow tests should not run, otherwise returns `true` and 368/// Returns `false` if slow tests should not run, otherwise returns `true` and
619/// also creates a file at `./target/.slow_tests_cookie` which serves as a flag 369/// also creates a file at `./target/.slow_tests_cookie` which serves as a flag
620/// that slow tests did run. 370/// that slow tests did run.
@@ -629,27 +379,8 @@ pub fn skip_slow_tests() -> bool {
629 should_skip 379 should_skip
630} 380}
631 381
632const REWRITE: bool = false; 382/// Returns the path to the root directory of `rust-analyzer` project.
633 383pub fn project_dir() -> PathBuf {
634/// Asserts that `expected` and `actual` strings are equal. If they differ only 384 let dir = env!("CARGO_MANIFEST_DIR");
635/// in trailing or leading whitespace the test won't fail and 385 PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned()
636/// the contents of `actual` will be written to the file located at `path`.
637fn assert_equal_text(expected: &str, actual: &str, path: &Path) {
638 if expected == actual {
639 return;
640 }
641 let dir = project_dir();
642 let pretty_path = path.strip_prefix(&dir).unwrap_or_else(|_| path);
643 if expected.trim() == actual.trim() {
644 println!("whitespace difference, rewriting");
645 println!("file: {}\n", pretty_path.display());
646 fs::write(path, actual).unwrap();
647 return;
648 }
649 if REWRITE {
650 println!("rewriting {}", pretty_path.display());
651 fs::write(path, actual).unwrap();
652 return;
653 }
654 assert_eq_text!(expected, actual, "file: {}", pretty_path.display());
655} 386}
diff --git a/crates/test_utils/src/mark.rs b/crates/test_utils/src/mark.rs
index 7c309a894..97f5a93ad 100644
--- a/crates/test_utils/src/mark.rs
+++ b/crates/test_utils/src/mark.rs
@@ -62,7 +62,7 @@ pub struct MarkChecker {
62 62
63impl MarkChecker { 63impl MarkChecker {
64 pub fn new(mark: &'static AtomicUsize) -> MarkChecker { 64 pub fn new(mark: &'static AtomicUsize) -> MarkChecker {
65 let value_on_entry = mark.load(Ordering::SeqCst); 65 let value_on_entry = mark.load(Ordering::Relaxed);
66 MarkChecker { mark, value_on_entry } 66 MarkChecker { mark, value_on_entry }
67 } 67 }
68} 68}
@@ -72,7 +72,7 @@ impl Drop for MarkChecker {
72 if std::thread::panicking() { 72 if std::thread::panicking() {
73 return; 73 return;
74 } 74 }
75 let value_on_exit = self.mark.load(Ordering::SeqCst); 75 let value_on_exit = self.mark.load(Ordering::Relaxed);
76 assert!(value_on_exit > self.value_on_entry, "mark was not hit") 76 assert!(value_on_exit > self.value_on_entry, "mark was not hit")
77 } 77 }
78} 78}
diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml
new file mode 100644
index 000000000..95c56ffa6
--- /dev/null
+++ b/crates/vfs-notify/Cargo.toml
@@ -0,0 +1,21 @@
1[package]
2name = "vfs-notify"
3version = "0.1.0"
4authors = ["rust-analyzer developers"]
5edition = "2018"
6license = "MIT OR Apache-2.0"
7
8[lib]
9doctest = false
10
11[dependencies]
12log = "0.4.8"
13rustc-hash = "1.0"
14jod-thread = "0.1.0"
15walkdir = "2.3.1"
16globset = "0.4.5"
17crossbeam-channel = "0.4.0"
18notify = "5.0.0-pre.3"
19
20vfs = { path = "../vfs" }
21paths = { path = "../paths" }
diff --git a/crates/vfs-notify/src/include.rs b/crates/vfs-notify/src/include.rs
new file mode 100644
index 000000000..59da3d77a
--- /dev/null
+++ b/crates/vfs-notify/src/include.rs
@@ -0,0 +1,42 @@
1//! See `Include`.
2use std::convert::TryFrom;
3
4use globset::{Glob, GlobSet, GlobSetBuilder};
5use paths::{RelPath, RelPathBuf};
6
7/// `Include` is the opposite of .gitignore.
8///
9/// It describes the set of files inside some directory.
10///
11/// The current implementation is very limited, it allows including file globs
12/// and recursively excluding directories.
13#[derive(Debug, Clone)]
14pub(crate) struct Include {
15 include_files: GlobSet,
16 exclude_dirs: Vec<RelPathBuf>,
17}
18
19impl Include {
20 pub(crate) fn new(include: Vec<String>) -> Include {
21 let mut include_files = GlobSetBuilder::new();
22 let mut exclude_dirs = Vec::new();
23
24 for glob in include {
25 if glob.starts_with("!/") {
26 if let Ok(path) = RelPathBuf::try_from(&glob["!/".len()..]) {
27 exclude_dirs.push(path)
28 }
29 } else {
30 include_files.add(Glob::new(&glob).unwrap());
31 }
32 }
33 let include_files = include_files.build().unwrap();
34 Include { include_files, exclude_dirs }
35 }
36 pub(crate) fn include_file(&self, path: &RelPath) -> bool {
37 self.include_files.is_match(path)
38 }
39 pub(crate) fn exclude_dir(&self, path: &RelPath) -> bool {
40 self.exclude_dirs.iter().any(|excluded| path.starts_with(excluded))
41 }
42}
diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs
new file mode 100644
index 000000000..6aaa53f63
--- /dev/null
+++ b/crates/vfs-notify/src/lib.rs
@@ -0,0 +1,230 @@
1//! An implementation of `loader::Handle`, based on `walkdir` and `notify`.
2//!
3//! The file watching bits here are untested and quite probably buggy. For this
4//! reason, by default we don't watch files and rely on editor's file watching
5//! capabilities.
6//!
7//! Hopefully, one day a reliable file watching/walking crate appears on
8//! crates.io, and we can reduce this to trivial glue code.
9mod include;
10
11use std::convert::{TryFrom, TryInto};
12
13use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
14use notify::{RecommendedWatcher, RecursiveMode, Watcher};
15use paths::{AbsPath, AbsPathBuf};
16use vfs::loader;
17use walkdir::WalkDir;
18
19use crate::include::Include;
20
21#[derive(Debug)]
22pub struct NotifyHandle {
23 // Relative order of fields below is significant.
24 sender: Sender<Message>,
25 thread: jod_thread::JoinHandle,
26}
27
28#[derive(Debug)]
29enum Message {
30 Config(loader::Config),
31 Invalidate(AbsPathBuf),
32}
33
34impl loader::Handle for NotifyHandle {
35 fn spawn(sender: loader::Sender) -> NotifyHandle {
36 let actor = NotifyActor::new(sender);
37 let (sender, receiver) = unbounded::<Message>();
38 let thread = jod_thread::spawn(move || actor.run(receiver));
39 NotifyHandle { sender, thread }
40 }
41 fn set_config(&mut self, config: loader::Config) {
42 self.sender.send(Message::Config(config)).unwrap()
43 }
44 fn invalidate(&mut self, path: AbsPathBuf) {
45 self.sender.send(Message::Invalidate(path)).unwrap();
46 }
47 fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>> {
48 read(path)
49 }
50}
51
52type NotifyEvent = notify::Result<notify::Event>;
53
54struct NotifyActor {
55 sender: loader::Sender,
56 config: Vec<(AbsPathBuf, Include, bool)>,
57 // Drop order is significant.
58 watcher: Option<(RecommendedWatcher, Receiver<NotifyEvent>)>,
59}
60
61#[derive(Debug)]
62enum Event {
63 Message(Message),
64 NotifyEvent(NotifyEvent),
65}
66
67impl NotifyActor {
68 fn new(sender: loader::Sender) -> NotifyActor {
69 NotifyActor { sender, config: Vec::new(), watcher: None }
70 }
71 fn next_event(&self, receiver: &Receiver<Message>) -> Option<Event> {
72 let watcher_receiver = self.watcher.as_ref().map(|(_, receiver)| receiver);
73 select! {
74 recv(receiver) -> it => it.ok().map(Event::Message),
75 recv(watcher_receiver.unwrap_or(&never())) -> it => Some(Event::NotifyEvent(it.unwrap())),
76 }
77 }
78 fn run(mut self, inbox: Receiver<Message>) {
79 while let Some(event) = self.next_event(&inbox) {
80 log::debug!("vfs-notify event: {:?}", event);
81 match event {
82 Event::Message(msg) => match msg {
83 Message::Config(config) => {
84 self.watcher = None;
85 if !config.watch.is_empty() {
86 let (watcher_sender, watcher_receiver) = unbounded();
87 let watcher = log_notify_error(Watcher::new_immediate(move |event| {
88 watcher_sender.send(event).unwrap()
89 }));
90 self.watcher = watcher.map(|it| (it, watcher_receiver));
91 }
92
93 let n_total = config.load.len();
94 self.send(loader::Message::Progress { n_total, n_done: 0 });
95
96 self.config.clear();
97
98 for (i, entry) in config.load.into_iter().enumerate() {
99 let watch = config.watch.contains(&i);
100 let files = self.load_entry(entry, watch);
101 self.send(loader::Message::Loaded { files });
102 self.send(loader::Message::Progress { n_total, n_done: i + 1 });
103 }
104 self.config.sort_by(|x, y| x.0.cmp(&y.0));
105 }
106 Message::Invalidate(path) => {
107 let contents = read(path.as_path());
108 let files = vec![(path, contents)];
109 self.send(loader::Message::Loaded { files });
110 }
111 },
112 Event::NotifyEvent(event) => {
113 if let Some(event) = log_notify_error(event) {
114 let files = event
115 .paths
116 .into_iter()
117 .map(|path| AbsPathBuf::try_from(path).unwrap())
118 .filter_map(|path| {
119 let is_dir = path.is_dir();
120 let is_file = path.is_file();
121
122 let config_idx =
123 match self.config.binary_search_by(|it| it.0.cmp(&path)) {
124 Ok(it) => it,
125 Err(it) => it.saturating_sub(1),
126 };
127 let include = self.config.get(config_idx).and_then(|it| {
128 let rel_path = path.strip_prefix(&it.0)?;
129 Some((rel_path, &it.1))
130 });
131
132 if let Some((rel_path, include)) = include {
133 if is_dir && include.exclude_dir(&rel_path)
134 || is_file && !include.include_file(&rel_path)
135 {
136 return None;
137 }
138 }
139
140 if is_dir {
141 self.watch(path);
142 return None;
143 }
144 if !is_file {
145 return None;
146 }
147 let contents = read(&path);
148 Some((path, contents))
149 })
150 .collect();
151 self.send(loader::Message::Loaded { files })
152 }
153 }
154 }
155 }
156 }
157 fn load_entry(
158 &mut self,
159 entry: loader::Entry,
160 watch: bool,
161 ) -> Vec<(AbsPathBuf, Option<Vec<u8>>)> {
162 match entry {
163 loader::Entry::Files(files) => files
164 .into_iter()
165 .map(|file| {
166 if watch {
167 self.watch(file.clone())
168 }
169 let contents = read(file.as_path());
170 (file, contents)
171 })
172 .collect::<Vec<_>>(),
173 loader::Entry::Directory { path, include } => {
174 let include = Include::new(include);
175 self.config.push((path.clone(), include.clone(), watch));
176
177 let files = WalkDir::new(&path)
178 .into_iter()
179 .filter_entry(|entry| {
180 let abs_path: &AbsPath = entry.path().try_into().unwrap();
181 match abs_path.strip_prefix(&path) {
182 Some(rel_path) => {
183 !(entry.file_type().is_dir() && include.exclude_dir(rel_path))
184 }
185 None => false,
186 }
187 })
188 .filter_map(|entry| entry.ok())
189 .filter_map(|entry| {
190 let is_dir = entry.file_type().is_dir();
191 let is_file = entry.file_type().is_file();
192 let abs_path = AbsPathBuf::try_from(entry.into_path()).unwrap();
193 if is_dir && watch {
194 self.watch(abs_path.clone());
195 }
196 let rel_path = abs_path.strip_prefix(&path)?;
197 if is_file && include.include_file(&rel_path) {
198 Some(abs_path)
199 } else {
200 None
201 }
202 });
203
204 files
205 .map(|file| {
206 let contents = read(file.as_path());
207 (file, contents)
208 })
209 .collect()
210 }
211 }
212 }
213
214 fn watch(&mut self, path: AbsPathBuf) {
215 if let Some((watcher, _)) = &mut self.watcher {
216 log_notify_error(watcher.watch(&path, RecursiveMode::NonRecursive));
217 }
218 }
219 fn send(&mut self, msg: loader::Message) {
220 (self.sender)(msg)
221 }
222}
223
224fn read(path: &AbsPath) -> Option<Vec<u8>> {
225 std::fs::read(path).ok()
226}
227
228fn log_notify_error<T>(res: notify::Result<T>) -> Option<T> {
229 res.map_err(|err| log::warn!("notify error: {}", err)).ok()
230}
diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml
new file mode 100644
index 000000000..b74cdb7ff
--- /dev/null
+++ b/crates/vfs/Cargo.toml
@@ -0,0 +1,15 @@
1[package]
2name = "vfs"
3version = "0.1.0"
4authors = ["rust-analyzer developers"]
5edition = "2018"
6license = "MIT OR Apache-2.0"
7
8[lib]
9doctest = false
10
11[dependencies]
12rustc-hash = "1.0"
13fst = "0.4"
14
15paths = { path = "../paths" }
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
new file mode 100644
index 000000000..e5e2ef530
--- /dev/null
+++ b/crates/vfs/src/file_set.rs
@@ -0,0 +1,172 @@
1//! Partitions a list of files into disjoint subsets.
2//!
3//! Files which do not belong to any explicitly configured `FileSet` belong to
4//! the default `FileSet`.
5use std::fmt;
6
7use fst::{IntoStreamer, Streamer};
8use rustc_hash::FxHashMap;
9
10use crate::{FileId, Vfs, VfsPath};
11
12#[derive(Default, Clone, Eq, PartialEq)]
13pub struct FileSet {
14 files: FxHashMap<VfsPath, FileId>,
15 paths: FxHashMap<FileId, VfsPath>,
16}
17
18impl FileSet {
19 pub fn len(&self) -> usize {
20 self.files.len()
21 }
22 pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
23 let mut base = self.paths[&anchor].clone();
24 base.pop();
25 let path = base.join(path)?;
26 let res = self.files.get(&path).copied();
27 res
28 }
29 pub fn insert(&mut self, file_id: FileId, path: VfsPath) {
30 self.files.insert(path.clone(), file_id);
31 self.paths.insert(file_id, path);
32 }
33 pub fn iter(&self) -> impl Iterator<Item = FileId> + '_ {
34 self.paths.keys().copied()
35 }
36}
37
38impl fmt::Debug for FileSet {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 f.debug_struct("FileSet").field("n_files", &self.files.len()).finish()
41 }
42}
43
44#[derive(Debug)]
45pub struct FileSetConfig {
46 n_file_sets: usize,
47 map: fst::Map<Vec<u8>>,
48}
49
50impl Default for FileSetConfig {
51 fn default() -> Self {
52 FileSetConfig::builder().build()
53 }
54}
55
56impl FileSetConfig {
57 pub fn builder() -> FileSetConfigBuilder {
58 FileSetConfigBuilder::default()
59 }
60 pub fn partition(&self, vfs: &Vfs) -> Vec<FileSet> {
61 let mut scratch_space = Vec::new();
62 let mut res = vec![FileSet::default(); self.len()];
63 for (file_id, path) in vfs.iter() {
64 let root = self.classify(&path, &mut scratch_space);
65 res[root].insert(file_id, path.clone())
66 }
67 res
68 }
69 fn len(&self) -> usize {
70 self.n_file_sets
71 }
72 fn classify(&self, path: &VfsPath, scratch_space: &mut Vec<u8>) -> usize {
73 scratch_space.clear();
74 path.encode(scratch_space);
75 let automaton = PrefixOf::new(scratch_space.as_slice());
76 let mut longest_prefix = self.len() - 1;
77 let mut stream = self.map.search(automaton).into_stream();
78 while let Some((_, v)) = stream.next() {
79 longest_prefix = v as usize;
80 }
81 longest_prefix
82 }
83}
84
85pub struct FileSetConfigBuilder {
86 roots: Vec<Vec<VfsPath>>,
87}
88
89impl Default for FileSetConfigBuilder {
90 fn default() -> Self {
91 FileSetConfigBuilder { roots: Vec::new() }
92 }
93}
94
95impl FileSetConfigBuilder {
96 pub fn len(&self) -> usize {
97 self.roots.len()
98 }
99 pub fn add_file_set(&mut self, roots: Vec<VfsPath>) {
100 self.roots.push(roots)
101 }
102 pub fn build(self) -> FileSetConfig {
103 let n_file_sets = self.roots.len() + 1;
104 let map = {
105 let mut entries = Vec::new();
106 for (i, paths) in self.roots.into_iter().enumerate() {
107 for p in paths {
108 let mut buf = Vec::new();
109 p.encode(&mut buf);
110 entries.push((buf, i as u64));
111 }
112 }
113 entries.sort();
114 entries.dedup_by(|(a, _), (b, _)| a == b);
115 fst::Map::from_iter(entries).unwrap()
116 };
117 FileSetConfig { n_file_sets, map }
118 }
119}
120
121struct PrefixOf<'a> {
122 prefix_of: &'a [u8],
123}
124
125impl<'a> PrefixOf<'a> {
126 fn new(prefix_of: &'a [u8]) -> Self {
127 Self { prefix_of }
128 }
129}
130
131impl fst::Automaton for PrefixOf<'_> {
132 type State = usize;
133 fn start(&self) -> usize {
134 0
135 }
136 fn is_match(&self, &state: &usize) -> bool {
137 state != !0
138 }
139 fn can_match(&self, &state: &usize) -> bool {
140 state != !0
141 }
142 fn accept(&self, &state: &usize, byte: u8) -> usize {
143 if self.prefix_of.get(state) == Some(&byte) {
144 state + 1
145 } else {
146 !0
147 }
148 }
149}
150
151#[test]
152fn test_partitioning() {
153 let mut file_set = FileSetConfig::builder();
154 file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo".into())]);
155 file_set.add_file_set(vec![VfsPath::new_virtual_path("/foo/bar/baz".into())]);
156 let file_set = file_set.build();
157
158 let mut vfs = Vfs::default();
159 vfs.set_file_contents(VfsPath::new_virtual_path("/foo/src/lib.rs".into()), Some(Vec::new()));
160 vfs.set_file_contents(
161 VfsPath::new_virtual_path("/foo/src/bar/baz/lib.rs".into()),
162 Some(Vec::new()),
163 );
164 vfs.set_file_contents(
165 VfsPath::new_virtual_path("/foo/bar/baz/lib.rs".into()),
166 Some(Vec::new()),
167 );
168 vfs.set_file_contents(VfsPath::new_virtual_path("/quux/lib.rs".into()), Some(Vec::new()));
169
170 let partition = file_set.partition(&vfs).into_iter().map(|it| it.len()).collect::<Vec<_>>();
171 assert_eq!(partition, vec![2, 1, 1]);
172}
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
new file mode 100644
index 000000000..cdf6f1fd0
--- /dev/null
+++ b/crates/vfs/src/lib.rs
@@ -0,0 +1,140 @@
1//! # Virtual File System
2//!
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
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
7//! triggers incremental recomputation.
8//!
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
11//! as an `std::path::PathBuf` internally, but this is an implementation detail.
12//!
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
15//! loading and file watching. `Handle` is dynamically configured with a set of
16//! directory entries which should be scanned and watched. `Handle` then
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
19//! in any specific way.
20//!
21//! A simple `WalkdirLoaderHandle` is provided, which doesn't implement watching
22//! and just scans the directory using walkdir.
23//!
24//! VFS stores a flat list of files. `FileSet` can partition this list of files
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.
29//!
30//! `file_set::FileSet` and `loader::Entry` play similar, but different roles.
31//! 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`
33//! may correspond to several `loader::Entry`. For example, a crate from
34//! 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
36//! have a single `FileSet` which unions the two sources.
37mod vfs_path;
38mod path_interner;
39pub mod file_set;
40pub mod loader;
41
42use std::{fmt, mem};
43
44use crate::path_interner::PathInterner;
45
46pub use crate::vfs_path::VfsPath;
47pub use paths::{AbsPath, AbsPathBuf};
48
49#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
50pub struct FileId(pub u32);
51
52#[derive(Default)]
53pub struct Vfs {
54 interner: PathInterner,
55 data: Vec<Option<Vec<u8>>>,
56 changes: Vec<ChangedFile>,
57}
58
59pub struct ChangedFile {
60 pub file_id: FileId,
61 pub change_kind: ChangeKind,
62}
63
64impl ChangedFile {
65 pub fn exists(&self) -> bool {
66 self.change_kind != ChangeKind::Delete
67 }
68 pub fn is_created_or_deleted(&self) -> bool {
69 matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete)
70 }
71}
72
73#[derive(Eq, PartialEq, Copy, Clone, Debug)]
74pub enum ChangeKind {
75 Create,
76 Modify,
77 Delete,
78}
79
80impl Vfs {
81 pub fn len(&self) -> usize {
82 self.data.len()
83 }
84 pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
85 self.interner.get(path).filter(|&it| self.get(it).is_some())
86 }
87 pub fn file_path(&self, file_id: FileId) -> VfsPath {
88 self.interner.lookup(file_id).clone()
89 }
90 pub fn file_contents(&self, file_id: FileId) -> &[u8] {
91 self.get(file_id).as_deref().unwrap()
92 }
93 pub fn iter(&self) -> impl Iterator<Item = (FileId, &VfsPath)> + '_ {
94 (0..self.data.len())
95 .map(|it| FileId(it as u32))
96 .filter(move |&file_id| self.get(file_id).is_some())
97 .map(move |file_id| {
98 let path = self.interner.lookup(file_id);
99 (file_id, path)
100 })
101 }
102 pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) {
103 let file_id = self.alloc_file_id(path);
104 let change_kind = match (&self.get(file_id), &contents) {
105 (None, None) => return,
106 (None, Some(_)) => ChangeKind::Create,
107 (Some(_), None) => ChangeKind::Delete,
108 (Some(old), Some(new)) if old == new => return,
109 (Some(_), Some(_)) => ChangeKind::Modify,
110 };
111
112 *self.get_mut(file_id) = contents;
113 self.changes.push(ChangedFile { file_id, change_kind })
114 }
115 pub fn has_changes(&self) -> bool {
116 !self.changes.is_empty()
117 }
118 pub fn take_changes(&mut self) -> Vec<ChangedFile> {
119 mem::take(&mut self.changes)
120 }
121 fn alloc_file_id(&mut self, path: VfsPath) -> FileId {
122 let file_id = self.interner.intern(path);
123 let idx = file_id.0 as usize;
124 let len = self.data.len().max(idx + 1);
125 self.data.resize_with(len, || None);
126 file_id
127 }
128 fn get(&self, file_id: FileId) -> &Option<Vec<u8>> {
129 &self.data[file_id.0 as usize]
130 }
131 fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> {
132 &mut self.data[file_id.0 as usize]
133 }
134}
135
136impl fmt::Debug for Vfs {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 f.debug_struct("Vfs").field("n_files", &self.data.len()).finish()
139 }
140}
diff --git a/crates/vfs/src/loader.rs b/crates/vfs/src/loader.rs
new file mode 100644
index 000000000..6de2e5b3f
--- /dev/null
+++ b/crates/vfs/src/loader.rs
@@ -0,0 +1,71 @@
1//! Object safe interface for file watching and reading.
2use std::fmt;
3
4use paths::{AbsPath, AbsPathBuf};
5
6#[derive(Debug)]
7pub enum Entry {
8 Files(Vec<AbsPathBuf>),
9 Directory { path: AbsPathBuf, include: Vec<String> },
10}
11
12#[derive(Debug)]
13pub struct Config {
14 pub load: Vec<Entry>,
15 pub watch: Vec<usize>,
16}
17
18pub enum Message {
19 Progress { n_total: usize, n_done: usize },
20 Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> },
21}
22
23pub type Sender = Box<dyn Fn(Message) + Send>;
24
25pub trait Handle: fmt::Debug {
26 fn spawn(sender: Sender) -> Self
27 where
28 Self: Sized;
29 fn set_config(&mut self, config: Config);
30 fn invalidate(&mut self, path: AbsPathBuf);
31 fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>;
32}
33
34impl Entry {
35 pub fn rs_files_recursively(base: AbsPathBuf) -> Entry {
36 Entry::Directory { path: base, include: globs(&["*.rs", "!/.git/"]) }
37 }
38 pub fn local_cargo_package(base: AbsPathBuf) -> Entry {
39 Entry::Directory { path: base, include: globs(&["*.rs", "!/target/", "!/.git/"]) }
40 }
41 pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry {
42 Entry::Directory {
43 path: base,
44 include: globs(&["*.rs", "!/tests/", "!/examples/", "!/benches/", "!/.git/"]),
45 }
46 }
47}
48
49fn globs(globs: &[&str]) -> Vec<String> {
50 globs.iter().map(|it| it.to_string()).collect()
51}
52
53impl fmt::Debug for Message {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 Message::Loaded { files } => {
57 f.debug_struct("Loaded").field("n_files", &files.len()).finish()
58 }
59 Message::Progress { n_total, n_done } => f
60 .debug_struct("Progress")
61 .field("n_total", n_total)
62 .field("n_done", n_done)
63 .finish(),
64 }
65 }
66}
67
68#[test]
69fn handle_is_object_safe() {
70 fn _assert(_: &dyn Handle) {}
71}
diff --git a/crates/vfs/src/path_interner.rs b/crates/vfs/src/path_interner.rs
new file mode 100644
index 000000000..4f70d61e8
--- /dev/null
+++ b/crates/vfs/src/path_interner.rs
@@ -0,0 +1,31 @@
1//! Maps paths to compact integer ids. We don't care about clearings paths which
2//! no longer exist -- the assumption is total size of paths we ever look at is
3//! not too big.
4use rustc_hash::FxHashMap;
5
6use crate::{FileId, VfsPath};
7
8#[derive(Default)]
9pub(crate) struct PathInterner {
10 map: FxHashMap<VfsPath, FileId>,
11 vec: Vec<VfsPath>,
12}
13
14impl PathInterner {
15 pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> {
16 self.map.get(path).copied()
17 }
18 pub(crate) fn intern(&mut self, path: VfsPath) -> FileId {
19 if let Some(id) = self.get(&path) {
20 return id;
21 }
22 let id = FileId(self.vec.len() as u32);
23 self.map.insert(path.clone(), id);
24 self.vec.push(path);
25 id
26 }
27
28 pub(crate) fn lookup(&self, id: FileId) -> &VfsPath {
29 &self.vec[id.0 as usize]
30 }
31}
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs
new file mode 100644
index 000000000..04a42264e
--- /dev/null
+++ b/crates/vfs/src/vfs_path.rs
@@ -0,0 +1,146 @@
1//! Abstract-ish representation of paths for VFS.
2use std::fmt;
3
4use paths::{AbsPath, AbsPathBuf};
5
6/// Long-term, we want to support files which do not reside in the file-system,
7/// so we treat VfsPaths as opaque identifiers.
8#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
9pub struct VfsPath(VfsPathRepr);
10
11impl VfsPath {
12 /// Creates an "in-memory" path from `/`-separates string.
13 /// This is most useful for testing, to avoid windows/linux differences
14 pub fn new_virtual_path(path: String) -> VfsPath {
15 assert!(path.starts_with('/'));
16 VfsPath(VfsPathRepr::VirtualPath(VirtualPath(path)))
17 }
18
19 pub fn as_path(&self) -> Option<&AbsPath> {
20 match &self.0 {
21 VfsPathRepr::PathBuf(it) => Some(it.as_path()),
22 VfsPathRepr::VirtualPath(_) => None,
23 }
24 }
25 pub fn join(&self, path: &str) -> Option<VfsPath> {
26 match &self.0 {
27 VfsPathRepr::PathBuf(it) => {
28 let res = it.join(path).normalize();
29 Some(VfsPath(VfsPathRepr::PathBuf(res)))
30 }
31 VfsPathRepr::VirtualPath(it) => {
32 let res = it.join(path)?;
33 Some(VfsPath(VfsPathRepr::VirtualPath(res)))
34 }
35 }
36 }
37 pub fn pop(&mut self) -> bool {
38 match &mut self.0 {
39 VfsPathRepr::PathBuf(it) => it.pop(),
40 VfsPathRepr::VirtualPath(it) => it.pop(),
41 }
42 }
43 pub fn starts_with(&self, other: &VfsPath) -> bool {
44 match (&self.0, &other.0) {
45 (VfsPathRepr::PathBuf(lhs), VfsPathRepr::PathBuf(rhs)) => lhs.starts_with(rhs),
46 (VfsPathRepr::PathBuf(_), _) => false,
47 (VfsPathRepr::VirtualPath(lhs), VfsPathRepr::VirtualPath(rhs)) => lhs.starts_with(rhs),
48 (VfsPathRepr::VirtualPath(_), _) => false,
49 }
50 }
51
52 // Don't make this `pub`
53 pub(crate) fn encode(&self, buf: &mut Vec<u8>) {
54 let tag = match &self.0 {
55 VfsPathRepr::PathBuf(_) => 0,
56 VfsPathRepr::VirtualPath(_) => 1,
57 };
58 buf.push(tag);
59 match &self.0 {
60 VfsPathRepr::PathBuf(it) => {
61 let path: &std::ffi::OsStr = it.as_os_str();
62 #[cfg(windows)]
63 {
64 use std::os::windows::ffi::OsStrExt;
65 for wchar in path.encode_wide() {
66 buf.extend(wchar.to_le_bytes().iter().copied());
67 }
68 }
69 #[cfg(unix)]
70 {
71 use std::os::unix::ffi::OsStrExt;
72 buf.extend(path.as_bytes());
73 }
74 #[cfg(not(any(windows, unix)))]
75 {
76 buf.extend(path.to_string_lossy().as_bytes());
77 }
78 }
79 VfsPathRepr::VirtualPath(VirtualPath(s)) => buf.extend(s.as_bytes()),
80 }
81 }
82}
83
84#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
85enum VfsPathRepr {
86 PathBuf(AbsPathBuf),
87 VirtualPath(VirtualPath),
88}
89
90impl From<AbsPathBuf> for VfsPath {
91 fn from(v: AbsPathBuf) -> Self {
92 VfsPath(VfsPathRepr::PathBuf(v.normalize()))
93 }
94}
95
96impl fmt::Display for VfsPath {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 match &self.0 {
99 VfsPathRepr::PathBuf(it) => fmt::Display::fmt(&it.display(), f),
100 VfsPathRepr::VirtualPath(VirtualPath(it)) => fmt::Display::fmt(it, f),
101 }
102 }
103}
104
105impl fmt::Debug for VfsPath {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 fmt::Debug::fmt(&self.0, f)
108 }
109}
110
111impl fmt::Debug for VfsPathRepr {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 match &self {
114 VfsPathRepr::PathBuf(it) => fmt::Debug::fmt(&it.display(), f),
115 VfsPathRepr::VirtualPath(VirtualPath(it)) => fmt::Debug::fmt(&it, f),
116 }
117 }
118}
119
120#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
121struct VirtualPath(String);
122
123impl VirtualPath {
124 fn starts_with(&self, other: &VirtualPath) -> bool {
125 self.0.starts_with(&other.0)
126 }
127 fn pop(&mut self) -> bool {
128 let pos = match self.0.rfind('/') {
129 Some(pos) => pos,
130 None => return false,
131 };
132 self.0 = self.0[..pos].to_string();
133 true
134 }
135 fn join(&self, mut path: &str) -> Option<VirtualPath> {
136 let mut res = self.clone();
137 while path.starts_with("../") {
138 if !res.pop() {
139 return None;
140 }
141 path = &path["../".len()..]
142 }
143 res.0 = format!("{}/{}", res.0, path);
144 Some(res)
145 }
146}